diff --git a/README.md b/README.md index 66b77faf1..f2f32157b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,21 @@ You can also import existing [Amazon Bedrock's KnowledgeBase](https://aws.amazon > [!Important] > For governance reasons, only allowed users are able to create customized bots. To allow the creation of customized bots, the user must be a member of group called `CreatingBotAllowed`, which can be set up via the management console > Amazon Cognito User pools or aws cli. Note that the user pool id can be referred by accessing CloudFormation > BedrockChatStack > Outputs > `AuthUserPoolIdxxxx`. +### Multi-Tenant Usage of Knowledge Base + +In Amazon Bedrock Knowledge Bases, by default, the number of Knowledge Bases that can be created in a single AWS account is limited to 100. To work around this limitation, you can use 'multi-tenant' mode, where a Knowledge Base with common settings is shared among multiple bots, and files uploaded by each bot are filtered by attaching the Bot ID as metadata. + +Newly created bots will have multi-tenant mode enabled by default. To migrate existing bots to multi-tenant mode, change the bot's knowledge settings to "Create a tenant in a shared Knowledge Base." + +To migrate multiple bots to multi-tenant mode in bulk, execute commands like the following: + +```bash +aws dynamodb execute-statement --statement "UPDATE \"$BotTableNameV3\" SET BedrockKnowledgeBase.type='shared' SET SyncStatus='QUEUED' WHERE PK='$UserID' AND SK='BOT#$BotID'" +# Execute for all target bots + +aws stepfunctions start-execution --state-machine-arn $EmbeddingStateMachineArn +``` + ### Administrative features API Management, Mark bots as essential, Analyze usage for bots. [detail](./docs/ADMINISTRATOR.md) @@ -194,7 +209,7 @@ It's an architecture built on AWS managed services, eliminating the need for inf - [Amazon Cognito](https://aws.amazon.com/cognito/): User authentication - [Amazon Bedrock](https://aws.amazon.com/bedrock/): Managed service to utilize foundational models via APIs - [Amazon Bedrock Knowledge Bases](https://aws.amazon.com/bedrock/knowledge-bases/): Provides a managed interface for Retrieval-Augmented Generation ([RAG](https://aws.amazon.com/what-is/retrieval-augmented-generation/)), offering services for embedding and parsing documents -- [Amazon EventBridge Pipes](https://aws.amazon.com/eventbridge/pipes/): Receiving event from DynamoDB stream and launching Step Functions to embed external knowledge +- [Amazon EventBridge Pipes](https://aws.amazon.com/eventbridge/pipes/): Receiving deletion event of bots from DynamoDB stream and delete CloudFormation stack related to the bot - [AWS Step Functions](https://aws.amazon.com/step-functions/): Orchestrating ingestion pipeline to embed external knowledge into Bedrock Knowledge Bases - [Amazon OpenSearch Serverless](https://aws.amazon.com/opensearch-service/features/serverless/): Serves as the backend database for Bedrock Knowledge Bases, providing full-text search and vector search capabilities, enabling accurate retrieval of relevant information - [Amazon Athena](https://aws.amazon.com/athena/): Query service to analyze S3 bucket diff --git a/backend/app/repositories/custom_bot.py b/backend/app/repositories/custom_bot.py index 84cc2af59..972dae9db 100644 --- a/backend/app/repositories/custom_bot.py +++ b/backend/app/repositories/custom_bot.py @@ -1,13 +1,8 @@ import base64 import json import logging -import os -from datetime import datetime from decimal import Decimal as decimal -from typing import Union -import boto3 -from app.config import DEFAULT_GENERATION_CONFIG from app.repositories.common import ( TRANSACTION_BATCH_READ_SIZE, RecordNotFoundError, @@ -26,8 +21,6 @@ ConversationQuickStarterModel, GenerationParamsModel, KnowledgeModel, - UsageStatsModel, - default_active_models, ) from app.repositories.models.custom_bot_guardrails import BedrockGuardrailsModel from app.repositories.models.custom_bot_kb import BedrockKnowledgeBaseModel @@ -96,6 +89,7 @@ def store_bot(custom_bot: BotModel): item["IsStarred"] = "TRUE" if custom_bot.bedrock_knowledge_base: item["BedrockKnowledgeBase"] = custom_bot.bedrock_knowledge_base.model_dump() + if custom_bot.bedrock_guardrails: item["GuardrailsParams"] = custom_bot.bedrock_guardrails.model_dump() @@ -160,12 +154,31 @@ def update_bot( ":active_models": active_models.model_dump(), # type: ignore[attr-defined] } if bedrock_knowledge_base: + if bedrock_knowledge_base.exist_knowledge_base_id is not None or ( + len(knowledge.source_urls) == 0 + and len(knowledge.sitemap_urls) == 0 + and len(knowledge.filenames) == 0 + and len(knowledge.s3_urls) == 0 + ): + # Clear Knowledge Base ID if the Knowledge Base is not needed. + bedrock_knowledge_base.type = None + bedrock_knowledge_base.knowledge_base_id = None + + elif bedrock_knowledge_base.type is None: + # Otherwise, if the type of Knowledge Base is omitted, it will default to `dedicated`. + bedrock_knowledge_base.type = "dedicated" + update_expression += ", BedrockKnowledgeBase = :bedrock_knowledge_base" expression_attribute_values[":bedrock_knowledge_base"] = ( bedrock_knowledge_base.model_dump() ) if bedrock_guardrails: + if not bedrock_guardrails.is_guardrail_enabled: + # Clear Guardrail ARN if the Guardrail is not needed. + bedrock_guardrails.guardrail_arn = "" + bedrock_guardrails.guardrail_version = "" + update_expression += ", GuardrailsParams = :bedrock_guardrails" expression_attribute_values[":bedrock_guardrails"] = ( bedrock_guardrails.model_dump() @@ -336,7 +349,10 @@ def update_alias_star_status(user_id: str, original_bot_id: str, starred: bool): def update_knowledge_base_id( - user_id: str, bot_id: str, knowledge_base_id: str, data_source_ids: list[str] + user_id: str, + bot_id: str, + knowledge_base_id: str | None, + data_source_ids: list[str] | None, ): table = get_bot_table_client() logger.info(f"Updating knowledge base id for bot: {bot_id}") @@ -678,88 +694,36 @@ def find_bot_by_id(bot_id: str) -> BotModel: if len(response["Items"]) == 0: raise RecordNotFoundError(f"Bot with id {bot_id} not found") - item = response["Items"][0] + items = response["Items"] - bot = BotModel( - id=item["BotId"], - owner_user_id=item["PK"], - title=item["Title"], - description=item["Description"], - instruction=item["Instruction"], - create_time=float(item["CreateTime"]), - last_used_time=float(item.get("LastUsedTime", item["CreateTime"])), - # Note: SharedScope is set to None for private shared_scope to use sparse index - shared_scope=item.get("SharedScope", "private"), - shared_status=item["SharedStatus"], - allowed_cognito_groups=item.get("AllowedCognitoGroups", []), - allowed_cognito_users=item.get("AllowedCognitoUsers", []), - # Note: IsStarred is set to False for non-starred bots to use sparse index - is_starred=item.get("IsStarred", False), - generation_params=GenerationParamsModel.model_validate( - { - **item.get("GenerationParams", DEFAULT_GENERATION_CONFIG), - # For backward compatibility - "reasoning_params": item.get("GenerationParams", {}).get( - "reasoning_params", - { - "budget_tokens": DEFAULT_GENERATION_CONFIG["reasoning_params"]["budget_tokens"], # type: ignore - }, - ), - } - ), - agent=( - AgentModel.model_validate(item["AgentData"]) - if "AgentData" in item - else AgentModel(tools=[]) - ), - knowledge=KnowledgeModel( - **{**item["Knowledge"], "s3_urls": item["Knowledge"].get("s3_urls", [])} - ), - prompt_caching_enabled=item.get("PromptCachingEnabled", True), - sync_status=item["SyncStatus"], - sync_status_reason=item["SyncStatusReason"], - sync_last_exec_id=item["LastExecId"], - published_api_stack_name=item.get("ApiPublishmentStackName", None), - published_api_datetime=item.get("ApiPublishedDatetime", None), - published_api_codebuild_id=item.get("ApiPublishCodeBuildId", None), - display_retrieved_chunks=item.get("DisplayRetrievedChunks", False), - conversation_quick_starters=item.get("ConversationQuickStarters", []), - bedrock_knowledge_base=( - BedrockKnowledgeBaseModel( - **{ - **item["BedrockKnowledgeBase"], - "chunking_configuration": item["BedrockKnowledgeBase"].get( - "chunking_configuration", None - ), - "parsing_model": item["BedrockKnowledgeBase"].get( - "parsing_model", "disabled" - ), - } - ) - if "BedrockKnowledgeBase" in item - else None - ), - bedrock_guardrails=( - BedrockGuardrailsModel(**item["GuardrailsParams"]) - if "GuardrailsParams" in item - else None - ), - active_models=( - ActiveModelsModel.model_validate(item.get("ActiveModels")) - if item.get("ActiveModels") - else default_active_models # for backward compatibility - ), - usage_stats=( - UsageStatsModel.model_validate(item.get("UsageStats")) - if item.get("UsageStats") - else UsageStatsModel(usage_count=0) # for backward compatibility - ), - ) + bot = BotModel.from_dynamo_item(items[0]) logger.info(f"Found bot: {bot}") return bot +def find_queued_bots() -> list[BotModel]: + """Find all 'QUEUED' bots.""" + bot_table = get_bot_table_client() + bots: list[BotModel] = [] + query_params = { + "IndexName": "SyncStatusIndex", + "KeyConditionExpression": Key("SyncStatus").eq("QUEUED"), + } + while True: + response = bot_table.query(**query_params) + items = response["Items"] + bots.extend(BotModel.from_dynamo_item(item) for item in items) + + last_evaluated_key = response.get("LastEvaluatedKey") + if last_evaluated_key is None: + break + + query_params["ExclusiveStartKey"] = last_evaluated_key + + return bots + + def find_pinned_public_bots() -> list[BotMeta]: """Find all pinned bots.""" table = get_bot_table_client() diff --git a/backend/app/repositories/models/custom_bot.py b/backend/app/repositories/models/custom_bot.py index 4feae1fe7..75fe14b8b 100644 --- a/backend/app/repositories/models/custom_bot.py +++ b/backend/app/repositories/models/custom_bot.py @@ -2,7 +2,6 @@ from typing import Annotated, Any, Dict, List, Literal, Optional, Self, Type, get_args from app.config import DEFAULT_GENERATION_CONFIG -from app.config import GenerationParams as GenerationParamsDict from app.repositories.models.common import DynamicBaseModel, Float, SecureString from app.repositories.models.custom_bot_guardrails import BedrockGuardrailsModel from app.repositories.models.custom_bot_kb import BedrockKnowledgeBaseModel @@ -39,7 +38,6 @@ ) from pydantic import ( BaseModel, - ConfigDict, Discriminator, Field, ValidationInfo, @@ -444,6 +442,35 @@ def validate_shared_scope(self) -> Self: ) return self + @model_validator(mode="after") + def validate_knowledge_base_type(self) -> Self: + if self.bedrock_knowledge_base is not None: + if self.bedrock_knowledge_base.exist_knowledge_base_id is not None or ( + len(self.knowledge.source_urls) == 0 + and len(self.knowledge.sitemap_urls) == 0 + and len(self.knowledge.filenames) == 0 + and len(self.knowledge.s3_urls) == 0 + ): + # Clear Knowledge Base ID if the Knowledge Base is not needed. + self.bedrock_knowledge_base.type = None + self.bedrock_knowledge_base.knowledge_base_id = None + + elif self.bedrock_knowledge_base.type is None: + # Otherwise, if the type of Knowledge Base is omitted, it will default to `dedicated`. + self.bedrock_knowledge_base.type = "dedicated" + + return self + + @model_validator(mode="after") + def validate_guardrails(self) -> Self: + if self.bedrock_guardrails is not None: + if not self.bedrock_guardrails.is_guardrail_enabled: + # Clear Guardrail ARN if the Guardrail is not needed. + self.bedrock_guardrails.guardrail_arn = "" + self.bedrock_guardrails.guardrail_version = "" + + return self + @field_validator("published_api_stack_name", mode="after") def validate_published_api_stack_name( cls, value: str | None, info: ValidationInfo @@ -605,6 +632,84 @@ def from_input( usage_stats=UsageStatsModel(usage_count=0), ) + @classmethod + def from_dynamo_item(cls, item: dict) -> Self: + return BotModel( + id=item["BotId"], + owner_user_id=item["PK"], + title=item["Title"], + description=item["Description"], + instruction=item["Instruction"], + create_time=float(item["CreateTime"]), + last_used_time=float(item.get("LastUsedTime", item["CreateTime"])), + # Note: SharedScope is set to None for private shared_scope to use sparse index + shared_scope=item.get("SharedScope", "private"), + shared_status=item["SharedStatus"], + allowed_cognito_groups=item.get("AllowedCognitoGroups", []), + allowed_cognito_users=item.get("AllowedCognitoUsers", []), + # Note: IsStarred is set to False for non-starred bots to use sparse index + is_starred=item.get("IsStarred", False), + generation_params=GenerationParamsModel.model_validate( + { + **item.get("GenerationParams", DEFAULT_GENERATION_CONFIG), + # For backward compatibility + "reasoning_params": item.get("GenerationParams", {}).get( + "reasoning_params", + { + "budget_tokens": DEFAULT_GENERATION_CONFIG["reasoning_params"]["budget_tokens"], # type: ignore + }, + ), + } + ), + agent=( + AgentModel.model_validate(item["AgentData"]) + if "AgentData" in item + else AgentModel(tools=[]) + ), + knowledge=KnowledgeModel( + **{**item["Knowledge"], "s3_urls": item["Knowledge"].get("s3_urls", [])} + ), + prompt_caching_enabled=item.get("PromptCachingEnabled", True), + sync_status=item["SyncStatus"], + sync_status_reason=item["SyncStatusReason"], + sync_last_exec_id=item["LastExecId"], + published_api_stack_name=item.get("ApiPublishmentStackName", None), + published_api_datetime=item.get("ApiPublishedDatetime", None), + published_api_codebuild_id=item.get("ApiPublishCodeBuildId", None), + display_retrieved_chunks=item.get("DisplayRetrievedChunks", False), + conversation_quick_starters=item.get("ConversationQuickStarters", []), + bedrock_knowledge_base=( + BedrockKnowledgeBaseModel( + **{ + **item["BedrockKnowledgeBase"], + "chunking_configuration": item["BedrockKnowledgeBase"].get( + "chunking_configuration", None + ), + "parsing_model": item["BedrockKnowledgeBase"].get( + "parsing_model", "disabled" + ), + } + ) + if "BedrockKnowledgeBase" in item + else None + ), + bedrock_guardrails=( + BedrockGuardrailsModel(**item["GuardrailsParams"]) + if "GuardrailsParams" in item + else None + ), + active_models=( + ActiveModelsModel.model_validate(item.get("ActiveModels")) + if item.get("ActiveModels") + else default_active_models # for backward compatibility + ), + usage_stats=( + UsageStatsModel.model_validate(item.get("UsageStats")) + if item.get("UsageStats") + else UsageStatsModel(usage_count=0) # for backward compatibility + ), + ) + def to_output(self) -> BotOutput: return BotOutput( id=self.id, diff --git a/backend/app/repositories/models/custom_bot_kb.py b/backend/app/repositories/models/custom_bot_kb.py index 1d4c0dd60..14518fcf1 100644 --- a/backend/app/repositories/models/custom_bot_kb.py +++ b/backend/app/repositories/models/custom_bot_kb.py @@ -1,3 +1,6 @@ +import base64 +from hashlib import md5 + from app.routes.schemas.bot_kb import ( type_kb_chunking_strategy, type_kb_embeddings_model, @@ -9,8 +12,8 @@ type_os_tokenizer, type_kb_resource_type, ) -from typing import Self -from pydantic import BaseModel, validator, model_validator +from typing import Literal +from pydantic import BaseModel class SearchParamsModel(BaseModel): @@ -74,6 +77,7 @@ class BedrockAgentGetKnowledgeBaseResponse(BaseModel): class BedrockKnowledgeBaseModel(BaseModel): + type: Literal["dedicated", "shared"] | None = None embeddings_model: type_kb_embeddings_model open_search: OpenSearchParamsModel chunking_configuration: ( @@ -93,3 +97,30 @@ class BedrockKnowledgeBaseModel(BaseModel): web_crawling_filters: WebCrawlingFiltersModel = WebCrawlingFiltersModel( exclude_patterns=[], include_patterns=[] ) + + +def calc_knowledge_base_hash(knowledge_base: BedrockKnowledgeBaseModel) -> str: + """Calculate hashcode of Knowledge Base settings. + + Args: + knowledge_base (BedrockKnowledgeBaseModel): Knowledge Base settings + + Returns: + str: BASE32 encoded MD5 hashcode of JSON-formatted Knowledge Base settings. + """ + return ( + base64.b32encode( + md5( + knowledge_base.model_dump_json( + exclude={ + "knowledge_base_id", + "exist_knowledge_base_id", + "data_source_ids", + } + ).encode(), + usedforsecurity=False, + ).digest() + ) + .decode() + .rstrip("=") + ) diff --git a/backend/app/routes/schemas/bot.py b/backend/app/routes/schemas/bot.py index 14d64da3d..ff5fde622 100644 --- a/backend/app/routes/schemas/bot.py +++ b/backend/app/routes/schemas/bot.py @@ -22,16 +22,19 @@ BedrockKnowledgeBaseOutput, ) from app.routes.schemas.conversation import type_model_name -from charset_normalizer.utils import is_punctuation from pydantic import ( Discriminator, Field, create_model, field_validator, - model_validator, validator, ) +from app.repositories.models.custom_bot_kb import ( + BedrockKnowledgeBaseModel, + calc_knowledge_base_hash, +) + if TYPE_CHECKING: from app.repositories.models.custom_bot import BotModel @@ -351,8 +354,63 @@ def is_embedding_required(self, current_bot_model: BotModel) -> bool: else: return True + # Check if the Knowledge Base settings have been changed. + if self.bedrock_knowledge_base is not None: + if current_bot_model.bedrock_knowledge_base is None: + return True + + knowledge_base_hash = calc_knowledge_base_hash( + BedrockKnowledgeBaseModel.model_validate( + self.bedrock_knowledge_base.model_dump() + ) + ) + current_knowledge_base_hash = calc_knowledge_base_hash( + current_bot_model.bedrock_knowledge_base + ) + if knowledge_base_hash != current_knowledge_base_hash: + return True + + elif current_bot_model.bedrock_knowledge_base is not None: + return True + return False + def is_sync_shared_knowledge_bases_required( + self, current_bot_model: BotModel + ) -> bool: + """Check if there have been any changes to the shared Knowledge Bases. + + Args: + current_bot_model (BotModel): Current bot settings. + + Returns: + bool: Whether there have been any changes to the shared Knowledge Bases. + """ + if self.bedrock_knowledge_base is None: + if current_bot_model.bedrock_knowledge_base is None: + return False + + return current_bot_model.bedrock_knowledge_base.type == "shared" + + elif current_bot_model.bedrock_knowledge_base is None: + return self.bedrock_knowledge_base.type == "shared" + + if ( + self.bedrock_knowledge_base.type != "shared" + and current_bot_model.bedrock_knowledge_base.type != "shared" + ): + return False + + knowledge_base_hash = calc_knowledge_base_hash( + BedrockKnowledgeBaseModel.model_validate( + self.bedrock_knowledge_base.model_dump() + ) + ) + current_knowledge_base_hash = calc_knowledge_base_hash( + current_bot_model.bedrock_knowledge_base + ) + return knowledge_base_hash != current_knowledge_base_hash + class BotModifyOutput(BaseSchema): id: str diff --git a/backend/app/routes/schemas/bot_kb.py b/backend/app/routes/schemas/bot_kb.py index 37c29ffdf..869c34e02 100644 --- a/backend/app/routes/schemas/bot_kb.py +++ b/backend/app/routes/schemas/bot_kb.py @@ -89,6 +89,7 @@ class WebCrawlingFilters(BaseSchema): class BedrockKnowledgeBaseInput(BaseSchema): + type: Literal["dedicated", "shared"] | None = None embeddings_model: type_kb_embeddings_model open_search: OpenSearchParams chunking_configuration: ( @@ -109,6 +110,7 @@ class BedrockKnowledgeBaseInput(BaseSchema): class BedrockKnowledgeBaseOutput(BaseSchema): + type: Literal["dedicated", "shared"] | None = None embeddings_model: type_kb_embeddings_model open_search: OpenSearchParams chunking_configuration: ( diff --git a/backend/app/usecases/bot.py b/backend/app/usecases/bot.py index f342edac4..dd7fda391 100644 --- a/backend/app/usecases/bot.py +++ b/backend/app/usecases/bot.py @@ -4,7 +4,6 @@ from app.agents.tools.agent_tool import AgentTool as LegacyAgentTool from app.config import DEFAULT_GENERATION_CONFIG -from app.config import GenerationParams as GenerationParamsDict from app.repositories.common import RecordNotFoundError from app.repositories.custom_bot import ( alias_exists, @@ -30,7 +29,6 @@ update_bot_stats, ) from app.repositories.models.custom_bot import ( - ActiveModelsModel, AgentModel, BotAliasModel, BotModel, @@ -44,7 +42,6 @@ from app.routes.schemas.admin import ( PushBotInput, PushBotInputPinned, - PushBotInputUnpinned, ) from app.routes.schemas.bot import ( ActiveModelsOutput, @@ -80,9 +77,8 @@ delete_file_from_s3, delete_files_with_prefix_from_s3, generate_presigned_url, - get_current_time, move_file_in_s3, - store_api_key_to_secret_manager, + start_embedding_state_machine, ) logger = logging.getLogger(__name__) @@ -145,6 +141,20 @@ def create_new_bot(user: User, bot_input: BotInput) -> BotOutput: new_bot = BotModel.from_input(bot_input, owner_user_id=user.id, knowledge=knowledge) store_bot(new_bot) + if new_bot.sync_status == "QUEUED": + # If necessary, start the Embedding state machine. + start_embedding_state_machine( + user_id=user.id, + bot_id=new_bot.id, + added_filenames=filenames, + unchanged_filenames=[], + deleted_filenames=[], + sync_shared_knowledge_bases_required=( + new_bot.bedrock_knowledge_base is not None + and new_bot.bedrock_knowledge_base.type == "shared" + ), + ) + return new_bot.to_output() @@ -163,29 +173,31 @@ def modify_owned_bot( sitemap_urls = [] filenames = [] s3_urls = [] - sync_status: type_sync_status = "QUEUED" + added_filenames = [] + unchanged_filenames = [] + deleted_filenames = [] if modify_input.knowledge: source_urls = modify_input.knowledge.source_urls sitemap_urls = modify_input.knowledge.sitemap_urls s3_urls = modify_input.knowledge.s3_urls + added_filenames = modify_input.knowledge.added_filenames + unchanged_filenames = modify_input.knowledge.unchanged_filenames + deleted_filenames = modify_input.knowledge.deleted_filenames # Commit changes to S3 _update_s3_documents_by_diff( user.id, bot_id, - modify_input.knowledge.added_filenames, - modify_input.knowledge.deleted_filenames, + added_filenames, + deleted_filenames, ) # Delete files from upload temp directory delete_files_with_prefix_from_s3( DOCUMENT_BUCKET, compose_upload_temp_s3_prefix(bot.owner_user_id, bot_id) ) - filenames = ( - modify_input.knowledge.added_filenames - + modify_input.knowledge.unchanged_filenames - ) + filenames = added_filenames + unchanged_filenames generation_params = ( GenerationParamsModel( @@ -205,7 +217,7 @@ def modify_owned_bot( # if knowledge is not updated, skip embeding process. # 'sync_status = "QUEUED"' will execute embeding process and update dynamodb record. # 'sync_status= "SUCCEEDED"' will update only dynamodb record. - sync_status = ( + sync_status: type_sync_status = ( "QUEUED" if modify_input.is_embedding_required(bot) or modify_input.is_guardrails_update_required(bot) @@ -272,6 +284,19 @@ def modify_owned_bot( ), ) + if sync_status == "QUEUED": + # If necessary, start the Embedding state machine. + start_embedding_state_machine( + user_id=user.id, + bot_id=bot.id, + added_filenames=added_filenames, + unchanged_filenames=unchanged_filenames, + deleted_filenames=deleted_filenames, + sync_shared_knowledge_bases_required=( + modify_input.is_sync_shared_knowledge_bases_required(bot) + ), + ) + return BotModifyOutput( id=bot_id, title=modify_input.title, diff --git a/backend/app/utils.py b/backend/app/utils.py index e32aeb116..8d9fa27fd 100644 --- a/backend/app/utils.py +++ b/backend/app/utils.py @@ -2,14 +2,14 @@ import logging import os from datetime import datetime -from typing import Any, Literal +from typing import Literal import boto3 -from app.repositories.models.custom_bot_guardrails import BedrockGuardrailsModel -from app.user import User from botocore.client import Config from botocore.exceptions import ClientError +from app.user import User + logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -25,6 +25,7 @@ "PUBLISH_API_CODEBUILD_PROJECT_NAME", "" ) USER_POOL_ID = os.environ.get("USER_POOL_ID", "") +EMBEDDING_STATE_MACHINE_ARN = os.environ.get("EMBEDDING_STATE_MACHINE_ARN") def snake_to_camel(snake_str): @@ -312,3 +313,43 @@ def delete_api_key_from_secret_manager(user_id: str, bot_id: str, prefix: str) - except ClientError as e: logger.error(f"Error accessing Secrets Manager: {e}") raise + + +def start_embedding_state_machine( + user_id: str, + bot_id: str, + added_filenames: list[str], + unchanged_filenames: list[str], + deleted_filenames: list[str], + sync_shared_knowledge_bases_required: bool, +): + """Start the Embedding state machine for `QUEUED` bot. + + Args: + user_id (str): User ID of the bot. + bot_id (str): Bot ID. + added_filenames (list[str]): Files added to the bot. + unchanged_filenames (list[str]): Files unchanged in the bot. + deleted_filenames (list[str]): Files deleted from the bot. + sync_shared_knowledge_bases_required (bool): Whether there have been any changes to the shared Knowledge Bases. + """ + client = boto3.client("stepfunctions") + client.start_execution( + stateMachineArn=EMBEDDING_STATE_MACHINE_ARN, + input=json.dumps( + { + "QueuedBots": [ + { + "OwnerUserId": user_id, + "BotId": bot_id, + "FilesDiff": { + "Added": added_filenames, + "Unchanged": unchanged_filenames, + "Deleted": deleted_filenames, + }, + "SyncSharedKnowledgeBasesRequired": sync_shared_knowledge_bases_required, + }, + ], + } + ), + ) diff --git a/backend/app/vector_search.py b/backend/app/vector_search.py index 882519996..cb1526972 100644 --- a/backend/app/vector_search.py +++ b/backend/app/vector_search.py @@ -1,24 +1,21 @@ import logging -from typing import TypedDict, Any +from typing import Any, TypedDict from urllib.parse import urlparse +from app.repositories.knowledge_base import get_knowledge_base_info from app.repositories.models.conversation import ( RelatedDocumentModel, TextToolResultModel, ) from app.repositories.models.custom_bot import BotModel -from app.repositories.knowledge_base import get_knowledge_base_info from app.utils import get_bedrock_agent_runtime_client from botocore.exceptions import ClientError +from mypy_boto3_bedrock_agent_runtime.literals import SearchTypeType from mypy_boto3_bedrock_agent_runtime.type_defs import ( KnowledgeBaseRetrievalResultTypeDef, KnowledgeBaseVectorSearchConfigurationTypeDef, RetrieveRequestTypeDef, ) -from mypy_boto3_bedrock_agent_runtime.literals import ( - SearchTypeType, -) - logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -86,6 +83,15 @@ def _bedrock_knowledge_base_search(bot: BotModel, query: str) -> list[SearchResu } }, } + if bot.bedrock_knowledge_base.type == "shared": + # Specify the Bot ID as a filter condition for the shared Knowledge Base. + retrieve_parameter["retrievalConfiguration"]["vectorSearchConfiguration"]["filter"] = { # type: ignore + "listContains": { + "key": "tenants", + # Note: metadata is attached on cdk/lambda/knowledge-base-custom-transformation/index.ts + "value": f"BOT#{bot.id}", # type: ignore + }, + } # Omit overrideSearchType parameter if needed def omit_override_search_type_parameter( diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/bootstrap_state_machine.py b/backend/embedding_statemachine/bedrock_knowledge_base/bootstrap_state_machine.py new file mode 100644 index 000000000..eacc85df5 --- /dev/null +++ b/backend/embedding_statemachine/bedrock_knowledge_base/bootstrap_state_machine.py @@ -0,0 +1,195 @@ +from typing import TypedDict, NotRequired +from boto3.dynamodb.conditions import Attr + +from app.repositories.custom_bot import ( + find_bot_by_id, + find_queued_bots, + get_bot_table_client, +) +from app.repositories.models.custom_bot import BotModel +from app.repositories.models.custom_bot_kb import ( + BedrockKnowledgeBaseModel, + calc_knowledge_base_hash, +) + + +def handler(event, context): + """To build the information necessary for processing the embedded state machine, retrieve information about bots and shared Knowledge Bases from the database.""" + queued_bots_from_event = event.get("QueuedBots") + if queued_bots_from_event is not None: + # The bots to be processed were specified in the input. + queued_bots = get_queued_bots_from_event(queued_bots_from_event) + + else: + # Otherwise, retrieve bots with `SynsStatus of `QUEUED` from the database. + queued_bots = get_queued_bots() + + if not queued_bots or any( + queued_bot["sync_shared_knowledge_bases_required"] for queued_bot in queued_bots + ): + # If there are updates to shared Knowledge Bases, or the state machine is started without specifying a queued bot, retrieve information about the shared Knowledge Bases from the database. + shared_knowledge_bases = find_shared_knowledge_bases() + + else: + # Otherwise, skip the processing related to shared Knowledge Bases. + shared_knowledge_bases = None + + return { + "QueuedBots": [ + { + # Bot configuration for State Machine processing + # - Shared bots: KnowledgeBaseHash set, KnowledgeBase empty + # - Dedicated bots: KnowledgeBase set, KnowledgeBaseHash None + # - FilesDiff: Present when bot has specific file changes + "OwnerUserId": queued_bot["bot"].owner_user_id, + "BotId": queued_bot["bot"].id, + **( + { + "FilesDiff": queued_bot["files_diff"], + } + if "files_diff" in queued_bot + else {} + ), + "Knowledge": queued_bot["bot"].knowledge.model_dump(), + "KnowledgeBaseHash": ( + calc_knowledge_base_hash(queued_bot["bot"].bedrock_knowledge_base) + if queued_bot["bot"].bedrock_knowledge_base is not None + else None + ), + "KnowledgeBase": ( + queued_bot["bot"].bedrock_knowledge_base.model_dump( + exclude={ + "knowledge_base_id", + "exist_knowledge_base_id", + "data_source_ids", + } + ) + if queued_bot["bot"].bedrock_knowledge_base is not None + and queued_bot["bot"].bedrock_knowledge_base.type == "dedicated" + else {} + ), + "Guardrails": ( + queued_bot["bot"].bedrock_guardrails.model_dump() + if queued_bot["bot"].bedrock_guardrails is not None + and queued_bot["bot"].bedrock_guardrails.is_guardrail_enabled + else {} + ), + } + for queued_bot in queued_bots + ], + "SharedKnowledgeBases": ( + [ + { + "KnowledgeBaseHash": shared_knowledge_base["knowledge_base_hash"], + "KnowledgeBase": shared_knowledge_base["knowledge_base"].model_dump( + exclude={ + "knowledge_base_id", + "exist_knowledge_base_id", + "data_source_ids", + } + ), + } + for shared_knowledge_base in shared_knowledge_bases + ] + if shared_knowledge_bases is not None + else None + ), + } + + +class FilesDiff(TypedDict): + Added: list[str] + Unchanged: list[str] + Deleted: list[str] + + +class QueuedBot(TypedDict): + bot: BotModel + files_diff: NotRequired[FilesDiff] + sync_shared_knowledge_bases_required: bool + + +def get_queued_bots_from_event(queued_bots_from_event: list[dict]) -> list[QueuedBot]: + result: list[QueuedBot] = [] + for queued_bot in queued_bots_from_event: + user_id = queued_bot.get("OwnerUserId") + bot_id = queued_bot.get("BotId") + if user_id and bot_id: + bot = find_bot_by_id(bot_id) + files_diff = queued_bot.get("FilesDiff", {}) + sync_shared_knowledge_bases_required = queued_bot.get( + "SyncSharedKnowledgeBasesRequired", True + ) + + added_files = files_diff.get("Added", []) + unchanged_files = files_diff.get("Unchanged", []) + deleted_files = files_diff.get("Deleted", []) + if added_files or unchanged_files or deleted_files: + result.append( + { + "bot": bot, + "files_diff": { + "Added": added_files, + "Unchanged": unchanged_files, + "Deleted": deleted_files, + }, + "sync_shared_knowledge_bases_required": sync_shared_knowledge_bases_required, + } + ) + + else: + result.append( + { + "bot": bot, + "sync_shared_knowledge_bases_required": sync_shared_knowledge_bases_required, + } + ) + + return result + + +def get_queued_bots() -> list[QueuedBot]: + bots = find_queued_bots() + return [ + { + "bot": bot, + "sync_shared_knowledge_bases_required": True, + } + for bot in bots + ] + + +class SharedKnowledgeBase(TypedDict): + knowledge_base_hash: str + knowledge_base: BedrockKnowledgeBaseModel + + +def find_shared_knowledge_bases() -> list[SharedKnowledgeBase]: + bot_table = get_bot_table_client() + scan_params = { + "FilterExpression": Attr("BedrockKnowledgeBase.type").eq("shared"), + } + + knowledge_bases: dict[str, SharedKnowledgeBase] = {} + while True: + response = bot_table.scan(**scan_params) + items = response["Items"] + for item in items: + bot = BotModel.from_dynamo_item(item) + if bot.bedrock_knowledge_base is not None: + knowledge_base_hash = calc_knowledge_base_hash( + bot.bedrock_knowledge_base + ) + if knowledge_base_hash not in knowledge_bases: + knowledge_bases[knowledge_base_hash] = { + "knowledge_base_hash": knowledge_base_hash, + "knowledge_base": bot.bedrock_knowledge_base, + } + + last_evaluated_key = response.get("LastEvaluatedKey") + if last_evaluated_key is None: + break + + scan_params["ExclusiveStartKey"] = last_evaluated_key + + return list(knowledge_bases.values()) diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/fetch_stack_output.py b/backend/embedding_statemachine/bedrock_knowledge_base/fetch_stack_output.py deleted file mode 100644 index 6f34ff194..000000000 --- a/backend/embedding_statemachine/bedrock_knowledge_base/fetch_stack_output.py +++ /dev/null @@ -1,71 +0,0 @@ -import os -from typing import Any, Dict, List, TypedDict - -import boto3 -from app.repositories.common import decompose_sk - -BEDROCK_REGION = os.environ.get("BEDROCK_REGION") - -cf_client = boto3.client("cloudformation", BEDROCK_REGION) - - -class StackItem(TypedDict): - KnowledgeBaseId: str - DataSourceId: str - GuardrailArn: str - GuardrailVersion: str - PK: str - SK: str - - -class StackResult(TypedDict): - KnowledgeBaseId: str - items: List[StackItem] - - -def handler(event: Dict[str, str], context: Any) -> StackResult: - print(event) - pk = event["pk"] - sk = event["sk"] - - bot_id = decompose_sk(sk) - - # Note: stack naming rule is defined on: - # cdk/bin/bedrock-knowledge-base.ts - stack_name = f"BrChatKbStack{bot_id}" - - response = cf_client.describe_stacks(StackName=stack_name) - outputs = response["Stacks"][0]["Outputs"] - - knowledge_base_id = None - data_source_ids: List[str] = [] - guardrail_arn = None - guardrail_version = None - result: StackResult = {"KnowledgeBaseId": "", "items": []} - - for output in outputs: - if output["OutputKey"] == "KnowledgeBaseId": - knowledge_base_id = output["OutputValue"] - result["KnowledgeBaseId"] = knowledge_base_id - elif output["OutputKey"].startswith("DataSource"): - data_source_ids.append(output["OutputValue"]) - elif output["OutputKey"] == "GuardrailArn": - guardrail_arn = output["OutputValue"] - elif output["OutputKey"] == "GuardrailVersion": - guardrail_version = output["OutputValue"] - - for data_source_id in data_source_ids: - result["items"].append( - { - "KnowledgeBaseId": knowledge_base_id or "", - "DataSourceId": data_source_id, - "GuardrailArn": guardrail_arn if guardrail_arn is not None else "", - "GuardrailVersion": ( - guardrail_version if guardrail_version is not None else "" - ), - "PK": pk, - "SK": sk, - } - ) - - return result diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/finalize_custom_bot_build.py b/backend/embedding_statemachine/bedrock_knowledge_base/finalize_custom_bot_build.py new file mode 100644 index 000000000..cafefaaa3 --- /dev/null +++ b/backend/embedding_statemachine/bedrock_knowledge_base/finalize_custom_bot_build.py @@ -0,0 +1,121 @@ +import os +from typing import List, TypedDict + +import boto3 +from app.repositories.custom_bot import ( + update_knowledge_base_id, + update_guardrails_params, +) + +BEDROCK_REGION = os.environ.get("BEDROCK_REGION") + +cfn = boto3.client( + service_name="cloudformation", + region_name=BEDROCK_REGION, +) + + +class BotFilesDiff(TypedDict): + OwnerUserId: str + BotId: str + Added: list[str] + Unchanged: list[str] + Deleted: list[str] + + +class DataSource(TypedDict): + KnowledgeBaseId: str + DataSourceId: str + FilesDiffs: list[BotFilesDiff] + + +def handler(event, context): + """Finalize custom bot build by retrieving CloudFormation outputs and setting up data sources. + + This handler processes both: + - Dedicated bots: Retrieves new KB/Guardrails from BrChatKbStack{botId} CloudFormation outputs + - Shared bots with file diffs: Inherits shared KB DataSources from previous flow + + All bots proceed to ingestion processing with their respective DataSources. + """ + user_id = event["OwnerUserId"] + bot_id = event["BotId"] + + bot_files_diffs: list[BotFilesDiff] = [] + + files_diff_from_event = event.get("FilesDiff") + if files_diff_from_event: + bot_files_diffs.append( + { + "OwnerUserId": user_id, + "BotId": bot_id, + "Added": files_diff_from_event["Added"], + "Unchanged": files_diff_from_event["Unchanged"], + "Deleted": files_diff_from_event["Deleted"], + } + ) + + data_sources: list[DataSource] = [] + + data_sources_from_event = event.get("DataSources") + if data_sources_from_event: + data_sources.extend( + { + "KnowledgeBaseId": data_source["KnowledgeBaseId"], + "DataSourceId": data_source["DataSourceId"], + "FilesDiffs": bot_files_diffs, + } + for data_source in data_sources_from_event + ) + + # Note: stack naming rule is defined on: + # cdk/bin/bedrock-custom-bot.ts + stack_name = f"BrChatKbStack{bot_id}" + + response = cfn.describe_stacks(StackName=stack_name) + outputs = response["Stacks"][0].get("Outputs") + if not outputs: + raise ValueError(f"No outputs found in CloudFormation stack '{stack_name}'") + + stack_outputs = dict( + (output["OutputKey"], output["OutputValue"]) + for output in outputs + if "OutputKey" in output and "OutputValue" in output + ) + + # Update `knowledge_base_id` of the bot using dedicated Knowledge Base. + knowledge_base_id = stack_outputs.get("KnowledgeBaseId") + if knowledge_base_id: + data_source_ids: List[str] = [ + value + for key, value in stack_outputs.items() + if key.startswith(f"DataSource") + ] + data_sources.extend( + { + "KnowledgeBaseId": knowledge_base_id, + "DataSourceId": data_source_id, + "FilesDiffs": bot_files_diffs, + } + for data_source_id in data_source_ids + ) + update_knowledge_base_id(user_id, bot_id, knowledge_base_id, data_source_ids) + + # Update `guardrail_arn` of the bot using dedicated Guardrail. + guardrail_arn = stack_outputs.get("GuardrailArn") + guardrail_version = stack_outputs.get("GuardrailVersion") + if guardrail_arn and guardrail_version: + update_guardrails_params(user_id, bot_id, guardrail_arn, guardrail_version) + + return { + "OwnerUserId": user_id, + "BotId": bot_id, + "DataSources": data_sources, + **( + { + "Lock": event["Lock"], + } + if "Lock" in event + else {} + ), + } diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/finalize_shared_knowledge_bases_build.py b/backend/embedding_statemachine/bedrock_knowledge_base/finalize_shared_knowledge_bases_build.py new file mode 100644 index 000000000..ff3743bb2 --- /dev/null +++ b/backend/embedding_statemachine/bedrock_knowledge_base/finalize_shared_knowledge_bases_build.py @@ -0,0 +1,160 @@ +import os +from typing import TypedDict + +import boto3 +from app.repositories.custom_bot import update_knowledge_base_id + +BEDROCK_REGION = os.environ.get("BEDROCK_REGION") + +cfn = boto3.client( + service_name="cloudformation", + region_name=BEDROCK_REGION, +) + + +class QueuedBot(TypedDict): + user_id: str + bot_id: str + + +class KnowledgeBase(TypedDict): + knowledge_base_id: str + data_source_ids: list[str] + queued_bots: list[QueuedBot] + + +class BotFilesDiff(TypedDict): + OwnerUserId: str + BotId: str + Added: list[str] + Unchanged: list[str] + Deleted: list[str] + + +class DataSource(TypedDict): + KnowledgeBaseId: str + DataSourceId: str + FilesDiffs: list[BotFilesDiff] + + +def handler(event, context): + """Obtain the ID of the shared Knowledge Bases built by `BrChatSharedKbStack`, and update `knowledge_base_id` of the referring bots.""" + queued_bots = event["QueuedBots"] + shared_knowledge_bases = event["SharedKnowledgeBases"] + + # Note: stack naming rule is defined on: + # cdk/bin/bedrock-shared-knowledge-bases.ts + stack_name = "BrChatSharedKbStack" + + response = cfn.describe_stacks(StackName=stack_name) + outputs = response["Stacks"][0].get("Outputs") + if not outputs: + raise ValueError(f"No outputs found in CloudFormation stack '{stack_name}'") + + stack_outputs = dict( + (output["OutputKey"], output["OutputValue"]) + for output in outputs + if "OutputKey" in output and "OutputValue" in output + ) + + # Dict of Knowledge Bases built by `BrChatSharedKbStack`. Key is knowledge base hash. + knowledge_bases: dict[str, KnowledgeBase] = {} + + for shared_knowledge_base in shared_knowledge_bases: + knowledge_base_hash = shared_knowledge_base["KnowledgeBaseHash"] + knowledge_base_id = stack_outputs.get(f"KnowledgeBaseId{knowledge_base_hash}") + if knowledge_base_id: + knowledge_bases[knowledge_base_hash] = { + "knowledge_base_id": knowledge_base_id, + "data_source_ids": [ + value + for key, value in stack_outputs.items() + if key.startswith(f"DataSource{knowledge_base_hash}") + ], + "queued_bots": [], + } + + # Dict of data sources that require entire synchronization. Key is data source ID. + data_sources: dict[str, DataSource] = {} + + if queued_bots: + # If there are `QUEUED` bots, synchronize only shared Knowledge Bases that they reference. + for queued_bot in queued_bots: + knowledge_base_hash = queued_bot.get("KnowledgeBaseHash") + if knowledge_base_hash and knowledge_base_hash in knowledge_bases: + knowledge_base = knowledge_bases[knowledge_base_hash] + if "FilesDiff" in queued_bot: + # Assign shared KB's DataSources to individual bot for processing in MapQueuedBots flow. + # This preserves bot-specific file diff information needed for: + # - Constructing bot-specific S3 paths (user_id/bot_id/filename) + # - Individual bot status tracking + # - Proper ingestion attribution + # The bot will update the shared Knowledge Base's DataSources in MapQueuedBots flow. + queued_bot["DataSources"] = [ + { + "KnowledgeBaseId": knowledge_base["knowledge_base_id"], + "DataSourceId": data_source_id, + } + for data_source_id in knowledge_base["data_source_ids"] + ] + + else: + # Otherwise, the data sources to be synchronized entirely. + data_sources.update( + ( + data_source_id, + { + "KnowledgeBaseId": knowledge_base["knowledge_base_id"], + "DataSourceId": data_source_id, + "FilesDiffs": [], + }, + ) + for data_source_id in knowledge_base["data_source_ids"] + ) + pass + + # Add the bots referencing this Knowledge Base to the list. + knowledge_base["queued_bots"].append( + { + "user_id": queued_bot["OwnerUserId"], + "bot_id": queued_bot["BotId"], + } + ) + + # Update `knowledge_base_id` of the bots using shared Knowledge Bases. + for knowledge_base in knowledge_bases.values(): + for queued_bot in knowledge_base["queued_bots"]: + update_knowledge_base_id( + user_id=queued_bot["user_id"], + bot_id=queued_bot["bot_id"], + knowledge_base_id=knowledge_base["knowledge_base_id"], + data_source_ids=knowledge_base["data_source_ids"], + ) + + else: + # Otherwise, synchronize all shared Knowledge Bases. + data_sources.update( + ( + data_source_id, + { + "KnowledgeBaseId": knowledge_base["knowledge_base_id"], + "DataSourceId": data_source_id, + "FilesDiffs": [], + }, + ) + for knowledge_base in knowledge_bases.values() + for data_source_id in knowledge_base["data_source_ids"] + ) + + return { + "QueuedBots": queued_bots, + "SharedKnowledgeBases": shared_knowledge_bases, + "DataSources": list(data_sources.values()), + **( + { + "Lock": event["Lock"], + } + if "Lock" in event + else {} + ), + } diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/lock.py b/backend/embedding_statemachine/bedrock_knowledge_base/lock.py new file mode 100644 index 000000000..621f6fc3e --- /dev/null +++ b/backend/embedding_statemachine/bedrock_knowledge_base/lock.py @@ -0,0 +1,107 @@ +import os + +import boto3 + +BEDROCK_REGION = os.environ.get("BEDROCK_REGION") +DOCUMENT_BUCKET = os.environ["DOCUMENT_BUCKET"] + +s3 = boto3.client( + service_name="s3", + region_name=BEDROCK_REGION, +) + + +def handler(event, context): + """Distributed locking using Amazon S3's conditional write feature.""" + action = event["Action"] + match action: + case "Acquire": + return handle_acquire(event) + + case "Release": + return handle_release(event) + + case _: + raise Exception(f"Invalid action {action}") + + +class RetryException(Exception): + pass + + +def lock_name_to_s3_key(lock_name: str) -> str: + return f".temp/.lock.{lock_name.lower()}" + + +def handle_acquire(event): + """Acquire a distributed lock.""" + lock_file_key = lock_name_to_s3_key(event["LockName"]) + owner = event["Owner"] + try: + # Create the lock file only if it does not already exist. Content is the owner ID. + response = s3.put_object( + Bucket=DOCUMENT_BUCKET, + Key=lock_file_key, + IfNoneMatch="*", + Body=owner, + ) + etag = response["ETag"] + + return { + "LockId": etag, + } + + except s3.exceptions.ClientError as ex: + error_code = ex.response.get("Error", {}).get("Code") + match error_code: + case "PreconditionFailed": + try: + # Check the owner ID because there is a possibility that `PreconditionFailed` occurred due to a retry caused by a network error, etc. + get_response = s3.get_object( + Bucket=DOCUMENT_BUCKET, + Key=lock_file_key, + ) + body = get_response["Body"].read().decode() + if body != owner: + raise RetryException() + + etag = get_response["ETag"] + return { + "LockId": etag, + } + + except s3.exceptions.NoSuchKey: + raise RetryException() + + case "ConditionalRequestConflict": + raise RetryException() + + case _: + raise ex + + +def handle_release(event): + """Release the acquired lock.""" + lock_file_key = lock_name_to_s3_key(event["LockName"]) + lock_id = event["LockId"] + try: + # Delete the lock file only if it have the same `ETag` as when it was created. + # Note: lock is automatically released after 1 day passed (see also: cdk/lib/bedrock-region-resources.ts) + s3.delete_object( + Bucket=DOCUMENT_BUCKET, + Key=lock_file_key, + IfMatch=lock_id, + ) + + except s3.exceptions.ClientError as ex: + error_code = ex.response.get("Error", {}).get("Code") + match error_code: + case "PreconditionFailed": + # If the lock file was replaced by another owner, the release of the lock is considered successful. + pass + + case "ConditionalRequestConflict": + raise RetryException() + + case _: + raise ex diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/store_knowledge_base_id.py b/backend/embedding_statemachine/bedrock_knowledge_base/store_knowledge_base_id.py deleted file mode 100644 index 91adea2b4..000000000 --- a/backend/embedding_statemachine/bedrock_knowledge_base/store_knowledge_base_id.py +++ /dev/null @@ -1,51 +0,0 @@ -import logging -import os -from typing import List - -from app.repositories.common import decompose_sk -from app.repositories.custom_bot import update_knowledge_base_id -from app.routes.schemas.bot import type_sync_status -from reretry import retry -from typing_extensions import TypedDict - -logger = logging.getLogger() -logger.setLevel(logging.INFO) - - -class Items(TypedDict): - KnowledgeBaseId: str - DataSourceId: str - GuardrailArn: str - GuardrailVersion: str - PK: str - SK: str - - -class StackOutput(TypedDict): - KnowledgeBaseId: str - items: List[Items] - - -def handler(event, context): - logger.info(f"Event: {event}") - pk = event["pk"] - sk = event["sk"] - stack_output: StackOutput = event["stack_output"] - - kb_id = ( - stack_output["KnowledgeBaseId"] if "KnowledgeBaseId" in stack_output else None - ) - if not kb_id: - raise ValueError("KnowledgeBaseId not found in stack outputs") - - # Filter out None values and ensure all elements are strings - data_source_ids: List[str] = [ - item["DataSourceId"] - for item in stack_output.get("items", []) - if item.get("DataSourceId") - ] - - user_id = pk - bot_id = decompose_sk(sk) - - update_knowledge_base_id(user_id, bot_id, kb_id, data_source_ids) diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/synchronize_data_source.py b/backend/embedding_statemachine/bedrock_knowledge_base/synchronize_data_source.py new file mode 100644 index 000000000..58d4bbe63 --- /dev/null +++ b/backend/embedding_statemachine/bedrock_knowledge_base/synchronize_data_source.py @@ -0,0 +1,279 @@ +import os +from itertools import islice +from typing import TypedDict + +from app.utils import ( + compose_upload_document_s3_path, + get_bedrock_agent_client, +) + +DOCUMENT_BUCKET = os.environ.get("DOCUMENT_BUCKET") +bedrock_agent = get_bedrock_agent_client() + + +def handler(event, context): + """Perform data source synchronization for a Knowledge Base.""" + match event["Action"]: + case "Ingest": + return handle_ingest(event) + + case "Check": + return handle_check(event) + + case _ as e: + raise Exception(f"Unknown action {e}") + + +class DocumentsDiff(TypedDict): + Added: list[str] + Deleted: list[str] + + +def compose_upload_document_s3_uri(user_id: str, bot_id: str, filename: str) -> str: + return f"s3://{DOCUMENT_BUCKET}/{compose_upload_document_s3_path(user_id, bot_id, filename)}" + + +def get_data_source_type(knowledge_base_id: str, data_source_id: str): + get_data_source_response = bedrock_agent.get_data_source( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + ) + return get_data_source_response["dataSource"]["dataSourceConfiguration"]["type"] + + +def handle_ingest(event): + """Perform data source synchronization for Knowledge Bases. + + This function handles both shared and dedicated Knowledge Base DataSources. + The target DataSource is determined by KnowledgeBaseId/DataSourceId in the event, + regardless of whether called from SharedKnowledgeBases flow or MapQueuedBots flow. + + Shared KB bots with file diffs reach here via MapQueuedBots flow but still + update the shared Knowledge Base's DataSources with bot-specific file changes. + """ + knowledge_base_id = event["KnowledgeBaseId"] + data_source_id = event["DataSourceId"] + + bot_files_diffs = event.get("FilesDiffs") + if ( + bot_files_diffs + and get_data_source_type( + knowledge_base_id=knowledge_base_id, + data_source_id=data_source_id, + ) + == "S3" + ): + # If the bot specifies which files should be ingested, perform direct ingestion. + added_documents: list[str] = [] + unchanged_documents: list[str] = [] + deleted_documents: list[str] = [] + + for bot_files_diff in bot_files_diffs: + user_id = bot_files_diff["OwnerUserId"] + bot_id = bot_files_diff["BotId"] + + added_documents.extend( + compose_upload_document_s3_uri(user_id, bot_id, added_file) + for added_file in bot_files_diff["Added"] + ) + unchanged_documents.extend( + compose_upload_document_s3_uri(user_id, bot_id, unchanged_file) + for unchanged_file in bot_files_diff["Unchanged"] + ) + deleted_documents.extend( + compose_upload_document_s3_uri(user_id, bot_id, deleted_file) + for deleted_file in bot_files_diff["Deleted"] + ) + + # If an 'unchanged' document doesn't actually exist, treat it as an 'added' document. + for i in range(0, len(unchanged_documents), 10): + get_documents_response = bedrock_agent.get_knowledge_base_documents( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + documentIdentifiers=[ + { + "dataSourceType": "S3", + "s3": { + "uri": document_identifier, + }, + } + for document_identifier in islice(unchanged_documents, i, i + 10) + ], + ) + added_documents.extend( + document["identifier"]["s3"]["uri"] + for document in get_documents_response["documentDetails"] + if document["status"] == "NOT_FOUND" and "s3" in document["identifier"] + ) + + documents_diff: DocumentsDiff = { + "Added": [], + "Deleted": [], + } + + # Ingest 'added' documents to the data source. + for i in range(0, len(added_documents), 10): + ingest_response = bedrock_agent.ingest_knowledge_base_documents( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + documents=[ + { + "content": { + "dataSourceType": "S3", + "s3": { + "s3Location": { + "uri": document_identifier, + }, + }, + }, + } + for document_identifier in islice(added_documents, i, i + 10) + ], + ) + documents_diff["Added"].extend( + document["identifier"]["s3"]["uri"] + for document in ingest_response["documentDetails"] + if document["status"] != "IGNORED" and "s3" in document["identifier"] + ) + + # Delete 'deleted' documents from the data source. + for i in range(0, len(deleted_documents), 10): + delete_response = bedrock_agent.delete_knowledge_base_documents( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + documentIdentifiers=[ + { + "dataSourceType": "S3", + "s3": { + "uri": document_identifier, + }, + } + for document_identifier in islice(deleted_documents, i, i + 10) + ], + ) + documents_diff["Deleted"].extend( + document["identifier"]["s3"]["uri"] + for document in delete_response["documentDetails"] + if "s3" in document["identifier"] + ) + + return { + "KnowledgeBaseId": knowledge_base_id, + "DataSourceId": data_source_id, + "DocumentsDiff": documents_diff, + "IngestionJobId": None, + } + + else: + # Otherwise, start the entire synchronization job. + start_job_response = bedrock_agent.start_ingestion_job( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + ) + ingestion_job_id = start_job_response["ingestionJob"]["ingestionJobId"] + + return { + "KnowledgeBaseId": knowledge_base_id, + "DataSourceId": data_source_id, + "DocumentsDiff": None, + "IngestionJobId": ingestion_job_id, + } + + +class RetryException(Exception): + pass + + +def handle_check(event): + """Check for the completion of direct ingestion or entire synchronization.""" + ingestion_job = event["IngestionJob"] + knowledge_base_id = ingestion_job["KnowledgeBaseId"] + data_source_id = ingestion_job["DataSourceId"] + + documents_diff = ingestion_job.get("DocumentsDiff") + if documents_diff: + # Check for the completion of indexing of 'added' documents. + added_documents = documents_diff["Added"] + for i in range(0, len(added_documents), 10): + get_documents_response = bedrock_agent.get_knowledge_base_documents( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + documentIdentifiers=[ + { + "dataSourceType": "S3", + "s3": { + "uri": document_identifier, + }, + } + for document_identifier in islice(added_documents, i, i + 10) + ], + ) + for document in get_documents_response["documentDetails"]: + status = document["status"] + uri = document["identifier"].get("s3", {})["uri"] + match status: + case "INDEXED": + pass + + case "PENDING" | "STARTING" | "IN_PROGRESS" | "PARTIALLY_INDEXED": + raise RetryException() + + case _: + raise Exception(f"File {uri}: Bad status '{status}'.") + + # Check for the absence of 'deleted' documents. + deleted_documents = documents_diff["Deleted"] + for i in range(0, len(deleted_documents), 10): + get_documents_response = bedrock_agent.get_knowledge_base_documents( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + documentIdentifiers=[ + { + "dataSourceType": "S3", + "s3": { + "uri": document_identifier, + }, + } + for document_identifier in islice(deleted_documents, i, i + 10) + ], + ) + for document in get_documents_response["documentDetails"]: + status = document["status"] + uri = document["identifier"].get("s3", {})["uri"] + match status: + case "NOT_FOUND": + pass + + case "PENDING" | "DELETING" | "DELETE_IN_PROGRESS": + raise RetryException() + + case _: + raise Exception(f"File '{uri}': Bad status '{status}'.") + + return + + else: + # Check the completion of entire synchronization job. + ingestion_job_id = ingestion_job.get("IngestionJobId") + if ingestion_job_id: + get_job_response = bedrock_agent.get_ingestion_job( + knowledgeBaseId=knowledge_base_id, + dataSourceId=data_source_id, + ingestionJobId=ingestion_job_id, + ) + status = get_job_response["ingestionJob"]["status"] + match status: + case "COMPLETE": + pass + + case "STARTING" | "IN_PROGRESS": + raise RetryException() + + case _: + raise Exception( + f"Ingestion Job '{ingestion_job_id}': Bad status '{status}'." + ) + + return + + raise Exception("Invalid parameters.") diff --git a/backend/embedding_statemachine/bedrock_knowledge_base/update_bot_status.py b/backend/embedding_statemachine/bedrock_knowledge_base/update_bot_status.py index 1e88571eb..322245d31 100644 --- a/backend/embedding_statemachine/bedrock_knowledge_base/update_bot_status.py +++ b/backend/embedding_statemachine/bedrock_knowledge_base/update_bot_status.py @@ -1,114 +1,82 @@ import json -import logging -import os -from typing import Literal +from typing import TypedDict -import boto3 -from app.repositories.common import compose_sk, decompose_sk, get_bot_table_client +from app.repositories.common import compose_sk, get_bot_table_client from app.routes.schemas.bot import type_sync_status from reretry import retry -logger = logging.getLogger() -logger.setLevel(logging.INFO) - -dynamodb = boto3.resource("dynamodb") - RETRIES_TO_UPDATE_SYNC_STATUS = 4 RETRY_DELAY_TO_UPDATE_SYNC_STATUS = 2 +class SyncStatus(TypedDict): + user_id: str + bot_id: str + status: type_sync_status + reason: str + last_exec_id: str + + @retry(tries=RETRIES_TO_UPDATE_SYNC_STATUS, delay=RETRY_DELAY_TO_UPDATE_SYNC_STATUS) -def update_sync_status( - user_id: str, - bot_id: str, - sync_status: type_sync_status, - sync_status_reason: str, - last_exec_id: str, -): +def update_sync_status(sync_status: SyncStatus): table = get_bot_table_client() table.update_item( - Key={"PK": user_id, "SK": compose_sk(bot_id, "bot")}, + Key={ + "PK": sync_status["user_id"], + "SK": compose_sk(sync_status["bot_id"], "bot"), + }, UpdateExpression="SET SyncStatus = :sync_status, SyncStatusReason = :sync_status_reason, LastExecId = :last_exec_id", ExpressionAttributeValues={ - ":sync_status": sync_status, - ":sync_status_reason": sync_status_reason, - ":last_exec_id": last_exec_id, + ":sync_status": sync_status["status"], + ":sync_status_reason": sync_status["reason"], + ":last_exec_id": sync_status["last_exec_id"], }, ) -def extract_from_cause(cause_str: str) -> tuple: - logger.debug(f"Extracting PK and SK from cause: {cause_str}") - cause = json.loads(cause_str) - logger.debug(f"Cause: {cause}") - environment_variables = cause["Build"]["Environment"]["EnvironmentVariables"] - logger.debug(f"Environment variables: {environment_variables}") - - pk = next( - (item["Value"] for item in environment_variables if item["Name"] == "PK"), None - ) - sk = next( - (item["Value"] for item in environment_variables if item["Name"] == "SK"), None - ) - - if not pk or not sk: - raise ValueError("PK or SK not found in cause.") - - build_arn = cause["Build"].get("Arn", "") - - logger.debug(f"PK: {pk}, SK: {sk}, Build ARN: {build_arn}") - - return pk, sk, build_arn - - def handler(event, context): - logger.info(f"Event: {event}") - try: - cause = event.get("cause", None) - ingestion_job = event.get("ingestion_job", None) - - # Initialize variables - pk: str - sk: str - sync_status: type_sync_status - sync_status_reason: str - last_exec_id: str - - if cause: - # UpdateSymcStatusFailed - pk, sk, build_arn = extract_from_cause(cause) - sync_status = "FAILED" - sync_status_reason = cause - last_exec_id = build_arn - elif ingestion_job: - # UpdateSymcStatusFailedForIngestion - pk = event["pk"] - sk = event["sk"] - sync_status = "FAILED" - sync_status_reason = str(ingestion_job["ingestionJob"]["failureReasons"]) - last_exec_id = ingestion_job["ingestionJob"]["ingestionJobId"] - else: - pk = event["pk"] - sk = event["sk"] - sync_status = event["sync_status"] - sync_status_reason = event.get("sync_status_reason", "") - last_exec_id = event.get("last_exec_id", "") - - user_id = pk - bot_id = decompose_sk(sk) - - logger.info( - f"Updating sync status for bot {bot_id} of user {user_id} to {sync_status} with reason: {sync_status_reason}" + # Initialize variables + queued_bots = event.get("QueuedBots") + user_id = event.get("OwnerUserId") + bot_id = event.get("BotId") + sync_status: type_sync_status = event["SyncStatus"] + sync_status_reason: str + last_exec_id: str + + build = event.get("Build") + if build: + # CodeBuild + sync_status_reason = json.dumps(build["Phases"]) + last_exec_id = build["Arn"] + + else: + sync_status_reason = event.get("SyncStatusReason", "") + last_exec_id = event.get("LastExecId", "") + + sync_status_updates: list[SyncStatus] = [] + + if user_id and bot_id: + sync_status_updates.append( + { + "user_id": user_id, + "bot_id": bot_id, + "status": sync_status, + "reason": sync_status_reason, + "last_exec_id": last_exec_id, + } ) - update_sync_status( - user_id, bot_id, sync_status, sync_status_reason, last_exec_id + if queued_bots: + sync_status_updates.extend( + { + "user_id": queued_bot["OwnerUserId"], + "bot_id": queued_bot["BotId"], + "status": sync_status, + "reason": sync_status_reason, + "last_exec_id": last_exec_id, + } + for queued_bot in queued_bots ) - return { - "statusCode": 200, - "body": json.dumps("Sync status updated successfully."), - } - except Exception as e: - logger.error(f"Error updating sync status: {e}") - return {"statusCode": 500, "body": json.dumps("Error updating sync status.")} + for sync_status_update in sync_status_updates: + update_sync_status(sync_status_update) diff --git a/backend/embedding_statemachine/guardrails/store_guardrail_arn.py b/backend/embedding_statemachine/guardrails/store_guardrail_arn.py deleted file mode 100644 index 8bfbf0a48..000000000 --- a/backend/embedding_statemachine/guardrails/store_guardrail_arn.py +++ /dev/null @@ -1,70 +0,0 @@ -import json -import logging -import os -from typing import List, TypedDict - -import boto3 -from app.repositories.common import decompose_sk -from app.repositories.custom_bot import update_guardrails_params -from app.routes.schemas.bot import type_sync_status -from reretry import retry - -logger = logging.getLogger() -logger.setLevel(logging.INFO) - - -class Items(TypedDict): - KnowledgeBaseId: str - DataSourceId: str - GuardrailArn: str - GuardrailVersion: str - PK: str - SK: str - - -class StackOutput(TypedDict): - """ - 'stack_output': { - 'KnowledgeBaseId': 'ABCDEFGHIJKL', - 'items': [ - { - 'KnowledgeBaseId': 'MNOPQRSTUVWX', - 'DataSourceId': 'YZABCDEFGHI', - 'GuardrailArn': 'arn:aws:bedrock:us-east-1:123456789012:guardrail/abcdefghijkl', - 'GuardrailVersion': 'DRAFT', - 'PK': '7801e3f0-40b1-70da-2e13-652d4adce1c3', - 'SK': '7801e3f0-40b1-70da-2e13-652d4adce1c3#BOT#01JKWE8RP6YWNX9SKFSCCNS73Z' - } - ] - } - """ - - KnowledgeBaseId: str - items: List[Items] - - -def handler(event, context): - logger.info(f"Event: {event}") - pk = event["pk"] - sk = event["sk"] - stack_output: StackOutput = event["stack_output"] - - # Check if stack_output is valid and has at least one item - if ( - not stack_output - or not isinstance(stack_output["items"], list) - or len(stack_output["items"]) == 0 - ): - logger.warning("Empty or invalid stack_output received") - guardrail_arn = "" - guardrail_version = "" - else: - # Access the first item directly since we know it exists - first_output = stack_output["items"][0] - guardrail_arn = first_output.get("GuardrailArn", "") - guardrail_version = first_output.get("GuardrailVersion", "") - - user_id = pk - bot_id = decompose_sk(sk) - - update_guardrails_params(user_id, bot_id, guardrail_arn, guardrail_version) diff --git a/backend/poetry.lock b/backend/poetry.lock index abacc4674..574a7a9ac 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -259,472 +259,474 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.39.17" +version = "1.40.56" description = "The AWS SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "boto3-1.39.17-py3-none-any.whl", hash = "sha256:6af9f7d6db7b5e72d6869ae22ebad1b0c6602591af2ef5d914b331a055953df5"}, - {file = "boto3-1.39.17.tar.gz", hash = "sha256:a6904a40b1c61f6a1766574b3155ec75a6020399fb570be2b51bf93a2c0a2b3d"}, + {file = "boto3-1.40.56-py3-none-any.whl", hash = "sha256:8985a840d57671aa3c6124b0c178e79be97e3447de4b5819156071793f82ee5c"}, + {file = "boto3-1.40.56.tar.gz", hash = "sha256:c1afdb04dd27418fc58400434ab8e05998bb452b69c428168d9ada344fe6b93e"}, ] [package.dependencies] -botocore = ">=1.39.17,<1.40.0" +botocore = ">=1.40.56,<1.41.0" jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.13.0,<0.14.0" +s3transfer = ">=0.14.0,<0.15.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "boto3-stubs" -version = "1.39.17" -description = "Type annotations for boto3 1.39.17 generated with mypy-boto3-builder 8.11.0" +version = "1.40.56" +description = "Type annotations for boto3 1.40.56 generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "boto3_stubs-1.39.17-py3-none-any.whl", hash = "sha256:5ca6cfb200263313223455497a818d051597c905f817251c613c8e3e41f2950e"}, - {file = "boto3_stubs-1.39.17.tar.gz", hash = "sha256:f32236a3beccd83c7fe50e06e99f5bcee06a24e1f58c4bde3a404750bfe6d911"}, + {file = "boto3_stubs-1.40.56-py3-none-any.whl", hash = "sha256:638398e9b58769db8d050f93cd81aab8213e6a617f13dcd1632c4b09c870c732"}, + {file = "boto3_stubs-1.40.56.tar.gz", hash = "sha256:e5644523eff7f50bade5e9f4a24544b40f849e9051949e3501203a20627f4263"}, ] [package.dependencies] -boto3 = {version = "1.39.17", optional = true, markers = "extra == \"boto3\""} +boto3 = {version = "1.40.56", optional = true, markers = "extra == \"boto3\""} botocore-stubs = "*" -mypy-boto3-bedrock = {version = ">=1.39.0,<1.40.0", optional = true, markers = "extra == \"bedrock\""} -mypy-boto3-bedrock-agent-runtime = {version = ">=1.39.0,<1.40.0", optional = true, markers = "extra == \"bedrock-agent-runtime\""} -mypy-boto3-bedrock-runtime = {version = ">=1.39.0,<1.40.0", optional = true, markers = "extra == \"bedrock-runtime\""} +mypy-boto3-bedrock = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"bedrock\""} +mypy-boto3-bedrock-agent = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"bedrock-agent\""} +mypy-boto3-bedrock-agent-runtime = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"bedrock-agent-runtime\""} +mypy-boto3-bedrock-runtime = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"bedrock-runtime\""} +mypy-boto3-cloudformation = {version = ">=1.40.0,<1.41.0", optional = true, markers = "extra == \"cloudformation\""} types-s3transfer = "*" [package.extras] -accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.39.0,<1.40.0)"] -account = ["mypy-boto3-account (>=1.39.0,<1.40.0)"] -acm = ["mypy-boto3-acm (>=1.39.0,<1.40.0)"] -acm-pca = ["mypy-boto3-acm-pca (>=1.39.0,<1.40.0)"] -aiops = ["mypy-boto3-aiops (>=1.39.0,<1.40.0)"] -all = ["mypy-boto3-accessanalyzer (>=1.39.0,<1.40.0)", "mypy-boto3-account (>=1.39.0,<1.40.0)", "mypy-boto3-acm (>=1.39.0,<1.40.0)", "mypy-boto3-acm-pca (>=1.39.0,<1.40.0)", "mypy-boto3-aiops (>=1.39.0,<1.40.0)", "mypy-boto3-amp (>=1.39.0,<1.40.0)", "mypy-boto3-amplify (>=1.39.0,<1.40.0)", "mypy-boto3-amplifybackend (>=1.39.0,<1.40.0)", "mypy-boto3-amplifyuibuilder (>=1.39.0,<1.40.0)", "mypy-boto3-apigateway (>=1.39.0,<1.40.0)", "mypy-boto3-apigatewaymanagementapi (>=1.39.0,<1.40.0)", "mypy-boto3-apigatewayv2 (>=1.39.0,<1.40.0)", "mypy-boto3-appconfig (>=1.39.0,<1.40.0)", "mypy-boto3-appconfigdata (>=1.39.0,<1.40.0)", "mypy-boto3-appfabric (>=1.39.0,<1.40.0)", "mypy-boto3-appflow (>=1.39.0,<1.40.0)", "mypy-boto3-appintegrations (>=1.39.0,<1.40.0)", "mypy-boto3-application-autoscaling (>=1.39.0,<1.40.0)", "mypy-boto3-application-insights (>=1.39.0,<1.40.0)", "mypy-boto3-application-signals (>=1.39.0,<1.40.0)", "mypy-boto3-applicationcostprofiler (>=1.39.0,<1.40.0)", "mypy-boto3-appmesh (>=1.39.0,<1.40.0)", "mypy-boto3-apprunner (>=1.39.0,<1.40.0)", "mypy-boto3-appstream (>=1.39.0,<1.40.0)", "mypy-boto3-appsync (>=1.39.0,<1.40.0)", "mypy-boto3-apptest (>=1.39.0,<1.40.0)", "mypy-boto3-arc-zonal-shift (>=1.39.0,<1.40.0)", "mypy-boto3-artifact (>=1.39.0,<1.40.0)", "mypy-boto3-athena (>=1.39.0,<1.40.0)", "mypy-boto3-auditmanager (>=1.39.0,<1.40.0)", "mypy-boto3-autoscaling (>=1.39.0,<1.40.0)", "mypy-boto3-autoscaling-plans (>=1.39.0,<1.40.0)", "mypy-boto3-b2bi (>=1.39.0,<1.40.0)", "mypy-boto3-backup (>=1.39.0,<1.40.0)", "mypy-boto3-backup-gateway (>=1.39.0,<1.40.0)", "mypy-boto3-backupsearch (>=1.39.0,<1.40.0)", "mypy-boto3-batch (>=1.39.0,<1.40.0)", "mypy-boto3-bcm-data-exports (>=1.39.0,<1.40.0)", "mypy-boto3-bcm-pricing-calculator (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-agent (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-agent-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-agentcore (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-agentcore-control (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-data-automation (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-bedrock-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-billing (>=1.39.0,<1.40.0)", "mypy-boto3-billingconductor (>=1.39.0,<1.40.0)", "mypy-boto3-braket (>=1.39.0,<1.40.0)", "mypy-boto3-budgets (>=1.39.0,<1.40.0)", "mypy-boto3-ce (>=1.39.0,<1.40.0)", "mypy-boto3-chatbot (>=1.39.0,<1.40.0)", "mypy-boto3-chime (>=1.39.0,<1.40.0)", "mypy-boto3-chime-sdk-identity (>=1.39.0,<1.40.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.39.0,<1.40.0)", "mypy-boto3-chime-sdk-meetings (>=1.39.0,<1.40.0)", "mypy-boto3-chime-sdk-messaging (>=1.39.0,<1.40.0)", "mypy-boto3-chime-sdk-voice (>=1.39.0,<1.40.0)", "mypy-boto3-cleanrooms (>=1.39.0,<1.40.0)", "mypy-boto3-cleanroomsml (>=1.39.0,<1.40.0)", "mypy-boto3-cloud9 (>=1.39.0,<1.40.0)", "mypy-boto3-cloudcontrol (>=1.39.0,<1.40.0)", "mypy-boto3-clouddirectory (>=1.39.0,<1.40.0)", "mypy-boto3-cloudformation (>=1.39.0,<1.40.0)", "mypy-boto3-cloudfront (>=1.39.0,<1.40.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.39.0,<1.40.0)", "mypy-boto3-cloudhsm (>=1.39.0,<1.40.0)", "mypy-boto3-cloudhsmv2 (>=1.39.0,<1.40.0)", "mypy-boto3-cloudsearch (>=1.39.0,<1.40.0)", "mypy-boto3-cloudsearchdomain (>=1.39.0,<1.40.0)", "mypy-boto3-cloudtrail (>=1.39.0,<1.40.0)", "mypy-boto3-cloudtrail-data (>=1.39.0,<1.40.0)", "mypy-boto3-cloudwatch (>=1.39.0,<1.40.0)", "mypy-boto3-codeartifact (>=1.39.0,<1.40.0)", "mypy-boto3-codebuild (>=1.39.0,<1.40.0)", "mypy-boto3-codecatalyst (>=1.39.0,<1.40.0)", "mypy-boto3-codecommit (>=1.39.0,<1.40.0)", "mypy-boto3-codeconnections (>=1.39.0,<1.40.0)", "mypy-boto3-codedeploy (>=1.39.0,<1.40.0)", "mypy-boto3-codeguru-reviewer (>=1.39.0,<1.40.0)", "mypy-boto3-codeguru-security (>=1.39.0,<1.40.0)", "mypy-boto3-codeguruprofiler (>=1.39.0,<1.40.0)", "mypy-boto3-codepipeline (>=1.39.0,<1.40.0)", "mypy-boto3-codestar-connections (>=1.39.0,<1.40.0)", "mypy-boto3-codestar-notifications (>=1.39.0,<1.40.0)", "mypy-boto3-cognito-identity (>=1.39.0,<1.40.0)", "mypy-boto3-cognito-idp (>=1.39.0,<1.40.0)", "mypy-boto3-cognito-sync (>=1.39.0,<1.40.0)", "mypy-boto3-comprehend (>=1.39.0,<1.40.0)", "mypy-boto3-comprehendmedical (>=1.39.0,<1.40.0)", "mypy-boto3-compute-optimizer (>=1.39.0,<1.40.0)", "mypy-boto3-config (>=1.39.0,<1.40.0)", "mypy-boto3-connect (>=1.39.0,<1.40.0)", "mypy-boto3-connect-contact-lens (>=1.39.0,<1.40.0)", "mypy-boto3-connectcampaigns (>=1.39.0,<1.40.0)", "mypy-boto3-connectcampaignsv2 (>=1.39.0,<1.40.0)", "mypy-boto3-connectcases (>=1.39.0,<1.40.0)", "mypy-boto3-connectparticipant (>=1.39.0,<1.40.0)", "mypy-boto3-controlcatalog (>=1.39.0,<1.40.0)", "mypy-boto3-controltower (>=1.39.0,<1.40.0)", "mypy-boto3-cost-optimization-hub (>=1.39.0,<1.40.0)", "mypy-boto3-cur (>=1.39.0,<1.40.0)", "mypy-boto3-customer-profiles (>=1.39.0,<1.40.0)", "mypy-boto3-databrew (>=1.39.0,<1.40.0)", "mypy-boto3-dataexchange (>=1.39.0,<1.40.0)", "mypy-boto3-datapipeline (>=1.39.0,<1.40.0)", "mypy-boto3-datasync (>=1.39.0,<1.40.0)", "mypy-boto3-datazone (>=1.39.0,<1.40.0)", "mypy-boto3-dax (>=1.39.0,<1.40.0)", "mypy-boto3-deadline (>=1.39.0,<1.40.0)", "mypy-boto3-detective (>=1.39.0,<1.40.0)", "mypy-boto3-devicefarm (>=1.39.0,<1.40.0)", "mypy-boto3-devops-guru (>=1.39.0,<1.40.0)", "mypy-boto3-directconnect (>=1.39.0,<1.40.0)", "mypy-boto3-discovery (>=1.39.0,<1.40.0)", "mypy-boto3-dlm (>=1.39.0,<1.40.0)", "mypy-boto3-dms (>=1.39.0,<1.40.0)", "mypy-boto3-docdb (>=1.39.0,<1.40.0)", "mypy-boto3-docdb-elastic (>=1.39.0,<1.40.0)", "mypy-boto3-drs (>=1.39.0,<1.40.0)", "mypy-boto3-ds (>=1.39.0,<1.40.0)", "mypy-boto3-ds-data (>=1.39.0,<1.40.0)", "mypy-boto3-dsql (>=1.39.0,<1.40.0)", "mypy-boto3-dynamodb (>=1.39.0,<1.40.0)", "mypy-boto3-dynamodbstreams (>=1.39.0,<1.40.0)", "mypy-boto3-ebs (>=1.39.0,<1.40.0)", "mypy-boto3-ec2 (>=1.39.0,<1.40.0)", "mypy-boto3-ec2-instance-connect (>=1.39.0,<1.40.0)", "mypy-boto3-ecr (>=1.39.0,<1.40.0)", "mypy-boto3-ecr-public (>=1.39.0,<1.40.0)", "mypy-boto3-ecs (>=1.39.0,<1.40.0)", "mypy-boto3-efs (>=1.39.0,<1.40.0)", "mypy-boto3-eks (>=1.39.0,<1.40.0)", "mypy-boto3-eks-auth (>=1.39.0,<1.40.0)", "mypy-boto3-elasticache (>=1.39.0,<1.40.0)", "mypy-boto3-elasticbeanstalk (>=1.39.0,<1.40.0)", "mypy-boto3-elastictranscoder (>=1.39.0,<1.40.0)", "mypy-boto3-elb (>=1.39.0,<1.40.0)", "mypy-boto3-elbv2 (>=1.39.0,<1.40.0)", "mypy-boto3-emr (>=1.39.0,<1.40.0)", "mypy-boto3-emr-containers (>=1.39.0,<1.40.0)", "mypy-boto3-emr-serverless (>=1.39.0,<1.40.0)", "mypy-boto3-entityresolution (>=1.39.0,<1.40.0)", "mypy-boto3-es (>=1.39.0,<1.40.0)", "mypy-boto3-events (>=1.39.0,<1.40.0)", "mypy-boto3-evidently (>=1.39.0,<1.40.0)", "mypy-boto3-evs (>=1.39.0,<1.40.0)", "mypy-boto3-finspace (>=1.39.0,<1.40.0)", "mypy-boto3-finspace-data (>=1.39.0,<1.40.0)", "mypy-boto3-firehose (>=1.39.0,<1.40.0)", "mypy-boto3-fis (>=1.39.0,<1.40.0)", "mypy-boto3-fms (>=1.39.0,<1.40.0)", "mypy-boto3-forecast (>=1.39.0,<1.40.0)", "mypy-boto3-forecastquery (>=1.39.0,<1.40.0)", "mypy-boto3-frauddetector (>=1.39.0,<1.40.0)", "mypy-boto3-freetier (>=1.39.0,<1.40.0)", "mypy-boto3-fsx (>=1.39.0,<1.40.0)", "mypy-boto3-gamelift (>=1.39.0,<1.40.0)", "mypy-boto3-gameliftstreams (>=1.39.0,<1.40.0)", "mypy-boto3-geo-maps (>=1.39.0,<1.40.0)", "mypy-boto3-geo-places (>=1.39.0,<1.40.0)", "mypy-boto3-geo-routes (>=1.39.0,<1.40.0)", "mypy-boto3-glacier (>=1.39.0,<1.40.0)", "mypy-boto3-globalaccelerator (>=1.39.0,<1.40.0)", "mypy-boto3-glue (>=1.39.0,<1.40.0)", "mypy-boto3-grafana (>=1.39.0,<1.40.0)", "mypy-boto3-greengrass (>=1.39.0,<1.40.0)", "mypy-boto3-greengrassv2 (>=1.39.0,<1.40.0)", "mypy-boto3-groundstation (>=1.39.0,<1.40.0)", "mypy-boto3-guardduty (>=1.39.0,<1.40.0)", "mypy-boto3-health (>=1.39.0,<1.40.0)", "mypy-boto3-healthlake (>=1.39.0,<1.40.0)", "mypy-boto3-iam (>=1.39.0,<1.40.0)", "mypy-boto3-identitystore (>=1.39.0,<1.40.0)", "mypy-boto3-imagebuilder (>=1.39.0,<1.40.0)", "mypy-boto3-importexport (>=1.39.0,<1.40.0)", "mypy-boto3-inspector (>=1.39.0,<1.40.0)", "mypy-boto3-inspector-scan (>=1.39.0,<1.40.0)", "mypy-boto3-inspector2 (>=1.39.0,<1.40.0)", "mypy-boto3-internetmonitor (>=1.39.0,<1.40.0)", "mypy-boto3-invoicing (>=1.39.0,<1.40.0)", "mypy-boto3-iot (>=1.39.0,<1.40.0)", "mypy-boto3-iot-data (>=1.39.0,<1.40.0)", "mypy-boto3-iot-jobs-data (>=1.39.0,<1.40.0)", "mypy-boto3-iot-managed-integrations (>=1.39.0,<1.40.0)", "mypy-boto3-iotanalytics (>=1.39.0,<1.40.0)", "mypy-boto3-iotdeviceadvisor (>=1.39.0,<1.40.0)", "mypy-boto3-iotevents (>=1.39.0,<1.40.0)", "mypy-boto3-iotevents-data (>=1.39.0,<1.40.0)", "mypy-boto3-iotfleethub (>=1.39.0,<1.40.0)", "mypy-boto3-iotfleetwise (>=1.39.0,<1.40.0)", "mypy-boto3-iotsecuretunneling (>=1.39.0,<1.40.0)", "mypy-boto3-iotsitewise (>=1.39.0,<1.40.0)", "mypy-boto3-iotthingsgraph (>=1.39.0,<1.40.0)", "mypy-boto3-iottwinmaker (>=1.39.0,<1.40.0)", "mypy-boto3-iotwireless (>=1.39.0,<1.40.0)", "mypy-boto3-ivs (>=1.39.0,<1.40.0)", "mypy-boto3-ivs-realtime (>=1.39.0,<1.40.0)", "mypy-boto3-ivschat (>=1.39.0,<1.40.0)", "mypy-boto3-kafka (>=1.39.0,<1.40.0)", "mypy-boto3-kafkaconnect (>=1.39.0,<1.40.0)", "mypy-boto3-kendra (>=1.39.0,<1.40.0)", "mypy-boto3-kendra-ranking (>=1.39.0,<1.40.0)", "mypy-boto3-keyspaces (>=1.39.0,<1.40.0)", "mypy-boto3-keyspacesstreams (>=1.39.0,<1.40.0)", "mypy-boto3-kinesis (>=1.39.0,<1.40.0)", "mypy-boto3-kinesis-video-archived-media (>=1.39.0,<1.40.0)", "mypy-boto3-kinesis-video-media (>=1.39.0,<1.40.0)", "mypy-boto3-kinesis-video-signaling (>=1.39.0,<1.40.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.39.0,<1.40.0)", "mypy-boto3-kinesisanalytics (>=1.39.0,<1.40.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.39.0,<1.40.0)", "mypy-boto3-kinesisvideo (>=1.39.0,<1.40.0)", "mypy-boto3-kms (>=1.39.0,<1.40.0)", "mypy-boto3-lakeformation (>=1.39.0,<1.40.0)", "mypy-boto3-lambda (>=1.39.0,<1.40.0)", "mypy-boto3-launch-wizard (>=1.39.0,<1.40.0)", "mypy-boto3-lex-models (>=1.39.0,<1.40.0)", "mypy-boto3-lex-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-lexv2-models (>=1.39.0,<1.40.0)", "mypy-boto3-lexv2-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-license-manager (>=1.39.0,<1.40.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.39.0,<1.40.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.39.0,<1.40.0)", "mypy-boto3-lightsail (>=1.39.0,<1.40.0)", "mypy-boto3-location (>=1.39.0,<1.40.0)", "mypy-boto3-logs (>=1.39.0,<1.40.0)", "mypy-boto3-lookoutequipment (>=1.39.0,<1.40.0)", "mypy-boto3-lookoutmetrics (>=1.39.0,<1.40.0)", "mypy-boto3-lookoutvision (>=1.39.0,<1.40.0)", "mypy-boto3-m2 (>=1.39.0,<1.40.0)", "mypy-boto3-machinelearning (>=1.39.0,<1.40.0)", "mypy-boto3-macie2 (>=1.39.0,<1.40.0)", "mypy-boto3-mailmanager (>=1.39.0,<1.40.0)", "mypy-boto3-managedblockchain (>=1.39.0,<1.40.0)", "mypy-boto3-managedblockchain-query (>=1.39.0,<1.40.0)", "mypy-boto3-marketplace-agreement (>=1.39.0,<1.40.0)", "mypy-boto3-marketplace-catalog (>=1.39.0,<1.40.0)", "mypy-boto3-marketplace-deployment (>=1.39.0,<1.40.0)", "mypy-boto3-marketplace-entitlement (>=1.39.0,<1.40.0)", "mypy-boto3-marketplace-reporting (>=1.39.0,<1.40.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.39.0,<1.40.0)", "mypy-boto3-mediaconnect (>=1.39.0,<1.40.0)", "mypy-boto3-mediaconvert (>=1.39.0,<1.40.0)", "mypy-boto3-medialive (>=1.39.0,<1.40.0)", "mypy-boto3-mediapackage (>=1.39.0,<1.40.0)", "mypy-boto3-mediapackage-vod (>=1.39.0,<1.40.0)", "mypy-boto3-mediapackagev2 (>=1.39.0,<1.40.0)", "mypy-boto3-mediastore (>=1.39.0,<1.40.0)", "mypy-boto3-mediastore-data (>=1.39.0,<1.40.0)", "mypy-boto3-mediatailor (>=1.39.0,<1.40.0)", "mypy-boto3-medical-imaging (>=1.39.0,<1.40.0)", "mypy-boto3-memorydb (>=1.39.0,<1.40.0)", "mypy-boto3-meteringmarketplace (>=1.39.0,<1.40.0)", "mypy-boto3-mgh (>=1.39.0,<1.40.0)", "mypy-boto3-mgn (>=1.39.0,<1.40.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.39.0,<1.40.0)", "mypy-boto3-migrationhub-config (>=1.39.0,<1.40.0)", "mypy-boto3-migrationhuborchestrator (>=1.39.0,<1.40.0)", "mypy-boto3-migrationhubstrategy (>=1.39.0,<1.40.0)", "mypy-boto3-mpa (>=1.39.0,<1.40.0)", "mypy-boto3-mq (>=1.39.0,<1.40.0)", "mypy-boto3-mturk (>=1.39.0,<1.40.0)", "mypy-boto3-mwaa (>=1.39.0,<1.40.0)", "mypy-boto3-neptune (>=1.39.0,<1.40.0)", "mypy-boto3-neptune-graph (>=1.39.0,<1.40.0)", "mypy-boto3-neptunedata (>=1.39.0,<1.40.0)", "mypy-boto3-network-firewall (>=1.39.0,<1.40.0)", "mypy-boto3-networkflowmonitor (>=1.39.0,<1.40.0)", "mypy-boto3-networkmanager (>=1.39.0,<1.40.0)", "mypy-boto3-networkmonitor (>=1.39.0,<1.40.0)", "mypy-boto3-notifications (>=1.39.0,<1.40.0)", "mypy-boto3-notificationscontacts (>=1.39.0,<1.40.0)", "mypy-boto3-oam (>=1.39.0,<1.40.0)", "mypy-boto3-observabilityadmin (>=1.39.0,<1.40.0)", "mypy-boto3-odb (>=1.39.0,<1.40.0)", "mypy-boto3-omics (>=1.39.0,<1.40.0)", "mypy-boto3-opensearch (>=1.39.0,<1.40.0)", "mypy-boto3-opensearchserverless (>=1.39.0,<1.40.0)", "mypy-boto3-opsworks (>=1.39.0,<1.40.0)", "mypy-boto3-opsworkscm (>=1.39.0,<1.40.0)", "mypy-boto3-organizations (>=1.39.0,<1.40.0)", "mypy-boto3-osis (>=1.39.0,<1.40.0)", "mypy-boto3-outposts (>=1.39.0,<1.40.0)", "mypy-boto3-panorama (>=1.39.0,<1.40.0)", "mypy-boto3-partnercentral-selling (>=1.39.0,<1.40.0)", "mypy-boto3-payment-cryptography (>=1.39.0,<1.40.0)", "mypy-boto3-payment-cryptography-data (>=1.39.0,<1.40.0)", "mypy-boto3-pca-connector-ad (>=1.39.0,<1.40.0)", "mypy-boto3-pca-connector-scep (>=1.39.0,<1.40.0)", "mypy-boto3-pcs (>=1.39.0,<1.40.0)", "mypy-boto3-personalize (>=1.39.0,<1.40.0)", "mypy-boto3-personalize-events (>=1.39.0,<1.40.0)", "mypy-boto3-personalize-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-pi (>=1.39.0,<1.40.0)", "mypy-boto3-pinpoint (>=1.39.0,<1.40.0)", "mypy-boto3-pinpoint-email (>=1.39.0,<1.40.0)", "mypy-boto3-pinpoint-sms-voice (>=1.39.0,<1.40.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.39.0,<1.40.0)", "mypy-boto3-pipes (>=1.39.0,<1.40.0)", "mypy-boto3-polly (>=1.39.0,<1.40.0)", "mypy-boto3-pricing (>=1.39.0,<1.40.0)", "mypy-boto3-proton (>=1.39.0,<1.40.0)", "mypy-boto3-qapps (>=1.39.0,<1.40.0)", "mypy-boto3-qbusiness (>=1.39.0,<1.40.0)", "mypy-boto3-qconnect (>=1.39.0,<1.40.0)", "mypy-boto3-qldb (>=1.39.0,<1.40.0)", "mypy-boto3-qldb-session (>=1.39.0,<1.40.0)", "mypy-boto3-quicksight (>=1.39.0,<1.40.0)", "mypy-boto3-ram (>=1.39.0,<1.40.0)", "mypy-boto3-rbin (>=1.39.0,<1.40.0)", "mypy-boto3-rds (>=1.39.0,<1.40.0)", "mypy-boto3-rds-data (>=1.39.0,<1.40.0)", "mypy-boto3-redshift (>=1.39.0,<1.40.0)", "mypy-boto3-redshift-data (>=1.39.0,<1.40.0)", "mypy-boto3-redshift-serverless (>=1.39.0,<1.40.0)", "mypy-boto3-rekognition (>=1.39.0,<1.40.0)", "mypy-boto3-repostspace (>=1.39.0,<1.40.0)", "mypy-boto3-resiliencehub (>=1.39.0,<1.40.0)", "mypy-boto3-resource-explorer-2 (>=1.39.0,<1.40.0)", "mypy-boto3-resource-groups (>=1.39.0,<1.40.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.39.0,<1.40.0)", "mypy-boto3-robomaker (>=1.39.0,<1.40.0)", "mypy-boto3-rolesanywhere (>=1.39.0,<1.40.0)", "mypy-boto3-route53 (>=1.39.0,<1.40.0)", "mypy-boto3-route53-recovery-cluster (>=1.39.0,<1.40.0)", "mypy-boto3-route53-recovery-control-config (>=1.39.0,<1.40.0)", "mypy-boto3-route53-recovery-readiness (>=1.39.0,<1.40.0)", "mypy-boto3-route53domains (>=1.39.0,<1.40.0)", "mypy-boto3-route53profiles (>=1.39.0,<1.40.0)", "mypy-boto3-route53resolver (>=1.39.0,<1.40.0)", "mypy-boto3-rum (>=1.39.0,<1.40.0)", "mypy-boto3-s3 (>=1.39.0,<1.40.0)", "mypy-boto3-s3control (>=1.39.0,<1.40.0)", "mypy-boto3-s3outposts (>=1.39.0,<1.40.0)", "mypy-boto3-s3tables (>=1.39.0,<1.40.0)", "mypy-boto3-s3vectors (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-edge (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-geospatial (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-metrics (>=1.39.0,<1.40.0)", "mypy-boto3-sagemaker-runtime (>=1.39.0,<1.40.0)", "mypy-boto3-savingsplans (>=1.39.0,<1.40.0)", "mypy-boto3-scheduler (>=1.39.0,<1.40.0)", "mypy-boto3-schemas (>=1.39.0,<1.40.0)", "mypy-boto3-sdb (>=1.39.0,<1.40.0)", "mypy-boto3-secretsmanager (>=1.39.0,<1.40.0)", "mypy-boto3-security-ir (>=1.39.0,<1.40.0)", "mypy-boto3-securityhub (>=1.39.0,<1.40.0)", "mypy-boto3-securitylake (>=1.39.0,<1.40.0)", "mypy-boto3-serverlessrepo (>=1.39.0,<1.40.0)", "mypy-boto3-service-quotas (>=1.39.0,<1.40.0)", "mypy-boto3-servicecatalog (>=1.39.0,<1.40.0)", "mypy-boto3-servicecatalog-appregistry (>=1.39.0,<1.40.0)", "mypy-boto3-servicediscovery (>=1.39.0,<1.40.0)", "mypy-boto3-ses (>=1.39.0,<1.40.0)", "mypy-boto3-sesv2 (>=1.39.0,<1.40.0)", "mypy-boto3-shield (>=1.39.0,<1.40.0)", "mypy-boto3-signer (>=1.39.0,<1.40.0)", "mypy-boto3-simspaceweaver (>=1.39.0,<1.40.0)", "mypy-boto3-sms (>=1.39.0,<1.40.0)", "mypy-boto3-snow-device-management (>=1.39.0,<1.40.0)", "mypy-boto3-snowball (>=1.39.0,<1.40.0)", "mypy-boto3-sns (>=1.39.0,<1.40.0)", "mypy-boto3-socialmessaging (>=1.39.0,<1.40.0)", "mypy-boto3-sqs (>=1.39.0,<1.40.0)", "mypy-boto3-ssm (>=1.39.0,<1.40.0)", "mypy-boto3-ssm-contacts (>=1.39.0,<1.40.0)", "mypy-boto3-ssm-guiconnect (>=1.39.0,<1.40.0)", "mypy-boto3-ssm-incidents (>=1.39.0,<1.40.0)", "mypy-boto3-ssm-quicksetup (>=1.39.0,<1.40.0)", "mypy-boto3-ssm-sap (>=1.39.0,<1.40.0)", "mypy-boto3-sso (>=1.39.0,<1.40.0)", "mypy-boto3-sso-admin (>=1.39.0,<1.40.0)", "mypy-boto3-sso-oidc (>=1.39.0,<1.40.0)", "mypy-boto3-stepfunctions (>=1.39.0,<1.40.0)", "mypy-boto3-storagegateway (>=1.39.0,<1.40.0)", "mypy-boto3-sts (>=1.39.0,<1.40.0)", "mypy-boto3-supplychain (>=1.39.0,<1.40.0)", "mypy-boto3-support (>=1.39.0,<1.40.0)", "mypy-boto3-support-app (>=1.39.0,<1.40.0)", "mypy-boto3-swf (>=1.39.0,<1.40.0)", "mypy-boto3-synthetics (>=1.39.0,<1.40.0)", "mypy-boto3-taxsettings (>=1.39.0,<1.40.0)", "mypy-boto3-textract (>=1.39.0,<1.40.0)", "mypy-boto3-timestream-influxdb (>=1.39.0,<1.40.0)", "mypy-boto3-timestream-query (>=1.39.0,<1.40.0)", "mypy-boto3-timestream-write (>=1.39.0,<1.40.0)", "mypy-boto3-tnb (>=1.39.0,<1.40.0)", "mypy-boto3-transcribe (>=1.39.0,<1.40.0)", "mypy-boto3-transfer (>=1.39.0,<1.40.0)", "mypy-boto3-translate (>=1.39.0,<1.40.0)", "mypy-boto3-trustedadvisor (>=1.39.0,<1.40.0)", "mypy-boto3-verifiedpermissions (>=1.39.0,<1.40.0)", "mypy-boto3-voice-id (>=1.39.0,<1.40.0)", "mypy-boto3-vpc-lattice (>=1.39.0,<1.40.0)", "mypy-boto3-waf (>=1.39.0,<1.40.0)", "mypy-boto3-waf-regional (>=1.39.0,<1.40.0)", "mypy-boto3-wafv2 (>=1.39.0,<1.40.0)", "mypy-boto3-wellarchitected (>=1.39.0,<1.40.0)", "mypy-boto3-wisdom (>=1.39.0,<1.40.0)", "mypy-boto3-workdocs (>=1.39.0,<1.40.0)", "mypy-boto3-workmail (>=1.39.0,<1.40.0)", "mypy-boto3-workmailmessageflow (>=1.39.0,<1.40.0)", "mypy-boto3-workspaces (>=1.39.0,<1.40.0)", "mypy-boto3-workspaces-instances (>=1.39.0,<1.40.0)", "mypy-boto3-workspaces-thin-client (>=1.39.0,<1.40.0)", "mypy-boto3-workspaces-web (>=1.39.0,<1.40.0)", "mypy-boto3-xray (>=1.39.0,<1.40.0)"] -amp = ["mypy-boto3-amp (>=1.39.0,<1.40.0)"] -amplify = ["mypy-boto3-amplify (>=1.39.0,<1.40.0)"] -amplifybackend = ["mypy-boto3-amplifybackend (>=1.39.0,<1.40.0)"] -amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.39.0,<1.40.0)"] -apigateway = ["mypy-boto3-apigateway (>=1.39.0,<1.40.0)"] -apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.39.0,<1.40.0)"] -apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.39.0,<1.40.0)"] -appconfig = ["mypy-boto3-appconfig (>=1.39.0,<1.40.0)"] -appconfigdata = ["mypy-boto3-appconfigdata (>=1.39.0,<1.40.0)"] -appfabric = ["mypy-boto3-appfabric (>=1.39.0,<1.40.0)"] -appflow = ["mypy-boto3-appflow (>=1.39.0,<1.40.0)"] -appintegrations = ["mypy-boto3-appintegrations (>=1.39.0,<1.40.0)"] -application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.39.0,<1.40.0)"] -application-insights = ["mypy-boto3-application-insights (>=1.39.0,<1.40.0)"] -application-signals = ["mypy-boto3-application-signals (>=1.39.0,<1.40.0)"] -applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.39.0,<1.40.0)"] -appmesh = ["mypy-boto3-appmesh (>=1.39.0,<1.40.0)"] -apprunner = ["mypy-boto3-apprunner (>=1.39.0,<1.40.0)"] -appstream = ["mypy-boto3-appstream (>=1.39.0,<1.40.0)"] -appsync = ["mypy-boto3-appsync (>=1.39.0,<1.40.0)"] -apptest = ["mypy-boto3-apptest (>=1.39.0,<1.40.0)"] -arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.39.0,<1.40.0)"] -artifact = ["mypy-boto3-artifact (>=1.39.0,<1.40.0)"] -athena = ["mypy-boto3-athena (>=1.39.0,<1.40.0)"] -auditmanager = ["mypy-boto3-auditmanager (>=1.39.0,<1.40.0)"] -autoscaling = ["mypy-boto3-autoscaling (>=1.39.0,<1.40.0)"] -autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.39.0,<1.40.0)"] -b2bi = ["mypy-boto3-b2bi (>=1.39.0,<1.40.0)"] -backup = ["mypy-boto3-backup (>=1.39.0,<1.40.0)"] -backup-gateway = ["mypy-boto3-backup-gateway (>=1.39.0,<1.40.0)"] -backupsearch = ["mypy-boto3-backupsearch (>=1.39.0,<1.40.0)"] -batch = ["mypy-boto3-batch (>=1.39.0,<1.40.0)"] -bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.39.0,<1.40.0)"] -bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.39.0,<1.40.0)"] -bedrock = ["mypy-boto3-bedrock (>=1.39.0,<1.40.0)"] -bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.39.0,<1.40.0)"] -bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.39.0,<1.40.0)"] -bedrock-agentcore = ["mypy-boto3-bedrock-agentcore (>=1.39.0,<1.40.0)"] -bedrock-agentcore-control = ["mypy-boto3-bedrock-agentcore-control (>=1.39.0,<1.40.0)"] -bedrock-data-automation = ["mypy-boto3-bedrock-data-automation (>=1.39.0,<1.40.0)"] -bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (>=1.39.0,<1.40.0)"] -bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.39.0,<1.40.0)"] -billing = ["mypy-boto3-billing (>=1.39.0,<1.40.0)"] -billingconductor = ["mypy-boto3-billingconductor (>=1.39.0,<1.40.0)"] -boto3 = ["boto3 (==1.39.17)"] -braket = ["mypy-boto3-braket (>=1.39.0,<1.40.0)"] -budgets = ["mypy-boto3-budgets (>=1.39.0,<1.40.0)"] -ce = ["mypy-boto3-ce (>=1.39.0,<1.40.0)"] -chatbot = ["mypy-boto3-chatbot (>=1.39.0,<1.40.0)"] -chime = ["mypy-boto3-chime (>=1.39.0,<1.40.0)"] -chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.39.0,<1.40.0)"] -chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.39.0,<1.40.0)"] -chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.39.0,<1.40.0)"] -chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.39.0,<1.40.0)"] -chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.39.0,<1.40.0)"] -cleanrooms = ["mypy-boto3-cleanrooms (>=1.39.0,<1.40.0)"] -cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.39.0,<1.40.0)"] -cloud9 = ["mypy-boto3-cloud9 (>=1.39.0,<1.40.0)"] -cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.39.0,<1.40.0)"] -clouddirectory = ["mypy-boto3-clouddirectory (>=1.39.0,<1.40.0)"] -cloudformation = ["mypy-boto3-cloudformation (>=1.39.0,<1.40.0)"] -cloudfront = ["mypy-boto3-cloudfront (>=1.39.0,<1.40.0)"] -cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.39.0,<1.40.0)"] -cloudhsm = ["mypy-boto3-cloudhsm (>=1.39.0,<1.40.0)"] -cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.39.0,<1.40.0)"] -cloudsearch = ["mypy-boto3-cloudsearch (>=1.39.0,<1.40.0)"] -cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.39.0,<1.40.0)"] -cloudtrail = ["mypy-boto3-cloudtrail (>=1.39.0,<1.40.0)"] -cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.39.0,<1.40.0)"] -cloudwatch = ["mypy-boto3-cloudwatch (>=1.39.0,<1.40.0)"] -codeartifact = ["mypy-boto3-codeartifact (>=1.39.0,<1.40.0)"] -codebuild = ["mypy-boto3-codebuild (>=1.39.0,<1.40.0)"] -codecatalyst = ["mypy-boto3-codecatalyst (>=1.39.0,<1.40.0)"] -codecommit = ["mypy-boto3-codecommit (>=1.39.0,<1.40.0)"] -codeconnections = ["mypy-boto3-codeconnections (>=1.39.0,<1.40.0)"] -codedeploy = ["mypy-boto3-codedeploy (>=1.39.0,<1.40.0)"] -codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.39.0,<1.40.0)"] -codeguru-security = ["mypy-boto3-codeguru-security (>=1.39.0,<1.40.0)"] -codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.39.0,<1.40.0)"] -codepipeline = ["mypy-boto3-codepipeline (>=1.39.0,<1.40.0)"] -codestar-connections = ["mypy-boto3-codestar-connections (>=1.39.0,<1.40.0)"] -codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.39.0,<1.40.0)"] -cognito-identity = ["mypy-boto3-cognito-identity (>=1.39.0,<1.40.0)"] -cognito-idp = ["mypy-boto3-cognito-idp (>=1.39.0,<1.40.0)"] -cognito-sync = ["mypy-boto3-cognito-sync (>=1.39.0,<1.40.0)"] -comprehend = ["mypy-boto3-comprehend (>=1.39.0,<1.40.0)"] -comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.39.0,<1.40.0)"] -compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.39.0,<1.40.0)"] -config = ["mypy-boto3-config (>=1.39.0,<1.40.0)"] -connect = ["mypy-boto3-connect (>=1.39.0,<1.40.0)"] -connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.39.0,<1.40.0)"] -connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.39.0,<1.40.0)"] -connectcampaignsv2 = ["mypy-boto3-connectcampaignsv2 (>=1.39.0,<1.40.0)"] -connectcases = ["mypy-boto3-connectcases (>=1.39.0,<1.40.0)"] -connectparticipant = ["mypy-boto3-connectparticipant (>=1.39.0,<1.40.0)"] -controlcatalog = ["mypy-boto3-controlcatalog (>=1.39.0,<1.40.0)"] -controltower = ["mypy-boto3-controltower (>=1.39.0,<1.40.0)"] -cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.39.0,<1.40.0)"] -cur = ["mypy-boto3-cur (>=1.39.0,<1.40.0)"] -customer-profiles = ["mypy-boto3-customer-profiles (>=1.39.0,<1.40.0)"] -databrew = ["mypy-boto3-databrew (>=1.39.0,<1.40.0)"] -dataexchange = ["mypy-boto3-dataexchange (>=1.39.0,<1.40.0)"] -datapipeline = ["mypy-boto3-datapipeline (>=1.39.0,<1.40.0)"] -datasync = ["mypy-boto3-datasync (>=1.39.0,<1.40.0)"] -datazone = ["mypy-boto3-datazone (>=1.39.0,<1.40.0)"] -dax = ["mypy-boto3-dax (>=1.39.0,<1.40.0)"] -deadline = ["mypy-boto3-deadline (>=1.39.0,<1.40.0)"] -detective = ["mypy-boto3-detective (>=1.39.0,<1.40.0)"] -devicefarm = ["mypy-boto3-devicefarm (>=1.39.0,<1.40.0)"] -devops-guru = ["mypy-boto3-devops-guru (>=1.39.0,<1.40.0)"] -directconnect = ["mypy-boto3-directconnect (>=1.39.0,<1.40.0)"] -discovery = ["mypy-boto3-discovery (>=1.39.0,<1.40.0)"] -dlm = ["mypy-boto3-dlm (>=1.39.0,<1.40.0)"] -dms = ["mypy-boto3-dms (>=1.39.0,<1.40.0)"] -docdb = ["mypy-boto3-docdb (>=1.39.0,<1.40.0)"] -docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.39.0,<1.40.0)"] -drs = ["mypy-boto3-drs (>=1.39.0,<1.40.0)"] -ds = ["mypy-boto3-ds (>=1.39.0,<1.40.0)"] -ds-data = ["mypy-boto3-ds-data (>=1.39.0,<1.40.0)"] -dsql = ["mypy-boto3-dsql (>=1.39.0,<1.40.0)"] -dynamodb = ["mypy-boto3-dynamodb (>=1.39.0,<1.40.0)"] -dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.39.0,<1.40.0)"] -ebs = ["mypy-boto3-ebs (>=1.39.0,<1.40.0)"] -ec2 = ["mypy-boto3-ec2 (>=1.39.0,<1.40.0)"] -ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.39.0,<1.40.0)"] -ecr = ["mypy-boto3-ecr (>=1.39.0,<1.40.0)"] -ecr-public = ["mypy-boto3-ecr-public (>=1.39.0,<1.40.0)"] -ecs = ["mypy-boto3-ecs (>=1.39.0,<1.40.0)"] -efs = ["mypy-boto3-efs (>=1.39.0,<1.40.0)"] -eks = ["mypy-boto3-eks (>=1.39.0,<1.40.0)"] -eks-auth = ["mypy-boto3-eks-auth (>=1.39.0,<1.40.0)"] -elasticache = ["mypy-boto3-elasticache (>=1.39.0,<1.40.0)"] -elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.39.0,<1.40.0)"] -elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.39.0,<1.40.0)"] -elb = ["mypy-boto3-elb (>=1.39.0,<1.40.0)"] -elbv2 = ["mypy-boto3-elbv2 (>=1.39.0,<1.40.0)"] -emr = ["mypy-boto3-emr (>=1.39.0,<1.40.0)"] -emr-containers = ["mypy-boto3-emr-containers (>=1.39.0,<1.40.0)"] -emr-serverless = ["mypy-boto3-emr-serverless (>=1.39.0,<1.40.0)"] -entityresolution = ["mypy-boto3-entityresolution (>=1.39.0,<1.40.0)"] -es = ["mypy-boto3-es (>=1.39.0,<1.40.0)"] -essential = ["mypy-boto3-cloudformation (>=1.39.0,<1.40.0)", "mypy-boto3-dynamodb (>=1.39.0,<1.40.0)", "mypy-boto3-ec2 (>=1.39.0,<1.40.0)", "mypy-boto3-lambda (>=1.39.0,<1.40.0)", "mypy-boto3-rds (>=1.39.0,<1.40.0)", "mypy-boto3-s3 (>=1.39.0,<1.40.0)", "mypy-boto3-sqs (>=1.39.0,<1.40.0)"] -events = ["mypy-boto3-events (>=1.39.0,<1.40.0)"] -evidently = ["mypy-boto3-evidently (>=1.39.0,<1.40.0)"] -evs = ["mypy-boto3-evs (>=1.39.0,<1.40.0)"] -finspace = ["mypy-boto3-finspace (>=1.39.0,<1.40.0)"] -finspace-data = ["mypy-boto3-finspace-data (>=1.39.0,<1.40.0)"] -firehose = ["mypy-boto3-firehose (>=1.39.0,<1.40.0)"] -fis = ["mypy-boto3-fis (>=1.39.0,<1.40.0)"] -fms = ["mypy-boto3-fms (>=1.39.0,<1.40.0)"] -forecast = ["mypy-boto3-forecast (>=1.39.0,<1.40.0)"] -forecastquery = ["mypy-boto3-forecastquery (>=1.39.0,<1.40.0)"] -frauddetector = ["mypy-boto3-frauddetector (>=1.39.0,<1.40.0)"] -freetier = ["mypy-boto3-freetier (>=1.39.0,<1.40.0)"] -fsx = ["mypy-boto3-fsx (>=1.39.0,<1.40.0)"] -full = ["boto3-stubs-full (>=1.39.0,<1.40.0)"] -gamelift = ["mypy-boto3-gamelift (>=1.39.0,<1.40.0)"] -gameliftstreams = ["mypy-boto3-gameliftstreams (>=1.39.0,<1.40.0)"] -geo-maps = ["mypy-boto3-geo-maps (>=1.39.0,<1.40.0)"] -geo-places = ["mypy-boto3-geo-places (>=1.39.0,<1.40.0)"] -geo-routes = ["mypy-boto3-geo-routes (>=1.39.0,<1.40.0)"] -glacier = ["mypy-boto3-glacier (>=1.39.0,<1.40.0)"] -globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.39.0,<1.40.0)"] -glue = ["mypy-boto3-glue (>=1.39.0,<1.40.0)"] -grafana = ["mypy-boto3-grafana (>=1.39.0,<1.40.0)"] -greengrass = ["mypy-boto3-greengrass (>=1.39.0,<1.40.0)"] -greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.39.0,<1.40.0)"] -groundstation = ["mypy-boto3-groundstation (>=1.39.0,<1.40.0)"] -guardduty = ["mypy-boto3-guardduty (>=1.39.0,<1.40.0)"] -health = ["mypy-boto3-health (>=1.39.0,<1.40.0)"] -healthlake = ["mypy-boto3-healthlake (>=1.39.0,<1.40.0)"] -iam = ["mypy-boto3-iam (>=1.39.0,<1.40.0)"] -identitystore = ["mypy-boto3-identitystore (>=1.39.0,<1.40.0)"] -imagebuilder = ["mypy-boto3-imagebuilder (>=1.39.0,<1.40.0)"] -importexport = ["mypy-boto3-importexport (>=1.39.0,<1.40.0)"] -inspector = ["mypy-boto3-inspector (>=1.39.0,<1.40.0)"] -inspector-scan = ["mypy-boto3-inspector-scan (>=1.39.0,<1.40.0)"] -inspector2 = ["mypy-boto3-inspector2 (>=1.39.0,<1.40.0)"] -internetmonitor = ["mypy-boto3-internetmonitor (>=1.39.0,<1.40.0)"] -invoicing = ["mypy-boto3-invoicing (>=1.39.0,<1.40.0)"] -iot = ["mypy-boto3-iot (>=1.39.0,<1.40.0)"] -iot-data = ["mypy-boto3-iot-data (>=1.39.0,<1.40.0)"] -iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.39.0,<1.40.0)"] -iot-managed-integrations = ["mypy-boto3-iot-managed-integrations (>=1.39.0,<1.40.0)"] -iotanalytics = ["mypy-boto3-iotanalytics (>=1.39.0,<1.40.0)"] -iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.39.0,<1.40.0)"] -iotevents = ["mypy-boto3-iotevents (>=1.39.0,<1.40.0)"] -iotevents-data = ["mypy-boto3-iotevents-data (>=1.39.0,<1.40.0)"] -iotfleethub = ["mypy-boto3-iotfleethub (>=1.39.0,<1.40.0)"] -iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.39.0,<1.40.0)"] -iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.39.0,<1.40.0)"] -iotsitewise = ["mypy-boto3-iotsitewise (>=1.39.0,<1.40.0)"] -iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.39.0,<1.40.0)"] -iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.39.0,<1.40.0)"] -iotwireless = ["mypy-boto3-iotwireless (>=1.39.0,<1.40.0)"] -ivs = ["mypy-boto3-ivs (>=1.39.0,<1.40.0)"] -ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.39.0,<1.40.0)"] -ivschat = ["mypy-boto3-ivschat (>=1.39.0,<1.40.0)"] -kafka = ["mypy-boto3-kafka (>=1.39.0,<1.40.0)"] -kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.39.0,<1.40.0)"] -kendra = ["mypy-boto3-kendra (>=1.39.0,<1.40.0)"] -kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.39.0,<1.40.0)"] -keyspaces = ["mypy-boto3-keyspaces (>=1.39.0,<1.40.0)"] -keyspacesstreams = ["mypy-boto3-keyspacesstreams (>=1.39.0,<1.40.0)"] -kinesis = ["mypy-boto3-kinesis (>=1.39.0,<1.40.0)"] -kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.39.0,<1.40.0)"] -kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.39.0,<1.40.0)"] -kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.39.0,<1.40.0)"] -kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.39.0,<1.40.0)"] -kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.39.0,<1.40.0)"] -kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.39.0,<1.40.0)"] -kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.39.0,<1.40.0)"] -kms = ["mypy-boto3-kms (>=1.39.0,<1.40.0)"] -lakeformation = ["mypy-boto3-lakeformation (>=1.39.0,<1.40.0)"] -lambda = ["mypy-boto3-lambda (>=1.39.0,<1.40.0)"] -launch-wizard = ["mypy-boto3-launch-wizard (>=1.39.0,<1.40.0)"] -lex-models = ["mypy-boto3-lex-models (>=1.39.0,<1.40.0)"] -lex-runtime = ["mypy-boto3-lex-runtime (>=1.39.0,<1.40.0)"] -lexv2-models = ["mypy-boto3-lexv2-models (>=1.39.0,<1.40.0)"] -lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.39.0,<1.40.0)"] -license-manager = ["mypy-boto3-license-manager (>=1.39.0,<1.40.0)"] -license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.39.0,<1.40.0)"] -license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.39.0,<1.40.0)"] -lightsail = ["mypy-boto3-lightsail (>=1.39.0,<1.40.0)"] -location = ["mypy-boto3-location (>=1.39.0,<1.40.0)"] -logs = ["mypy-boto3-logs (>=1.39.0,<1.40.0)"] -lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.39.0,<1.40.0)"] -lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.39.0,<1.40.0)"] -lookoutvision = ["mypy-boto3-lookoutvision (>=1.39.0,<1.40.0)"] -m2 = ["mypy-boto3-m2 (>=1.39.0,<1.40.0)"] -machinelearning = ["mypy-boto3-machinelearning (>=1.39.0,<1.40.0)"] -macie2 = ["mypy-boto3-macie2 (>=1.39.0,<1.40.0)"] -mailmanager = ["mypy-boto3-mailmanager (>=1.39.0,<1.40.0)"] -managedblockchain = ["mypy-boto3-managedblockchain (>=1.39.0,<1.40.0)"] -managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.39.0,<1.40.0)"] -marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.39.0,<1.40.0)"] -marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.39.0,<1.40.0)"] -marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.39.0,<1.40.0)"] -marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.39.0,<1.40.0)"] -marketplace-reporting = ["mypy-boto3-marketplace-reporting (>=1.39.0,<1.40.0)"] -marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.39.0,<1.40.0)"] -mediaconnect = ["mypy-boto3-mediaconnect (>=1.39.0,<1.40.0)"] -mediaconvert = ["mypy-boto3-mediaconvert (>=1.39.0,<1.40.0)"] -medialive = ["mypy-boto3-medialive (>=1.39.0,<1.40.0)"] -mediapackage = ["mypy-boto3-mediapackage (>=1.39.0,<1.40.0)"] -mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.39.0,<1.40.0)"] -mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.39.0,<1.40.0)"] -mediastore = ["mypy-boto3-mediastore (>=1.39.0,<1.40.0)"] -mediastore-data = ["mypy-boto3-mediastore-data (>=1.39.0,<1.40.0)"] -mediatailor = ["mypy-boto3-mediatailor (>=1.39.0,<1.40.0)"] -medical-imaging = ["mypy-boto3-medical-imaging (>=1.39.0,<1.40.0)"] -memorydb = ["mypy-boto3-memorydb (>=1.39.0,<1.40.0)"] -meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.39.0,<1.40.0)"] -mgh = ["mypy-boto3-mgh (>=1.39.0,<1.40.0)"] -mgn = ["mypy-boto3-mgn (>=1.39.0,<1.40.0)"] -migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.39.0,<1.40.0)"] -migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.39.0,<1.40.0)"] -migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.39.0,<1.40.0)"] -migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.39.0,<1.40.0)"] -mpa = ["mypy-boto3-mpa (>=1.39.0,<1.40.0)"] -mq = ["mypy-boto3-mq (>=1.39.0,<1.40.0)"] -mturk = ["mypy-boto3-mturk (>=1.39.0,<1.40.0)"] -mwaa = ["mypy-boto3-mwaa (>=1.39.0,<1.40.0)"] -neptune = ["mypy-boto3-neptune (>=1.39.0,<1.40.0)"] -neptune-graph = ["mypy-boto3-neptune-graph (>=1.39.0,<1.40.0)"] -neptunedata = ["mypy-boto3-neptunedata (>=1.39.0,<1.40.0)"] -network-firewall = ["mypy-boto3-network-firewall (>=1.39.0,<1.40.0)"] -networkflowmonitor = ["mypy-boto3-networkflowmonitor (>=1.39.0,<1.40.0)"] -networkmanager = ["mypy-boto3-networkmanager (>=1.39.0,<1.40.0)"] -networkmonitor = ["mypy-boto3-networkmonitor (>=1.39.0,<1.40.0)"] -notifications = ["mypy-boto3-notifications (>=1.39.0,<1.40.0)"] -notificationscontacts = ["mypy-boto3-notificationscontacts (>=1.39.0,<1.40.0)"] -oam = ["mypy-boto3-oam (>=1.39.0,<1.40.0)"] -observabilityadmin = ["mypy-boto3-observabilityadmin (>=1.39.0,<1.40.0)"] -odb = ["mypy-boto3-odb (>=1.39.0,<1.40.0)"] -omics = ["mypy-boto3-omics (>=1.39.0,<1.40.0)"] -opensearch = ["mypy-boto3-opensearch (>=1.39.0,<1.40.0)"] -opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.39.0,<1.40.0)"] -opsworks = ["mypy-boto3-opsworks (>=1.39.0,<1.40.0)"] -opsworkscm = ["mypy-boto3-opsworkscm (>=1.39.0,<1.40.0)"] -organizations = ["mypy-boto3-organizations (>=1.39.0,<1.40.0)"] -osis = ["mypy-boto3-osis (>=1.39.0,<1.40.0)"] -outposts = ["mypy-boto3-outposts (>=1.39.0,<1.40.0)"] -panorama = ["mypy-boto3-panorama (>=1.39.0,<1.40.0)"] -partnercentral-selling = ["mypy-boto3-partnercentral-selling (>=1.39.0,<1.40.0)"] -payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.39.0,<1.40.0)"] -payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.39.0,<1.40.0)"] -pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.39.0,<1.40.0)"] -pca-connector-scep = ["mypy-boto3-pca-connector-scep (>=1.39.0,<1.40.0)"] -pcs = ["mypy-boto3-pcs (>=1.39.0,<1.40.0)"] -personalize = ["mypy-boto3-personalize (>=1.39.0,<1.40.0)"] -personalize-events = ["mypy-boto3-personalize-events (>=1.39.0,<1.40.0)"] -personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.39.0,<1.40.0)"] -pi = ["mypy-boto3-pi (>=1.39.0,<1.40.0)"] -pinpoint = ["mypy-boto3-pinpoint (>=1.39.0,<1.40.0)"] -pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.39.0,<1.40.0)"] -pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.39.0,<1.40.0)"] -pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.39.0,<1.40.0)"] -pipes = ["mypy-boto3-pipes (>=1.39.0,<1.40.0)"] -polly = ["mypy-boto3-polly (>=1.39.0,<1.40.0)"] -pricing = ["mypy-boto3-pricing (>=1.39.0,<1.40.0)"] -proton = ["mypy-boto3-proton (>=1.39.0,<1.40.0)"] -qapps = ["mypy-boto3-qapps (>=1.39.0,<1.40.0)"] -qbusiness = ["mypy-boto3-qbusiness (>=1.39.0,<1.40.0)"] -qconnect = ["mypy-boto3-qconnect (>=1.39.0,<1.40.0)"] -qldb = ["mypy-boto3-qldb (>=1.39.0,<1.40.0)"] -qldb-session = ["mypy-boto3-qldb-session (>=1.39.0,<1.40.0)"] -quicksight = ["mypy-boto3-quicksight (>=1.39.0,<1.40.0)"] -ram = ["mypy-boto3-ram (>=1.39.0,<1.40.0)"] -rbin = ["mypy-boto3-rbin (>=1.39.0,<1.40.0)"] -rds = ["mypy-boto3-rds (>=1.39.0,<1.40.0)"] -rds-data = ["mypy-boto3-rds-data (>=1.39.0,<1.40.0)"] -redshift = ["mypy-boto3-redshift (>=1.39.0,<1.40.0)"] -redshift-data = ["mypy-boto3-redshift-data (>=1.39.0,<1.40.0)"] -redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.39.0,<1.40.0)"] -rekognition = ["mypy-boto3-rekognition (>=1.39.0,<1.40.0)"] -repostspace = ["mypy-boto3-repostspace (>=1.39.0,<1.40.0)"] -resiliencehub = ["mypy-boto3-resiliencehub (>=1.39.0,<1.40.0)"] -resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.39.0,<1.40.0)"] -resource-groups = ["mypy-boto3-resource-groups (>=1.39.0,<1.40.0)"] -resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.39.0,<1.40.0)"] -robomaker = ["mypy-boto3-robomaker (>=1.39.0,<1.40.0)"] -rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.39.0,<1.40.0)"] -route53 = ["mypy-boto3-route53 (>=1.39.0,<1.40.0)"] -route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.39.0,<1.40.0)"] -route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.39.0,<1.40.0)"] -route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.39.0,<1.40.0)"] -route53domains = ["mypy-boto3-route53domains (>=1.39.0,<1.40.0)"] -route53profiles = ["mypy-boto3-route53profiles (>=1.39.0,<1.40.0)"] -route53resolver = ["mypy-boto3-route53resolver (>=1.39.0,<1.40.0)"] -rum = ["mypy-boto3-rum (>=1.39.0,<1.40.0)"] -s3 = ["mypy-boto3-s3 (>=1.39.0,<1.40.0)"] -s3control = ["mypy-boto3-s3control (>=1.39.0,<1.40.0)"] -s3outposts = ["mypy-boto3-s3outposts (>=1.39.0,<1.40.0)"] -s3tables = ["mypy-boto3-s3tables (>=1.39.0,<1.40.0)"] -s3vectors = ["mypy-boto3-s3vectors (>=1.39.0,<1.40.0)"] -sagemaker = ["mypy-boto3-sagemaker (>=1.39.0,<1.40.0)"] -sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.39.0,<1.40.0)"] -sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.39.0,<1.40.0)"] -sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.39.0,<1.40.0)"] -sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.39.0,<1.40.0)"] -sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.39.0,<1.40.0)"] -sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.39.0,<1.40.0)"] -savingsplans = ["mypy-boto3-savingsplans (>=1.39.0,<1.40.0)"] -scheduler = ["mypy-boto3-scheduler (>=1.39.0,<1.40.0)"] -schemas = ["mypy-boto3-schemas (>=1.39.0,<1.40.0)"] -sdb = ["mypy-boto3-sdb (>=1.39.0,<1.40.0)"] -secretsmanager = ["mypy-boto3-secretsmanager (>=1.39.0,<1.40.0)"] -security-ir = ["mypy-boto3-security-ir (>=1.39.0,<1.40.0)"] -securityhub = ["mypy-boto3-securityhub (>=1.39.0,<1.40.0)"] -securitylake = ["mypy-boto3-securitylake (>=1.39.0,<1.40.0)"] -serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.39.0,<1.40.0)"] -service-quotas = ["mypy-boto3-service-quotas (>=1.39.0,<1.40.0)"] -servicecatalog = ["mypy-boto3-servicecatalog (>=1.39.0,<1.40.0)"] -servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.39.0,<1.40.0)"] -servicediscovery = ["mypy-boto3-servicediscovery (>=1.39.0,<1.40.0)"] -ses = ["mypy-boto3-ses (>=1.39.0,<1.40.0)"] -sesv2 = ["mypy-boto3-sesv2 (>=1.39.0,<1.40.0)"] -shield = ["mypy-boto3-shield (>=1.39.0,<1.40.0)"] -signer = ["mypy-boto3-signer (>=1.39.0,<1.40.0)"] -simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.39.0,<1.40.0)"] -sms = ["mypy-boto3-sms (>=1.39.0,<1.40.0)"] -snow-device-management = ["mypy-boto3-snow-device-management (>=1.39.0,<1.40.0)"] -snowball = ["mypy-boto3-snowball (>=1.39.0,<1.40.0)"] -sns = ["mypy-boto3-sns (>=1.39.0,<1.40.0)"] -socialmessaging = ["mypy-boto3-socialmessaging (>=1.39.0,<1.40.0)"] -sqs = ["mypy-boto3-sqs (>=1.39.0,<1.40.0)"] -ssm = ["mypy-boto3-ssm (>=1.39.0,<1.40.0)"] -ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.39.0,<1.40.0)"] -ssm-guiconnect = ["mypy-boto3-ssm-guiconnect (>=1.39.0,<1.40.0)"] -ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.39.0,<1.40.0)"] -ssm-quicksetup = ["mypy-boto3-ssm-quicksetup (>=1.39.0,<1.40.0)"] -ssm-sap = ["mypy-boto3-ssm-sap (>=1.39.0,<1.40.0)"] -sso = ["mypy-boto3-sso (>=1.39.0,<1.40.0)"] -sso-admin = ["mypy-boto3-sso-admin (>=1.39.0,<1.40.0)"] -sso-oidc = ["mypy-boto3-sso-oidc (>=1.39.0,<1.40.0)"] -stepfunctions = ["mypy-boto3-stepfunctions (>=1.39.0,<1.40.0)"] -storagegateway = ["mypy-boto3-storagegateway (>=1.39.0,<1.40.0)"] -sts = ["mypy-boto3-sts (>=1.39.0,<1.40.0)"] -supplychain = ["mypy-boto3-supplychain (>=1.39.0,<1.40.0)"] -support = ["mypy-boto3-support (>=1.39.0,<1.40.0)"] -support-app = ["mypy-boto3-support-app (>=1.39.0,<1.40.0)"] -swf = ["mypy-boto3-swf (>=1.39.0,<1.40.0)"] -synthetics = ["mypy-boto3-synthetics (>=1.39.0,<1.40.0)"] -taxsettings = ["mypy-boto3-taxsettings (>=1.39.0,<1.40.0)"] -textract = ["mypy-boto3-textract (>=1.39.0,<1.40.0)"] -timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.39.0,<1.40.0)"] -timestream-query = ["mypy-boto3-timestream-query (>=1.39.0,<1.40.0)"] -timestream-write = ["mypy-boto3-timestream-write (>=1.39.0,<1.40.0)"] -tnb = ["mypy-boto3-tnb (>=1.39.0,<1.40.0)"] -transcribe = ["mypy-boto3-transcribe (>=1.39.0,<1.40.0)"] -transfer = ["mypy-boto3-transfer (>=1.39.0,<1.40.0)"] -translate = ["mypy-boto3-translate (>=1.39.0,<1.40.0)"] -trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.39.0,<1.40.0)"] -verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.39.0,<1.40.0)"] -voice-id = ["mypy-boto3-voice-id (>=1.39.0,<1.40.0)"] -vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.39.0,<1.40.0)"] -waf = ["mypy-boto3-waf (>=1.39.0,<1.40.0)"] -waf-regional = ["mypy-boto3-waf-regional (>=1.39.0,<1.40.0)"] -wafv2 = ["mypy-boto3-wafv2 (>=1.39.0,<1.40.0)"] -wellarchitected = ["mypy-boto3-wellarchitected (>=1.39.0,<1.40.0)"] -wisdom = ["mypy-boto3-wisdom (>=1.39.0,<1.40.0)"] -workdocs = ["mypy-boto3-workdocs (>=1.39.0,<1.40.0)"] -workmail = ["mypy-boto3-workmail (>=1.39.0,<1.40.0)"] -workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.39.0,<1.40.0)"] -workspaces = ["mypy-boto3-workspaces (>=1.39.0,<1.40.0)"] -workspaces-instances = ["mypy-boto3-workspaces-instances (>=1.39.0,<1.40.0)"] -workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.39.0,<1.40.0)"] -workspaces-web = ["mypy-boto3-workspaces-web (>=1.39.0,<1.40.0)"] -xray = ["mypy-boto3-xray (>=1.39.0,<1.40.0)"] +accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.40.0,<1.41.0)"] +account = ["mypy-boto3-account (>=1.40.0,<1.41.0)"] +acm = ["mypy-boto3-acm (>=1.40.0,<1.41.0)"] +acm-pca = ["mypy-boto3-acm-pca (>=1.40.0,<1.41.0)"] +aiops = ["mypy-boto3-aiops (>=1.40.0,<1.41.0)"] +all = ["mypy-boto3-accessanalyzer (>=1.40.0,<1.41.0)", "mypy-boto3-account (>=1.40.0,<1.41.0)", "mypy-boto3-acm (>=1.40.0,<1.41.0)", "mypy-boto3-acm-pca (>=1.40.0,<1.41.0)", "mypy-boto3-aiops (>=1.40.0,<1.41.0)", "mypy-boto3-amp (>=1.40.0,<1.41.0)", "mypy-boto3-amplify (>=1.40.0,<1.41.0)", "mypy-boto3-amplifybackend (>=1.40.0,<1.41.0)", "mypy-boto3-amplifyuibuilder (>=1.40.0,<1.41.0)", "mypy-boto3-apigateway (>=1.40.0,<1.41.0)", "mypy-boto3-apigatewaymanagementapi (>=1.40.0,<1.41.0)", "mypy-boto3-apigatewayv2 (>=1.40.0,<1.41.0)", "mypy-boto3-appconfig (>=1.40.0,<1.41.0)", "mypy-boto3-appconfigdata (>=1.40.0,<1.41.0)", "mypy-boto3-appfabric (>=1.40.0,<1.41.0)", "mypy-boto3-appflow (>=1.40.0,<1.41.0)", "mypy-boto3-appintegrations (>=1.40.0,<1.41.0)", "mypy-boto3-application-autoscaling (>=1.40.0,<1.41.0)", "mypy-boto3-application-insights (>=1.40.0,<1.41.0)", "mypy-boto3-application-signals (>=1.40.0,<1.41.0)", "mypy-boto3-applicationcostprofiler (>=1.40.0,<1.41.0)", "mypy-boto3-appmesh (>=1.40.0,<1.41.0)", "mypy-boto3-apprunner (>=1.40.0,<1.41.0)", "mypy-boto3-appstream (>=1.40.0,<1.41.0)", "mypy-boto3-appsync (>=1.40.0,<1.41.0)", "mypy-boto3-apptest (>=1.40.0,<1.41.0)", "mypy-boto3-arc-region-switch (>=1.40.0,<1.41.0)", "mypy-boto3-arc-zonal-shift (>=1.40.0,<1.41.0)", "mypy-boto3-artifact (>=1.40.0,<1.41.0)", "mypy-boto3-athena (>=1.40.0,<1.41.0)", "mypy-boto3-auditmanager (>=1.40.0,<1.41.0)", "mypy-boto3-autoscaling (>=1.40.0,<1.41.0)", "mypy-boto3-autoscaling-plans (>=1.40.0,<1.41.0)", "mypy-boto3-b2bi (>=1.40.0,<1.41.0)", "mypy-boto3-backup (>=1.40.0,<1.41.0)", "mypy-boto3-backup-gateway (>=1.40.0,<1.41.0)", "mypy-boto3-backupsearch (>=1.40.0,<1.41.0)", "mypy-boto3-batch (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-dashboards (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-data-exports (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-pricing-calculator (>=1.40.0,<1.41.0)", "mypy-boto3-bcm-recommended-actions (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agent (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agent-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agentcore (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-agentcore-control (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-data-automation (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-bedrock-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-billing (>=1.40.0,<1.41.0)", "mypy-boto3-billingconductor (>=1.40.0,<1.41.0)", "mypy-boto3-braket (>=1.40.0,<1.41.0)", "mypy-boto3-budgets (>=1.40.0,<1.41.0)", "mypy-boto3-ce (>=1.40.0,<1.41.0)", "mypy-boto3-chatbot (>=1.40.0,<1.41.0)", "mypy-boto3-chime (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-identity (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-meetings (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-messaging (>=1.40.0,<1.41.0)", "mypy-boto3-chime-sdk-voice (>=1.40.0,<1.41.0)", "mypy-boto3-cleanrooms (>=1.40.0,<1.41.0)", "mypy-boto3-cleanroomsml (>=1.40.0,<1.41.0)", "mypy-boto3-cloud9 (>=1.40.0,<1.41.0)", "mypy-boto3-cloudcontrol (>=1.40.0,<1.41.0)", "mypy-boto3-clouddirectory (>=1.40.0,<1.41.0)", "mypy-boto3-cloudformation (>=1.40.0,<1.41.0)", "mypy-boto3-cloudfront (>=1.40.0,<1.41.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.40.0,<1.41.0)", "mypy-boto3-cloudhsm (>=1.40.0,<1.41.0)", "mypy-boto3-cloudhsmv2 (>=1.40.0,<1.41.0)", "mypy-boto3-cloudsearch (>=1.40.0,<1.41.0)", "mypy-boto3-cloudsearchdomain (>=1.40.0,<1.41.0)", "mypy-boto3-cloudtrail (>=1.40.0,<1.41.0)", "mypy-boto3-cloudtrail-data (>=1.40.0,<1.41.0)", "mypy-boto3-cloudwatch (>=1.40.0,<1.41.0)", "mypy-boto3-codeartifact (>=1.40.0,<1.41.0)", "mypy-boto3-codebuild (>=1.40.0,<1.41.0)", "mypy-boto3-codecatalyst (>=1.40.0,<1.41.0)", "mypy-boto3-codecommit (>=1.40.0,<1.41.0)", "mypy-boto3-codeconnections (>=1.40.0,<1.41.0)", "mypy-boto3-codedeploy (>=1.40.0,<1.41.0)", "mypy-boto3-codeguru-reviewer (>=1.40.0,<1.41.0)", "mypy-boto3-codeguru-security (>=1.40.0,<1.41.0)", "mypy-boto3-codeguruprofiler (>=1.40.0,<1.41.0)", "mypy-boto3-codepipeline (>=1.40.0,<1.41.0)", "mypy-boto3-codestar-connections (>=1.40.0,<1.41.0)", "mypy-boto3-codestar-notifications (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-identity (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-idp (>=1.40.0,<1.41.0)", "mypy-boto3-cognito-sync (>=1.40.0,<1.41.0)", "mypy-boto3-comprehend (>=1.40.0,<1.41.0)", "mypy-boto3-comprehendmedical (>=1.40.0,<1.41.0)", "mypy-boto3-compute-optimizer (>=1.40.0,<1.41.0)", "mypy-boto3-config (>=1.40.0,<1.41.0)", "mypy-boto3-connect (>=1.40.0,<1.41.0)", "mypy-boto3-connect-contact-lens (>=1.40.0,<1.41.0)", "mypy-boto3-connectcampaigns (>=1.40.0,<1.41.0)", "mypy-boto3-connectcampaignsv2 (>=1.40.0,<1.41.0)", "mypy-boto3-connectcases (>=1.40.0,<1.41.0)", "mypy-boto3-connectparticipant (>=1.40.0,<1.41.0)", "mypy-boto3-controlcatalog (>=1.40.0,<1.41.0)", "mypy-boto3-controltower (>=1.40.0,<1.41.0)", "mypy-boto3-cost-optimization-hub (>=1.40.0,<1.41.0)", "mypy-boto3-cur (>=1.40.0,<1.41.0)", "mypy-boto3-customer-profiles (>=1.40.0,<1.41.0)", "mypy-boto3-databrew (>=1.40.0,<1.41.0)", "mypy-boto3-dataexchange (>=1.40.0,<1.41.0)", "mypy-boto3-datapipeline (>=1.40.0,<1.41.0)", "mypy-boto3-datasync (>=1.40.0,<1.41.0)", "mypy-boto3-datazone (>=1.40.0,<1.41.0)", "mypy-boto3-dax (>=1.40.0,<1.41.0)", "mypy-boto3-deadline (>=1.40.0,<1.41.0)", "mypy-boto3-detective (>=1.40.0,<1.41.0)", "mypy-boto3-devicefarm (>=1.40.0,<1.41.0)", "mypy-boto3-devops-guru (>=1.40.0,<1.41.0)", "mypy-boto3-directconnect (>=1.40.0,<1.41.0)", "mypy-boto3-discovery (>=1.40.0,<1.41.0)", "mypy-boto3-dlm (>=1.40.0,<1.41.0)", "mypy-boto3-dms (>=1.40.0,<1.41.0)", "mypy-boto3-docdb (>=1.40.0,<1.41.0)", "mypy-boto3-docdb-elastic (>=1.40.0,<1.41.0)", "mypy-boto3-drs (>=1.40.0,<1.41.0)", "mypy-boto3-ds (>=1.40.0,<1.41.0)", "mypy-boto3-ds-data (>=1.40.0,<1.41.0)", "mypy-boto3-dsql (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodb (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodbstreams (>=1.40.0,<1.41.0)", "mypy-boto3-ebs (>=1.40.0,<1.41.0)", "mypy-boto3-ec2 (>=1.40.0,<1.41.0)", "mypy-boto3-ec2-instance-connect (>=1.40.0,<1.41.0)", "mypy-boto3-ecr (>=1.40.0,<1.41.0)", "mypy-boto3-ecr-public (>=1.40.0,<1.41.0)", "mypy-boto3-ecs (>=1.40.0,<1.41.0)", "mypy-boto3-efs (>=1.40.0,<1.41.0)", "mypy-boto3-eks (>=1.40.0,<1.41.0)", "mypy-boto3-eks-auth (>=1.40.0,<1.41.0)", "mypy-boto3-elasticache (>=1.40.0,<1.41.0)", "mypy-boto3-elasticbeanstalk (>=1.40.0,<1.41.0)", "mypy-boto3-elastictranscoder (>=1.40.0,<1.41.0)", "mypy-boto3-elb (>=1.40.0,<1.41.0)", "mypy-boto3-elbv2 (>=1.40.0,<1.41.0)", "mypy-boto3-emr (>=1.40.0,<1.41.0)", "mypy-boto3-emr-containers (>=1.40.0,<1.41.0)", "mypy-boto3-emr-serverless (>=1.40.0,<1.41.0)", "mypy-boto3-entityresolution (>=1.40.0,<1.41.0)", "mypy-boto3-es (>=1.40.0,<1.41.0)", "mypy-boto3-events (>=1.40.0,<1.41.0)", "mypy-boto3-evidently (>=1.40.0,<1.41.0)", "mypy-boto3-evs (>=1.40.0,<1.41.0)", "mypy-boto3-finspace (>=1.40.0,<1.41.0)", "mypy-boto3-finspace-data (>=1.40.0,<1.41.0)", "mypy-boto3-firehose (>=1.40.0,<1.41.0)", "mypy-boto3-fis (>=1.40.0,<1.41.0)", "mypy-boto3-fms (>=1.40.0,<1.41.0)", "mypy-boto3-forecast (>=1.40.0,<1.41.0)", "mypy-boto3-forecastquery (>=1.40.0,<1.41.0)", "mypy-boto3-frauddetector (>=1.40.0,<1.41.0)", "mypy-boto3-freetier (>=1.40.0,<1.41.0)", "mypy-boto3-fsx (>=1.40.0,<1.41.0)", "mypy-boto3-gamelift (>=1.40.0,<1.41.0)", "mypy-boto3-gameliftstreams (>=1.40.0,<1.41.0)", "mypy-boto3-geo-maps (>=1.40.0,<1.41.0)", "mypy-boto3-geo-places (>=1.40.0,<1.41.0)", "mypy-boto3-geo-routes (>=1.40.0,<1.41.0)", "mypy-boto3-glacier (>=1.40.0,<1.41.0)", "mypy-boto3-globalaccelerator (>=1.40.0,<1.41.0)", "mypy-boto3-glue (>=1.40.0,<1.41.0)", "mypy-boto3-grafana (>=1.40.0,<1.41.0)", "mypy-boto3-greengrass (>=1.40.0,<1.41.0)", "mypy-boto3-greengrassv2 (>=1.40.0,<1.41.0)", "mypy-boto3-groundstation (>=1.40.0,<1.41.0)", "mypy-boto3-guardduty (>=1.40.0,<1.41.0)", "mypy-boto3-health (>=1.40.0,<1.41.0)", "mypy-boto3-healthlake (>=1.40.0,<1.41.0)", "mypy-boto3-iam (>=1.40.0,<1.41.0)", "mypy-boto3-identitystore (>=1.40.0,<1.41.0)", "mypy-boto3-imagebuilder (>=1.40.0,<1.41.0)", "mypy-boto3-importexport (>=1.40.0,<1.41.0)", "mypy-boto3-inspector (>=1.40.0,<1.41.0)", "mypy-boto3-inspector-scan (>=1.40.0,<1.41.0)", "mypy-boto3-inspector2 (>=1.40.0,<1.41.0)", "mypy-boto3-internetmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-invoicing (>=1.40.0,<1.41.0)", "mypy-boto3-iot (>=1.40.0,<1.41.0)", "mypy-boto3-iot-data (>=1.40.0,<1.41.0)", "mypy-boto3-iot-jobs-data (>=1.40.0,<1.41.0)", "mypy-boto3-iot-managed-integrations (>=1.40.0,<1.41.0)", "mypy-boto3-iotanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-iotdeviceadvisor (>=1.40.0,<1.41.0)", "mypy-boto3-iotevents (>=1.40.0,<1.41.0)", "mypy-boto3-iotevents-data (>=1.40.0,<1.41.0)", "mypy-boto3-iotfleethub (>=1.40.0,<1.41.0)", "mypy-boto3-iotfleetwise (>=1.40.0,<1.41.0)", "mypy-boto3-iotsecuretunneling (>=1.40.0,<1.41.0)", "mypy-boto3-iotsitewise (>=1.40.0,<1.41.0)", "mypy-boto3-iotthingsgraph (>=1.40.0,<1.41.0)", "mypy-boto3-iottwinmaker (>=1.40.0,<1.41.0)", "mypy-boto3-iotwireless (>=1.40.0,<1.41.0)", "mypy-boto3-ivs (>=1.40.0,<1.41.0)", "mypy-boto3-ivs-realtime (>=1.40.0,<1.41.0)", "mypy-boto3-ivschat (>=1.40.0,<1.41.0)", "mypy-boto3-kafka (>=1.40.0,<1.41.0)", "mypy-boto3-kafkaconnect (>=1.40.0,<1.41.0)", "mypy-boto3-kendra (>=1.40.0,<1.41.0)", "mypy-boto3-kendra-ranking (>=1.40.0,<1.41.0)", "mypy-boto3-keyspaces (>=1.40.0,<1.41.0)", "mypy-boto3-keyspacesstreams (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-archived-media (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-media (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-signaling (>=1.40.0,<1.41.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.40.0,<1.41.0)", "mypy-boto3-kinesisvideo (>=1.40.0,<1.41.0)", "mypy-boto3-kms (>=1.40.0,<1.41.0)", "mypy-boto3-lakeformation (>=1.40.0,<1.41.0)", "mypy-boto3-lambda (>=1.40.0,<1.41.0)", "mypy-boto3-launch-wizard (>=1.40.0,<1.41.0)", "mypy-boto3-lex-models (>=1.40.0,<1.41.0)", "mypy-boto3-lex-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-lexv2-models (>=1.40.0,<1.41.0)", "mypy-boto3-lexv2-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.40.0,<1.41.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.40.0,<1.41.0)", "mypy-boto3-lightsail (>=1.40.0,<1.41.0)", "mypy-boto3-location (>=1.40.0,<1.41.0)", "mypy-boto3-logs (>=1.40.0,<1.41.0)", "mypy-boto3-lookoutequipment (>=1.40.0,<1.41.0)", "mypy-boto3-lookoutmetrics (>=1.40.0,<1.41.0)", "mypy-boto3-lookoutvision (>=1.40.0,<1.41.0)", "mypy-boto3-m2 (>=1.40.0,<1.41.0)", "mypy-boto3-machinelearning (>=1.40.0,<1.41.0)", "mypy-boto3-macie2 (>=1.40.0,<1.41.0)", "mypy-boto3-mailmanager (>=1.40.0,<1.41.0)", "mypy-boto3-managedblockchain (>=1.40.0,<1.41.0)", "mypy-boto3-managedblockchain-query (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-agreement (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-catalog (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-deployment (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-entitlement (>=1.40.0,<1.41.0)", "mypy-boto3-marketplace-reporting (>=1.40.0,<1.41.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.40.0,<1.41.0)", "mypy-boto3-mediaconnect (>=1.40.0,<1.41.0)", "mypy-boto3-mediaconvert (>=1.40.0,<1.41.0)", "mypy-boto3-medialive (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackage (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackage-vod (>=1.40.0,<1.41.0)", "mypy-boto3-mediapackagev2 (>=1.40.0,<1.41.0)", "mypy-boto3-mediastore (>=1.40.0,<1.41.0)", "mypy-boto3-mediastore-data (>=1.40.0,<1.41.0)", "mypy-boto3-mediatailor (>=1.40.0,<1.41.0)", "mypy-boto3-medical-imaging (>=1.40.0,<1.41.0)", "mypy-boto3-memorydb (>=1.40.0,<1.41.0)", "mypy-boto3-meteringmarketplace (>=1.40.0,<1.41.0)", "mypy-boto3-mgh (>=1.40.0,<1.41.0)", "mypy-boto3-mgn (>=1.40.0,<1.41.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhub-config (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhuborchestrator (>=1.40.0,<1.41.0)", "mypy-boto3-migrationhubstrategy (>=1.40.0,<1.41.0)", "mypy-boto3-mpa (>=1.40.0,<1.41.0)", "mypy-boto3-mq (>=1.40.0,<1.41.0)", "mypy-boto3-mturk (>=1.40.0,<1.41.0)", "mypy-boto3-mwaa (>=1.40.0,<1.41.0)", "mypy-boto3-neptune (>=1.40.0,<1.41.0)", "mypy-boto3-neptune-graph (>=1.40.0,<1.41.0)", "mypy-boto3-neptunedata (>=1.40.0,<1.41.0)", "mypy-boto3-network-firewall (>=1.40.0,<1.41.0)", "mypy-boto3-networkflowmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-networkmanager (>=1.40.0,<1.41.0)", "mypy-boto3-networkmonitor (>=1.40.0,<1.41.0)", "mypy-boto3-notifications (>=1.40.0,<1.41.0)", "mypy-boto3-notificationscontacts (>=1.40.0,<1.41.0)", "mypy-boto3-oam (>=1.40.0,<1.41.0)", "mypy-boto3-observabilityadmin (>=1.40.0,<1.41.0)", "mypy-boto3-odb (>=1.40.0,<1.41.0)", "mypy-boto3-omics (>=1.40.0,<1.41.0)", "mypy-boto3-opensearch (>=1.40.0,<1.41.0)", "mypy-boto3-opensearchserverless (>=1.40.0,<1.41.0)", "mypy-boto3-organizations (>=1.40.0,<1.41.0)", "mypy-boto3-osis (>=1.40.0,<1.41.0)", "mypy-boto3-outposts (>=1.40.0,<1.41.0)", "mypy-boto3-panorama (>=1.40.0,<1.41.0)", "mypy-boto3-partnercentral-selling (>=1.40.0,<1.41.0)", "mypy-boto3-payment-cryptography (>=1.40.0,<1.41.0)", "mypy-boto3-payment-cryptography-data (>=1.40.0,<1.41.0)", "mypy-boto3-pca-connector-ad (>=1.40.0,<1.41.0)", "mypy-boto3-pca-connector-scep (>=1.40.0,<1.41.0)", "mypy-boto3-pcs (>=1.40.0,<1.41.0)", "mypy-boto3-personalize (>=1.40.0,<1.41.0)", "mypy-boto3-personalize-events (>=1.40.0,<1.41.0)", "mypy-boto3-personalize-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-pi (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-email (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-sms-voice (>=1.40.0,<1.41.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.40.0,<1.41.0)", "mypy-boto3-pipes (>=1.40.0,<1.41.0)", "mypy-boto3-polly (>=1.40.0,<1.41.0)", "mypy-boto3-pricing (>=1.40.0,<1.41.0)", "mypy-boto3-proton (>=1.40.0,<1.41.0)", "mypy-boto3-qapps (>=1.40.0,<1.41.0)", "mypy-boto3-qbusiness (>=1.40.0,<1.41.0)", "mypy-boto3-qconnect (>=1.40.0,<1.41.0)", "mypy-boto3-qldb (>=1.40.0,<1.41.0)", "mypy-boto3-qldb-session (>=1.40.0,<1.41.0)", "mypy-boto3-quicksight (>=1.40.0,<1.41.0)", "mypy-boto3-ram (>=1.40.0,<1.41.0)", "mypy-boto3-rbin (>=1.40.0,<1.41.0)", "mypy-boto3-rds (>=1.40.0,<1.41.0)", "mypy-boto3-rds-data (>=1.40.0,<1.41.0)", "mypy-boto3-redshift (>=1.40.0,<1.41.0)", "mypy-boto3-redshift-data (>=1.40.0,<1.41.0)", "mypy-boto3-redshift-serverless (>=1.40.0,<1.41.0)", "mypy-boto3-rekognition (>=1.40.0,<1.41.0)", "mypy-boto3-repostspace (>=1.40.0,<1.41.0)", "mypy-boto3-resiliencehub (>=1.40.0,<1.41.0)", "mypy-boto3-resource-explorer-2 (>=1.40.0,<1.41.0)", "mypy-boto3-resource-groups (>=1.40.0,<1.41.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.40.0,<1.41.0)", "mypy-boto3-robomaker (>=1.40.0,<1.41.0)", "mypy-boto3-rolesanywhere (>=1.40.0,<1.41.0)", "mypy-boto3-route53 (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-cluster (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-control-config (>=1.40.0,<1.41.0)", "mypy-boto3-route53-recovery-readiness (>=1.40.0,<1.41.0)", "mypy-boto3-route53domains (>=1.40.0,<1.41.0)", "mypy-boto3-route53profiles (>=1.40.0,<1.41.0)", "mypy-boto3-route53resolver (>=1.40.0,<1.41.0)", "mypy-boto3-rum (>=1.40.0,<1.41.0)", "mypy-boto3-s3 (>=1.40.0,<1.41.0)", "mypy-boto3-s3control (>=1.40.0,<1.41.0)", "mypy-boto3-s3outposts (>=1.40.0,<1.41.0)", "mypy-boto3-s3tables (>=1.40.0,<1.41.0)", "mypy-boto3-s3vectors (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-edge (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-geospatial (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-metrics (>=1.40.0,<1.41.0)", "mypy-boto3-sagemaker-runtime (>=1.40.0,<1.41.0)", "mypy-boto3-savingsplans (>=1.40.0,<1.41.0)", "mypy-boto3-scheduler (>=1.40.0,<1.41.0)", "mypy-boto3-schemas (>=1.40.0,<1.41.0)", "mypy-boto3-sdb (>=1.40.0,<1.41.0)", "mypy-boto3-secretsmanager (>=1.40.0,<1.41.0)", "mypy-boto3-security-ir (>=1.40.0,<1.41.0)", "mypy-boto3-securityhub (>=1.40.0,<1.41.0)", "mypy-boto3-securitylake (>=1.40.0,<1.41.0)", "mypy-boto3-serverlessrepo (>=1.40.0,<1.41.0)", "mypy-boto3-service-quotas (>=1.40.0,<1.41.0)", "mypy-boto3-servicecatalog (>=1.40.0,<1.41.0)", "mypy-boto3-servicecatalog-appregistry (>=1.40.0,<1.41.0)", "mypy-boto3-servicediscovery (>=1.40.0,<1.41.0)", "mypy-boto3-ses (>=1.40.0,<1.41.0)", "mypy-boto3-sesv2 (>=1.40.0,<1.41.0)", "mypy-boto3-shield (>=1.40.0,<1.41.0)", "mypy-boto3-signer (>=1.40.0,<1.41.0)", "mypy-boto3-simspaceweaver (>=1.40.0,<1.41.0)", "mypy-boto3-snow-device-management (>=1.40.0,<1.41.0)", "mypy-boto3-snowball (>=1.40.0,<1.41.0)", "mypy-boto3-sns (>=1.40.0,<1.41.0)", "mypy-boto3-socialmessaging (>=1.40.0,<1.41.0)", "mypy-boto3-sqs (>=1.40.0,<1.41.0)", "mypy-boto3-ssm (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-contacts (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-guiconnect (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-incidents (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-quicksetup (>=1.40.0,<1.41.0)", "mypy-boto3-ssm-sap (>=1.40.0,<1.41.0)", "mypy-boto3-sso (>=1.40.0,<1.41.0)", "mypy-boto3-sso-admin (>=1.40.0,<1.41.0)", "mypy-boto3-sso-oidc (>=1.40.0,<1.41.0)", "mypy-boto3-stepfunctions (>=1.40.0,<1.41.0)", "mypy-boto3-storagegateway (>=1.40.0,<1.41.0)", "mypy-boto3-sts (>=1.40.0,<1.41.0)", "mypy-boto3-supplychain (>=1.40.0,<1.41.0)", "mypy-boto3-support (>=1.40.0,<1.41.0)", "mypy-boto3-support-app (>=1.40.0,<1.41.0)", "mypy-boto3-swf (>=1.40.0,<1.41.0)", "mypy-boto3-synthetics (>=1.40.0,<1.41.0)", "mypy-boto3-taxsettings (>=1.40.0,<1.41.0)", "mypy-boto3-textract (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-influxdb (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-query (>=1.40.0,<1.41.0)", "mypy-boto3-timestream-write (>=1.40.0,<1.41.0)", "mypy-boto3-tnb (>=1.40.0,<1.41.0)", "mypy-boto3-transcribe (>=1.40.0,<1.41.0)", "mypy-boto3-transfer (>=1.40.0,<1.41.0)", "mypy-boto3-translate (>=1.40.0,<1.41.0)", "mypy-boto3-trustedadvisor (>=1.40.0,<1.41.0)", "mypy-boto3-verifiedpermissions (>=1.40.0,<1.41.0)", "mypy-boto3-voice-id (>=1.40.0,<1.41.0)", "mypy-boto3-vpc-lattice (>=1.40.0,<1.41.0)", "mypy-boto3-waf (>=1.40.0,<1.41.0)", "mypy-boto3-waf-regional (>=1.40.0,<1.41.0)", "mypy-boto3-wafv2 (>=1.40.0,<1.41.0)", "mypy-boto3-wellarchitected (>=1.40.0,<1.41.0)", "mypy-boto3-wisdom (>=1.40.0,<1.41.0)", "mypy-boto3-workdocs (>=1.40.0,<1.41.0)", "mypy-boto3-workmail (>=1.40.0,<1.41.0)", "mypy-boto3-workmailmessageflow (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-instances (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-thin-client (>=1.40.0,<1.41.0)", "mypy-boto3-workspaces-web (>=1.40.0,<1.41.0)", "mypy-boto3-xray (>=1.40.0,<1.41.0)"] +amp = ["mypy-boto3-amp (>=1.40.0,<1.41.0)"] +amplify = ["mypy-boto3-amplify (>=1.40.0,<1.41.0)"] +amplifybackend = ["mypy-boto3-amplifybackend (>=1.40.0,<1.41.0)"] +amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.40.0,<1.41.0)"] +apigateway = ["mypy-boto3-apigateway (>=1.40.0,<1.41.0)"] +apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.40.0,<1.41.0)"] +apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.40.0,<1.41.0)"] +appconfig = ["mypy-boto3-appconfig (>=1.40.0,<1.41.0)"] +appconfigdata = ["mypy-boto3-appconfigdata (>=1.40.0,<1.41.0)"] +appfabric = ["mypy-boto3-appfabric (>=1.40.0,<1.41.0)"] +appflow = ["mypy-boto3-appflow (>=1.40.0,<1.41.0)"] +appintegrations = ["mypy-boto3-appintegrations (>=1.40.0,<1.41.0)"] +application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.40.0,<1.41.0)"] +application-insights = ["mypy-boto3-application-insights (>=1.40.0,<1.41.0)"] +application-signals = ["mypy-boto3-application-signals (>=1.40.0,<1.41.0)"] +applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.40.0,<1.41.0)"] +appmesh = ["mypy-boto3-appmesh (>=1.40.0,<1.41.0)"] +apprunner = ["mypy-boto3-apprunner (>=1.40.0,<1.41.0)"] +appstream = ["mypy-boto3-appstream (>=1.40.0,<1.41.0)"] +appsync = ["mypy-boto3-appsync (>=1.40.0,<1.41.0)"] +apptest = ["mypy-boto3-apptest (>=1.40.0,<1.41.0)"] +arc-region-switch = ["mypy-boto3-arc-region-switch (>=1.40.0,<1.41.0)"] +arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.40.0,<1.41.0)"] +artifact = ["mypy-boto3-artifact (>=1.40.0,<1.41.0)"] +athena = ["mypy-boto3-athena (>=1.40.0,<1.41.0)"] +auditmanager = ["mypy-boto3-auditmanager (>=1.40.0,<1.41.0)"] +autoscaling = ["mypy-boto3-autoscaling (>=1.40.0,<1.41.0)"] +autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.40.0,<1.41.0)"] +b2bi = ["mypy-boto3-b2bi (>=1.40.0,<1.41.0)"] +backup = ["mypy-boto3-backup (>=1.40.0,<1.41.0)"] +backup-gateway = ["mypy-boto3-backup-gateway (>=1.40.0,<1.41.0)"] +backupsearch = ["mypy-boto3-backupsearch (>=1.40.0,<1.41.0)"] +batch = ["mypy-boto3-batch (>=1.40.0,<1.41.0)"] +bcm-dashboards = ["mypy-boto3-bcm-dashboards (>=1.40.0,<1.41.0)"] +bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.40.0,<1.41.0)"] +bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.40.0,<1.41.0)"] +bcm-recommended-actions = ["mypy-boto3-bcm-recommended-actions (>=1.40.0,<1.41.0)"] +bedrock = ["mypy-boto3-bedrock (>=1.40.0,<1.41.0)"] +bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.40.0,<1.41.0)"] +bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.40.0,<1.41.0)"] +bedrock-agentcore = ["mypy-boto3-bedrock-agentcore (>=1.40.0,<1.41.0)"] +bedrock-agentcore-control = ["mypy-boto3-bedrock-agentcore-control (>=1.40.0,<1.41.0)"] +bedrock-data-automation = ["mypy-boto3-bedrock-data-automation (>=1.40.0,<1.41.0)"] +bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (>=1.40.0,<1.41.0)"] +bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.40.0,<1.41.0)"] +billing = ["mypy-boto3-billing (>=1.40.0,<1.41.0)"] +billingconductor = ["mypy-boto3-billingconductor (>=1.40.0,<1.41.0)"] +boto3 = ["boto3 (==1.40.56)"] +braket = ["mypy-boto3-braket (>=1.40.0,<1.41.0)"] +budgets = ["mypy-boto3-budgets (>=1.40.0,<1.41.0)"] +ce = ["mypy-boto3-ce (>=1.40.0,<1.41.0)"] +chatbot = ["mypy-boto3-chatbot (>=1.40.0,<1.41.0)"] +chime = ["mypy-boto3-chime (>=1.40.0,<1.41.0)"] +chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.40.0,<1.41.0)"] +chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.40.0,<1.41.0)"] +chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.40.0,<1.41.0)"] +chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.40.0,<1.41.0)"] +chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.40.0,<1.41.0)"] +cleanrooms = ["mypy-boto3-cleanrooms (>=1.40.0,<1.41.0)"] +cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.40.0,<1.41.0)"] +cloud9 = ["mypy-boto3-cloud9 (>=1.40.0,<1.41.0)"] +cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.40.0,<1.41.0)"] +clouddirectory = ["mypy-boto3-clouddirectory (>=1.40.0,<1.41.0)"] +cloudformation = ["mypy-boto3-cloudformation (>=1.40.0,<1.41.0)"] +cloudfront = ["mypy-boto3-cloudfront (>=1.40.0,<1.41.0)"] +cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.40.0,<1.41.0)"] +cloudhsm = ["mypy-boto3-cloudhsm (>=1.40.0,<1.41.0)"] +cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.40.0,<1.41.0)"] +cloudsearch = ["mypy-boto3-cloudsearch (>=1.40.0,<1.41.0)"] +cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.40.0,<1.41.0)"] +cloudtrail = ["mypy-boto3-cloudtrail (>=1.40.0,<1.41.0)"] +cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.40.0,<1.41.0)"] +cloudwatch = ["mypy-boto3-cloudwatch (>=1.40.0,<1.41.0)"] +codeartifact = ["mypy-boto3-codeartifact (>=1.40.0,<1.41.0)"] +codebuild = ["mypy-boto3-codebuild (>=1.40.0,<1.41.0)"] +codecatalyst = ["mypy-boto3-codecatalyst (>=1.40.0,<1.41.0)"] +codecommit = ["mypy-boto3-codecommit (>=1.40.0,<1.41.0)"] +codeconnections = ["mypy-boto3-codeconnections (>=1.40.0,<1.41.0)"] +codedeploy = ["mypy-boto3-codedeploy (>=1.40.0,<1.41.0)"] +codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.40.0,<1.41.0)"] +codeguru-security = ["mypy-boto3-codeguru-security (>=1.40.0,<1.41.0)"] +codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.40.0,<1.41.0)"] +codepipeline = ["mypy-boto3-codepipeline (>=1.40.0,<1.41.0)"] +codestar-connections = ["mypy-boto3-codestar-connections (>=1.40.0,<1.41.0)"] +codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.40.0,<1.41.0)"] +cognito-identity = ["mypy-boto3-cognito-identity (>=1.40.0,<1.41.0)"] +cognito-idp = ["mypy-boto3-cognito-idp (>=1.40.0,<1.41.0)"] +cognito-sync = ["mypy-boto3-cognito-sync (>=1.40.0,<1.41.0)"] +comprehend = ["mypy-boto3-comprehend (>=1.40.0,<1.41.0)"] +comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.40.0,<1.41.0)"] +compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.40.0,<1.41.0)"] +config = ["mypy-boto3-config (>=1.40.0,<1.41.0)"] +connect = ["mypy-boto3-connect (>=1.40.0,<1.41.0)"] +connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.40.0,<1.41.0)"] +connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.40.0,<1.41.0)"] +connectcampaignsv2 = ["mypy-boto3-connectcampaignsv2 (>=1.40.0,<1.41.0)"] +connectcases = ["mypy-boto3-connectcases (>=1.40.0,<1.41.0)"] +connectparticipant = ["mypy-boto3-connectparticipant (>=1.40.0,<1.41.0)"] +controlcatalog = ["mypy-boto3-controlcatalog (>=1.40.0,<1.41.0)"] +controltower = ["mypy-boto3-controltower (>=1.40.0,<1.41.0)"] +cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.40.0,<1.41.0)"] +cur = ["mypy-boto3-cur (>=1.40.0,<1.41.0)"] +customer-profiles = ["mypy-boto3-customer-profiles (>=1.40.0,<1.41.0)"] +databrew = ["mypy-boto3-databrew (>=1.40.0,<1.41.0)"] +dataexchange = ["mypy-boto3-dataexchange (>=1.40.0,<1.41.0)"] +datapipeline = ["mypy-boto3-datapipeline (>=1.40.0,<1.41.0)"] +datasync = ["mypy-boto3-datasync (>=1.40.0,<1.41.0)"] +datazone = ["mypy-boto3-datazone (>=1.40.0,<1.41.0)"] +dax = ["mypy-boto3-dax (>=1.40.0,<1.41.0)"] +deadline = ["mypy-boto3-deadline (>=1.40.0,<1.41.0)"] +detective = ["mypy-boto3-detective (>=1.40.0,<1.41.0)"] +devicefarm = ["mypy-boto3-devicefarm (>=1.40.0,<1.41.0)"] +devops-guru = ["mypy-boto3-devops-guru (>=1.40.0,<1.41.0)"] +directconnect = ["mypy-boto3-directconnect (>=1.40.0,<1.41.0)"] +discovery = ["mypy-boto3-discovery (>=1.40.0,<1.41.0)"] +dlm = ["mypy-boto3-dlm (>=1.40.0,<1.41.0)"] +dms = ["mypy-boto3-dms (>=1.40.0,<1.41.0)"] +docdb = ["mypy-boto3-docdb (>=1.40.0,<1.41.0)"] +docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.40.0,<1.41.0)"] +drs = ["mypy-boto3-drs (>=1.40.0,<1.41.0)"] +ds = ["mypy-boto3-ds (>=1.40.0,<1.41.0)"] +ds-data = ["mypy-boto3-ds-data (>=1.40.0,<1.41.0)"] +dsql = ["mypy-boto3-dsql (>=1.40.0,<1.41.0)"] +dynamodb = ["mypy-boto3-dynamodb (>=1.40.0,<1.41.0)"] +dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.40.0,<1.41.0)"] +ebs = ["mypy-boto3-ebs (>=1.40.0,<1.41.0)"] +ec2 = ["mypy-boto3-ec2 (>=1.40.0,<1.41.0)"] +ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.40.0,<1.41.0)"] +ecr = ["mypy-boto3-ecr (>=1.40.0,<1.41.0)"] +ecr-public = ["mypy-boto3-ecr-public (>=1.40.0,<1.41.0)"] +ecs = ["mypy-boto3-ecs (>=1.40.0,<1.41.0)"] +efs = ["mypy-boto3-efs (>=1.40.0,<1.41.0)"] +eks = ["mypy-boto3-eks (>=1.40.0,<1.41.0)"] +eks-auth = ["mypy-boto3-eks-auth (>=1.40.0,<1.41.0)"] +elasticache = ["mypy-boto3-elasticache (>=1.40.0,<1.41.0)"] +elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.40.0,<1.41.0)"] +elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.40.0,<1.41.0)"] +elb = ["mypy-boto3-elb (>=1.40.0,<1.41.0)"] +elbv2 = ["mypy-boto3-elbv2 (>=1.40.0,<1.41.0)"] +emr = ["mypy-boto3-emr (>=1.40.0,<1.41.0)"] +emr-containers = ["mypy-boto3-emr-containers (>=1.40.0,<1.41.0)"] +emr-serverless = ["mypy-boto3-emr-serverless (>=1.40.0,<1.41.0)"] +entityresolution = ["mypy-boto3-entityresolution (>=1.40.0,<1.41.0)"] +es = ["mypy-boto3-es (>=1.40.0,<1.41.0)"] +essential = ["mypy-boto3-cloudformation (>=1.40.0,<1.41.0)", "mypy-boto3-dynamodb (>=1.40.0,<1.41.0)", "mypy-boto3-ec2 (>=1.40.0,<1.41.0)", "mypy-boto3-lambda (>=1.40.0,<1.41.0)", "mypy-boto3-rds (>=1.40.0,<1.41.0)", "mypy-boto3-s3 (>=1.40.0,<1.41.0)", "mypy-boto3-sqs (>=1.40.0,<1.41.0)"] +events = ["mypy-boto3-events (>=1.40.0,<1.41.0)"] +evidently = ["mypy-boto3-evidently (>=1.40.0,<1.41.0)"] +evs = ["mypy-boto3-evs (>=1.40.0,<1.41.0)"] +finspace = ["mypy-boto3-finspace (>=1.40.0,<1.41.0)"] +finspace-data = ["mypy-boto3-finspace-data (>=1.40.0,<1.41.0)"] +firehose = ["mypy-boto3-firehose (>=1.40.0,<1.41.0)"] +fis = ["mypy-boto3-fis (>=1.40.0,<1.41.0)"] +fms = ["mypy-boto3-fms (>=1.40.0,<1.41.0)"] +forecast = ["mypy-boto3-forecast (>=1.40.0,<1.41.0)"] +forecastquery = ["mypy-boto3-forecastquery (>=1.40.0,<1.41.0)"] +frauddetector = ["mypy-boto3-frauddetector (>=1.40.0,<1.41.0)"] +freetier = ["mypy-boto3-freetier (>=1.40.0,<1.41.0)"] +fsx = ["mypy-boto3-fsx (>=1.40.0,<1.41.0)"] +full = ["boto3-stubs-full (>=1.40.0,<1.41.0)"] +gamelift = ["mypy-boto3-gamelift (>=1.40.0,<1.41.0)"] +gameliftstreams = ["mypy-boto3-gameliftstreams (>=1.40.0,<1.41.0)"] +geo-maps = ["mypy-boto3-geo-maps (>=1.40.0,<1.41.0)"] +geo-places = ["mypy-boto3-geo-places (>=1.40.0,<1.41.0)"] +geo-routes = ["mypy-boto3-geo-routes (>=1.40.0,<1.41.0)"] +glacier = ["mypy-boto3-glacier (>=1.40.0,<1.41.0)"] +globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.40.0,<1.41.0)"] +glue = ["mypy-boto3-glue (>=1.40.0,<1.41.0)"] +grafana = ["mypy-boto3-grafana (>=1.40.0,<1.41.0)"] +greengrass = ["mypy-boto3-greengrass (>=1.40.0,<1.41.0)"] +greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.40.0,<1.41.0)"] +groundstation = ["mypy-boto3-groundstation (>=1.40.0,<1.41.0)"] +guardduty = ["mypy-boto3-guardduty (>=1.40.0,<1.41.0)"] +health = ["mypy-boto3-health (>=1.40.0,<1.41.0)"] +healthlake = ["mypy-boto3-healthlake (>=1.40.0,<1.41.0)"] +iam = ["mypy-boto3-iam (>=1.40.0,<1.41.0)"] +identitystore = ["mypy-boto3-identitystore (>=1.40.0,<1.41.0)"] +imagebuilder = ["mypy-boto3-imagebuilder (>=1.40.0,<1.41.0)"] +importexport = ["mypy-boto3-importexport (>=1.40.0,<1.41.0)"] +inspector = ["mypy-boto3-inspector (>=1.40.0,<1.41.0)"] +inspector-scan = ["mypy-boto3-inspector-scan (>=1.40.0,<1.41.0)"] +inspector2 = ["mypy-boto3-inspector2 (>=1.40.0,<1.41.0)"] +internetmonitor = ["mypy-boto3-internetmonitor (>=1.40.0,<1.41.0)"] +invoicing = ["mypy-boto3-invoicing (>=1.40.0,<1.41.0)"] +iot = ["mypy-boto3-iot (>=1.40.0,<1.41.0)"] +iot-data = ["mypy-boto3-iot-data (>=1.40.0,<1.41.0)"] +iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.40.0,<1.41.0)"] +iot-managed-integrations = ["mypy-boto3-iot-managed-integrations (>=1.40.0,<1.41.0)"] +iotanalytics = ["mypy-boto3-iotanalytics (>=1.40.0,<1.41.0)"] +iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.40.0,<1.41.0)"] +iotevents = ["mypy-boto3-iotevents (>=1.40.0,<1.41.0)"] +iotevents-data = ["mypy-boto3-iotevents-data (>=1.40.0,<1.41.0)"] +iotfleethub = ["mypy-boto3-iotfleethub (>=1.40.0,<1.41.0)"] +iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.40.0,<1.41.0)"] +iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.40.0,<1.41.0)"] +iotsitewise = ["mypy-boto3-iotsitewise (>=1.40.0,<1.41.0)"] +iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.40.0,<1.41.0)"] +iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.40.0,<1.41.0)"] +iotwireless = ["mypy-boto3-iotwireless (>=1.40.0,<1.41.0)"] +ivs = ["mypy-boto3-ivs (>=1.40.0,<1.41.0)"] +ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.40.0,<1.41.0)"] +ivschat = ["mypy-boto3-ivschat (>=1.40.0,<1.41.0)"] +kafka = ["mypy-boto3-kafka (>=1.40.0,<1.41.0)"] +kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.40.0,<1.41.0)"] +kendra = ["mypy-boto3-kendra (>=1.40.0,<1.41.0)"] +kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.40.0,<1.41.0)"] +keyspaces = ["mypy-boto3-keyspaces (>=1.40.0,<1.41.0)"] +keyspacesstreams = ["mypy-boto3-keyspacesstreams (>=1.40.0,<1.41.0)"] +kinesis = ["mypy-boto3-kinesis (>=1.40.0,<1.41.0)"] +kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.40.0,<1.41.0)"] +kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.40.0,<1.41.0)"] +kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.40.0,<1.41.0)"] +kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.40.0,<1.41.0)"] +kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.40.0,<1.41.0)"] +kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.40.0,<1.41.0)"] +kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.40.0,<1.41.0)"] +kms = ["mypy-boto3-kms (>=1.40.0,<1.41.0)"] +lakeformation = ["mypy-boto3-lakeformation (>=1.40.0,<1.41.0)"] +lambda = ["mypy-boto3-lambda (>=1.40.0,<1.41.0)"] +launch-wizard = ["mypy-boto3-launch-wizard (>=1.40.0,<1.41.0)"] +lex-models = ["mypy-boto3-lex-models (>=1.40.0,<1.41.0)"] +lex-runtime = ["mypy-boto3-lex-runtime (>=1.40.0,<1.41.0)"] +lexv2-models = ["mypy-boto3-lexv2-models (>=1.40.0,<1.41.0)"] +lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.40.0,<1.41.0)"] +license-manager = ["mypy-boto3-license-manager (>=1.40.0,<1.41.0)"] +license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.40.0,<1.41.0)"] +license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.40.0,<1.41.0)"] +lightsail = ["mypy-boto3-lightsail (>=1.40.0,<1.41.0)"] +location = ["mypy-boto3-location (>=1.40.0,<1.41.0)"] +logs = ["mypy-boto3-logs (>=1.40.0,<1.41.0)"] +lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.40.0,<1.41.0)"] +lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.40.0,<1.41.0)"] +lookoutvision = ["mypy-boto3-lookoutvision (>=1.40.0,<1.41.0)"] +m2 = ["mypy-boto3-m2 (>=1.40.0,<1.41.0)"] +machinelearning = ["mypy-boto3-machinelearning (>=1.40.0,<1.41.0)"] +macie2 = ["mypy-boto3-macie2 (>=1.40.0,<1.41.0)"] +mailmanager = ["mypy-boto3-mailmanager (>=1.40.0,<1.41.0)"] +managedblockchain = ["mypy-boto3-managedblockchain (>=1.40.0,<1.41.0)"] +managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.40.0,<1.41.0)"] +marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.40.0,<1.41.0)"] +marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.40.0,<1.41.0)"] +marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.40.0,<1.41.0)"] +marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.40.0,<1.41.0)"] +marketplace-reporting = ["mypy-boto3-marketplace-reporting (>=1.40.0,<1.41.0)"] +marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.40.0,<1.41.0)"] +mediaconnect = ["mypy-boto3-mediaconnect (>=1.40.0,<1.41.0)"] +mediaconvert = ["mypy-boto3-mediaconvert (>=1.40.0,<1.41.0)"] +medialive = ["mypy-boto3-medialive (>=1.40.0,<1.41.0)"] +mediapackage = ["mypy-boto3-mediapackage (>=1.40.0,<1.41.0)"] +mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.40.0,<1.41.0)"] +mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.40.0,<1.41.0)"] +mediastore = ["mypy-boto3-mediastore (>=1.40.0,<1.41.0)"] +mediastore-data = ["mypy-boto3-mediastore-data (>=1.40.0,<1.41.0)"] +mediatailor = ["mypy-boto3-mediatailor (>=1.40.0,<1.41.0)"] +medical-imaging = ["mypy-boto3-medical-imaging (>=1.40.0,<1.41.0)"] +memorydb = ["mypy-boto3-memorydb (>=1.40.0,<1.41.0)"] +meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.40.0,<1.41.0)"] +mgh = ["mypy-boto3-mgh (>=1.40.0,<1.41.0)"] +mgn = ["mypy-boto3-mgn (>=1.40.0,<1.41.0)"] +migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.40.0,<1.41.0)"] +migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.40.0,<1.41.0)"] +migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.40.0,<1.41.0)"] +migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.40.0,<1.41.0)"] +mpa = ["mypy-boto3-mpa (>=1.40.0,<1.41.0)"] +mq = ["mypy-boto3-mq (>=1.40.0,<1.41.0)"] +mturk = ["mypy-boto3-mturk (>=1.40.0,<1.41.0)"] +mwaa = ["mypy-boto3-mwaa (>=1.40.0,<1.41.0)"] +neptune = ["mypy-boto3-neptune (>=1.40.0,<1.41.0)"] +neptune-graph = ["mypy-boto3-neptune-graph (>=1.40.0,<1.41.0)"] +neptunedata = ["mypy-boto3-neptunedata (>=1.40.0,<1.41.0)"] +network-firewall = ["mypy-boto3-network-firewall (>=1.40.0,<1.41.0)"] +networkflowmonitor = ["mypy-boto3-networkflowmonitor (>=1.40.0,<1.41.0)"] +networkmanager = ["mypy-boto3-networkmanager (>=1.40.0,<1.41.0)"] +networkmonitor = ["mypy-boto3-networkmonitor (>=1.40.0,<1.41.0)"] +notifications = ["mypy-boto3-notifications (>=1.40.0,<1.41.0)"] +notificationscontacts = ["mypy-boto3-notificationscontacts (>=1.40.0,<1.41.0)"] +oam = ["mypy-boto3-oam (>=1.40.0,<1.41.0)"] +observabilityadmin = ["mypy-boto3-observabilityadmin (>=1.40.0,<1.41.0)"] +odb = ["mypy-boto3-odb (>=1.40.0,<1.41.0)"] +omics = ["mypy-boto3-omics (>=1.40.0,<1.41.0)"] +opensearch = ["mypy-boto3-opensearch (>=1.40.0,<1.41.0)"] +opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.40.0,<1.41.0)"] +organizations = ["mypy-boto3-organizations (>=1.40.0,<1.41.0)"] +osis = ["mypy-boto3-osis (>=1.40.0,<1.41.0)"] +outposts = ["mypy-boto3-outposts (>=1.40.0,<1.41.0)"] +panorama = ["mypy-boto3-panorama (>=1.40.0,<1.41.0)"] +partnercentral-selling = ["mypy-boto3-partnercentral-selling (>=1.40.0,<1.41.0)"] +payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.40.0,<1.41.0)"] +payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.40.0,<1.41.0)"] +pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.40.0,<1.41.0)"] +pca-connector-scep = ["mypy-boto3-pca-connector-scep (>=1.40.0,<1.41.0)"] +pcs = ["mypy-boto3-pcs (>=1.40.0,<1.41.0)"] +personalize = ["mypy-boto3-personalize (>=1.40.0,<1.41.0)"] +personalize-events = ["mypy-boto3-personalize-events (>=1.40.0,<1.41.0)"] +personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.40.0,<1.41.0)"] +pi = ["mypy-boto3-pi (>=1.40.0,<1.41.0)"] +pinpoint = ["mypy-boto3-pinpoint (>=1.40.0,<1.41.0)"] +pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.40.0,<1.41.0)"] +pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.40.0,<1.41.0)"] +pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.40.0,<1.41.0)"] +pipes = ["mypy-boto3-pipes (>=1.40.0,<1.41.0)"] +polly = ["mypy-boto3-polly (>=1.40.0,<1.41.0)"] +pricing = ["mypy-boto3-pricing (>=1.40.0,<1.41.0)"] +proton = ["mypy-boto3-proton (>=1.40.0,<1.41.0)"] +qapps = ["mypy-boto3-qapps (>=1.40.0,<1.41.0)"] +qbusiness = ["mypy-boto3-qbusiness (>=1.40.0,<1.41.0)"] +qconnect = ["mypy-boto3-qconnect (>=1.40.0,<1.41.0)"] +qldb = ["mypy-boto3-qldb (>=1.40.0,<1.41.0)"] +qldb-session = ["mypy-boto3-qldb-session (>=1.40.0,<1.41.0)"] +quicksight = ["mypy-boto3-quicksight (>=1.40.0,<1.41.0)"] +ram = ["mypy-boto3-ram (>=1.40.0,<1.41.0)"] +rbin = ["mypy-boto3-rbin (>=1.40.0,<1.41.0)"] +rds = ["mypy-boto3-rds (>=1.40.0,<1.41.0)"] +rds-data = ["mypy-boto3-rds-data (>=1.40.0,<1.41.0)"] +redshift = ["mypy-boto3-redshift (>=1.40.0,<1.41.0)"] +redshift-data = ["mypy-boto3-redshift-data (>=1.40.0,<1.41.0)"] +redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.40.0,<1.41.0)"] +rekognition = ["mypy-boto3-rekognition (>=1.40.0,<1.41.0)"] +repostspace = ["mypy-boto3-repostspace (>=1.40.0,<1.41.0)"] +resiliencehub = ["mypy-boto3-resiliencehub (>=1.40.0,<1.41.0)"] +resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.40.0,<1.41.0)"] +resource-groups = ["mypy-boto3-resource-groups (>=1.40.0,<1.41.0)"] +resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.40.0,<1.41.0)"] +robomaker = ["mypy-boto3-robomaker (>=1.40.0,<1.41.0)"] +rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.40.0,<1.41.0)"] +route53 = ["mypy-boto3-route53 (>=1.40.0,<1.41.0)"] +route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.40.0,<1.41.0)"] +route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.40.0,<1.41.0)"] +route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.40.0,<1.41.0)"] +route53domains = ["mypy-boto3-route53domains (>=1.40.0,<1.41.0)"] +route53profiles = ["mypy-boto3-route53profiles (>=1.40.0,<1.41.0)"] +route53resolver = ["mypy-boto3-route53resolver (>=1.40.0,<1.41.0)"] +rum = ["mypy-boto3-rum (>=1.40.0,<1.41.0)"] +s3 = ["mypy-boto3-s3 (>=1.40.0,<1.41.0)"] +s3control = ["mypy-boto3-s3control (>=1.40.0,<1.41.0)"] +s3outposts = ["mypy-boto3-s3outposts (>=1.40.0,<1.41.0)"] +s3tables = ["mypy-boto3-s3tables (>=1.40.0,<1.41.0)"] +s3vectors = ["mypy-boto3-s3vectors (>=1.40.0,<1.41.0)"] +sagemaker = ["mypy-boto3-sagemaker (>=1.40.0,<1.41.0)"] +sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.40.0,<1.41.0)"] +sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.40.0,<1.41.0)"] +sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.40.0,<1.41.0)"] +sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.40.0,<1.41.0)"] +sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.40.0,<1.41.0)"] +sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.40.0,<1.41.0)"] +savingsplans = ["mypy-boto3-savingsplans (>=1.40.0,<1.41.0)"] +scheduler = ["mypy-boto3-scheduler (>=1.40.0,<1.41.0)"] +schemas = ["mypy-boto3-schemas (>=1.40.0,<1.41.0)"] +sdb = ["mypy-boto3-sdb (>=1.40.0,<1.41.0)"] +secretsmanager = ["mypy-boto3-secretsmanager (>=1.40.0,<1.41.0)"] +security-ir = ["mypy-boto3-security-ir (>=1.40.0,<1.41.0)"] +securityhub = ["mypy-boto3-securityhub (>=1.40.0,<1.41.0)"] +securitylake = ["mypy-boto3-securitylake (>=1.40.0,<1.41.0)"] +serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.40.0,<1.41.0)"] +service-quotas = ["mypy-boto3-service-quotas (>=1.40.0,<1.41.0)"] +servicecatalog = ["mypy-boto3-servicecatalog (>=1.40.0,<1.41.0)"] +servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.40.0,<1.41.0)"] +servicediscovery = ["mypy-boto3-servicediscovery (>=1.40.0,<1.41.0)"] +ses = ["mypy-boto3-ses (>=1.40.0,<1.41.0)"] +sesv2 = ["mypy-boto3-sesv2 (>=1.40.0,<1.41.0)"] +shield = ["mypy-boto3-shield (>=1.40.0,<1.41.0)"] +signer = ["mypy-boto3-signer (>=1.40.0,<1.41.0)"] +simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.40.0,<1.41.0)"] +snow-device-management = ["mypy-boto3-snow-device-management (>=1.40.0,<1.41.0)"] +snowball = ["mypy-boto3-snowball (>=1.40.0,<1.41.0)"] +sns = ["mypy-boto3-sns (>=1.40.0,<1.41.0)"] +socialmessaging = ["mypy-boto3-socialmessaging (>=1.40.0,<1.41.0)"] +sqs = ["mypy-boto3-sqs (>=1.40.0,<1.41.0)"] +ssm = ["mypy-boto3-ssm (>=1.40.0,<1.41.0)"] +ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.40.0,<1.41.0)"] +ssm-guiconnect = ["mypy-boto3-ssm-guiconnect (>=1.40.0,<1.41.0)"] +ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.40.0,<1.41.0)"] +ssm-quicksetup = ["mypy-boto3-ssm-quicksetup (>=1.40.0,<1.41.0)"] +ssm-sap = ["mypy-boto3-ssm-sap (>=1.40.0,<1.41.0)"] +sso = ["mypy-boto3-sso (>=1.40.0,<1.41.0)"] +sso-admin = ["mypy-boto3-sso-admin (>=1.40.0,<1.41.0)"] +sso-oidc = ["mypy-boto3-sso-oidc (>=1.40.0,<1.41.0)"] +stepfunctions = ["mypy-boto3-stepfunctions (>=1.40.0,<1.41.0)"] +storagegateway = ["mypy-boto3-storagegateway (>=1.40.0,<1.41.0)"] +sts = ["mypy-boto3-sts (>=1.40.0,<1.41.0)"] +supplychain = ["mypy-boto3-supplychain (>=1.40.0,<1.41.0)"] +support = ["mypy-boto3-support (>=1.40.0,<1.41.0)"] +support-app = ["mypy-boto3-support-app (>=1.40.0,<1.41.0)"] +swf = ["mypy-boto3-swf (>=1.40.0,<1.41.0)"] +synthetics = ["mypy-boto3-synthetics (>=1.40.0,<1.41.0)"] +taxsettings = ["mypy-boto3-taxsettings (>=1.40.0,<1.41.0)"] +textract = ["mypy-boto3-textract (>=1.40.0,<1.41.0)"] +timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.40.0,<1.41.0)"] +timestream-query = ["mypy-boto3-timestream-query (>=1.40.0,<1.41.0)"] +timestream-write = ["mypy-boto3-timestream-write (>=1.40.0,<1.41.0)"] +tnb = ["mypy-boto3-tnb (>=1.40.0,<1.41.0)"] +transcribe = ["mypy-boto3-transcribe (>=1.40.0,<1.41.0)"] +transfer = ["mypy-boto3-transfer (>=1.40.0,<1.41.0)"] +translate = ["mypy-boto3-translate (>=1.40.0,<1.41.0)"] +trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.40.0,<1.41.0)"] +verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.40.0,<1.41.0)"] +voice-id = ["mypy-boto3-voice-id (>=1.40.0,<1.41.0)"] +vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.40.0,<1.41.0)"] +waf = ["mypy-boto3-waf (>=1.40.0,<1.41.0)"] +waf-regional = ["mypy-boto3-waf-regional (>=1.40.0,<1.41.0)"] +wafv2 = ["mypy-boto3-wafv2 (>=1.40.0,<1.41.0)"] +wellarchitected = ["mypy-boto3-wellarchitected (>=1.40.0,<1.41.0)"] +wisdom = ["mypy-boto3-wisdom (>=1.40.0,<1.41.0)"] +workdocs = ["mypy-boto3-workdocs (>=1.40.0,<1.41.0)"] +workmail = ["mypy-boto3-workmail (>=1.40.0,<1.41.0)"] +workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.40.0,<1.41.0)"] +workspaces = ["mypy-boto3-workspaces (>=1.40.0,<1.41.0)"] +workspaces-instances = ["mypy-boto3-workspaces-instances (>=1.40.0,<1.41.0)"] +workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.40.0,<1.41.0)"] +workspaces-web = ["mypy-boto3-workspaces-web (>=1.40.0,<1.41.0)"] +xray = ["mypy-boto3-xray (>=1.40.0,<1.41.0)"] [[package]] name = "botocore" -version = "1.39.17" +version = "1.40.56" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "botocore-1.39.17-py3-none-any.whl", hash = "sha256:41db169e919f821b3ef684794c5e67dd7bb1f5ab905d33729b1d8c27fafe8004"}, - {file = "botocore-1.39.17.tar.gz", hash = "sha256:1a1f0b29dab5d1b10d16f14423c16ac0a3043272f579e9ab0d757753ee9a7d2b"}, + {file = "botocore-1.40.56-py3-none-any.whl", hash = "sha256:0962dfc9bfb0afa1855042a88a72cc722cc7f9c08f51d2c5c88181d525a59a27"}, + {file = "botocore-1.40.56.tar.gz", hash = "sha256:b29df3418a299609632cab240ee79275463b176ebeb3adc841ba367a3fa0c4db"}, ] [package.dependencies] @@ -733,7 +735,7 @@ python-dateutil = ">=2.1,<3.0.0" urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] -crt = ["awscrt (==0.23.8)"] +crt = ["awscrt (==0.27.6)"] [[package]] name = "botocore-stubs" @@ -1663,38 +1665,62 @@ reports = ["lxml"] [[package]] name = "mypy-boto3-bedrock" -version = "1.39.12" -description = "Type annotations for boto3 Bedrock 1.39.12 service generated with mypy-boto3-builder 8.11.0" +version = "1.40.53" +description = "Type annotations for boto3 Bedrock 1.40.53 service generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "mypy_boto3_bedrock-1.39.12-py3-none-any.whl", hash = "sha256:db1227426a227a0a5f76ec5bfd1311a2d7a79cef3abfe5ca207bd4aa49e53664"}, - {file = "mypy_boto3_bedrock-1.39.12.tar.gz", hash = "sha256:ba88d138cd724eb6ed7830b9a808a2c45721a501799a084bfae5f02ecd76b5a7"}, + {file = "mypy_boto3_bedrock-1.40.53-py3-none-any.whl", hash = "sha256:bd082fb3974be6b24f2e7513ec516a051552413148326ee58a7cbfb3e07a8a70"}, + {file = "mypy_boto3_bedrock-1.40.53.tar.gz", hash = "sha256:bee419d0080881748d15742bc528f5d534fddd0a98e8d4f5e4ce8876eef932db"}, +] + +[[package]] +name = "mypy-boto3-bedrock-agent" +version = "1.40.11" +description = "Type annotations for boto3 AgentsforBedrock 1.40.11 service generated with mypy-boto3-builder 8.11.0" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mypy_boto3_bedrock_agent-1.40.11-py3-none-any.whl", hash = "sha256:b5788e43d31c49b3caa17b6d059c501ea0854f670a5a9b2940e48ac2516b7c7a"}, + {file = "mypy_boto3_bedrock_agent-1.40.11.tar.gz", hash = "sha256:4acb7be8a58124c9214fac4175ec4348e5647f29ab42f29f398f57e64bc9310f"}, ] [[package]] name = "mypy-boto3-bedrock-agent-runtime" -version = "1.39.0" -description = "Type annotations for boto3 AgentsforBedrockRuntime 1.39.0 service generated with mypy-boto3-builder 8.11.0" +version = "1.40.40" +description = "Type annotations for boto3 AgentsforBedrockRuntime 1.40.40 service generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "mypy_boto3_bedrock_agent_runtime-1.39.0-py3-none-any.whl", hash = "sha256:c1c87c6824157517ecda03f6b6334331271063df82a637b04b6ee75560503a59"}, - {file = "mypy_boto3_bedrock_agent_runtime-1.39.0.tar.gz", hash = "sha256:03337e9793dba735c95a2e08d6f545d3bc11e71edea0335a5fb7a19d622e80c7"}, + {file = "mypy_boto3_bedrock_agent_runtime-1.40.40-py3-none-any.whl", hash = "sha256:9be6f0faa4945327b62cf9e4297bb6abe4638621e37c391dc24014702219b6cb"}, + {file = "mypy_boto3_bedrock_agent_runtime-1.40.40.tar.gz", hash = "sha256:674049ed09614f69beb9a83c4553f07a985da8bfab772fb5ca8bb8092abe6d51"}, ] [[package]] name = "mypy-boto3-bedrock-runtime" -version = "1.39.7" -description = "Type annotations for boto3 BedrockRuntime 1.39.7 service generated with mypy-boto3-builder 8.11.0" +version = "1.40.41" +description = "Type annotations for boto3 BedrockRuntime 1.40.41 service generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "mypy_boto3_bedrock_runtime-1.39.7-py3-none-any.whl", hash = "sha256:f6a264ca714562c8d60874d5157fec537c329d19270b2a3d65a03084a3641da0"}, - {file = "mypy_boto3_bedrock_runtime-1.39.7.tar.gz", hash = "sha256:3c235cb575d7519ecc973b35dc708c8d1e2a0b9fce0adbbe87e2201f2d4e7f01"}, + {file = "mypy_boto3_bedrock_runtime-1.40.41-py3-none-any.whl", hash = "sha256:d65dff200986ff06c6b3579ddcea102555f2067c8987fca379bf4f9ed8ba3121"}, + {file = "mypy_boto3_bedrock_runtime-1.40.41.tar.gz", hash = "sha256:ee9bda6d6d478c8d0995e84e884bdf1798e150d437974ae27c175774a58ffaa5"}, +] + +[[package]] +name = "mypy-boto3-cloudformation" +version = "1.40.44" +description = "Type annotations for boto3 CloudFormation 1.40.44 service generated with mypy-boto3-builder 8.11.0" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mypy_boto3_cloudformation-1.40.44-py3-none-any.whl", hash = "sha256:64c8fe58ab7661fbb0bdea07c7375d3ebc3875760140feb6ad8f591a08a22647"}, + {file = "mypy_boto3_cloudformation-1.40.44.tar.gz", hash = "sha256:3d82f5504382c86ad195a1b80a2a82f73587c37e1b636864ebb85dd43bd79a5b"}, ] [[package]] @@ -2599,14 +2625,14 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.13.1" +version = "0.14.0" description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724"}, - {file = "s3transfer-0.13.1.tar.gz", hash = "sha256:c3fdba22ba1bd367922f27ec8032d6a1cf5f10c934fb5d68cf60fd5a23d936cf"}, + {file = "s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456"}, + {file = "s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125"}, ] [package.dependencies] @@ -3216,4 +3242,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "^3.13.0" -content-hash = "47f85bb761d87c1f51d2719f9b75c1733b6d380b90988559fcf6aa73b7e238b7" +content-hash = "f2286d94681d2f803b961de856497cf1c203f133ec5aeaeea5319c0b43e2fe76" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 6cade5363..c75998945 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -20,7 +20,7 @@ types-retry = ">=0.9.9.4,<1" opensearch-py = ">=2.0.0" requests-aws4auth = ">=1.0.0" duckduckgo-search = "^7.3.0" -boto3-stubs = {extras = ["bedrock", "bedrock-agent-runtime", "bedrock-runtime", "boto3"], version = "^1.37.0"} +boto3-stubs = {extras = ["bedrock", "bedrock-agent", "bedrock-agent-runtime", "bedrock-runtime", "boto3", "cloudformation"], version = "^1.40.56"} firecrawl-py = "^2.16.3" reretry = "^0.11.8" strands-agents = "^1.8.0" diff --git a/backend/tests/test_repositories/test_custom_bot.py b/backend/tests/test_repositories/test_custom_bot.py index 51dc7d614..73bac81fa 100644 --- a/backend/tests/test_repositories/test_custom_bot.py +++ b/backend/tests/test_repositories/test_custom_bot.py @@ -89,6 +89,7 @@ def test_store_and_find_bot(self): ConversationQuickStarterModel(title="QS title", example="QS example") ], bedrock_knowledge_base=BedrockKnowledgeBaseModel( + type="dedicated", embeddings_model="titan_v2", open_search=OpenSearchParamsModel( analyzer=AnalyzerParamsModel( @@ -159,6 +160,7 @@ def test_store_and_find_bot(self): self.assertEqual(len(bot.conversation_quick_starters), 1) self.assertEqual(bot.conversation_quick_starters[0].title, "QS title") self.assertEqual(bot.conversation_quick_starters[0].example, "QS example") + self.assertEqual(bot.bedrock_knowledge_base.type, "dedicated") self.assertEqual(bot.bedrock_knowledge_base.embeddings_model, "titan_v2") self.assertEqual( bot.bedrock_knowledge_base.chunking_configuration.max_tokens, 2000 @@ -275,6 +277,7 @@ def test_update_knowledge_base_id(self): False, "user1", bedrock_knowledge_base=BedrockKnowledgeBaseModel( + type="dedicated", embeddings_model="titan_v2", open_search=OpenSearchParamsModel( analyzer=AnalyzerParamsModel( @@ -339,6 +342,7 @@ def test_update_bot(self): ConversationQuickStarterModel(title="QS title", example="QS example") ], bedrock_knowledge_base=BedrockKnowledgeBaseModel( + type="dedicated", embeddings_model="titan_v2", open_search=OpenSearchParamsModel( analyzer=AnalyzerParamsModel( @@ -397,6 +401,7 @@ def test_update_bot(self): self.assertEqual(bot.conversation_quick_starters[0].title, "QS title") self.assertEqual(bot.conversation_quick_starters[0].example, "QS example") + self.assertEqual(bot.bedrock_knowledge_base.type, "dedicated") self.assertEqual(bot.bedrock_knowledge_base.embeddings_model, "titan_v2") self.assertEqual( bot.bedrock_knowledge_base.chunking_configuration.max_tokens, 2500 diff --git a/backend/tests/test_repositories/test_models/test_bot.py b/backend/tests/test_repositories/test_models/test_bot.py index 9493fd52c..4c798c299 100644 --- a/backend/tests/test_repositories/test_models/test_bot.py +++ b/backend/tests/test_repositories/test_models/test_bot.py @@ -128,6 +128,7 @@ def setUp(self) -> None: ], bedrock_knowledge_base=( BedrockKnowledgeBaseModel( + type="dedicated", embeddings_model="titan_v2", open_search=OpenSearchParamsModel(analyzer=None), chunking_configuration=None, diff --git a/backend/tests/test_repositories/utils/bot_factory.py b/backend/tests/test_repositories/utils/bot_factory.py index 33a69869e..bc11d356e 100644 --- a/backend/tests/test_repositories/utils/bot_factory.py +++ b/backend/tests/test_repositories/utils/bot_factory.py @@ -143,6 +143,7 @@ def _create_test_bot_model( ), bedrock_knowledge_base=( BedrockKnowledgeBaseModel( + type="dedicated", embeddings_model="titan_v2", open_search=OpenSearchParamsModel(analyzer=None), chunking_configuration=None, diff --git a/cdk/bin/bedrock-custom-bot.ts b/cdk/bin/bedrock-custom-bot.ts index 71d7206bc..43281e73b 100644 --- a/cdk/bin/bedrock-custom-bot.ts +++ b/cdk/bin/bedrock-custom-bot.ts @@ -2,6 +2,7 @@ import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { BedrockCustomBotStack } from "../lib/bedrock-custom-bot-stack"; import { + getKnowledgeBaseType, getEmbeddingModel, getChunkingStrategy, getAnalyzer, @@ -40,10 +41,12 @@ interface BaseConfig { } interface KnowledgeConfig { + knowledgeBaseType: "dedicated" | "shared" | undefined; embeddingsModel: BedrockFoundationModel; parsingModel: BedrockFoundationModel | undefined; existKnowledgeBaseId?: string; existingS3Urls: string[]; + filenames: string[]; sourceUrls: string[]; instruction?: string; analyzer?: Analyzer | undefined; @@ -83,100 +86,95 @@ const baseConfig: BaseConfig = { envName: params.envName, envPrefix: params.envPrefix, bedrockRegion: params.bedrockRegion, - ownerUserId: params.pk, - botId: params.sk.split("#")[1], + ownerUserId: params.ownerUserId, + botId: params.botId, documentBucketName: params.documentBucketName, enableRagReplicas: params.enableRagReplicas === true, }; const knowledgeConfig: KnowledgeConfig = { - embeddingsModel: getEmbeddingModel(knowledgeBaseJson.embeddings_model.S), - parsingModel: getParsingModel(knowledgeBaseJson.parsing_model.S), - existKnowledgeBaseId: knowledgeBaseJson.exist_knowledge_base_id?.S, - existingS3Urls: knowledgeJson.s3_urls.L.map((s3Url: any) => s3Url.S), - sourceUrls: knowledgeJson.source_urls.L.map((sourceUrl: any) => sourceUrl.S), - instruction: knowledgeBaseJson.instruction?.S, - analyzer: knowledgeBaseJson.open_search.M.analyzer.M - ? getAnalyzer(knowledgeBaseJson.open_search.M.analyzer.M) + knowledgeBaseType: getKnowledgeBaseType(knowledgeBaseJson.type), + embeddingsModel: getEmbeddingModel(knowledgeBaseJson.embeddings_model), + parsingModel: getParsingModel(knowledgeBaseJson.parsing_model), + existKnowledgeBaseId: knowledgeBaseJson.exist_knowledge_base_id, + existingS3Urls: knowledgeJson.s3_urls.map((s3Url: any) => s3Url), + filenames: knowledgeJson.filenames.map((filename: any) => filename), + sourceUrls: knowledgeJson.source_urls.map((sourceUrl: any) => sourceUrl), + instruction: knowledgeBaseJson.instruction, + analyzer: knowledgeBaseJson.open_search?.analyzer + ? getAnalyzer(knowledgeBaseJson.open_search.analyzer) : undefined, }; // Extract chunking configuration const chunkingParams = { - maxTokens: knowledgeBaseJson.chunking_configuration.M.max_tokens - ? Number(knowledgeBaseJson.chunking_configuration.M.max_tokens.N) + maxTokens: knowledgeBaseJson.chunking_configuration?.max_tokens + ? knowledgeBaseJson.chunking_configuration.max_tokens : undefined, - overlapPercentage: knowledgeBaseJson.chunking_configuration.M - .overlap_percentage - ? Number(knowledgeBaseJson.chunking_configuration.M.overlap_percentage.N) + overlapPercentage: knowledgeBaseJson.chunking_configuration?.overlap_percentage + ? knowledgeBaseJson.chunking_configuration.overlap_percentage : undefined, - overlapTokens: knowledgeBaseJson.chunking_configuration.M.overlap_tokens - ? Number(knowledgeBaseJson.chunking_configuration.M.overlap_tokens.N) + overlapTokens: knowledgeBaseJson.chunking_configuration?.overlap_tokens + ? knowledgeBaseJson.chunking_configuration.overlap_tokens : undefined, - maxParentTokenSize: knowledgeBaseJson.chunking_configuration.M - .max_parent_token_size - ? Number(knowledgeBaseJson.chunking_configuration.M.max_parent_token_size.N) + maxParentTokenSize: knowledgeBaseJson.chunking_configuration?.max_parent_token_size + ? knowledgeBaseJson.chunking_configuration.max_parent_token_size : undefined, - maxChildTokenSize: knowledgeBaseJson.chunking_configuration.M - .max_child_token_size - ? Number(knowledgeBaseJson.chunking_configuration.M.max_child_token_size.N) + maxChildTokenSize: knowledgeBaseJson.chunking_configuration?.max_child_token_size + ? knowledgeBaseJson.chunking_configuration.max_child_token_size : undefined, - bufferSize: knowledgeBaseJson.chunking_configuration.M.buffer_size - ? Number(knowledgeBaseJson.chunking_configuration.M.buffer_size.N) + bufferSize: knowledgeBaseJson.chunking_configuration?.buffer_size + ? knowledgeBaseJson.chunking_configuration.buffer_size : undefined, - breakpointPercentileThreshold: knowledgeBaseJson.chunking_configuration.M - .breakpoint_percentile_threshold - ? Number( - knowledgeBaseJson.chunking_configuration.M - .breakpoint_percentile_threshold.N - ) + breakpointPercentileThreshold: knowledgeBaseJson.chunking_configuration?.breakpoint_percentile_threshold + ? knowledgeBaseJson.chunking_configuration.breakpoint_percentile_threshold : undefined, }; const chunkingConfig: ChunkingConfig = { ...chunkingParams, chunkingStrategy: getChunkingStrategy( - knowledgeBaseJson.chunking_configuration.M.chunking_strategy.S, - knowledgeBaseJson.embeddings_model.S, + knowledgeBaseJson.chunking_configuration?.chunking_strategy, + knowledgeBaseJson.embeddings_model, chunkingParams ), }; const crawlingConfig: CrawlingConfig = { - crawlingScope: getCrowlingScope(knowledgeBaseJson.web_crawling_scope.S), - crawlingFilters: getCrawlingFilters(knowledgeBaseJson.web_crawling_filters.M), + crawlingScope: getCrowlingScope(knowledgeBaseJson.web_crawling_scope), + crawlingFilters: getCrawlingFilters(knowledgeBaseJson.web_crawling_filters), }; const guardrailConfig: GuardrailConfig = { is_guardrail_enabled: guardrailsJson.is_guardrail_enabled - ? Boolean(guardrailsJson.is_guardrail_enabled.BOOL) + ? guardrailsJson.is_guardrail_enabled : undefined, hateThreshold: guardrailsJson.hate_threshold - ? Number(guardrailsJson.hate_threshold.N) + ? guardrailsJson.hate_threshold : undefined, insultsThreshold: guardrailsJson.insults_threshold - ? Number(guardrailsJson.insults_threshold.N) + ? guardrailsJson.insults_threshold : undefined, sexualThreshold: guardrailsJson.sexual_threshold - ? Number(guardrailsJson.sexual_threshold.N) + ? guardrailsJson.sexual_threshold : undefined, violenceThreshold: guardrailsJson.violence_threshold - ? Number(guardrailsJson.violence_threshold.N) + ? guardrailsJson.violence_threshold : undefined, misconductThreshold: guardrailsJson.misconduct_threshold - ? Number(guardrailsJson.misconduct_threshold.N) + ? guardrailsJson.misconduct_threshold : undefined, groundingThreshold: guardrailsJson.grounding_threshold - ? Number(guardrailsJson.grounding_threshold.N) + ? guardrailsJson.grounding_threshold : undefined, relevanceThreshold: guardrailsJson.relevance_threshold - ? Number(guardrailsJson.relevance_threshold.N) + ? guardrailsJson.relevance_threshold : undefined, guardrailArn: guardrailsJson.guardrail_arn - ? Number(guardrailsJson.guardrail_arn.N) + ? guardrailsJson.guardrail_arn : undefined, guardrailVersion: guardrailsJson.guardrail_version - ? Number(guardrailsJson.guardrail_version.N) + ? guardrailsJson.guardrail_version : undefined, }; @@ -236,10 +234,12 @@ new BedrockCustomBotStack(app, `BrChatKbStack${baseConfig.botId}`, { enableRagReplicas: baseConfig.enableRagReplicas, // Knowledge base configuration + knowledgeBaseType: knowledgeConfig.knowledgeBaseType, embeddingsModel: knowledgeConfig.embeddingsModel, parsingModel: knowledgeConfig.parsingModel, existKnowledgeBaseId: knowledgeConfig.existKnowledgeBaseId, existingS3Urls: knowledgeConfig.existingS3Urls, + filenames: knowledgeConfig.filenames, sourceUrls: knowledgeConfig.sourceUrls, instruction: knowledgeConfig.instruction, analyzer: knowledgeConfig.analyzer, diff --git a/cdk/bin/bedrock-shared-knowledge-bases.ts b/cdk/bin/bedrock-shared-knowledge-bases.ts new file mode 100644 index 000000000..f3f902ff9 --- /dev/null +++ b/cdk/bin/bedrock-shared-knowledge-bases.ts @@ -0,0 +1,165 @@ +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { BedrockSharedKnowledgeBasesStack } from "../lib/bedrock-shared-knowledge-bases-stack"; +import { + getEmbeddingModel, + getChunkingStrategy, + getAnalyzer, + getParsingModel, +} from "../lib/utils/bedrock-knowledge-base-args"; +import { BedrockFoundationModel } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; +import { ChunkingStrategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/chunking"; +import { Analyzer } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearch-vectorindex"; +import { resolveBedrockSharedKnowledgeBasesParameters } from "../lib/utils/parameter-models"; + +const app = new cdk.App(); + +// Get parameters specific to Bedrock Custom Bot +const params = resolveBedrockSharedKnowledgeBasesParameters(); + +// Parse JSON strings into objects +const sharedKnowledgeBasesJson = JSON.parse(params.sharedKnowledgeBases); + +// Define interfaces for typed configuration objects +interface BaseConfig { + envName: string; + envPrefix: string; + bedrockRegion: string; + documentBucketName: string; + enableRagReplicas: boolean; +} + +interface KnowledgeConfig { + knowledgeBaseHash: string; + embeddingsModel: BedrockFoundationModel; + parsingModel: BedrockFoundationModel | undefined; + instruction?: string; + analyzer?: Analyzer | undefined; +} + +interface ChunkingConfig { + chunkingStrategy: ChunkingStrategy; + maxTokens?: number; + overlapPercentage?: number; + overlapTokens?: number; + maxParentTokenSize?: number; + maxChildTokenSize?: number; + bufferSize?: number; + breakpointPercentileThreshold?: number; +} + +// Extract and organize configuration by category +const baseConfig: BaseConfig = { + envName: params.envName, + envPrefix: params.envPrefix, + bedrockRegion: params.bedrockRegion, + documentBucketName: params.documentBucketName, + enableRagReplicas: params.enableRagReplicas === true, +}; + +const knowledgeBases = sharedKnowledgeBasesJson.map((sharedKnowledgeBase: any) => { + const knowledgeBaseJson = sharedKnowledgeBase.KnowledgeBase; + const knowledgeBaseHash = sharedKnowledgeBase.KnowledgeBaseHash; + const knowledgeConfig: KnowledgeConfig = { + knowledgeBaseHash: knowledgeBaseHash, + embeddingsModel: getEmbeddingModel(knowledgeBaseJson.embeddings_model), + parsingModel: getParsingModel(knowledgeBaseJson.parsing_model), + instruction: knowledgeBaseJson.instruction, + analyzer: knowledgeBaseJson.open_search?.analyzer + ? getAnalyzer(knowledgeBaseJson.open_search.analyzer) + : undefined, + }; + + // Extract chunking configuration + const chunkingParams = { + maxTokens: knowledgeBaseJson.chunking_configuration?.max_tokens + ? knowledgeBaseJson.chunking_configuration.max_tokens + : undefined, + overlapPercentage: knowledgeBaseJson.chunking_configuration?.overlap_percentage + ? knowledgeBaseJson.chunking_configuration.overlap_percentage + : undefined, + overlapTokens: knowledgeBaseJson.chunking_configuration?.overlap_tokens + ? knowledgeBaseJson.chunking_configuration.overlap_tokens + : undefined, + maxParentTokenSize: knowledgeBaseJson.chunking_configuration?.max_parent_token_size + ? knowledgeBaseJson.chunking_configuration.max_parent_token_size + : undefined, + maxChildTokenSize: knowledgeBaseJson.chunking_configuration?.max_child_token_size + ? knowledgeBaseJson.chunking_configuration.max_child_token_size + : undefined, + bufferSize: knowledgeBaseJson.chunking_configuration?.buffer_size + ? knowledgeBaseJson.chunking_configuration.buffer_size + : undefined, + breakpointPercentileThreshold: knowledgeBaseJson.chunking_configuration?.breakpoint_percentile_threshold + ? knowledgeBaseJson.chunking_configuration.breakpoint_percentile_threshold + : undefined, + }; + + const chunkingConfig: ChunkingConfig = { + ...chunkingParams, + chunkingStrategy: getChunkingStrategy( + knowledgeBaseJson.chunking_configuration?.chunking_strategy, + knowledgeBaseJson.embeddings_model, + chunkingParams + ), + }; + + // Log organized configurations for debugging + console.log("Base Configuration:", JSON.stringify(baseConfig, null, 2)); + console.log( + "Knowledge Configuration:", + JSON.stringify( + { + ...knowledgeConfig, + embeddingsModel: knowledgeConfig.embeddingsModel.toString(), + parsingModel: knowledgeConfig.parsingModel?.toString(), + analyzer: knowledgeConfig.analyzer ? "configured" : "undefined", + }, + null, + 2 + ) + ); + console.log( + "Chunking Configuration:", + JSON.stringify( + { + ...chunkingConfig, + chunkingStrategy: chunkingConfig.chunkingStrategy.toString(), + }, + null, + 2 + ) + ); + return { + knowledgeConfig, + chunkingConfig, + }; +}); + +// Create the stack +new BedrockSharedKnowledgeBasesStack(app, "BrChatSharedKbStack", { + // Environment configuration + env: { + region: baseConfig.bedrockRegion, + }, + + // Base configuration + documentBucketName: baseConfig.documentBucketName, + enableRagReplicas: baseConfig.enableRagReplicas, + + knowledgeBases: knowledgeBases.map((knowledgeBase: any) => ({ + // Knowledge base configuration + knowledgeBaseHash: knowledgeBase.knowledgeConfig.knowledgeBaseHash, + embeddingsModel: knowledgeBase.knowledgeConfig.embeddingsModel, + parsingModel: knowledgeBase.knowledgeConfig.parsingModel, + instruction: knowledgeBase.knowledgeConfig.instruction, + analyzer: knowledgeBase.knowledgeConfig.analyzer, + + // Chunking configuration + chunkingStrategy: knowledgeBase.chunkingConfig.chunkingStrategy, + maxTokens: knowledgeBase.chunkingConfig.maxTokens, + overlapPercentage: knowledgeBase.chunkingConfig.overlapPercentage, + })), +}); + +cdk.Tags.of(app).add("CDKEnvironment", baseConfig.envName); diff --git a/cdk/lambda/knowledge-base-custom-transformation/index.ts b/cdk/lambda/knowledge-base-custom-transformation/index.ts new file mode 100644 index 000000000..2f87d53c3 --- /dev/null +++ b/cdk/lambda/knowledge-base-custom-transformation/index.ts @@ -0,0 +1,40 @@ +import { type Handler } from "aws-lambda"; + +/** + * Custom transformation function used in the S3 data source of a shared Knowledge Base. + * https://docs.aws.amazon.com/bedrock/latest/userguide/kb-custom-transformation.html + */ +export const handler: Handler = async (event, context) => { + console.log(`Event: ${JSON.stringify(event)}`); + + const inputFiles = event.inputFiles as any[]; + + return { + outputFiles: inputFiles.flatMap((file) => { + const originalFileLocation = file.originalFileLocation; + const s3Uri = new URL(originalFileLocation.s3_location.uri); + + // Ignore files that do not exist in the directory for bots. + const groups = s3Uri.pathname.match( + /^\/(?[^/]+)\/(?[^/]+)\/documents\/(?[^/]+)$/ + )?.groups; + const userId = groups?.userId; + const botId = groups?.botId; + // Note: `.temp` is used for distributed lock + if (groups == null || botId == null || userId === ".temp") { + return []; + } + + // For files uploaded by bots, set the bot's ID in the metadata field named 'tenants'. + return [ + { + originalFileLocation, + contentBatches: file.contentBatches, + fileMetadata: { + tenants: [`BOT#${botId}`], + }, + }, + ]; + }), + }; +}; diff --git a/cdk/lambda/knowledge-base-custom-transformation/package-lock.json b/cdk/lambda/knowledge-base-custom-transformation/package-lock.json new file mode 100644 index 000000000..d7c2aca5a --- /dev/null +++ b/cdk/lambda/knowledge-base-custom-transformation/package-lock.json @@ -0,0 +1,2185 @@ +{ + "name": "transformation", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "transformation", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aws-sdk/client-s3": "^3.901.0" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.155", + "@types/node": "^24.6.2", + "esbuild": "^0.25.10", + "typescript": "^5.9.3" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.901.0.tgz", + "integrity": "sha512-wyKhZ51ur1tFuguZ6PgrUsot9KopqD0Tmxw8O8P/N3suQDxFPr0Yo7Y77ezDRDZQ95Ml3C0jlvx79HCo8VxdWA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-node": "3.901.0", + "@aws-sdk/middleware-bucket-endpoint": "3.901.0", + "@aws-sdk/middleware-expect-continue": "3.901.0", + "@aws-sdk/middleware-flexible-checksums": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-location-constraint": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/middleware-ssec": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/signature-v4-multi-region": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/eventstream-serde-browser": "^4.2.0", + "@smithy/eventstream-serde-config-resolver": "^4.3.0", + "@smithy/eventstream-serde-node": "^4.2.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-blob-browser": "^4.2.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/hash-stream-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/md5-js": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/util-waiter": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.901.0.tgz", + "integrity": "sha512-sGyDjjkJ7ppaE+bAKL/Q5IvVCxtoyBIzN+7+hWTS/mUxWJ9EOq9238IqmVIIK6sYNIzEf9yhobfMARasPYVTNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.901.0.tgz", + "integrity": "sha512-brKAc3y64tdhyuEf+OPIUln86bRTqkLgb9xkd6kUdIeA5+qmp/N6amItQz+RN4k4O3kqkCPYnAd3LonTKluobw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws-sdk/xml-builder": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.901.0.tgz", + "integrity": "sha512-5hAdVl3tBuARh3zX5MLJ1P/d+Kr5kXtDU3xm1pxUEF4xt2XkEEpwiX5fbkNkz2rbh3BCt2gOHsAbh6b3M7n+DA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.901.0.tgz", + "integrity": "sha512-Ggr7+0M6QZEsrqRkK7iyJLf4LkIAacAxHz9c4dm9hnDdU7vqrlJm6g73IxMJXWN1bIV7IxfpzB11DsRrB/oNjQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.901.0.tgz", + "integrity": "sha512-zxadcDS0hNJgv8n4hFYJNOXyfjaNE1vvqIiF/JzZSQpSSYXzCd+WxXef5bQh+W3giDtRUmkvP5JLbamEFjZKyw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.901.0.tgz", + "integrity": "sha512-dPuFzMF7L1s/lQyT3wDxqLe82PyTH+5o1jdfseTEln64LJMl0ZMWaKX/C1UFNDxaTd35Cgt1bDbjjAWHMiKSFQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.901.0", + "@aws-sdk/credential-provider-http": "3.901.0", + "@aws-sdk/credential-provider-ini": "3.901.0", + "@aws-sdk/credential-provider-process": "3.901.0", + "@aws-sdk/credential-provider-sso": "3.901.0", + "@aws-sdk/credential-provider-web-identity": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.901.0.tgz", + "integrity": "sha512-/IWgmgM3Cl1wTdJA5HqKMAojxLkYchh5kDuphApxKhupLu6Pu0JBOHU8A5GGeFvOycyaVwosod6zDduINZxe+A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.901.0.tgz", + "integrity": "sha512-SjmqZQHmqFSET7+6xcZgtH7yEyh5q53LN87GqwYlJZ6KJ5oNw11acUNEhUOL1xTSJEvaWqwTIkS2zqrzLcM9bw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.901.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/token-providers": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.901.0.tgz", + "integrity": "sha512-NYjy/6NLxH9m01+pfpB4ql8QgAorJcu8tw69kzHwUd/ql6wUDTbC7HcXqtKlIwWjzjgj2BKL7j6SyFapgCuafA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.901.0.tgz", + "integrity": "sha512-mPF3N6eZlVs9G8aBSzvtoxR1RZqMo1aIwR+X8BAZSkhfj55fVF2no4IfPXfdFO3I66N+zEQ8nKoB0uTATWrogQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.901.0.tgz", + "integrity": "sha512-bwq9nj6MH38hlJwOY9QXIDwa6lI48UsaZpaXbdD71BljEIRlxDzfB4JaYb+ZNNK7RIAdzsP/K05mJty6KJAQHw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.901.0.tgz", + "integrity": "sha512-63lcKfggVUFyXhE4SsFXShCTCyh7ZHEqXLyYEL4DwX+VWtxutf9t9m3fF0TNUYDE8eEGWiRXhegj8l4FjuW+wA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.901.0.tgz", + "integrity": "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.901.0.tgz", + "integrity": "sha512-MuCS5R2ngNoYifkVt05CTULvYVWX0dvRT0/Md4jE3a0u0yMygYy31C1zorwfE/SUgAQXyLmUx8ATmPp9PppImQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.901.0.tgz", + "integrity": "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.901.0.tgz", + "integrity": "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@aws/lambda-invoke-store": "^0.0.1", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.901.0.tgz", + "integrity": "sha512-prgjVC3fDT2VIlmQPiw/cLee8r4frTam9GILRUVQyDdNtshNwV3MiaSCLzzQJjKJlLgnBLNUHJCSmvUVtg+3iA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.14.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.901.0.tgz", + "integrity": "sha512-YiLLJmA3RvjL38mFLuu8fhTTGWtp2qT24VqpucgfoyziYcTgIQkJJmKi90Xp6R6/3VcArqilyRgM1+x8i/em+Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.901.0.tgz", + "integrity": "sha512-Zby4F03fvD9xAgXGPywyk4bC1jCbnyubMEYChLYohD+x20ULQCf+AimF/Btn7YL+hBpzh1+RmqmvZcx+RgwgNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@smithy/core": "^3.14.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.901.0.tgz", + "integrity": "sha512-feAAAMsVwctk2Tms40ONybvpfJPLCmSdI+G+OTrNpizkGLNl6ik2Ng2RzxY6UqOfN8abqKP/DOUj1qYDRDG8ag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.901.0", + "@aws-sdk/middleware-host-header": "3.901.0", + "@aws-sdk/middleware-logger": "3.901.0", + "@aws-sdk/middleware-recursion-detection": "3.901.0", + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/region-config-resolver": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@aws-sdk/util-endpoints": "3.901.0", + "@aws-sdk/util-user-agent-browser": "3.901.0", + "@aws-sdk/util-user-agent-node": "3.901.0", + "@smithy/config-resolver": "^4.3.0", + "@smithy/core": "^3.14.0", + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/hash-node": "^4.2.0", + "@smithy/invalid-dependency": "^4.2.0", + "@smithy/middleware-content-length": "^4.2.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-retry": "^4.4.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.0", + "@smithy/util-defaults-mode-browser": "^4.2.0", + "@smithy/util-defaults-mode-node": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.901.0.tgz", + "integrity": "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.901.0.tgz", + "integrity": "sha512-2IWxbll/pRucp1WQkHi2W5E2SVPGBvk4Is923H7gpNksbVFws18ItjMM8ZpGm44cJEoy1zR5gjhLFklatpuoOw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/signature-v4": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.901.0.tgz", + "integrity": "sha512-pJEr1Ggbc/uVTDqp9IbNu9hdr0eQf3yZix3s4Nnyvmg4xmJSGAlbPC9LrNr5u3CDZoc8Z9CuLrvbP4MwYquNpQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.901.0", + "@aws-sdk/nested-clients": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.901.0.tgz", + "integrity": "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.901.0.tgz", + "integrity": "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-endpoints": "^3.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.901.0.tgz", + "integrity": "sha512-Ntb6V/WFI21Ed4PDgL/8NSfoZQQf9xzrwNgiwvnxgAl/KvAvRBgQtqj5gHsDX8Nj2YmJuVoHfH9BGjL9VQ4WNg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.901.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.901.0.tgz", + "integrity": "sha512-l59KQP5TY7vPVUfEURc7P5BJKuNg1RSsAKBQW7LHLECXjLqDUbo2SMLrexLBEoArSt6E8QOrIN0C8z/0Xk0jYw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.901.0", + "@aws-sdk/types": "3.901.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.901.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.901.0.tgz", + "integrity": "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz", + "integrity": "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", + "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.0.tgz", + "integrity": "sha512-HNbGWdyTfSM1nfrZKQjYTvD8k086+M8s1EYkBUdGC++lhxegUp2HgNf5RIt6oOGVvsC26hBCW/11tv8KbwLn/Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", + "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.14.0.tgz", + "integrity": "sha512-XJ4z5FxvY/t0Dibms/+gLJrI5niRoY0BCmE02fwmPcRYFPI4KI876xaE79YGWIKnEslMbuQPsIEsoU/DXa0DoA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-stream": "^4.4.0", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", + "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.0.tgz", + "integrity": "sha512-XE7CtKfyxYiNZ5vz7OvyTf1osrdbJfmUy+rbh+NLQmZumMGvY0mT0Cq1qKSfhrvLtRYzMsOBuRpi10dyI0EBPg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.0.tgz", + "integrity": "sha512-U53p7fcrk27k8irLhOwUu+UYnBqsXNLKl1XevOpsxK3y1Lndk8R7CSiZV6FN3fYFuTPuJy5pP6qa/bjDzEkRvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.0.tgz", + "integrity": "sha512-uwx54t8W2Yo9Jr3nVF5cNnkAAnMCJ8Wrm+wDlQY6rY/IrEgZS3OqagtCu/9ceIcZFQ1zVW/zbN9dxb5esuojfA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.0.tgz", + "integrity": "sha512-yjM2L6QGmWgJjVu/IgYd6hMzwm/tf4VFX0lm8/SvGbGBwc+aFl3hOzvO/e9IJ2XI+22Tx1Zg3vRpFRs04SWFcg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.0.tgz", + "integrity": "sha512-C3jxz6GeRzNyGKhU7oV656ZbuHY93mrfkT12rmjDdZch142ykjn8do+VOkeRNjSGKw01p4g+hdalPYPhmMwk1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.0.tgz", + "integrity": "sha512-BG3KSmsx9A//KyIfw+sqNmWFr1YBUr+TwpxFT7yPqAk0yyDh7oSNgzfNH7pS6OC099EGx2ltOULvumCFe8bcgw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.0.tgz", + "integrity": "sha512-MWmrRTPqVKpN8NmxmJPTeQuhewTt8Chf+waB38LXHZoA02+BeWYVQ9ViAwHjug8m7lQb1UWuGqp3JoGDOWvvuA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", + "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.0.tgz", + "integrity": "sha512-8dELAuGv+UEjtzrpMeNBZc1sJhO8GxFVV/Yh21wE35oX4lOE697+lsMHBoUIFAUuYkTMIeu0EuJSEsH7/8Y+UQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", + "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.0.tgz", + "integrity": "sha512-LFEPniXGKRQArFmDQ3MgArXlClFJMsXDteuQQY8WG1/zzv6gVSo96+qpkuu1oJp4MZsKrwchY0cuAoPKzEbaNA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", + "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.0.tgz", + "integrity": "sha512-jFVjuQeV8TkxaRlcCNg0GFVgg98tscsmIrIwRFeC74TIUyLE3jmY9xgc1WXrPQYRjQNK3aRoaIk6fhFRGOIoGw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.14.0", + "@smithy/middleware-serde": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/url-parser": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.0.tgz", + "integrity": "sha512-yaVBR0vQnOnzex45zZ8ZrPzUnX73eUC8kVFaAAbn04+6V7lPtxn56vZEBBAhgS/eqD6Zm86o6sJs6FuQVoX5qg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/service-error-classification": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-retry": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", + "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", + "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", + "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.0", + "@smithy/shared-ini-file-loader": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", + "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/querystring-builder": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", + "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", + "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", + "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", + "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", + "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", + "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", + "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.0", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.0.tgz", + "integrity": "sha512-3BDx/aCCPf+kkinYf5QQhdQ9UAGihgOVqI3QO5xQfSaIWvUE4KYLtiGRWsNe1SR7ijXC0QEPqofVp5Sb0zC8xQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.14.0", + "@smithy/middleware-endpoint": "^4.3.0", + "@smithy/middleware-stack": "^4.2.0", + "@smithy/protocol-http": "^5.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-stream": "^4.4.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", + "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", + "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.2.0.tgz", + "integrity": "sha512-+erInz8WDv5KPe7xCsJCp+1WCjSbah9gWcmUXc9NqmhyPx59tf7jqFz+za1tRG1Y5KM1Cy1rWCcGypylFp4mvA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.0.tgz", + "integrity": "sha512-U8q1WsSZFjXijlD7a4wsDQOvOwV+72iHSfq1q7VD+V75xP/pdtm0WIGuaFJ3gcADDOKj2MIBn4+zisi140HEnQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.2.0.tgz", + "integrity": "sha512-qzHp7ZDk1Ba4LDwQVCNp90xPGqSu7kmL7y5toBpccuhi3AH7dcVBIT/pUxYcInK4jOy6FikrcTGq5wxcka8UaQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.0.tgz", + "integrity": "sha512-FxUHS3WXgx3bTWR6yQHNHHkQHZm/XKIi/CchTnKvBulN6obWpcbzJ6lDToXn+Wp0QlVKd7uYAz2/CTw1j7m+Kg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.3.0", + "@smithy/credential-provider-imds": "^4.2.0", + "@smithy/node-config-provider": "^4.3.0", + "@smithy/property-provider": "^4.2.0", + "@smithy/smithy-client": "^4.7.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", + "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", + "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", + "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.4.0.tgz", + "integrity": "sha512-vtO7ktbixEcrVzMRmpQDnw/Ehr9UWjBvSJ9fyAbadKkC4w5Cm/4lMO8cHz8Ysb8uflvQUNRcuux/oNHKPXkffg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.0", + "@smithy/node-http-handler": "^4.3.0", + "@smithy/types": "^4.6.0", + "@smithy/util-base64": "^4.2.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.0.tgz", + "integrity": "sha512-0Z+nxUU4/4T+SL8BCNN4ztKdQjToNvUYmkF1kXO5T7Yz3Gafzh0HeIG6mrkN8Fz3gn9hSyxuAT+6h4vM+iQSBQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.0", + "@smithy/types": "^4.6.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.155", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.155.tgz", + "integrity": "sha512-wd1XgoL0gy/ybo7WozUKQBd+IOgUkdfG6uUGI0fQOTEq06FBFdO7tmPDSxgjkFkl8GlfApvk5TvqZlAl0g+Lbg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.6.2.tgz", + "integrity": "sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.13.0" + } + }, + "node_modules/bowser": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", + "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.13.0.tgz", + "integrity": "sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/cdk/lambda/knowledge-base-custom-transformation/package.json b/cdk/lambda/knowledge-base-custom-transformation/package.json new file mode 100644 index 000000000..9b36a8f5f --- /dev/null +++ b/cdk/lambda/knowledge-base-custom-transformation/package.json @@ -0,0 +1,21 @@ +{ + "name": "transformation", + "version": "1.0.0", + "description": "", + "license": "ISC", + "author": "", + "type": "module", + "main": "index.js", + "scripts": { + "build": "esbuild index.ts --bundle --minify --sourcemap --platform=node --target=es2022 --outfile=dist/index.js" + }, + "devDependencies": { + "@types/aws-lambda": "^8.10.155", + "@types/node": "^24.6.2", + "esbuild": "^0.25.10", + "typescript": "^5.9.3" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.901.0" + } +} diff --git a/cdk/lambda/knowledge-base-custom-transformation/tsconfig.json b/cdk/lambda/knowledge-base-custom-transformation/tsconfig.json new file mode 100644 index 000000000..a5cab2b46 --- /dev/null +++ b/cdk/lambda/knowledge-base-custom-transformation/tsconfig.json @@ -0,0 +1,51 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "ES2022", + "lib": ["esnext"], + "types": ["node"], + + // Other Outputs + "preserveConstEnums": true, + "noEmit": true, + "sourceMap": false, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + // "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + + "moduleResolution": "nodenext", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + }, + "exclude": [ + "node_modules", + "**/*.test.ts" + ] +} diff --git a/cdk/lib/bedrock-chat-stack.ts b/cdk/lib/bedrock-chat-stack.ts index 9ee3d4768..73d2f9e60 100644 --- a/cdk/lib/bedrock-chat-stack.ts +++ b/cdk/lib/bedrock-chat-stack.ts @@ -25,6 +25,7 @@ import * as iam from "aws-cdk-lib/aws-iam"; import * as logs from "aws-cdk-lib/aws-logs"; import * as path from "path"; import { BedrockCustomBotCodebuild } from "./constructs/bedrock-custom-bot-codebuild"; +import { BedrockSharedKnowledgeBasesCodebuild } from "./constructs/bedrock-shared-knowledge-bases-codebuild"; import { BotStore, Language } from "./constructs/bot-store"; import { Duration } from "aws-cdk-lib"; @@ -143,6 +144,17 @@ export class BedrockChatStack extends cdk.Stack { bedrockRegion: props.bedrockRegion, } ); + // CodeBuild used for KnowledgeBase + const bedrockSharedKnowledgeBasesCodebuild = new BedrockSharedKnowledgeBasesCodebuild( + this, + "BedrockSharedKnowledgeBasesCodebuild", + { + sourceBucket, + envName: props.envName, + envPrefix: props.envPrefix, + bedrockRegion: props.bedrockRegion, + } + ); const frontend = new Frontend(this, "Frontend", { accessLogBucket, @@ -210,6 +222,15 @@ export class BedrockChatStack extends cdk.Stack { sourceDatabase: database, }); + const embedding = new Embedding(this, "Embedding", { + bedrockRegion: props.bedrockRegion, + database, + documentBucket: props.documentBucket, + bedrockCustomBotProject: bedrockCustomBotCodebuild.project, + bedrockSharedKnowledgeBasesProject: bedrockSharedKnowledgeBasesCodebuild.project, + enableRagReplicas: props.enableRagReplicas, + }); + const backendApi = new Api(this, "BackendApi", { envName: props.envName, envPrefix: props.envPrefix, @@ -219,6 +240,8 @@ export class BedrockChatStack extends cdk.Stack { documentBucket: props.documentBucket, apiPublishProject: apiPublishCodebuild.project, bedrockCustomBotProject: bedrockCustomBotCodebuild.project, + bedrockSharedKnowledgeBasesProject: bedrockSharedKnowledgeBasesCodebuild.project, + embeddingStateMachine: embedding.stateMachine, usageAnalysis, largeMessageBucket, enableBedrockGlobalInference: @@ -269,7 +292,6 @@ export class BedrockChatStack extends cdk.Stack { const websocket = new WebSocket(this, "WebSocket", { accessLogBucket, database, - websocketSessionTable: database.websocketSessionTable, auth, bedrockRegion: props.bedrockRegion, largeMessageBucket, @@ -301,14 +323,6 @@ export class BedrockChatStack extends cdk.Stack { maxAge: 3000, }); - const embedding = new Embedding(this, "Embedding", { - bedrockRegion: props.bedrockRegion, - database, - documentBucket: props.documentBucket, - bedrockCustomBotProject: bedrockCustomBotCodebuild.project, - enableRagReplicas: props.enableRagReplicas, - }); - // WebAcl for published API const webAclForPublishedApi = new WebAclForPublishedApi( this, @@ -351,5 +365,8 @@ export class BedrockChatStack extends cdk.Stack { value: largeMessageBucket.bucketName, exportName: `${props.envPrefix}${sepHyphen}BedrockClaudeChatLargeMessageBucketName`, }); + new CfnOutput(this, 'EmbeddingStateMachineArn', { + value: embedding.stateMachine.stateMachineArn, + }); } } diff --git a/cdk/lib/bedrock-custom-bot-stack.ts b/cdk/lib/bedrock-custom-bot-stack.ts index c6747b266..c7b5f90c1 100644 --- a/cdk/lib/bedrock-custom-bot-stack.ts +++ b/cdk/lib/bedrock-custom-bot-stack.ts @@ -8,7 +8,7 @@ import { import { VectorCollectionStandbyReplicas } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearchserverless"; import * as s3 from "aws-cdk-lib/aws-s3"; import * as iam from "aws-cdk-lib/aws-iam"; -import { BedrockFoundationModel } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; +import { BedrockFoundationModel, VectorStoreType } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; import { ChunkingStrategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/chunking"; import { S3DataSource } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/s3-data-source"; import { @@ -16,11 +16,10 @@ import { CrawlingScope, CrawlingFilters, } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/web-crawler-data-source"; -import { ParsingStategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/parsing"; +import { ParsingStrategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/parsing"; import { - KnowledgeBase, - IKnowledgeBase, + VectorKnowledgeBase, } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; import { aws_bedrock as bedrock } from "aws-cdk-lib"; import { @@ -54,10 +53,12 @@ interface BedrockCustomBotStackProps extends StackProps { readonly enableRagReplicas?: boolean; // Knowledge base configuration + readonly knowledgeBaseType: "dedicated" | "shared" | undefined; readonly embeddingsModel: BedrockFoundationModel; readonly parsingModel?: BedrockFoundationModel; readonly existKnowledgeBaseId: string | undefined; readonly existingS3Urls: string[]; + readonly filenames: string[]; readonly sourceUrls: string[]; readonly instruction?: string; readonly analyzer?: Analyzer; @@ -81,216 +82,105 @@ export class BedrockCustomBotStack extends Stack { const { docBucketsAndPrefixes } = this.setupBucketsAndPrefixes(props); - let kb: IKnowledgeBase; - // if knowledge base arn does not exist if (props.existKnowledgeBaseId == undefined) { - const vectorCollection = new VectorCollection(this, "KBVectors", { - collectionName: `kb-${props.botId.slice(0, 20).toLowerCase()}`, - standbyReplicas: - props.enableRagReplicas === true - ? VectorCollectionStandbyReplicas.ENABLED - : VectorCollectionStandbyReplicas.DISABLED, - }); - const vectorIndex = new VectorIndex(this, "KBIndex", { - collection: vectorCollection, - // DO NOT CHANGE THIS VALUE - indexName: "bedrock-knowledge-base-default-index", - // DO NOT CHANGE THIS VALUE - vectorField: "bedrock-knowledge-base-default-vector", - vectorDimensions: props.embeddingsModel.vectorDimensions!, - mappings: [ - { - mappingField: "AMAZON_BEDROCK_TEXT_CHUNK", - dataType: "text", - filterable: true, - }, - { - mappingField: "AMAZON_BEDROCK_METADATA", - dataType: "text", - filterable: false, - }, - ], - analyzer: props.analyzer, - }); - - kb = new KnowledgeBase(this, "KB", { - embeddingsModel: props.embeddingsModel, - vectorStore: vectorCollection, - vectorIndex: vectorIndex, - instruction: props.instruction, - }); + if (props.knowledgeBaseType === "dedicated" + && (docBucketsAndPrefixes.length > 0 || props.sourceUrls.length > 0) + ) { + const vectorCollection = new VectorCollection(this, "VectorCollection", { + standbyReplicas: + props.enableRagReplicas === true + ? VectorCollectionStandbyReplicas.ENABLED + : VectorCollectionStandbyReplicas.DISABLED, + }); + const vectorIndex = new VectorIndex(this, "VectorIndex", { + collection: vectorCollection, + // DO NOT CHANGE THIS VALUE + indexName: "bedrock-knowledge-base-default-index", + // DO NOT CHANGE THIS VALUE + vectorField: "bedrock-knowledge-base-default-vector", + vectorDimensions: props.embeddingsModel.vectorDimensions!, + precision: "float", + distanceType: "l2", + mappings: [ + { + mappingField: "AMAZON_BEDROCK_TEXT_CHUNK", + dataType: "text", + filterable: true, + }, + { + mappingField: "AMAZON_BEDROCK_METADATA", + dataType: "text", + filterable: false, + }, + ], + analyzer: props.analyzer, + }); + vectorIndex.node.addDependency(vectorCollection); - const dataSources = docBucketsAndPrefixes.map(({ bucket, prefix }) => { - bucket.grantRead(kb.role); - const inclusionPrefixes = prefix === "" ? undefined : [prefix]; - return new S3DataSource(this, `DataSource${prefix}`, { - bucket: bucket, - knowledgeBase: kb, - dataSourceName: bucket.bucketName, - chunkingStrategy: props.chunkingStrategy, - parsingStrategy: props.parsingModel - ? ParsingStategy.foundationModel({ - parsingModel: props.parsingModel.asIModel(this), - }) - : undefined, - inclusionPrefixes: inclusionPrefixes, + const kb = new VectorKnowledgeBase(this, "KnowledgeBase", { + embeddingsModel: props.embeddingsModel, + vectorStore: vectorCollection, + vectorIndex: vectorIndex, + instruction: props.instruction, + }); + new CfnOutput(this, "KnowledgeBaseId", { + value: kb.knowledgeBaseId, + }); + new CfnOutput(this, "KnowledgeBaseArn", { + value: kb.knowledgeBaseArn, }); - }); - // Add Web Crawler Data Sources - if (props.sourceUrls.length > 0) { - const webCrawlerDataSource = new WebCrawlerDataSource( - this, - "WebCrawlerDataSource", - { + const dataSources = docBucketsAndPrefixes.map(({ bucket, prefix }) => { + bucket.grantRead(kb.role); + const inclusionPrefixes = prefix === "" ? undefined : [prefix]; + return new S3DataSource(this, `DataSource${prefix}`, { + bucket: bucket, knowledgeBase: kb, - sourceUrls: props.sourceUrls, + dataSourceName: bucket.bucketName, chunkingStrategy: props.chunkingStrategy, parsingStrategy: props.parsingModel - ? ParsingStategy.foundationModel({ - parsingModel: props.parsingModel.asIModel(this), + ? ParsingStrategy.foundationModel({ + parsingModel: props.parsingModel, }) : undefined, - crawlingScope: props.crawlingScope, - filters: { - excludePatterns: props.crawlingFilters?.excludePatterns, - includePatterns: props.crawlingFilters?.includePatterns, - }, - } - ); - new CfnOutput(this, "DataSourceIdWebCrawler", { - value: webCrawlerDataSource.dataSourceId, - }); - } - - if (props.guardrail?.is_guardrail_enabled == true) { - // Use only parameters with a value greater than or equal to 0 - let contentPolicyConfigFiltersConfig = []; - let contextualGroundingFiltersConfig = []; - console.log("props.guardrail: ", props.guardrail); - - if ( - props.guardrail.hateThreshold != undefined && - props.guardrail.hateThreshold > 0 - ) { - contentPolicyConfigFiltersConfig.push({ - inputStrength: getThreshold(props.guardrail.hateThreshold), - outputStrength: getThreshold(props.guardrail.hateThreshold), - type: "HATE", - }); - } - - if ( - props.guardrail.insultsThreshold != undefined && - props.guardrail.insultsThreshold > 0 - ) { - contentPolicyConfigFiltersConfig.push({ - inputStrength: getThreshold(props.guardrail.insultsThreshold), - outputStrength: getThreshold(props.guardrail.insultsThreshold), - type: "INSULTS", - }); - } - - if ( - props.guardrail.sexualThreshold != undefined && - props.guardrail.sexualThreshold > 0 - ) { - contentPolicyConfigFiltersConfig.push({ - inputStrength: getThreshold(props.guardrail.sexualThreshold), - outputStrength: getThreshold(props.guardrail.sexualThreshold), - type: "SEXUAL", + inclusionPrefixes: inclusionPrefixes, }); - } - - if ( - props.guardrail.violenceThreshold != undefined && - props.guardrail.violenceThreshold > 0 - ) { - contentPolicyConfigFiltersConfig.push({ - inputStrength: getThreshold(props.guardrail.violenceThreshold), - outputStrength: getThreshold(props.guardrail.violenceThreshold), - type: "VIOLENCE", - }); - } - - if ( - props.guardrail.misconductThreshold != undefined && - props.guardrail.misconductThreshold > 0 - ) { - contentPolicyConfigFiltersConfig.push({ - inputStrength: getThreshold(props.guardrail.misconductThreshold), - outputStrength: getThreshold(props.guardrail.misconductThreshold), - type: "MISCONDUCT", - }); - } - - if ( - props.guardrail.groundingThreshold != undefined && - props.guardrail.groundingThreshold > 0 - ) { - contextualGroundingFiltersConfig.push({ - threshold: props.guardrail.groundingThreshold!, - type: "GROUNDING", - }); - } - - if ( - props.guardrail.relevanceThreshold != undefined && - props.guardrail.relevanceThreshold > 0 - ) { - contextualGroundingFiltersConfig.push({ - threshold: props.guardrail.relevanceThreshold!, - type: "RELEVANCE", - }); - } + }); - console.log( - "contentPolicyConfigFiltersConfig: ", - contentPolicyConfigFiltersConfig - ); - console.log( - "contextualGroundingFiltersConfig: ", - contextualGroundingFiltersConfig - ); - - // Deploy Guardrail if it contains at least one configuration value - if ( - contentPolicyConfigFiltersConfig.length > 0 || - contextualGroundingFiltersConfig.length > 0 - ) { - const guardrail = new bedrock.CfnGuardrail(this, "Guardrail", { - name: props.botId, - blockedInputMessaging: BLOCKED_INPUT_MESSAGE, - blockedOutputsMessaging: BLOCKED_OUTPUT_MESSAGE, - contentPolicyConfig: - contentPolicyConfigFiltersConfig.length > 0 - ? { - filtersConfig: contentPolicyConfigFiltersConfig, - } - : undefined, - contextualGroundingPolicyConfig: - contextualGroundingFiltersConfig.length > 0 - ? { - filtersConfig: contextualGroundingFiltersConfig, - } + // Add Web Crawler Data Sources + if (props.sourceUrls.length > 0) { + const webCrawlerDataSource = new WebCrawlerDataSource( + this, + "WebCrawlerDataSource", + { + knowledgeBase: kb, + sourceUrls: props.sourceUrls, + chunkingStrategy: props.chunkingStrategy, + parsingStrategy: props.parsingModel + ? ParsingStrategy.foundationModel({ + parsingModel: props.parsingModel, + }) : undefined, - }); - new CfnOutput(this, "GuardrailArn", { - value: guardrail.attrGuardrailArn, - }); - new CfnOutput(this, "GuardrailVersion", { - value: guardrail.attrVersion, + crawlingScope: props.crawlingScope, + filters: { + excludePatterns: props.crawlingFilters?.excludePatterns, + includePatterns: props.crawlingFilters?.includePatterns, + }, + } + ); + new CfnOutput(this, "DataSourceIdWebCrawler", { + value: webCrawlerDataSource.dataSourceId, }); } - } - // This output is used by Sfn to synchronize KB data. - dataSources.forEach((dataSource, index) => { - new CfnOutput(this, `DataSource${index}`, { - value: dataSource.dataSourceId, + // This output is used by Sfn to synchronize KB data. + dataSources.forEach((dataSource, index) => { + new CfnOutput(this, `DataSource${index}`, { + value: dataSource.dataSourceId, + }); }); - }); + } } else { // if knowledgeBaseArn exists const getKnowledgeBase = new AwsCustomResource(this, "GetKnowledgeBase", { @@ -314,18 +204,140 @@ export class BedrockCustomBotStack extends Stack { const executionRoleArn = getKnowledgeBase.getResponseField("roleArn"); - kb = KnowledgeBase.fromKnowledgeBaseAttributes(this, "MyKnowledgeBase", { + const kb = VectorKnowledgeBase.fromKnowledgeBaseAttributes(this, "MyKnowledgeBase", { + vectorStoreType: VectorStoreType.OPENSEARCH_SERVERLESS, knowledgeBaseId: props.existKnowledgeBaseId, executionRoleArn: executionRoleArn, }); + new CfnOutput(this, "KnowledgeBaseId", { + value: kb.knowledgeBaseId, + }); + new CfnOutput(this, "KnowledgeBaseArn", { + value: kb.knowledgeBaseArn, + }); + } + + if (props.guardrail?.is_guardrail_enabled == true) { + // Use only parameters with a value greater than or equal to 0 + let contentPolicyConfigFiltersConfig = []; + let contextualGroundingFiltersConfig = []; + console.log("props.guardrail: ", props.guardrail); + + if ( + props.guardrail.hateThreshold != undefined && + props.guardrail.hateThreshold > 0 + ) { + contentPolicyConfigFiltersConfig.push({ + inputStrength: getThreshold(props.guardrail.hateThreshold), + outputStrength: getThreshold(props.guardrail.hateThreshold), + type: "HATE", + }); + } + + if ( + props.guardrail.insultsThreshold != undefined && + props.guardrail.insultsThreshold > 0 + ) { + contentPolicyConfigFiltersConfig.push({ + inputStrength: getThreshold(props.guardrail.insultsThreshold), + outputStrength: getThreshold(props.guardrail.insultsThreshold), + type: "INSULTS", + }); + } + + if ( + props.guardrail.sexualThreshold != undefined && + props.guardrail.sexualThreshold > 0 + ) { + contentPolicyConfigFiltersConfig.push({ + inputStrength: getThreshold(props.guardrail.sexualThreshold), + outputStrength: getThreshold(props.guardrail.sexualThreshold), + type: "SEXUAL", + }); + } + + if ( + props.guardrail.violenceThreshold != undefined && + props.guardrail.violenceThreshold > 0 + ) { + contentPolicyConfigFiltersConfig.push({ + inputStrength: getThreshold(props.guardrail.violenceThreshold), + outputStrength: getThreshold(props.guardrail.violenceThreshold), + type: "VIOLENCE", + }); + } + + if ( + props.guardrail.misconductThreshold != undefined && + props.guardrail.misconductThreshold > 0 + ) { + contentPolicyConfigFiltersConfig.push({ + inputStrength: getThreshold(props.guardrail.misconductThreshold), + outputStrength: getThreshold(props.guardrail.misconductThreshold), + type: "MISCONDUCT", + }); + } + + if ( + props.guardrail.groundingThreshold != undefined && + props.guardrail.groundingThreshold > 0 + ) { + contextualGroundingFiltersConfig.push({ + threshold: props.guardrail.groundingThreshold!, + type: "GROUNDING", + }); + } + + if ( + props.guardrail.relevanceThreshold != undefined && + props.guardrail.relevanceThreshold > 0 + ) { + contextualGroundingFiltersConfig.push({ + threshold: props.guardrail.relevanceThreshold!, + type: "RELEVANCE", + }); + } + + console.log( + "contentPolicyConfigFiltersConfig: ", + contentPolicyConfigFiltersConfig + ); + console.log( + "contextualGroundingFiltersConfig: ", + contextualGroundingFiltersConfig + ); + + // Deploy Guardrail if it contains at least one configuration value + if ( + contentPolicyConfigFiltersConfig.length > 0 || + contextualGroundingFiltersConfig.length > 0 + ) { + const guardrail = new bedrock.CfnGuardrail(this, "Guardrail", { + name: props.botId, + blockedInputMessaging: BLOCKED_INPUT_MESSAGE, + blockedOutputsMessaging: BLOCKED_OUTPUT_MESSAGE, + contentPolicyConfig: + contentPolicyConfigFiltersConfig.length > 0 + ? { + filtersConfig: contentPolicyConfigFiltersConfig, + } + : undefined, + contextualGroundingPolicyConfig: + contextualGroundingFiltersConfig.length > 0 + ? { + filtersConfig: contextualGroundingFiltersConfig, + } + : undefined, + }); + new CfnOutput(this, "GuardrailArn", { + value: guardrail.attrGuardrailArn, + }); + new CfnOutput(this, "GuardrailVersion", { + value: guardrail.attrVersion, + }); + } } - new CfnOutput(this, "KnowledgeBaseId", { - value: kb.knowledgeBaseId, - }); - new CfnOutput(this, "KnowledgeBaseArn", { - value: kb.knowledgeBaseArn, - }); new CfnOutput(this, "OwnerUserId", { value: props.ownerUserId, }); @@ -349,15 +361,17 @@ export class BedrockCustomBotStack extends Stack { */ const docBucketsAndPrefixes: { bucket: s3.IBucket; prefix: string }[] = []; - // Always add the default bucket with its default prefix - docBucketsAndPrefixes.push({ - bucket: s3.Bucket.fromBucketName( - this, - props.bedrockClaudeChatDocumentBucketName, - props.bedrockClaudeChatDocumentBucketName - ), - prefix: `${props.ownerUserId}/${props.botId}/documents/`, - }); + // Add the default bucket with its default prefix if needed. + if (props.filenames && props.filenames.length > 0) { + docBucketsAndPrefixes.push({ + bucket: s3.Bucket.fromBucketName( + this, + props.bedrockClaudeChatDocumentBucketName, + props.bedrockClaudeChatDocumentBucketName + ), + prefix: `${props.ownerUserId}/${props.botId}/documents/`, + }); + } if (props.existingS3Urls && props.existingS3Urls.length > 0) { props.existingS3Urls.forEach((url) => { diff --git a/cdk/lib/bedrock-region-resources.ts b/cdk/lib/bedrock-region-resources.ts index 481600600..5a70cabcf 100644 --- a/cdk/lib/bedrock-region-resources.ts +++ b/cdk/lib/bedrock-region-resources.ts @@ -1,4 +1,10 @@ -import { CfnOutput, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; +import { + CfnOutput, + Duration, + RemovalPolicy, + Stack, + StackProps, +} from "aws-cdk-lib"; import { Construct } from "constructs"; import { BlockPublicAccess, @@ -42,6 +48,10 @@ export class BedrockRegionResourcesStack extends Stack { serverAccessLogsBucket: accessLogBucket, serverAccessLogsPrefix: "DocumentBucket", }); + this.documentBucket.addLifecycleRule({ + prefix: ".temp/", + expiration: Duration.days(1), + }); new CfnOutput(this, "DocumentBucketName", { value: this.documentBucket.bucketName, diff --git a/cdk/lib/bedrock-shared-knowledge-bases-stack.ts b/cdk/lib/bedrock-shared-knowledge-bases-stack.ts new file mode 100644 index 000000000..417800281 --- /dev/null +++ b/cdk/lib/bedrock-shared-knowledge-bases-stack.ts @@ -0,0 +1,157 @@ +import { CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from "aws-cdk-lib"; +import { Construct } from "constructs"; +import * as lambdaNodeJs from 'aws-cdk-lib/aws-lambda-nodejs'; +import { VectorCollection } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearchserverless"; +import { + Analyzer, + VectorIndex, +} from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearch-vectorindex"; +import { VectorCollectionStandbyReplicas } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/opensearchserverless"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import { BedrockFoundationModel, CustomTransformation } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; +import { ChunkingStrategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/chunking"; +import { S3DataSource } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/s3-data-source"; +import { ParsingStrategy } from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock/data-sources/parsing"; + +import { + VectorKnowledgeBase, +} from "@cdklabs/generative-ai-cdk-constructs/lib/cdk-lib/bedrock"; + +import * as path from "path"; + +interface BedrockSharedKnowledgeBasesStackProps extends StackProps { + // Base configuration + readonly documentBucketName: string; + readonly enableRagReplicas?: boolean; + + readonly knowledgeBases: Omit[]; +} + +export class BedrockSharedKnowledgeBasesStack extends Stack { + constructor(scope: Construct, id: string, props: BedrockSharedKnowledgeBasesStackProps) { + super(scope, id, props); + + const bucket = s3.Bucket.fromBucketName( + this, + props.documentBucketName, + props.documentBucketName + ); + + props.knowledgeBases.forEach(knowledgeBase => { + const hash = knowledgeBase.knowledgeBaseHash; + const sharedKnowledgeBase = new SharedKnowledgeBase(this, `KnowledgeBase${hash}`, { + documentBucket: bucket, + enableRagReplicas: props.enableRagReplicas, + ...knowledgeBase, + }); + new CfnOutput(this, `KnowledgeBaseId${hash}`, { + value: sharedKnowledgeBase.kb.knowledgeBaseId, + }); + new CfnOutput(this, `KnowledgeBaseArn${hash}`, { + value: sharedKnowledgeBase.kb.knowledgeBaseArn, + }); + + // This output is used by Sfn to synchronize KB data. + new CfnOutput(this, `DataSource${hash}0`, { + value: sharedKnowledgeBase.dataSource.dataSourceId, + }); + }); + } +} + +interface BedrockKnowledgeBaseProps { + // Base configuration + readonly documentBucket: s3.IBucket; + readonly enableRagReplicas?: boolean; + + // Knowledge base configuration + readonly knowledgeBaseHash: string; + readonly embeddingsModel: BedrockFoundationModel; + readonly parsingModel?: BedrockFoundationModel; + readonly instruction?: string; + readonly analyzer?: Analyzer; + + // Chunking configuration + readonly chunkingStrategy: ChunkingStrategy; + readonly maxTokens?: number; + readonly overlapPercentage?: number; +} + +class SharedKnowledgeBase extends Construct { + readonly kb: VectorKnowledgeBase; + readonly dataSource: S3DataSource; + + constructor(scope: Construct, id: string, props: BedrockKnowledgeBaseProps) { + super(scope, id); + + const vectorCollection = new VectorCollection(this, "VectorCollection", { + standbyReplicas: + props.enableRagReplicas === true + ? VectorCollectionStandbyReplicas.ENABLED + : VectorCollectionStandbyReplicas.DISABLED, + }); + const vectorIndex = new VectorIndex(this, "VectorIndex", { + collection: vectorCollection, + // DO NOT CHANGE THIS VALUE + indexName: "bedrock-knowledge-base-default-index", + // DO NOT CHANGE THIS VALUE + vectorField: "bedrock-knowledge-base-default-vector", + vectorDimensions: props.embeddingsModel.vectorDimensions!, + precision: "float", + distanceType: "l2", + mappings: [ + { + mappingField: "AMAZON_BEDROCK_TEXT_CHUNK", + dataType: "text", + filterable: true, + }, + { + mappingField: "AMAZON_BEDROCK_METADATA", + dataType: "text", + filterable: false, + }, + ], + analyzer: props.analyzer, + }); + vectorIndex.node.addDependency(vectorCollection); + + const tempBucket = new s3.Bucket(this, 'TempBucket', { + enforceSSL: true, + autoDeleteObjects: true, + removalPolicy: RemovalPolicy.DESTROY, + }); + const transformationFunction = new lambdaNodeJs.NodejsFunction(this, 'TransformationFunction', { + entry: path.resolve(__dirname, '../lambda/knowledge-base-custom-transformation/index.ts'), + depsLockFilePath: path.resolve(__dirname, '../lambda/knowledge-base-custom-transformation/package-lock.json'), + + timeout: Duration.minutes(15), + }); + props.documentBucket.grantReadWrite(transformationFunction); + tempBucket.grantReadWrite(transformationFunction); + + this.kb = new VectorKnowledgeBase(this, "KnowledgeBase", { + embeddingsModel: props.embeddingsModel, + vectorStore: vectorCollection, + vectorIndex: vectorIndex, + instruction: props.instruction, + }); + tempBucket.grantReadWrite(this.kb.role); + + props.documentBucket.grantRead(this.kb.role); + this.dataSource = new S3DataSource(this, `DocumentBucketDataSource`, { + bucket: props.documentBucket, + knowledgeBase: this.kb, + dataSourceName: props.documentBucket.bucketName, + chunkingStrategy: props.chunkingStrategy, + parsingStrategy: props.parsingModel + ? ParsingStrategy.foundationModel({ + parsingModel: props.parsingModel, + }) + : undefined, + customTransformation: CustomTransformation.lambda({ + lambdaFunction: transformationFunction, + s3BucketUri: tempBucket.s3UrlForObject(), + }), + }); + } +} diff --git a/cdk/lib/constructs/api-publish-codebuild.ts b/cdk/lib/constructs/api-publish-codebuild.ts index 114c4f2b8..563973893 100644 --- a/cdk/lib/constructs/api-publish-codebuild.ts +++ b/cdk/lib/constructs/api-publish-codebuild.ts @@ -38,11 +38,11 @@ export class ApiPublishCodebuild extends Construct { BEDROCK_REGION: { value: props.bedrockRegion }, }, buildSpec: codebuild.BuildSpec.fromObject({ - version: "0.2", + version: 0.2, phases: { install: { "runtime-versions": { - nodejs: "18", + nodejs: 22, }, "on-failure": "ABORT", }, diff --git a/cdk/lib/constructs/api.ts b/cdk/lib/constructs/api.ts index e81f1ae07..2384d6a98 100644 --- a/cdk/lib/constructs/api.ts +++ b/cdk/lib/constructs/api.ts @@ -22,6 +22,7 @@ import * as logs from "aws-cdk-lib/aws-logs"; import * as path from "path"; import { IBucket } from "aws-cdk-lib/aws-s3"; import * as codebuild from "aws-cdk-lib/aws-codebuild"; +import * as sfn from "aws-cdk-lib/aws-stepfunctions"; import { UsageAnalysis } from "./usage-analysis"; import { excludeDockerImage } from "../constants/docker"; import { PythonFunction } from "@aws-cdk/aws-lambda-python-alpha"; @@ -38,6 +39,8 @@ export interface ApiProps { readonly largeMessageBucket: IBucket; readonly apiPublishProject: codebuild.IProject; readonly bedrockCustomBotProject: codebuild.IProject; + readonly bedrockSharedKnowledgeBasesProject: codebuild.IProject; + readonly embeddingStateMachine: sfn.IStateMachine; readonly usageAnalysis?: UsageAnalysis; readonly enableBedrockGlobalInference: boolean; readonly enableBedrockCrossRegionInference: boolean; @@ -87,9 +90,11 @@ export class Api extends Construct { resources: [ props.apiPublishProject.projectArn, props.bedrockCustomBotProject.projectArn, + props.bedrockSharedKnowledgeBasesProject.projectArn, ], }) ); + props.embeddingStateMachine.grantStartExecution(handlerRole); handlerRole.addToPolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, @@ -110,6 +115,7 @@ export class Api extends Construct { resources: [ props.apiPublishProject.projectArn, props.bedrockCustomBotProject.projectArn, + props.bedrockSharedKnowledgeBasesProject.projectArn, ], }) ); @@ -256,8 +262,7 @@ export class Api extends Construct { DOCUMENT_BUCKET: props.documentBucket.bucketName, LARGE_MESSAGE_BUCKET: props.largeMessageBucket.bucketName, PUBLISH_API_CODEBUILD_PROJECT_NAME: props.apiPublishProject.projectName, - // KNOWLEDGE_BASE_CODEBUILD_PROJECT_NAME: - // props.bedrockCustomBotProject.projectName, + EMBEDDING_STATE_MACHINE_ARN: props.embeddingStateMachine.stateMachineArn, USAGE_ANALYSIS_DATABASE: props.usageAnalysis?.database.databaseName || "", USAGE_ANALYSIS_TABLE: diff --git a/cdk/lib/constructs/bedrock-custom-bot-codebuild.ts b/cdk/lib/constructs/bedrock-custom-bot-codebuild.ts index 3f82767c5..f04789ab8 100644 --- a/cdk/lib/constructs/bedrock-custom-bot-codebuild.ts +++ b/cdk/lib/constructs/bedrock-custom-bot-codebuild.ts @@ -36,11 +36,11 @@ export class BedrockCustomBotCodebuild extends Construct { BEDROCK_REGION: { value: props.bedrockRegion }, }, buildSpec: codebuild.BuildSpec.fromObject({ - version: "0.2", + version: 0.2, phases: { install: { "runtime-versions": { - nodejs: "18", + nodejs: 22, }, "on-failure": "ABORT", }, @@ -48,8 +48,6 @@ export class BedrockCustomBotCodebuild extends Construct { commands: [ "cd cdk", "npm ci", - // Extract BOT_ID from SK. Note that SK is given like BOT# - `export BOT_ID=$(echo $SK | awk -F'#' '{print $2}')`, // Replace cdk's entrypoint. This is a workaround to avoid the issue that cdk synthesize all stacks. "sed -i 's|bin/bedrock-chat.ts|bin/bedrock-custom-bot.ts|' cdk.json", `npx cdk deploy --require-approval never BrChatKbStack$BOT_ID`, diff --git a/cdk/lib/constructs/bedrock-shared-knowledge-bases-codebuild.ts b/cdk/lib/constructs/bedrock-shared-knowledge-bases-codebuild.ts new file mode 100644 index 000000000..8bf59363f --- /dev/null +++ b/cdk/lib/constructs/bedrock-shared-knowledge-bases-codebuild.ts @@ -0,0 +1,80 @@ +import { Construct } from "constructs"; +import * as codebuild from "aws-cdk-lib/aws-codebuild"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import * as iam from "aws-cdk-lib/aws-iam"; +import { NagSuppressions } from "cdk-nag"; + +export interface BedrockSharedKnowledgeBasesCodebuildProps { + readonly envName: string; + readonly envPrefix: string; + readonly bedrockRegion: string; + readonly sourceBucket: s3.Bucket; +} + +export class BedrockSharedKnowledgeBasesCodebuild extends Construct { + public readonly project: codebuild.Project; + + constructor(scope: Construct, id: string, props: BedrockSharedKnowledgeBasesCodebuildProps) { + super(scope, id); + + const sourceBucket = props.sourceBucket; + const project = new codebuild.Project(this, "Project", { + source: codebuild.Source.s3({ + bucket: sourceBucket, + path: "", + }), + environment: { + buildImage: codebuild.LinuxBuildImage.STANDARD_7_0, + privileged: true, + }, + environmentVariables: { + ENV_NAME: { value: props.envName }, + ENV_PREFIX: { value: props.envPrefix }, + BEDROCK_REGION: { value: props.bedrockRegion }, + }, + buildSpec: codebuild.BuildSpec.fromObject({ + version: 0.2, + phases: { + install: { + "runtime-versions": { + nodejs: 22, + }, + "on-failure": "ABORT", + }, + build: { + commands: [ + "cd cdk", + "npm ci", + // Replace cdk's entrypoint. This is a workaround to avoid the issue that cdk synthesize all stacks. + "sed -i 's|bin/bedrock-chat.ts|bin/bedrock-shared-knowledge-bases.ts|' cdk.json", + "npx cdk deploy --require-approval never BrChatSharedKbStack", + ], + }, + }, + }), + }); + sourceBucket.grantRead(project.role!); + + // Allow `cdk deploy` + project.role!.addToPrincipalPolicy( + new iam.PolicyStatement({ + actions: ["sts:AssumeRole"], + resources: ["arn:aws:iam::*:role/cdk-*"], + }) + ); + + NagSuppressions.addResourceSuppressions(project, [ + { + id: "AwsPrototyping-CodeBuildProjectKMSEncryptedArtifacts", + reason: + "default: The AWS-managed CMK for Amazon Simple Storage Service (Amazon S3) is used.", + }, + { + id: "AwsPrototyping-CodeBuildProjectPrivilegedModeDisabled", + reason: "for running on the docker daemon on the docker container", + }, + ]); + + this.project = project; + } +} diff --git a/cdk/lib/constructs/database.ts b/cdk/lib/constructs/database.ts index 9faa9c89c..67dc39c24 100644 --- a/cdk/lib/constructs/database.ts +++ b/cdk/lib/constructs/database.ts @@ -79,6 +79,14 @@ export class Database extends Construct { indexName: "ItemTypeIndex", partitionKey: { name: "ItemType", type: AttributeType.STRING }, }); + // GSI-4 + botTable.addGlobalSecondaryIndex({ + indexName: "SyncStatusIndex", + partitionKey: { + name: "SyncStatus", + type: AttributeType.STRING, + }, + }); const tableAccessRole = new Role(this, "TableAccessRole", { assumedBy: new AccountPrincipal(Stack.of(this).account), diff --git a/cdk/lib/constructs/embedding.ts b/cdk/lib/constructs/embedding.ts index ff15e39e8..4a53455ab 100644 --- a/cdk/lib/constructs/embedding.ts +++ b/cdk/lib/constructs/embedding.ts @@ -2,8 +2,6 @@ import { Construct } from "constructs"; import * as path from "path"; import { Duration, RemovalPolicy, Stack } from "aws-cdk-lib"; import * as iam from "aws-cdk-lib/aws-iam"; -import { ITable } from "aws-cdk-lib/aws-dynamodb"; -import { CfnPipe } from "aws-cdk-lib/aws-pipes"; import * as logs from "aws-cdk-lib/aws-logs"; import { IBucket } from "aws-cdk-lib/aws-s3"; import * as lambda from "aws-cdk-lib/aws-lambda"; @@ -25,31 +23,30 @@ export interface EmbeddingProps { readonly bedrockRegion: string; readonly documentBucket: IBucket; readonly bedrockCustomBotProject: codebuild.IProject; + readonly bedrockSharedKnowledgeBasesProject: codebuild.IProject; readonly enableRagReplicas: boolean; } export class Embedding extends Construct { + readonly stateMachine: sfn.StateMachine; readonly removalHandler: IFunction; + private _updateSyncStatusHandler: IFunction; - private _fetchStackOutputHandler: IFunction; - private _StoreKnowledgeBaseIdHandler: IFunction; - private _StoreGuardrailArnHandler: IFunction; - private _pipeRole: iam.Role; - private _stateMachine: sfn.StateMachine; - private _removalHandler: IFunction; + private _bootstrapStateMachineHandler: IFunction; + private _finalizeCustomBotBuildHandler: IFunction; + private _finalizeSharedKnowledgeBasesBuildHandler: IFunction; + private _synchronizeDataSourceHandler: IFunction; + private _lockHandler: IFunction; constructor(scope: Construct, id: string, props: EmbeddingProps) { super(scope, id); - this.setupStateMachineHandlers(props) - .setupStateMachine(props) - .setupEventBridgePipe(props) - .setupRemovalHandler(props); - - this.removalHandler = this._removalHandler; + this.setupStateMachineHandlers(props); + this.stateMachine = this.setupStateMachine(props) + this.removalHandler = this.setupRemovalHandler(props); } - private setupStateMachineHandlers(props: EmbeddingProps): this { + private setupStateMachineHandlers(props: EmbeddingProps) { const handlerRole = new iam.Role(this, "HandlerRole", { assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"), }); @@ -87,6 +84,7 @@ export class Embedding extends Construct { resources: ["arn:aws:logs:*:*:*"], }) ); + props.documentBucket.grantReadWrite(handlerRole); this._updateSyncStatusHandler = new DockerImageFunction( this, @@ -117,9 +115,9 @@ export class Embedding extends Construct { } ); - this._fetchStackOutputHandler = new DockerImageFunction( + this._bootstrapStateMachineHandler = new DockerImageFunction( this, - "FetchStackOutputHandler", + "BootstrapStateMachineHandler", { code: DockerImageCode.fromImageAsset( path.join(__dirname, "../../../backend"), @@ -127,7 +125,7 @@ export class Embedding extends Construct { platform: Platform.LINUX_AMD64, file: "lambda.Dockerfile", cmd: [ - "embedding_statemachine.bedrock_knowledge_base.fetch_stack_output.handler", + "embedding_statemachine.bedrock_knowledge_base.bootstrap_state_machine.handler", ], exclude: [...excludeDockerImage], } @@ -136,14 +134,17 @@ export class Embedding extends Construct { timeout: Duration.minutes(1), role: handlerRole, environment: { - BEDROCK_REGION: props.bedrockRegion, + ACCOUNT: Stack.of(this).account, + REGION: Stack.of(this).region, + BOT_TABLE_NAME: props.database.botTable.tableName, + TABLE_ACCESS_ROLE_ARN: props.database.tableAccessRole.roleArn, }, logRetention: logs.RetentionDays.THREE_MONTHS, } ); - this._StoreKnowledgeBaseIdHandler = new DockerImageFunction( + this._finalizeCustomBotBuildHandler = new DockerImageFunction( this, - "StoreKnowledgeBaseIdHandler", + "FinalizeCustomBotBuildHandler", { code: DockerImageCode.fromImageAsset( path.join(__dirname, "../../../backend"), @@ -151,27 +152,28 @@ export class Embedding extends Construct { platform: Platform.LINUX_AMD64, file: "lambda.Dockerfile", cmd: [ - "embedding_statemachine.bedrock_knowledge_base.store_knowledge_base_id.handler", + "embedding_statemachine.bedrock_knowledge_base.finalize_custom_bot_build.handler", ], exclude: [...excludeDockerImage], } ), memorySize: 512, timeout: Duration.minutes(1), + role: handlerRole, environment: { ACCOUNT: Stack.of(this).account, REGION: Stack.of(this).region, + BEDROCK_REGION: props.bedrockRegion, CONVERSATION_TABLE_NAME: props.database.conversationTable.tableName, BOT_TABLE_NAME: props.database.botTable.tableName, TABLE_ACCESS_ROLE_ARN: props.database.tableAccessRole.roleArn, }, - role: handlerRole, logRetention: logs.RetentionDays.THREE_MONTHS, } ); - this._StoreGuardrailArnHandler = new DockerImageFunction( + this._finalizeSharedKnowledgeBasesBuildHandler = new DockerImageFunction( this, - "StoreGuardrailArnHandler", + "FinalizeSharedKnowledgeBasesBuildHandler", { code: DockerImageCode.fromImageAsset( path.join(__dirname, "../../../backend"), @@ -179,346 +181,426 @@ export class Embedding extends Construct { platform: Platform.LINUX_AMD64, file: "lambda.Dockerfile", cmd: [ - "embedding_statemachine.guardrails.store_guardrail_arn.handler", + "embedding_statemachine.bedrock_knowledge_base.finalize_shared_knowledge_bases_build.handler", ], exclude: [...excludeDockerImage], } ), memorySize: 512, timeout: Duration.minutes(1), + role: handlerRole, environment: { ACCOUNT: Stack.of(this).account, REGION: Stack.of(this).region, + BEDROCK_REGION: props.bedrockRegion, CONVERSATION_TABLE_NAME: props.database.conversationTable.tableName, BOT_TABLE_NAME: props.database.botTable.tableName, TABLE_ACCESS_ROLE_ARN: props.database.tableAccessRole.roleArn, }, - role: handlerRole, logRetention: logs.RetentionDays.THREE_MONTHS, } ); - return this; + + this._synchronizeDataSourceHandler = new DockerImageFunction(this, "SynchronizeDataSourceHandler", { + code: DockerImageCode.fromImageAsset(path.join(__dirname, "../../../backend"), { + platform: Platform.LINUX_AMD64, + file: "lambda.Dockerfile", + cmd: [ + "embedding_statemachine.bedrock_knowledge_base.synchronize_data_source.handler", + ], + exclude: [...excludeDockerImage], + }), + memorySize: 512, + timeout: Duration.minutes(15), + environment: { + ACCOUNT: Stack.of(this).account, + REGION: Stack.of(this).region, + BEDROCK_REGION: props.bedrockRegion, + DOCUMENT_BUCKET: props.documentBucket.bucketName, + }, + role: handlerRole, + logRetention: logs.RetentionDays.THREE_MONTHS, + }); + + this._lockHandler = new DockerImageFunction(this, "LockHandler", { + code: DockerImageCode.fromImageAsset(path.join(__dirname, "../../../backend"), { + platform: Platform.LINUX_AMD64, + file: "lambda.Dockerfile", + cmd: [ + "embedding_statemachine.bedrock_knowledge_base.lock.handler", + ], + exclude: [...excludeDockerImage], + }), + memorySize: 512, + timeout: Duration.minutes(1), + environment: { + ACCOUNT: Stack.of(this).account, + REGION: Stack.of(this).region, + BEDROCK_REGION: props.bedrockRegion, + DOCUMENT_BUCKET: props.documentBucket.bucketName, + }, + role: handlerRole, + logRetention: logs.RetentionDays.THREE_MONTHS, + }); } - private setupStateMachine(props: EmbeddingProps): this { - const extractFirstElement = new sfn.Pass(this, "ExtractFirstElement", { - parameters: { - "dynamodb.$": "$[0].dynamodb", - "eventID.$": "$[0].eventID", - "eventName.$": "$[0].eventName", - "eventSource.$": "$[0].eventSource", - "eventVersion.$": "$[0].eventVersion", - "awsRegion.$": "$[0].awsRegion", - "eventSourceARN.$": "$[0].eventSourceARN", + private setupStateMachine(props: EmbeddingProps): sfn.StateMachine { + // To build the information necessary for processing the embedded state machine, retrieve information about bots and shared Knowledge Bases from the database. + const bootstrapStateMachine = new tasks.LambdaInvoke(this, "BootstrapStateMachine", { + lambdaFunction: this._bootstrapStateMachineHandler, + resultSelector: { + QueuedBots: sfn.JsonPath.objectAt("$.Payload.QueuedBots"), + SharedKnowledgeBases: sfn.JsonPath.objectAt("$.Payload.SharedKnowledgeBases"), }, - resultPath: "$", }); - const startCustomBotBuild = new tasks.CodeBuildStartBuild( - this, - "StartCustomBotBuild", - { - project: props.bedrockCustomBotProject, - integrationPattern: sfn.IntegrationPattern.RUN_JOB, - environmentVariablesOverride: { - PK: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: sfn.JsonPath.stringAt("$.dynamodb.NewImage.PK.S"), - }, - SK: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: sfn.JsonPath.stringAt("$.dynamodb.NewImage.SK.S"), - }, - // Bucket name provisioned by the bedrock stack - BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: props.documentBucket.bucketName, - }, - // Source info e.g. file names, URLs, etc. - KNOWLEDGE: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: sfn.JsonPath.stringAt( - "States.JsonToString($.dynamodb.NewImage.Knowledge.M)" - ), - }, - // Bedrock Knowledge Base configuration - BEDROCK_KNOWLEDGE_BASE: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: sfn.JsonPath.stringAt( - "States.JsonToString($.dynamodb.NewImage.BedrockKnowledgeBase.M)" - ), - }, - BEDROCK_GUARDRAILS: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: sfn.JsonPath.stringAt( - "States.JsonToString($.dynamodb.NewImage.GuardrailsParams.M)" - ), - }, - ENABLE_RAG_REPLICAS: { - type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, - value: props.enableRagReplicas.toString(), - }, - }, - resultPath: "$.Build", - } - ); + const checkSyncSharedKnowledgeBasesRequired = new sfn.Choice(this, "CheckSyncSharedKnowledgeBasesRequired"); - const updateSyncStatusRunning = this.createUpdateSyncStatusTask( - "UpdateSyncStatusRunning", - "RUNNING" - ); + // Acquire a distributed lock for shared Knowledge Bases. + const acquireLockForSharedKnowledgeBases = this.createAcquireLockTask("ForSharedKnowledgeBases", { + name: "shared-knowledge-bases", + resultPath: "$.Lock", + }); - const updateSyncStatusSucceeded = this.createUpdateSyncStatusTask( - "UpdateSyncStatusSuccess", - "SUCCEEDED", - "Knowledge base sync succeeded" + // Release the acquired lock for shared Knowledge Bases. + const releaseLockForSharedKnowledgeBasesOnFailed = this.createReleaseLockTask("ForSharedKnowledgeBasesOnFailed", { + name: "shared-knowledge-bases", + lockId: sfn.JsonPath.stringAt("$.Lock.LockId"), + }); + const syncSharedKnowledgeBasesFailed = new sfn.Fail(this, "SyncSharedKnowledgeBasesFailed", { + cause: "Shared knowledge bases sync failed", + }); + const releaseLockFallback = ( + releaseLockForSharedKnowledgeBasesOnFailed + .next(syncSharedKnowledgeBasesFailed) ); - const updateSyncStatusFailed = new tasks.LambdaInvoke( - this, - "UpdateSyncStatusFailed", - { - lambdaFunction: this._updateSyncStatusHandler, - payload: sfn.TaskInput.fromObject({ - "cause.$": "$.Cause", - }), - resultPath: sfn.JsonPath.DISCARD, - } - ); + const updateSyncStatusRunning = new tasks.LambdaInvoke(this, "UpdateSyncStatusRunning", { + lambdaFunction: this._updateSyncStatusHandler, + payload: sfn.TaskInput.fromObject({ + QueuedBots: sfn.JsonPath.objectAt("$.QueuedBots"), + SyncStatus: "RUNNING", + }), + resultPath: sfn.JsonPath.DISCARD, + }); + updateSyncStatusRunning.addCatch(releaseLockFallback, { + resultPath: "$.Error", + }); - const fallback = updateSyncStatusFailed.next( - new sfn.Fail(this, "Fail", { - cause: "Knowledge base sync failed", - error: "Knowledge base sync failed", - }) - ); - startCustomBotBuild.addCatch(fallback); + const buildSharedKnowledgeBases = new tasks.CodeBuildStartBuild(this, "BuildSharedKnowledgeBases", { + project: props.bedrockSharedKnowledgeBasesProject, + integrationPattern: sfn.IntegrationPattern.RUN_JOB, + environmentVariablesOverride: { + SHARED_KNOWLEDGE_BASES: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt("$.SharedKnowledgeBases")), + }, + // Bucket name provisioned by the bedrock stack + BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: props.documentBucket.bucketName, + }, + ENABLE_RAG_REPLICAS: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: props.enableRagReplicas.toString(), + }, + }, + resultPath: sfn.JsonPath.DISCARD, + }); - const fetchStackOutput = new tasks.LambdaInvoke(this, "FetchStackOutput", { - lambdaFunction: this._fetchStackOutputHandler, + const passSharedKnowledgeBasesBuildError = new sfn.Pass(this, "PassSharedKnowledgeBasesBuildError", { + parameters: { + QueuedBots: sfn.JsonPath.objectAt("$.QueuedBots"), + Lock: sfn.JsonPath.objectAt("$.Lock"), + Error: sfn.JsonPath.stringAt("$.Error.Error"), + Cause: sfn.JsonPath.stringToJson(sfn.JsonPath.stringAt("$.Error.Cause")), + }, + }); + const updateSyncStatusFailedForSharedKnowledgeBasesBuild = new tasks.LambdaInvoke(this, "UpdateSyncStatusFailedForSharedKnowledgeBasesBuild", { + lambdaFunction: this._updateSyncStatusHandler, payload: sfn.TaskInput.fromObject({ - "pk.$": "$.dynamodb.NewImage.PK.S", - "sk.$": "$.dynamodb.NewImage.SK.S", + QueuedBots: sfn.JsonPath.objectAt("$.QueuedBots"), + SyncStatus: "FAILED", + Build: sfn.JsonPath.objectAt("$.Cause.Build"), }), - resultPath: "$.StackOutput", + resultPath: sfn.JsonPath.DISCARD, + }); + updateSyncStatusFailedForSharedKnowledgeBasesBuild.addCatch(releaseLockFallback, { + resultPath: "$.Error", }); - fetchStackOutput.addCatch(fallback); - const storeKnowledgeBaseId = new tasks.LambdaInvoke( - this, - "StoreKnowledgeBaseId", - { - lambdaFunction: this._StoreKnowledgeBaseIdHandler, - payload: sfn.TaskInput.fromObject({ - "pk.$": "$.dynamodb.NewImage.PK.S", - "sk.$": "$.dynamodb.NewImage.SK.S", - "stack_output.$": "$.StackOutput.Payload", - }), - resultPath: sfn.JsonPath.DISCARD, - } + const buildSharedKnowledgeBasesFallback = ( + passSharedKnowledgeBasesBuildError + .next(updateSyncStatusFailedForSharedKnowledgeBasesBuild) + .next(releaseLockFallback) ); - storeKnowledgeBaseId.addCatch(fallback); + buildSharedKnowledgeBases.addCatch(buildSharedKnowledgeBasesFallback, { + resultPath: "$.Error", + }) + + // Obtain the ID of the shared Knowledge Bases built by `BrChatSharedKbStack`, and update `knowledge_base_id` of the referring bots. + const finalizeSharedKnowledgeBasesBuild = new tasks.LambdaInvoke(this, "FinalizeSharedKnowledgeBasesBuild", { + lambdaFunction: this._finalizeSharedKnowledgeBasesBuildHandler, + resultSelector: { + QueuedBots: sfn.JsonPath.objectAt("$.Payload.QueuedBots"), + SharedKnowledgeBases: sfn.JsonPath.objectAt("$.Payload.SharedKnowledgeBases"), + DataSources: sfn.JsonPath.objectAt("$.Payload.DataSources"), + Lock: sfn.JsonPath.objectAt("$.Payload.Lock"), + }, + }); - const storeGuardrailArn = new tasks.LambdaInvoke( - this, - "StoreGuardrailArn", - { - lambdaFunction: this._StoreGuardrailArnHandler, - payload: sfn.TaskInput.fromObject({ - "pk.$": "$.dynamodb.NewImage.PK.S", - "sk.$": "$.dynamodb.NewImage.SK.S", - "stack_output.$": "$.StackOutput.Payload", - }), - resultPath: sfn.JsonPath.DISCARD, - } + const mapIngestionJobsForSharedKnowledgeBases = new sfn.Map(this, "MapIngestionJobsForSharedKnowledgeBases", { + inputPath: "$.DataSources", + resultPath: sfn.JsonPath.DISCARD, + maxConcurrency: 1, + }); + // Perform entire synchronization into the data source of shared Knowledge Bases. + const ingestionJobForSharedKnowledgeBases = this.createIngestionTask("Shared", {}); + + const updateSyncStatusFailedForSharedKnowledgeBases = new tasks.LambdaInvoke(this, "UpdateSyncStatusFailedForSharedKnowledgeBases", { + lambdaFunction: this._updateSyncStatusHandler, + payload: sfn.TaskInput.fromObject({ + QueuedBots: sfn.JsonPath.objectAt("$.QueuedBots"), + SyncStatus: "FAILED", + }), + resultPath: sfn.JsonPath.DISCARD, + }) + updateSyncStatusFailedForSharedKnowledgeBases.addCatch(releaseLockFallback, { + resultPath: "$.Error", + }); + + const syncSharedKnowledgeBasesFallback = ( + updateSyncStatusFailedForSharedKnowledgeBases + .next(releaseLockFallback) ); - storeGuardrailArn.addCatch(fallback); + finalizeSharedKnowledgeBasesBuild.addCatch(syncSharedKnowledgeBasesFallback, { + resultPath: "$.Error", + }); + mapIngestionJobsForSharedKnowledgeBases.addCatch(syncSharedKnowledgeBasesFallback, { + resultPath: "$.Error", + }); - const startIngestionJob = new tasks.CallAwsServiceCrossRegion( - this, - "StartIngestionJob", - { - service: "bedrock-agent", - action: "startIngestionJob", - iamAction: "bedrock:StartIngestionJob", - region: props.bedrockRegion, - parameters: { - dataSourceId: sfn.JsonPath.stringAt("$.DataSourceId"), - knowledgeBaseId: sfn.JsonPath.stringAt("$.KnowledgeBaseId"), + // Release the acquired lock for shared Knowledge Bases. + const releaseLockForSharedKnowledgeBases = this.createReleaseLockTask("ForSharedKnowledgeBases", { + name: "shared-knowledge-bases", + lockId: sfn.JsonPath.stringAt("$.Lock.LockId"), + }); + + const mapQueuedBots = new sfn.Map(this, "MapQueuedBots", { + itemsPath: "$.QueuedBots", + resultPath: sfn.JsonPath.DISCARD, + }); + + // Acquire a distributed lock for custom bots. + const acquireLockForCustomBot = this.createAcquireLockTask("ForCustomBot", { + name: sfn.JsonPath.format("custombot-{}", sfn.JsonPath.stringAt("$.BotId")), + resultPath: "$.Lock", + }); + + const startCustomBotBuild = new tasks.CodeBuildStartBuild(this, "StartCustomBotBuild", { + project: props.bedrockCustomBotProject, + integrationPattern: sfn.IntegrationPattern.RUN_JOB, + environmentVariablesOverride: { + OWNER_USER_ID: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.stringAt("$.OwnerUserId"), }, - // Ref: https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazonbedrock.html#amazonbedrock-knowledge-base - iamResources: [ - `arn:${Stack.of(this).partition}:bedrock:${props.bedrockRegion}:${ - Stack.of(this).account - }:knowledge-base/*`, - ], - resultPath: "$.IngestionJob", - } + BOT_ID: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.stringAt("$.BotId"), + }, + // Bucket name provisioned by the bedrock stack + BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: props.documentBucket.bucketName, + }, + // Source info e.g. file names, URLs, etc. + KNOWLEDGE: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt("$.Knowledge")), + }, + // Bedrock Knowledge Base configuration + KNOWLEDGE_BASE: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt("$.KnowledgeBase")), + }, + GUARDRAILS: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: sfn.JsonPath.jsonToString(sfn.JsonPath.objectAt("$.Guardrails")), + }, + ENABLE_RAG_REPLICAS: { + type: codebuild.BuildEnvironmentVariableType.PLAINTEXT, + value: props.enableRagReplicas.toString(), + }, + }, + resultPath: sfn.JsonPath.DISCARD, + }); + + // Release the acquired lock for custom bots. + const releaseLockForCustomBot = this.createReleaseLockTask("ForCustomBot", { + name: sfn.JsonPath.format("custombot-{}", sfn.JsonPath.stringAt("$.BotId")), + lockId: sfn.JsonPath.stringAt("$.Lock.LockId"), + }); + const syncCustomBotSucceeded = new sfn.Succeed(this, "SyncCustomBotSucceeded"); + const syncCustomBotFinished = ( + releaseLockForCustomBot + .next(syncCustomBotSucceeded) ); - const getIngestionJob = new tasks.CallAwsServiceCrossRegion( - this, - "GetIngestionJob", - { - service: "bedrock-agent", - action: "getIngestionJob", - iamAction: "bedrock:GetIngestionJob", - region: props.bedrockRegion, - parameters: { - dataSourceId: sfn.JsonPath.stringAt( - "$.IngestionJob.ingestionJob.dataSourceId" - ), - knowledgeBaseId: sfn.JsonPath.stringAt( - "$.IngestionJob.ingestionJob.knowledgeBaseId" - ), - ingestionJobId: sfn.JsonPath.stringAt( - "$.IngestionJob.ingestionJob.ingestionJobId" - ), - }, - // Ref: https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazonbedrock.html#amazonbedrock-knowledge-base - iamResources: [ - `arn:${Stack.of(this).partition}:bedrock:${props.bedrockRegion}:${ - Stack.of(this).account - }:knowledge-base/*`, - ], - resultPath: "$.IngestionJob", - } + const passCustomBotBuildError = new sfn.Pass(this, "PassCustomBotBuildError", { + parameters: { + OwnerUserId: sfn.JsonPath.stringAt("$.OwnerUserId"), + BotId: sfn.JsonPath.stringAt("$.BotId"), + Lock: sfn.JsonPath.objectAt("$.Lock"), + Error: sfn.JsonPath.stringAt("$.Error.Error"), + Cause: sfn.JsonPath.stringToJson(sfn.JsonPath.stringAt("$.Error.Cause")), + }, + }); + const updateSyncStatusFailedForCustomBotBuild = new tasks.LambdaInvoke(this, "UpdateSyncStatusFailedForCustomBotBuild", { + lambdaFunction: this._updateSyncStatusHandler, + payload: sfn.TaskInput.fromObject({ + OwnerUserId: sfn.JsonPath.stringAt("$.OwnerUserId"), + BotId: sfn.JsonPath.stringAt("$.BotId"), + SyncStatus: "FAILED", + Build: sfn.JsonPath.objectAt("$.Cause.Build"), + }), + resultPath: sfn.JsonPath.DISCARD, + }); + updateSyncStatusFailedForCustomBotBuild.addCatch(syncCustomBotFinished, { + resultPath: "$.Error", + }); + + const buildCustomBotFallback = ( + passCustomBotBuildError + .next(updateSyncStatusFailedForCustomBotBuild) + .next(syncCustomBotFinished) ); - const waitTask = new sfn.Wait(this, "WaitSeconds", { - time: sfn.WaitTime.duration(Duration.seconds(3)), + startCustomBotBuild.addCatch(buildCustomBotFallback, { + resultPath: "$.Error", }); - const checkIngestionJobStatus = new sfn.Choice( - this, - "CheckIngestionJobStatus" - ) - .when( - sfn.Condition.stringEquals( - "$.IngestionJob.ingestionJob.status", - "COMPLETE" - ), - new sfn.Pass(this, "IngestionJobCompleted") - ) - .when( - sfn.Condition.stringEquals( - "$.IngestionJob.ingestionJob.status", - "FAILED" - ), - new tasks.LambdaInvoke(this, "UpdateSyncStatusFailedForIngestion", { - lambdaFunction: this._updateSyncStatusHandler, - payload: sfn.TaskInput.fromObject({ - pk: sfn.JsonPath.stringAt("$.PK"), - sk: sfn.JsonPath.stringAt("$.SK"), - ingestion_job: sfn.JsonPath.stringAt("$.IngestionJob"), - }), - resultPath: sfn.JsonPath.DISCARD, - }).next( - new sfn.Fail(this, "IngestionFail", { - cause: "Ingestion job failed", - error: "Ingestion job failed", - }) - ) - ) - .otherwise(waitTask.next(getIngestionJob)); + // Obtain the ID of the dedicated Knowledge Bases and Guardrails built by `BrChatKbStackXXX`, and update `knowledge_base_id` and `guardrail_arn` of the bot. + const finalizeCustomBotBuild = new tasks.LambdaInvoke(this, "FinalizeCustomBotBuild", { + lambdaFunction: this._finalizeCustomBotBuildHandler, + resultSelector: { + OwnerUserId: sfn.JsonPath.stringAt("$.Payload.OwnerUserId"), + BotId: sfn.JsonPath.stringAt("$.Payload.BotId"), + DataSources: sfn.JsonPath.objectAt("$.Payload.DataSources"), + Lock: sfn.JsonPath.objectAt("$.Payload.Lock"), + }, + }); - const mapIngestionJobs = new sfn.Map(this, "MapIngestionJobs", { - inputPath: "$.StackOutput.Payload.items", + const mapIngestionJobsForCustomBot = new sfn.Map(this, "MapIngestionJobsForCustomBot", { + inputPath: "$.DataSources", resultPath: sfn.JsonPath.DISCARD, maxConcurrency: 1, - }).itemProcessor( - startIngestionJob.next(getIngestionJob).next(checkIngestionJobStatus) - ); - - const definition = extractFirstElement - .next(updateSyncStatusRunning) - .next(startCustomBotBuild) - .next(fetchStackOutput) - .next(storeKnowledgeBaseId) - .next(storeGuardrailArn) - .next(mapIngestionJobs) - .next(updateSyncStatusSucceeded); + }); + // Perform direct ingestion or entire synchronization into the data source of dedicated Knowledge Bases. + const ingestionJobForCustomBot = this.createIngestionTask("CustomBot", {}); - this._stateMachine = new sfn.StateMachine(this, "StateMachine", { - definitionBody: sfn.DefinitionBody.fromChainable(definition), + const updateSyncStatusFailedForCustomBot = new tasks.LambdaInvoke(this, "UpdateSyncStatusFailedForCustomBot", { + lambdaFunction: this._updateSyncStatusHandler, + payload: sfn.TaskInput.fromObject({ + OwnerUserId: sfn.JsonPath.stringAt("$.OwnerUserId"), + BotId: sfn.JsonPath.stringAt("$.BotId"), + SyncStatus: "FAILED", + }), + resultPath: sfn.JsonPath.DISCARD, + }); + updateSyncStatusFailedForCustomBot.addCatch(syncCustomBotFinished, { + resultPath: "$.Error", }); - return this; - } - private setupEventBridgePipe(props: EmbeddingProps): this { - if (!this._stateMachine) { - throw new Error( - "State machine must be set up before setting up the EventBridge pipe" - ); - } + const syncCustomBotFallback = ( + updateSyncStatusFailedForCustomBot + .next(syncCustomBotFinished) + ); + finalizeCustomBotBuild.addCatch(syncCustomBotFallback, { + resultPath: "$.Error", + }); + mapIngestionJobsForCustomBot.addCatch(syncCustomBotFallback, { + resultPath: "$.Error", + }); - const pipeLogGroup = new logs.LogGroup(this, "PipeLogGroup", { - removalPolicy: RemovalPolicy.DESTROY, - retention: logs.RetentionDays.ONE_WEEK, + const updateSyncStatusSucceeded = new tasks.LambdaInvoke(this, "UpdateSyncStatusSuccess", { + lambdaFunction: this._updateSyncStatusHandler, + payload: sfn.TaskInput.fromObject({ + OwnerUserId: sfn.JsonPath.stringAt("$.OwnerUserId"), + BotId: sfn.JsonPath.stringAt("$.BotId"), + SyncStatus: "SUCCEEDED", + SyncStatusReason: "Knowledge base sync succeeded", + }), + resultPath: sfn.JsonPath.DISCARD, }); - this._pipeRole = new iam.Role(this, "PipeRole", { - assumedBy: new iam.ServicePrincipal("pipes.amazonaws.com"), + updateSyncStatusSucceeded.addCatch(syncCustomBotFinished, { + resultPath: "$.Error", }); - this._pipeRole.addToPolicy( - new iam.PolicyStatement({ - actions: [ - "dynamodb:DescribeStream", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - "dynamodb:ListStreams", - ], - resources: [props.database.botTable.tableStreamArn!], - }) - ); - this._pipeRole.addToPolicy( - new iam.PolicyStatement({ - actions: ["states:StartExecution"], - resources: [this._stateMachine.stateMachineArn], - }) - ); - this._pipeRole.addToPolicy( - new iam.PolicyStatement({ - actions: ["logs:CreateLogStream", "logs:PutLogEvents"], - resources: [pipeLogGroup.logGroupArn], - }) + + /** + * Knowledge Base Synchronization State Machine + * + * This state machine processes both Shared and Dedicated Knowledge Bases through two main flows: + * + * 1. SharedKnowledgeBases Flow: + * - Executes when SharedKnowledgeBases != null + * - Handles full synchronization of shared Knowledge Bases + * - Processes bots without file diffs via global data source sync + * + * 2. MapQueuedBots Flow: + * - Always executes (after SharedKnowledgeBases flow or standalone) + * - Processes each queued bot individually for: + * a) Dedicated bots: Creates KB + processes file diffs + * b) Shared bots with file diffs: Processes bot-specific file changes to shared KB + * c) Guardrails management for all bots + * + * Why shared bots with file diffs use MapQueuedBots flow: + * - File diffs contain bot-specific metadata (OwnerUserId, BotId) + * - Requires bot-specific S3 path construction (user_id/bot_id/filename) + * - Enables individual bot status tracking during ingestion + */ + const definition = ( + bootstrapStateMachine + .next( + checkSyncSharedKnowledgeBasesRequired + .when(sfn.Condition.isNotNull("$.SharedKnowledgeBases"), ( + // If there are updates to the shared Knowledge Base, build shared Knowledge Bases and synchronize data sources. + acquireLockForSharedKnowledgeBases + .next(updateSyncStatusRunning) + .next(buildSharedKnowledgeBases) + .next(finalizeSharedKnowledgeBasesBuild) + .next( + mapIngestionJobsForSharedKnowledgeBases.itemProcessor( + ingestionJobForSharedKnowledgeBases + ) + ) + .next(releaseLockForSharedKnowledgeBases) + .next(mapQueuedBots) + )) + .otherwise( + // Otherwise, skip the processing related to shared Knowledge Bases. + mapQueuedBots.itemProcessor( + acquireLockForCustomBot + .next(startCustomBotBuild) + .next(finalizeCustomBotBuild) + .next( + mapIngestionJobsForCustomBot.itemProcessor( + ingestionJobForCustomBot + ) + ) + .next(updateSyncStatusSucceeded) + .next(syncCustomBotFinished) + ) + ) + ) ); - new CfnPipe(this, "Pipe", { - source: props.database.botTable.tableStreamArn!, - sourceParameters: { - dynamoDbStreamParameters: { - batchSize: 1, - startingPosition: "LATEST", - maximumRetryAttempts: 1, - }, - filterCriteria: { - filters: [ - { - pattern: - '{"dynamodb":{"NewImage":{"SyncStatus":{"S":[{"prefix":"QUEUED"}]}}}}', - }, - ], - }, - }, - target: this._stateMachine.stateMachineArn, - targetParameters: { - stepFunctionStateMachineParameters: { - invocationType: "FIRE_AND_FORGET", - }, - }, - logConfiguration: { - cloudwatchLogsLogDestination: { - logGroupArn: pipeLogGroup.logGroupArn, - }, - level: "INFO", - }, - roleArn: this._pipeRole.roleArn, + return new sfn.StateMachine(this, "StateMachine", { + definitionBody: sfn.DefinitionBody.fromChainable(definition), }); - - return this; } - private setupRemovalHandler(props: EmbeddingProps): this { + private setupRemovalHandler(props: EmbeddingProps) { const removeHandlerRole = new iam.Role(this, "RemovalHandlerRole", { assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"), }); @@ -577,7 +659,7 @@ export class Embedding extends Construct { props.database.botTable.grantStreamRead(removeHandlerRole); props.documentBucket.grantReadWrite(removeHandlerRole); - this._removalHandler = new DockerImageFunction(this, "BotRemovalHandler", { + const removalHandler = new DockerImageFunction(this, "BotRemovalHandler", { code: DockerImageCode.fromImageAsset( path.join(__dirname, "../../../backend"), { @@ -600,7 +682,7 @@ export class Embedding extends Construct { role: removeHandlerRole, logRetention: logs.RetentionDays.THREE_MONTHS, }); - this._removalHandler.addEventSource( + removalHandler.addEventSource( new DynamoEventSource(props.database.botTable, { startingPosition: lambda.StartingPosition.TRIM_HORIZON, batchSize: 1, @@ -613,30 +695,112 @@ export class Embedding extends Construct { }) ); - return this; + return removalHandler; } - private createUpdateSyncStatusTask( - id: string, - syncStatus: string, - syncStatusReason?: string, - lastExecIdPath?: string - ): tasks.LambdaInvoke { - const payload: { [key: string]: any } = { - "pk.$": "$.dynamodb.NewImage.PK.S", - "sk.$": "$.dynamodb.NewImage.SK.S", - sync_status: syncStatus, - sync_status_reason: syncStatusReason || "", - }; - - if (lastExecIdPath) { - payload["last_exec_id.$"] = lastExecIdPath; - } - - return new tasks.LambdaInvoke(this, id, { - lambdaFunction: this._updateSyncStatusHandler, - payload: sfn.TaskInput.fromObject(payload), + private createIngestionTask(idSuffix: string, { + timeout = Duration.hours(12), + }: { + timeout?: Duration, + }) { + // Perform direct ingestion or entire synchronization into the data source + const startIngestionJob = new tasks.LambdaInvoke(this, `StartIngestionJob${idSuffix}`, { + lambdaFunction: this._synchronizeDataSourceHandler, + payload: sfn.TaskInput.fromObject({ + Action: "Ingest", + KnowledgeBaseId: sfn.JsonPath.stringAt("$.KnowledgeBaseId"), + DataSourceId: sfn.JsonPath.stringAt("$.DataSourceId"), + FilesDiffs: sfn.JsonPath.objectAt("$.FilesDiffs"), + }), + resultSelector: { + KnowledgeBaseId: sfn.JsonPath.stringAt("$.Payload.KnowledgeBaseId"), + DataSourceId: sfn.JsonPath.stringAt("$.Payload.DataSourceId"), + DocumentsDiff: sfn.JsonPath.objectAt("$.Payload.DocumentsDiff"), + IngestionJobId: sfn.JsonPath.stringAt("$.Payload.IngestionJobId"), + }, + resultPath: "$.IngestionJob", + }); + + // Check for the completion of direct ingestion or entire synchronization. + const checkIngestionJob = new tasks.LambdaInvoke(this, `CheckIngestionJob${idSuffix}`, { + lambdaFunction: this._synchronizeDataSourceHandler, + payload: sfn.TaskInput.fromObject({ + Action: "Check", + IngestionJob: sfn.JsonPath.objectAt("$.IngestionJob"), + }), + resultPath: sfn.JsonPath.DISCARD, + }); + + const ingestionComplete = new sfn.Pass(this, `IngestionComplete${idSuffix}`); + return startIngestionJob + .next( + checkIngestionJob.addRetry({ + interval: Duration.seconds(15), + maxAttempts: timeout.toSeconds() / 15, + backoffRate: 1, + errors: [ + 'RetryException', + ], + }).addCatch(ingestionComplete, { + resultPath: sfn.JsonPath.stringAt('$.Error'), + }) + ).next(ingestionComplete) + } + + private createAcquireLockTask(idSuffix: string, { + name, + owner = sfn.JsonPath.executionName, + timeout = Duration.hours(12), + resultPath, + }: { + name: string; + owner?: string; + timeout?: Duration; + resultPath?: string; + }) { + // Acquire a distributed lock. + return new tasks.LambdaInvoke(this, `AcquireLock${idSuffix}`, { + lambdaFunction: this._lockHandler, + payload: sfn.TaskInput.fromObject({ + Action: "Acquire", + LockName: name, + Owner: owner, + }), + resultSelector: { + LockId: sfn.JsonPath.stringAt("$.Payload.LockId"), + }, + resultPath: resultPath, + }).addRetry({ + interval: Duration.seconds(15), + maxAttempts: timeout.toSeconds() / 15, + backoffRate: 1, + errors: [ + 'RetryException', + ], + }); + } + + private createReleaseLockTask(idSuffix: string, { + name, + lockId, + }: { + name: string; + lockId: string; + }) { + // Release the acquired lock. + return new tasks.LambdaInvoke(this, `ReleaseLock${idSuffix}`, { + lambdaFunction: this._lockHandler, + payload: sfn.TaskInput.fromObject({ + Action: "Release", + LockName: name, + LockId: lockId, + }), resultPath: sfn.JsonPath.DISCARD, + }).addRetry({ + maxAttempts: 5, + errors: [ + 'RetryException', + ], }); } } diff --git a/cdk/lib/constructs/websocket.ts b/cdk/lib/constructs/websocket.ts index 8264ccdfb..ea95eeb8b 100644 --- a/cdk/lib/constructs/websocket.ts +++ b/cdk/lib/constructs/websocket.ts @@ -20,7 +20,6 @@ export interface WebSocketProps { readonly auth: Auth; readonly bedrockRegion: string; readonly documentBucket: s3.IBucket; - readonly websocketSessionTable: ITable; readonly largeMessageBucket: s3.IBucket; readonly accessLogBucket?: s3.Bucket; readonly enableBedrockGlobalInference: boolean; @@ -101,7 +100,7 @@ export class WebSocket extends Construct { ); largePayloadSupportBucket.grantRead(handlerRole); - props.websocketSessionTable.grantReadWriteData(handlerRole); + database.websocketSessionTable.grantReadWriteData(handlerRole); props.largeMessageBucket.grantReadWrite(handlerRole); props.documentBucket.grantRead(handlerRole); @@ -126,7 +125,7 @@ export class WebSocket extends Construct { TABLE_ACCESS_ROLE_ARN: tableAccessRole.roleArn, LARGE_MESSAGE_BUCKET: props.largeMessageBucket.bucketName, LARGE_PAYLOAD_SUPPORT_BUCKET: largePayloadSupportBucket.bucketName, - WEBSOCKET_SESSION_TABLE_NAME: props.websocketSessionTable.tableName, + WEBSOCKET_SESSION_TABLE_NAME: database.websocketSessionTable.tableName, ENABLE_BEDROCK_GLOBAL_INFERENCE: props.enableBedrockGlobalInference.toString(), ENABLE_BEDROCK_CROSS_REGION_INFERENCE: diff --git a/cdk/lib/utils/bedrock-knowledge-base-args.ts b/cdk/lib/utils/bedrock-knowledge-base-args.ts index facd17bb5..a04d58249 100644 --- a/cdk/lib/utils/bedrock-knowledge-base-args.ts +++ b/cdk/lib/utils/bedrock-knowledge-base-args.ts @@ -28,9 +28,29 @@ interface SemanticOptions { readonly breakpointPercentileThreshold: number; } +export const getKnowledgeBaseType = ( + type: unknown +): "dedicated" | "shared" | undefined => { + if (type == null || typeof(type) !== "string") { + return undefined; + } + + switch(type) { + case "dedicated": + case "shared": + return type; + + default: + return undefined; + } +}; + export const getEmbeddingModel = ( - embeddingsModel: string + embeddingsModel?: string ): BedrockFoundationModel => { + if (embeddingsModel == null) { + return BedrockFoundationModel.TITAN_EMBED_TEXT_V2_1024; + } switch (embeddingsModel) { case "titan_v2": return BedrockFoundationModel.TITAN_EMBED_TEXT_V2_1024; @@ -42,8 +62,11 @@ export const getEmbeddingModel = ( }; export const getParsingModel = ( - parsingModel: string + parsingModel?: string ): BedrockFoundationModel | undefined => { + if (parsingModel == null) { + return undefined; + } switch (parsingModel) { case "anthropic.claude-3-5-sonnet-v1": return BedrockFoundationModel.ANTHROPIC_CLAUDE_3_5_SONNET_V1_0; @@ -57,9 +80,11 @@ export const getParsingModel = ( } export const getCrowlingScope = ( - web_crawling_scope: string + web_crawling_scope?: string ): CrawlingScope | undefined => { - + if (web_crawling_scope == null) { + return CrawlingScope.DEFAULT; + } switch(web_crawling_scope) { case "DEFAULT": return CrawlingScope.DEFAULT @@ -73,16 +98,20 @@ export const getCrowlingScope = ( } export const getCrawlingFilters =( - web_crawling_filters: any + web_crawling_filters?: any ): CrawlingFilters => { - const regularJson = unmarshall(web_crawling_filters); - console.log(`regularJson: ${JSON.stringify(regularJson)}`) + if (web_crawling_filters == null) { + return { + excludePatterns: [], + includePatterns: [], + }; + } let excludePatterns = undefined let includePatterns = undefined - if (regularJson.exclude_patterns.length > 0 && regularJson.exclude_patterns[0] != "") excludePatterns = regularJson.exclude_patterns - if (regularJson.include_patterns.length > 0 && regularJson.include_patterns[0] != "") includePatterns = regularJson.include_patterns + if (web_crawling_filters.exclude_patterns.length > 0 && web_crawling_filters.exclude_patterns[0] != "") excludePatterns = web_crawling_filters.exclude_patterns + if (web_crawling_filters.include_patterns.length > 0 && web_crawling_filters.include_patterns[0] != "") includePatterns = web_crawling_filters.include_patterns return { excludePatterns, @@ -91,10 +120,13 @@ export const getCrawlingFilters =( } export const getChunkingStrategy = ( - chunkingStrategy: string, - embeddingsModel: string, + chunkingStrategy?: string, + embeddingsModel?: string, options?: Partial ): ChunkingStrategy => { + if (chunkingStrategy == undefined) { + return ChunkingStrategy.DEFAULT; + } switch (chunkingStrategy) { case "default": return ChunkingStrategy.DEFAULT; @@ -114,7 +146,7 @@ export const getChunkingStrategy = ( maxChildTokenSize: options.maxChildTokenSize }); } - return embeddingsModel === 'titan_v2' ? ChunkingStrategy.HIERARCHICAL_TITAN : ChunkingStrategy.HIERARCHICAL_COHERE; + return (embeddingsModel == null || embeddingsModel === 'titan_v2') ? ChunkingStrategy.HIERARCHICAL_TITAN : ChunkingStrategy.HIERARCHICAL_COHERE; case "semantic": // Check that it is not explicitly undefined because bufferSize is set to 0, it will be created with the default value even if other parameters changed. if (options?.maxTokens !== undefined && options?.bufferSize !== undefined && options?.breakpointPercentileThreshold !== undefined) { @@ -135,63 +167,50 @@ export const getChunkingStrategy = ( export const getAnalyzer = (analyzer: any): Analyzer | undefined => { // Example of analyzer: // { - // "character_filters": { - // "L": [ - // { - // "S": "icu_normalizer" - // } - // ] - // }, - // "token_filters": { - // "L": [ - // { - // "S": "kuromoji_baseform" - // }, - // { - // "S": "kuromoji_part_of_speech" - // } - // ] - // }, - // "tokenizer": { - // "S": "kuromoji_tokenizer" - // } + // "character_filters": [ + // "icu_normalizer" + // ], + // "token_filters": [ + // "kuromoji_baseform", + // "kuromoji_part_of_speech" + // ], + // "tokenizer": "kuromoji_tokenizer" // } console.log("getAnalyzer: analyzer: ", analyzer); if ( !analyzer || - !analyzer.character_filters || - !analyzer.character_filters.L + !analyzer.character_filters ) { return undefined; } const characterFilters: CharacterFilterType[] = - analyzer.character_filters.L.map((filter: any) => { - switch (filter.S) { + analyzer.character_filters.map((filter: any) => { + switch (filter) { case "icu_normalizer": return CharacterFilterType.ICU_NORMALIZER; default: - throw new Error(`Unknown character filter: ${filter.S}`); + throw new Error(`Unknown character filter: ${filter}`); } }); const tokenizer: TokenizerType = (() => { - if (!analyzer.tokenizer || !analyzer.tokenizer.S) { - throw new Error(`Tokenizer is not defined`); + if (!analyzer.tokenizer) { + return TokenizerType.KUROMOJI_TOKENIZER; } - switch (analyzer.tokenizer.S) { + switch (analyzer.tokenizer) { case "kuromoji_tokenizer": return TokenizerType.KUROMOJI_TOKENIZER; case "icu_tokenizer": return TokenizerType.ICU_TOKENIZER; default: - throw new Error(`Unknown tokenizer: ${analyzer.tokenizer.S}`); + throw new Error(`Unknown tokenizer: ${analyzer.tokenizer}`); } })(); const tokenFilters: TokenFilterType[] = - analyzer.token_filters?.L.map((filter: any) => { - switch (filter.S) { + analyzer.token_filters?.map((filter: any) => { + switch (filter) { case "kuromoji_baseform": return TokenFilterType.KUROMOJI_BASEFORM; case "kuromoji_part_of_speech": @@ -207,7 +226,7 @@ export const getAnalyzer = (analyzer: any): Analyzer | undefined => { case "icu_folding": return TokenFilterType.ICU_FOLDING; default: - throw new Error(`Unknown token filter: ${filter.S}`); + throw new Error(`Unknown token filter: ${filter}`); } }) || []; diff --git a/cdk/lib/utils/parameter-models.ts b/cdk/lib/utils/parameter-models.ts index 42e76c31c..d80d60dd7 100644 --- a/cdk/lib/utils/parameter-models.ts +++ b/cdk/lib/utils/parameter-models.ts @@ -150,8 +150,8 @@ const ApiPublishParametersSchema = BaseParametersSchema.extend({ */ const BedrockCustomBotParametersSchema = BaseParametersSchema.extend({ // Bot configuration - pk: z.string(), - sk: z.string(), + ownerUserId: z.string(), + botId: z.string(), documentBucketName: z.string(), knowledge: z.string(), knowledgeBase: z.string(), @@ -163,6 +163,20 @@ const BedrockCustomBotParametersSchema = BaseParametersSchema.extend({ .default("false"), }); +/** + * Parameters schema for shared Knowledge Bases + */ +const BedrockSharedKnowledgeBasesParametersSchema = BaseParametersSchema.extend({ + // Knowledge Base configuration + sharedKnowledgeBases: z.string(), + documentBucketName: z.string(), + enableRagReplicas: z + .string() + .optional() + .transform((val) => val === "true") + .default("false"), +}); + /** * Type definitions for each parameter set */ @@ -177,6 +191,9 @@ export type ApiPublishParametersInput = z.input< export type BedrockCustomBotParametersInput = z.input< typeof BedrockCustomBotParametersSchema >; +export type BedrockSharedKnowledgeBasesarametersInput = z.input< + typeof BedrockCustomBotParametersSchema +>; // Output types (for function returns, all properties are required) export type BaseParameters = z.infer; @@ -185,6 +202,9 @@ export type ApiPublishParameters = z.infer; export type BedrockCustomBotParameters = z.infer< typeof BedrockCustomBotParametersSchema >; +export type BedrockSharedKnowledgeBasesParameters = z.infer< + typeof BedrockSharedKnowledgeBasesParametersSchema +>; /** * Parse and validate parameters for the main Bedrock Chat application. @@ -344,14 +364,33 @@ export function resolveBedrockCustomBotParameters(): BedrockCustomBotParameters envName: getEnvVar("ENV_NAME"), envPrefix: getEnvVar("ENV_PREFIX"), bedrockRegion: getEnvVar("BEDROCK_REGION"), - pk: getEnvVar("PK"), - sk: getEnvVar("SK"), + ownerUserId: getEnvVar("OWNER_USER_ID"), + botId: getEnvVar("BOT_ID"), documentBucketName: getEnvVar("BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME"), knowledge: getEnvVar("KNOWLEDGE"), - knowledgeBase: getEnvVar("BEDROCK_KNOWLEDGE_BASE"), - guardrails: getEnvVar("BEDROCK_GUARDRAILS"), + knowledgeBase: getEnvVar("KNOWLEDGE_BASE"), + guardrails: getEnvVar("GUARDRAILS"), enableRagReplicas: getEnvVar("ENABLE_RAG_REPLICAS"), }; return BedrockCustomBotParametersSchema.parse(envVars); } + +/** + * Parse and validate parameters for shared Knowledge Bases. + * This function is executed by CDK in CodeBuild launched via the API. + * Therefore, this is not intend to be set values using cdk.json or parameter.ts. + * @returns Validated parameters object from environment variables + */ +export function resolveBedrockSharedKnowledgeBasesParameters(): BedrockSharedKnowledgeBasesParameters { + const envVars = { + envName: getEnvVar("ENV_NAME"), + envPrefix: getEnvVar("ENV_PREFIX"), + bedrockRegion: getEnvVar("BEDROCK_REGION"), + sharedKnowledgeBases: getEnvVar("SHARED_KNOWLEDGE_BASES"), + documentBucketName: getEnvVar("BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME"), + enableRagReplicas: getEnvVar("ENABLE_RAG_REPLICAS"), + }; + + return BedrockSharedKnowledgeBasesParametersSchema.parse(envVars); +} diff --git a/cdk/package-lock.json b/cdk/package-lock.json index 553f1c850..08e1b7028 100644 --- a/cdk/package-lock.json +++ b/cdk/package-lock.json @@ -23,7 +23,6 @@ "cdk": "bin/cdk.js" }, "devDependencies": { - "@aws-prototyping-sdk/pdk-nag": "^0.19.68", "@types/jest": "^29.5.3", "@types/node": "20.4.2", "aws-cdk": "^2.189.1", @@ -48,9 +47,9 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.232", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.232.tgz", - "integrity": "sha512-x9aFQG9gA+RgGj9bGB+WC6y1Nq2/Y8R2yXFoKWoQZOet8PRFJ8M5/FeXoh9XmdWI4weJVctLU4WTIve6rOvPtA==", + "version": "2.2.242", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.242.tgz", + "integrity": "sha512-4c1bAy2ISzcdKXYS1k4HYZsNrgiwbiDzj36ybwFVxEWZXVAP0dimQTCaB9fxu7sWzEjw3d+eaw6Fon+QTfTIpQ==", "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { @@ -85,9 +84,9 @@ } }, "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "41.2.0", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-41.2.0.tgz", - "integrity": "sha512-JaulVS6z9y5+u4jNmoWbHZRs9uGOnmn/ktXygNWKNu1k6lF3ad4so3s18eRu15XCbUIomxN9WPYT6Ehh7hzONw==", + "version": "48.14.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-48.14.0.tgz", + "integrity": "sha512-mo1MaPKsNVh3QSuosREcwIh8TrVZ9deHVLAjzbjkQuTxb/7LGqkf1E5HHxlAt6Z0+z+DZt7dTPIJ92NcR0tjDQ==", "bundleDependencies": [ "jsonschema", "semver" @@ -95,10 +94,10 @@ "license": "Apache-2.0", "dependencies": { "jsonschema": "~1.4.1", - "semver": "^7.7.1" + "semver": "^7.7.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.0.0" } }, "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": { @@ -110,7 +109,7 @@ } }, "node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": { - "version": "7.7.1", + "version": "7.7.2", "inBundle": true, "license": "ISC", "bin": { @@ -125,7 +124,6 @@ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -141,7 +139,6 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -154,7 +151,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -168,7 +164,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -182,7 +177,6 @@ "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -197,7 +191,6 @@ "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" } @@ -207,7 +200,6 @@ "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -219,7 +211,6 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -232,7 +223,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -246,7 +236,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -255,18 +244,6 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-prototyping-sdk/pdk-nag": { - "version": "0.19.68", - "resolved": "https://registry.npmjs.org/@aws-prototyping-sdk/pdk-nag/-/pdk-nag-0.19.68.tgz", - "integrity": "sha512-feOoX3iMEkv8hX2XdzIZeiaIAUO3wLsOSAYhMlXWF5w/IC6EfYd08IpeC+VeORhYl2LGv+7SB9HodMZpKjjuxQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "peerDependencies": { - "aws-cdk-lib": "^2.81.0", - "cdk-nag": "^2.27.24", - "constructs": "^10.2.39" - } - }, "node_modules/@aws-sdk/client-dynamodb": { "version": "3.693.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.693.0.tgz", @@ -329,7 +306,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.693.0.tgz", "integrity": "sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -485,7 +461,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.693.0.tgz", "integrity": "sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/core": "^2.5.2", @@ -508,7 +483,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.693.0.tgz", "integrity": "sha512-hMUZaRSF7+iBKZfBHNLihFs9zvpM1CB8MBOTnTp5NGCVkRYF3SB2LH+Kcippe0ats4qCyB1eEoyQX99rERp2iQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -525,7 +499,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.693.0.tgz", "integrity": "sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -547,7 +520,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.693.0.tgz", "integrity": "sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/credential-provider-env": "3.693.0", @@ -574,7 +546,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.693.0.tgz", "integrity": "sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/credential-provider-env": "3.693.0", "@aws-sdk/credential-provider-http": "3.693.0", @@ -598,7 +569,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.693.0.tgz", "integrity": "sha512-cvxQkrTWHHjeHrPlj7EWXPnFSq8x7vMx+Zn1oTsMpCY445N9KuzjfJTkmNGwU2GT6rSZI9/0MM02aQvl5bBBTQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -616,7 +586,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.693.0.tgz", "integrity": "sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/client-sso": "3.693.0", "@aws-sdk/core": "3.693.0", @@ -636,7 +605,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.693.0.tgz", "integrity": "sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -656,7 +624,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.693.0.tgz", "integrity": "sha512-/zK0ZZncBf5FbTfo8rJMcQIXXk4Ibhe5zEMiwFNivVPR2uNC0+oqfwXz7vjxwY0t6BPE3Bs4h9uFEz4xuGCY6w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "mnemonist": "0.38.3", "tslib": "^2.6.2" @@ -670,7 +637,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.693.0.tgz", "integrity": "sha512-OG8WM8OzYuAt3Ueb8TZoBgA+vqNgPaXksHhiy8SFTQxNamSMMRvKrDSBbdUuV96mq0lcJq1mFgJ4oRXJ1HPh6A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/endpoint-cache": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -688,7 +654,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.693.0.tgz", "integrity": "sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/protocol-http": "^4.1.6", @@ -704,7 +669,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.693.0.tgz", "integrity": "sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -719,7 +683,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.693.0.tgz", "integrity": "sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/protocol-http": "^4.1.6", @@ -735,7 +698,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.693.0.tgz", "integrity": "sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/core": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -754,7 +716,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.693.0.tgz", "integrity": "sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/node-config-provider": "^3.1.10", @@ -772,7 +733,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.693.0.tgz", "integrity": "sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/property-provider": "^3.1.9", @@ -792,7 +752,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.692.0.tgz", "integrity": "sha512-RpNvzD7zMEhiKgmlxGzyXaEcg2khvM7wd5sSHVapOcrde1awQSOMGI4zKBQ+wy5TnDfrm170ROz/ERLYtrjPZA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.0", "tslib": "^2.6.2" @@ -821,7 +780,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.693.0.tgz", "integrity": "sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -837,7 +795,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz", "integrity": "sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -850,7 +807,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.693.0.tgz", "integrity": "sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "3.692.0", "@smithy/types": "^3.7.0", @@ -863,7 +819,6 @@ "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.693.0.tgz", "integrity": "sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/middleware-user-agent": "3.693.0", "@aws-sdk/types": "3.692.0", @@ -912,6 +867,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.5", @@ -1368,24 +1324,39 @@ "dev": true }, "node_modules/@cdklabs/generative-ai-cdk-constructs": { - "version": "0.1.275", - "resolved": "https://registry.npmjs.org/@cdklabs/generative-ai-cdk-constructs/-/generative-ai-cdk-constructs-0.1.275.tgz", - "integrity": "sha512-k91LaO4jhP4Y4YhZmO0qGxHkrmXFM7C0B2lllYKMQl0MgEB1/pOq1ccKHfoo0vjXPYX3J0TYk4PkxkrII3N4NQ==", + "version": "0.1.310", + "resolved": "https://registry.npmjs.org/@cdklabs/generative-ai-cdk-constructs/-/generative-ai-cdk-constructs-0.1.310.tgz", + "integrity": "sha512-81njM9LN226ehtsCo4aqj4YhfBncB+gEi3tHKuskBERI32eYp7Cakh8oP4doZIoQLHE3uSy9g68+3SsDw9Ehog==", "bundleDependencies": [ + "@aws-cdk/aws-lambda-python-alpha", "deepmerge" ], + "license": "Apache-2.0", "dependencies": { - "cdk-nag": "^2.29.8", + "@aws-cdk/aws-lambda-python-alpha": "2.219.0-alpha.0", + "cdk-nag": "^2.37.47", "deepmerge": "^4.3.1" }, "engines": { - "node": ">= 18.12.0 <= 20.x" + "node": ">= 18.12.0 <= 22.x" }, "peerDependencies": { - "aws-cdk-lib": "^2.162.1", + "aws-cdk-lib": "^2.219.0", "constructs": "^10.3.0" } }, + "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/@aws-cdk/aws-lambda-python-alpha": { + "version": "2.219.0-alpha.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.219.0", + "constructs": "^10.0.0" + } + }, "node_modules/@cdklabs/generative-ai-cdk-constructs/node_modules/deepmerge": { "version": "4.3.1", "inBundle": true, @@ -1802,7 +1773,6 @@ "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.8.tgz", "integrity": "sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -1816,7 +1786,6 @@ "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.12.tgz", "integrity": "sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.11", "@smithy/types": "^3.7.1", @@ -1833,7 +1802,6 @@ "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.3.tgz", "integrity": "sha512-96uW8maifUSmehaeW7uydWn7wBc98NEeNI3zN8vqakGpyCQgzyJaA64Z4FCOUmAdCJkhppd/7SZ798Fo4Xx37g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/middleware-serde": "^3.0.10", "@smithy/protocol-http": "^4.1.7", @@ -1853,7 +1821,6 @@ "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.7.tgz", "integrity": "sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.11", "@smithy/property-provider": "^3.1.10", @@ -1870,7 +1837,6 @@ "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/protocol-http": "^4.1.7", "@smithy/querystring-builder": "^3.0.10", @@ -1884,7 +1850,6 @@ "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.10.tgz", "integrity": "sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "@smithy/util-buffer-from": "^3.0.0", @@ -1900,7 +1865,6 @@ "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.10.tgz", "integrity": "sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -1911,7 +1875,6 @@ "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -1924,7 +1887,6 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.12.tgz", "integrity": "sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/protocol-http": "^4.1.7", "@smithy/types": "^3.7.1", @@ -1939,7 +1901,6 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.3.tgz", "integrity": "sha512-Hdl9296i/EMptaX7agrSzJZDiz5Y8XPUeBbctTmMtnCguGpqfU3jVsTUan0VLaOhsnquqWLL8Bl5HrlbVGT1og==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/core": "^2.5.3", "@smithy/middleware-serde": "^3.0.10", @@ -1959,7 +1920,6 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.27.tgz", "integrity": "sha512-H3J/PjJpLL7Tt+fxDKiOD25sMc94YetlQhCnYeNmina2LZscAdu0ZEZPas/kwePHABaEtqp7hqa5S4UJgMs1Tg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.11", "@smithy/protocol-http": "^4.1.7", @@ -1980,7 +1940,6 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.10.tgz", "integrity": "sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -1994,7 +1953,6 @@ "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.10.tgz", "integrity": "sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2008,7 +1966,6 @@ "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.11.tgz", "integrity": "sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/property-provider": "^3.1.10", "@smithy/shared-ini-file-loader": "^3.1.11", @@ -2024,7 +1981,6 @@ "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.1.tgz", "integrity": "sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/abort-controller": "^3.1.8", "@smithy/protocol-http": "^4.1.7", @@ -2041,7 +1997,6 @@ "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.10.tgz", "integrity": "sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2055,7 +2010,6 @@ "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.7.tgz", "integrity": "sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2069,7 +2023,6 @@ "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.10.tgz", "integrity": "sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "@smithy/util-uri-escape": "^3.0.0", @@ -2084,7 +2037,6 @@ "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.10.tgz", "integrity": "sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2098,7 +2050,6 @@ "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.10.tgz", "integrity": "sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1" }, @@ -2111,7 +2062,6 @@ "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.11.tgz", "integrity": "sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2125,7 +2075,6 @@ "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.3.tgz", "integrity": "sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.7", @@ -2145,7 +2094,6 @@ "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.4.tgz", "integrity": "sha512-dPGoJuSZqvirBq+yROapBcHHvFjChoAQT8YPWJ820aPHHiowBlB3RL1Q4kPT1hx0qKgJuf+HhyzKi5Gbof4fNA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/core": "^2.5.3", "@smithy/middleware-endpoint": "^3.2.3", @@ -2164,7 +2112,6 @@ "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.1.tgz", "integrity": "sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2177,7 +2124,6 @@ "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.10.tgz", "integrity": "sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/querystring-parser": "^3.0.10", "@smithy/types": "^3.7.1", @@ -2189,7 +2135,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", @@ -2204,7 +2149,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" } @@ -2214,7 +2158,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2227,7 +2170,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" @@ -2241,7 +2183,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2254,7 +2195,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.27.tgz", "integrity": "sha512-GV8NvPy1vAGp7u5iD/xNKUxCorE4nQzlyl057qRac+KwpH5zq8wVq6rE3lPPeuFLyQXofPN6JwxL1N9ojGapiQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/property-provider": "^3.1.10", "@smithy/smithy-client": "^3.4.4", @@ -2271,7 +2211,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.27.tgz", "integrity": "sha512-7+4wjWfZqZxZVJvDutO+i1GvL6bgOajEkop4FuR6wudFlqBiqwxw3HoH6M9NgeCd37km8ga8NPp2JacQEtAMPg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/config-resolver": "^3.0.12", "@smithy/credential-provider-imds": "^3.2.7", @@ -2290,7 +2229,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.6.tgz", "integrity": "sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/node-config-provider": "^3.1.11", "@smithy/types": "^3.7.1", @@ -2305,7 +2243,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2318,7 +2255,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.10.tgz", "integrity": "sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/types": "^3.7.1", "tslib": "^2.6.2" @@ -2332,7 +2268,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.10.tgz", "integrity": "sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/service-error-classification": "^3.0.10", "@smithy/types": "^3.7.1", @@ -2347,7 +2282,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.1.tgz", "integrity": "sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/fetch-http-handler": "^4.1.1", "@smithy/node-http-handler": "^3.3.1", @@ -2367,7 +2301,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2380,7 +2313,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" @@ -2394,7 +2326,6 @@ "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.9.tgz", "integrity": "sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/abort-controller": "^3.1.8", "@smithy/types": "^3.7.1", @@ -2516,7 +2447,8 @@ "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/prettier": { "version": "2.7.3", @@ -2534,8 +2466,7 @@ "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.24", @@ -2641,9 +2572,9 @@ } }, "node_modules/aws-cdk": { - "version": "2.1020.2", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1020.2.tgz", - "integrity": "sha512-yWdt3dJh4aPm1VNyEgfG3lozGrvddw0i7avt+Cl9KOYixmisQtAg39/aZqzVVqjzVZVEanXmz+tlhzzh75Z69A==", + "version": "2.1030.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1030.0.tgz", + "integrity": "sha512-jYgOy1Hqx8cOTWW9On9xpypXLecjOqSZ4X2q5U0Gzd14xI+HLmpaRJV5ILJ8vYrLKVbqjhiog0pdxAC7vwF9uQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2657,9 +2588,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.189.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.189.1.tgz", - "integrity": "sha512-9JU0yUr2iRTJ1oCPrHyx7hOtBDWyUfyOcdb6arlumJnMcQr2cyAMASY8HuAXHc8Y10ipVp8dRTW+J4/132IIYA==", + "version": "2.219.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.219.0.tgz", + "integrity": "sha512-Rq1/f3exfFEWee1znNq8yvR1TuRQ4xQZz3JNkliBW9dFwyrDe7l/dmlAf6DVvB3nuiZAaUS+vh4ua1LZ7Ec8kg==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -2674,24 +2605,25 @@ "mime-types" ], "license": "Apache-2.0", + "peer": true, "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.229", + "@aws-cdk/asset-awscli-v1": "2.2.242", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", - "@aws-cdk/cloud-assembly-schema": "^41.0.0", + "@aws-cdk/cloud-assembly-schema": "^48.6.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^11.3.0", + "fs-extra": "^11.3.1", "ignore": "^5.3.2", "jsonschema": "^1.5.0", "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.7.1", + "semver": "^7.7.2", "table": "^6.9.0", "yaml": "1.10.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.0.0" }, "peerDependencies": { "constructs": "^10.0.0" @@ -2753,7 +2685,7 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/brace-expansion": { - "version": "1.1.11", + "version": "1.1.12", "inBundle": true, "license": "MIT", "dependencies": { @@ -2801,7 +2733,7 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/fast-uri": { - "version": "3.0.6", + "version": "3.1.0", "funding": [ { "type": "github", @@ -2816,7 +2748,7 @@ "license": "BSD-3-Clause" }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "11.3.0", + "version": "11.3.1", "inBundle": true, "license": "MIT", "dependencies": { @@ -2855,7 +2787,7 @@ "license": "MIT" }, "node_modules/aws-cdk-lib/node_modules/jsonfile": { - "version": "6.1.0", + "version": "6.2.0", "inBundle": true, "license": "MIT", "dependencies": { @@ -2925,7 +2857,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.7.1", + "version": "7.7.2", "inBundle": true, "license": "ISC", "bin": { @@ -3107,8 +3039,7 @@ "version": "2.11.0", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.12", @@ -3152,6 +3083,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001503", "electron-to-chromium": "^1.4.431", @@ -3242,11 +3174,13 @@ } }, "node_modules/cdk-nag": { - "version": "2.29.11", - "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.29.11.tgz", - "integrity": "sha512-qB+WV8svr3f5yRid5AfZ97lZUfO+GzfY5aPH+wLNHJ3f1v9M7kUphzhebhwjBXpDwpLr+hvMnMFW73zQliaTAw==", + "version": "2.37.51", + "resolved": "https://registry.npmjs.org/cdk-nag/-/cdk-nag-2.37.51.tgz", + "integrity": "sha512-oqlbHxxHQaSOg/9ME4f/S2uSHidBwPVTIfgGhaZPGM20HpGE+cv2YqTB5ID3qKL5/fcpSNDPF7nosU84W2N7nQ==", + "license": "Apache-2.0", + "peer": true, "peerDependencies": { - "aws-cdk-lib": "^2.156.0", + "aws-cdk-lib": "^2.176.0", "constructs": "^10.0.5" } }, @@ -3354,6 +3288,7 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", + "peer": true, "engines": { "node": ">= 16.14.0" } @@ -3591,7 +3526,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "strnum": "^1.0.5" }, @@ -3947,6 +3881,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz", "integrity": "sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.6.1", "@jest/types": "^29.6.1", @@ -4717,7 +4652,6 @@ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", "license": "MIT", - "peer": true, "dependencies": { "obliterator": "^1.6.1" } @@ -4771,8 +4705,7 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/once": { "version": "1.4.0", @@ -5215,8 +5148,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", @@ -5355,6 +5287,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -5425,6 +5358,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5472,7 +5406,6 @@ "https://github.com/sponsors/ctavan" ], "license": "MIT", - "peer": true, "bin": { "uuid": "dist/bin/uuid" } diff --git a/cdk/package.json b/cdk/package.json index 94acbfde7..cbd4fafc6 100644 --- a/cdk/package.json +++ b/cdk/package.json @@ -11,7 +11,6 @@ "cdk": "cdk" }, "devDependencies": { - "@aws-prototyping-sdk/pdk-nag": "^0.19.68", "@types/jest": "^29.5.3", "@types/node": "20.4.2", "aws-cdk": "^2.189.1", diff --git a/cdk/test/cdk.test.ts b/cdk/test/cdk.test.ts index 2d05676ed..627be0ec0 100644 --- a/cdk/test/cdk.test.ts +++ b/cdk/test/cdk.test.ts @@ -1,11 +1,11 @@ import * as cdk from "aws-cdk-lib"; import { BedrockChatStack } from "../lib/bedrock-chat-stack"; import { Template } from "aws-cdk-lib/assertions"; -import { AwsPrototypingChecks } from "@aws-prototyping-sdk/pdk-nag"; import { getEmbeddingModel, getChunkingStrategy, getAnalyzer, + getKnowledgeBaseType, } from "../lib/utils/bedrock-knowledge-base-args"; import { BedrockCustomBotStack } from "../lib/bedrock-custom-bot-stack"; import { BedrockRegionResourcesStack } from "../lib/bedrock-region-resources"; @@ -171,8 +171,6 @@ describe("Bedrock Chat Stack Test", () => { test("default stack", () => { const app = new cdk.App(); - // Security check - cdk.Aspects.of(app).add(new AwsPrototypingChecks()); const bedrockRegionResourcesStack = new BedrockRegionResourcesStack( app, @@ -612,111 +610,65 @@ describe("Bedrock Chat Stack Test", () => { describe("Bedrock Knowledge Base Stack", () => { const setupStack = (params: any = {}) => { const app = new cdk.App(); - // Security check - cdk.Aspects.of(app).add(new AwsPrototypingChecks()); - const PK: string = "test-user-id"; - const SK: string = "test-user-id#BOT#test-bot-id"; + const OWNER_USER_ID: string = "test-user-id"; + const BOT_ID: string = "test-bot-id"; const KNOWLEDGE = { - sitemap_urls: { - L: [], - }, - filenames: { - L: [ - { - S: "test-filename.pdf", - }, - ], - }, - source_urls: { - L: [ - { - S: "https://example.com", - }, - ], - }, - s3_urls: params.s3Urls !== undefined ? params.s3Urls : { L: [] }, + sitemap_urls: [], + filenames: [ + "test-filename.pdf", + ], + source_urls: [ + "https://example.com", + ], + s3_urls: params.s3Urls !== undefined ? params.s3Urls : [], }; - const BEDROCK_KNOWLEDGE_BASE = { - chunking_strategy: { - S: "fixed_size", - }, - max_tokens: - params.maxTokens !== undefined - ? { N: String(params.maxTokens) } - : undefined, - instruction: - params.instruction !== undefined - ? { S: params.instruction } - : undefined, - overlap_percentage: - params.overlapPercentage !== undefined - ? { N: String(params.overlapPercentage) } - : undefined, + const KNOWLEDGE_BASE = { + type: "dedicated", + chunking_strategy: "fixed_size", + max_tokens: params.maxTokens, + instruction: params.instruction, + overlap_percentage: params.overlapPercentage, open_search: { - M: { - analyzer: - params.analyzer !== undefined - ? JSON.parse(params.analyzer) - : { - character_filters: { - L: [ - { - S: "icu_normalizer", - }, - ], - }, - token_filters: { - L: [ - { - S: "kuromoji_baseform", - }, - { - S: "kuromoji_part_of_speech", - }, - ], - }, - tokenizer: { - S: "kuromoji_tokenizer", - }, - }, - }, - }, - embeddings_model: { - S: "titan_v2", + analyzer: + params.analyzer !== undefined + ? JSON.parse(params.analyzer) + : { + character_filters: [ + "icu_normalizer", + ], + token_filters: [ + "kuromoji_baseform", + "kuromoji_part_of_speech", + ], + tokenizer: "kuromoji_tokenizer", + }, }, - }; + embeddings_model: "titan_v2", + } as const; const BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME = "test-document-bucket-name"; - const ownerUserId: string = PK; - const botId: string = SK.split("#")[2]; - const knowledgeBase = BEDROCK_KNOWLEDGE_BASE; + const ownerUserId: string = OWNER_USER_ID; + const botId: string = BOT_ID; + const knowledgeBase = KNOWLEDGE_BASE; const knowledge = KNOWLEDGE; - const existingS3Urls: string[] = knowledge.s3_urls.L.map( - (s3Url: any) => s3Url.S - ); + const existingS3Urls: string[] = knowledge.s3_urls; - const embeddingsModel = getEmbeddingModel(knowledgeBase.embeddings_model.S); + const knowledgeBaseType = getKnowledgeBaseType(knowledgeBase.type); + const embeddingsModel = getEmbeddingModel(knowledgeBase.embeddings_model); const chunkingStrategy = getChunkingStrategy( - knowledgeBase.chunking_strategy.S, - knowledgeBase.embeddings_model.S + knowledgeBase.chunking_strategy, + knowledgeBase.embeddings_model ); - const maxTokens: number | undefined = knowledgeBase.max_tokens - ? Number(knowledgeBase.max_tokens.N) - : undefined; - const instruction: string | undefined = knowledgeBase.instruction - ? knowledgeBase.instruction.S + const maxTokens: number | undefined = knowledgeBase.max_tokens; + const instruction: string | undefined = knowledgeBase.instruction; + const analyzer = knowledgeBase.open_search.analyzer + ? getAnalyzer(knowledgeBase.open_search.analyzer) : undefined; - const analyzer = knowledgeBase.open_search.M.analyzer - ? getAnalyzer(knowledgeBase.open_search.M.analyzer) - : undefined; - const overlapPercentage: number | undefined = - knowledgeBase.overlap_percentage - ? Number(knowledgeBase.overlap_percentage.N) - : undefined; + const overlapPercentage: number | undefined = knowledgeBase.overlap_percentage; const stack = new BedrockCustomBotStack(app, "BedrockCustomBotStackStack", { ownerUserId, @@ -724,13 +676,15 @@ describe("Bedrock Knowledge Base Stack", () => { embeddingsModel, bedrockClaudeChatDocumentBucketName: BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME, + knowledgeBaseType, chunkingStrategy, existingS3Urls, maxTokens, instruction, analyzer, overlapPercentage, - sourceUrls: knowledge.source_urls.L.map((sourceUrl: any) => sourceUrl.S), + filenames: knowledge.filenames, + sourceUrls: knowledge.source_urls, existKnowledgeBaseId: undefined, }); @@ -739,37 +693,21 @@ describe("Bedrock Knowledge Base Stack", () => { test("default kb stack", () => { const template = setupStack({ - s3Urls: { - L: [ - { - S: "s3://test-bucket/test-key", - }, - ], - }, + s3Urls: [ + "s3://test-bucket/test-key", + ], maxTokens: 500, instruction: "This is an example instruction.", overlapPercentage: 10, analyzer: `{ - "character_filters": { - "L": [ - { - "S": "icu_normalizer" - } - ] - }, - "token_filters": { - "L": [ - { - "S": "kuromoji_baseform" - }, - { - "S": "kuromoji_part_of_speech" - } - ] - }, - "tokenizer": { - "S": "kuromoji_tokenizer" - } + "character_filters": [ + "icu_normalizer" + ], + "token_filters": [ + "kuromoji_baseform", + "kuromoji_part_of_speech" + ], + "tokenizer": "kuromoji_tokenizer" }`, }); expect(template).toBeDefined(); @@ -780,26 +718,14 @@ describe("Bedrock Knowledge Base Stack", () => { instruction: "This is an example instruction.", overlapPercentage: 10, analyzer: `{ - "character_filters": { - "L": [ - { - "S": "icu_normalizer" - } - ] - }, - "token_filters": { - "L": [ - { - "S": "kuromoji_baseform" - }, - { - "S": "kuromoji_part_of_speech" - } - ] - }, - "tokenizer": { - "S": "kuromoji_tokenizer" - } + "character_filters": [ + "icu_normalizer" + ], + "token_filters": [ + "kuromoji_baseform", + "kuromoji_part_of_speech" + ], + "tokenizer": "kuromoji_tokenizer" }`, }); expect(template).toBeDefined(); @@ -810,26 +736,14 @@ describe("Bedrock Knowledge Base Stack", () => { maxTokens: 500, overlapPercentage: 10, analyzer: `{ - "character_filters": { - "L": [ - { - "S": "icu_normalizer" - } - ] - }, - "token_filters": { - "L": [ - { - "S": "kuromoji_baseform" - }, - { - "S": "kuromoji_part_of_speech" - } - ] - }, - "tokenizer": { - "S": "kuromoji_tokenizer" - } + "character_filters": [ + "icu_normalizer" + ], + "token_filters": [ + "kuromoji_baseform", + "kuromoji_part_of_speech" + ], + "tokenizer": "kuromoji_tokenizer" }`, }); expect(template).toBeDefined(); @@ -849,26 +763,14 @@ describe("Bedrock Knowledge Base Stack", () => { maxTokens: 500, instruction: "This is an example instruction.", analyzer: `{ - "character_filters": { - "L": [ - { - "S": "icu_normalizer" - } - ] - }, - "token_filters": { - "L": [ - { - "S": "kuromoji_baseform" - }, - { - "S": "kuromoji_part_of_speech" - } - ] - }, - "tokenizer": { - "S": "kuromoji_tokenizer" - } + "character_filters": [ + "icu_normalizer" + ], + "token_filters": [ + "kuromoji_baseform", + "kuromoji_part_of_speech" + ], + "tokenizer": "kuromoji_tokenizer" }`, }); expect(template).toBeDefined(); diff --git a/cdk/test/utils/parameter-models.test.ts b/cdk/test/utils/parameter-models.test.ts index 4938f0dc8..b91dcd013 100644 --- a/cdk/test/utils/parameter-models.test.ts +++ b/cdk/test/utils/parameter-models.test.ts @@ -736,12 +736,12 @@ describe("resolveBedrockCustomBotParameters", () => { ENV_NAME: "testEnv", ENV_PREFIX: "test-prefix", BEDROCK_REGION: "us-east-1", - PK: "env-pk", - SK: "env-sk", + OWNER_USER_ID: "env-pk", + BOT_ID: "env-sk", BEDROCK_CLAUDE_CHAT_DOCUMENT_BUCKET_NAME: "env-bucket", KNOWLEDGE: '{"env": "knowledge"}', - BEDROCK_KNOWLEDGE_BASE: '{"env": "kb"}', - BEDROCK_GUARDRAILS: '{"env": "guardrails"}', + KNOWLEDGE_BASE: '{"env": "kb"}', + GUARDRAILS: '{"env": "guardrails"}', ENABLE_RAG_REPLICAS: "true", }; @@ -753,8 +753,8 @@ describe("resolveBedrockCustomBotParameters", () => { expect(result.bedrockRegion).toBe("us-east-1"); expect(result.envName).toBe("testEnv"); expect(result.envPrefix).toBe("test-prefix"); - expect(result.pk).toBe("env-pk"); - expect(result.sk).toBe("env-sk"); + expect(result.ownerUserId).toBe("env-pk"); + expect(result.botId).toBe("env-sk"); expect(result.documentBucketName).toBe("env-bucket"); expect(result.knowledge).toBe('{"env": "knowledge"}'); expect(result.knowledgeBase).toBe('{"env": "kb"}'); diff --git a/deploy.yml b/deploy.yml index f5a3deb61..32f0617d0 100644 --- a/deploy.yml +++ b/deploy.yml @@ -151,7 +151,7 @@ Resources: "phases": { "install": { "runtime-versions": { - "nodejs": "18" + "nodejs": 22 }, "on-failure": "ABORT" }, diff --git a/docs/imgs/customized_bot_creation.png b/docs/imgs/customized_bot_creation.png index 784599c20..5e9ba20d9 100644 Binary files a/docs/imgs/customized_bot_creation.png and b/docs/imgs/customized_bot_creation.png differ diff --git a/docs/imgs/import_existing_kb.png b/docs/imgs/import_existing_kb.png index 24c57ac36..bbc3d73d6 100644 Binary files a/docs/imgs/import_existing_kb.png and b/docs/imgs/import_existing_kb.png differ diff --git a/frontend/src/features/knowledgeBase/constants/index.ts b/frontend/src/features/knowledgeBase/constants/index.ts index 5082aa649..0ada105ff 100644 --- a/frontend/src/features/knowledgeBase/constants/index.ts +++ b/frontend/src/features/knowledgeBase/constants/index.ts @@ -48,6 +48,7 @@ export const DEFAULT_OPENSEARCH_ANALYZER: { } as const; export const DEFAULT_BEDROCK_KNOWLEDGEBASE: BedrockKnowledgeBase = { + type: "dedicated", knowledgeBaseId: null, existKnowledgeBaseId: null, embeddingsModel: 'cohere_multilingual_v3', diff --git a/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx b/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx index fa4f09f4d..9bf700c78 100644 --- a/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx +++ b/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx @@ -18,7 +18,7 @@ import { ConversationQuickStarter, ActiveModels, } from '../../../@types/bot'; -import { ParsingModel } from '../types'; +import { BedrockKnowledgeBaseType, ParsingModel } from '../types'; import { ulid } from 'ulid'; import { EDGE_GENERATION_PARAMS, @@ -128,8 +128,31 @@ const BotKbEditPage: React.FC = () => { string | null >(null); const [knowledgeBaseType, setKnowledgeBaseType] = useState< - 'new' | 'existing' - >('new'); + 'new' | 'shared' | 'existing' + >('shared'); + + const bedrockKnowledgeBaseType = useMemo(() => { + if (existKnowledgeBaseId != null) { + return undefined; + } + switch (knowledgeBaseType) { + case 'new': { + if (files.length === 0 && urls.length === 0 && s3Urls.length === 0) { + return undefined; + } + return 'dedicated'; + } + case 'shared': { + if (files.length === 0) { + return undefined; + } + return 'shared'; + } + case 'existing': { + return undefined; + } + } + }, [existKnowledgeBaseId, knowledgeBaseType, files, urls, s3Urls]); // When loading an existing bot that already has a knowledge base id(s), // default the radio selection to 'existing' so the UI reflects the bot state. @@ -155,6 +178,17 @@ const BotKbEditPage: React.FC = () => { const [relevanceThreshold, setRelevanceThreshold] = useState(0); const [guardrailArn, setGuardrailArn] = useState(''); const [guardrailVersion, setGuardrailVersion] = useState(''); + + const isGuardrailEnabled = useMemo(() => ( + hateThreshold > 0 || + insultsThreshold > 0 || + sexualThreshold > 0 || + violenceThreshold > 0 || + misconductThreshold > 0 || + groundingThreshold > 0 || + relevanceThreshold > 0 + ), [hateThreshold, insultsThreshold, sexualThreshold, violenceThreshold, misconductThreshold, groundingThreshold, relevanceThreshold]); + const [parsingModel, setParsingModel] = useState( undefined ); @@ -482,6 +516,18 @@ const BotKbEditPage: React.FC = () => { setS3Urls( bot.knowledge.s3Urls.length === 0 ? [''] : bot.knowledge.s3Urls ); + switch (bot.bedrockKnowledgeBase.type) { + case 'dedicated': + setKnowledgeBaseType('new'); + break; + + case 'shared': + setKnowledgeBaseType('shared'); + break; + + default: + break; + } setFiles( bot.knowledge.filenames.map((filename) => ({ filename, @@ -1256,7 +1302,8 @@ const BotKbEditPage: React.FC = () => { (qs) => qs.title !== '' && qs.example !== '' ), bedrockKnowledgeBase: { - knowledgeBaseId, + type: bedrockKnowledgeBaseType, + knowledgeBaseId: null, existKnowledgeBaseId, embeddingsModel, chunkingConfiguration: (() => { @@ -1280,14 +1327,7 @@ const BotKbEditPage: React.FC = () => { webCrawlingFilters, }, bedrockGuardrails: { - isGuardrailEnabled: - hateThreshold > 0 || - insultsThreshold > 0 || - sexualThreshold > 0 || - violenceThreshold > 0 || - misconductThreshold > 0 || - groundingThreshold > 0 || - relevanceThreshold > 0, + isGuardrailEnabled, hateThreshold: hateThreshold, insultsThreshold: insultsThreshold, sexualThreshold: sexualThreshold, @@ -1328,7 +1368,7 @@ const BotKbEditPage: React.FC = () => { promptCachingEnabled, conversationQuickStarters, navigate, - knowledgeBaseId, + bedrockKnowledgeBaseType, existKnowledgeBaseId, embeddingsModel, chunkingStrategy, @@ -1336,6 +1376,7 @@ const BotKbEditPage: React.FC = () => { hierarchicalParams, semanticParams, openSearchParams, + isGuardrailEnabled, hateThreshold, insultsThreshold, sexualThreshold, @@ -1387,7 +1428,8 @@ const BotKbEditPage: React.FC = () => { (qs) => qs.title !== '' && qs.example !== '' ), bedrockKnowledgeBase: { - knowledgeBaseId, + type: bedrockKnowledgeBaseType, + knowledgeBaseId: (bedrockKnowledgeBaseType != null) ? knowledgeBaseId : null, existKnowledgeBaseId, embeddingsModel, chunkingConfiguration: (() => { @@ -1411,14 +1453,7 @@ const BotKbEditPage: React.FC = () => { webCrawlingFilters, }, bedrockGuardrails: { - isGuardrailEnabled: - hateThreshold > 0 || - insultsThreshold > 0 || - sexualThreshold > 0 || - violenceThreshold > 0 || - misconductThreshold > 0 || - groundingThreshold > 0 || - relevanceThreshold > 0, + isGuardrailEnabled, hateThreshold: hateThreshold, insultsThreshold: insultsThreshold, sexualThreshold: sexualThreshold, @@ -1426,8 +1461,8 @@ const BotKbEditPage: React.FC = () => { misconductThreshold: misconductThreshold, groundingThreshold: groundingThreshold, relevanceThreshold: relevanceThreshold, - guardrailArn: guardrailArn, - guardrailVersion: guardrailVersion, + guardrailArn: (isGuardrailEnabled) ? guardrailArn : '', + guardrailVersion: (isGuardrailEnabled) ? guardrailVersion : '', }, activeModels, }) @@ -1463,6 +1498,7 @@ const BotKbEditPage: React.FC = () => { promptCachingEnabled, conversationQuickStarters, navigate, + bedrockKnowledgeBaseType, knowledgeBaseId, existKnowledgeBaseId, embeddingsModel, @@ -1471,6 +1507,7 @@ const BotKbEditPage: React.FC = () => { hierarchicalParams, semanticParams, openSearchParams, + isGuardrailEnabled, hateThreshold, insultsThreshold, sexualThreshold, @@ -1565,16 +1602,25 @@ const BotKbEditPage: React.FC = () => { value="new" checked={knowledgeBaseType === 'new'} label={t( - 'knowledgeBaseSettings.advancedConfigration.existKnowledgeBaseId.createNewKb.label' + 'knowledgeBaseSettings.advancedConfigration.createDedicatedKnowledgeBase.label' )} onChange={() => setKnowledgeBaseType('new')} /> + setKnowledgeBaseType('shared')} + /> setKnowledgeBaseType('existing')} /> @@ -1586,7 +1632,7 @@ const BotKbEditPage: React.FC = () => {
{ />
{t( - 'knowledgeBaseSettings.advancedConfigration.existKnowledgeBaseId.description' + 'knowledgeBaseSettings.advancedConfigration.existingKnowledgeBaseId.description' )}
@@ -1618,7 +1664,7 @@ const BotKbEditPage: React.FC = () => { ); } - if (knowledgeBaseType === 'new') { + if (knowledgeBaseType === 'new' || knowledgeBaseType === 'shared') { return (
@@ -1641,174 +1687,54 @@ const BotKbEditPage: React.FC = () => {
-
-
- {t('bot.label.s3url')} -
-
- {t('bot.help.knowledge.s3url')} -
-
- {s3Urls.map((s3Url, idx) => ( -
- { - onChangeS3Url(s, idx); - }} - errorMessage={errorMessages[`s3Urls-${idx}`]} - /> - { - onClickRemoveS3Url(idx); - }}> - - -
- ))} -
-
- -
-
- -
-
- {t('bot.label.url')} -
-
- {t('bot.help.knowledge.url')} -
-
- {urls.map((url, idx) => ( -
- { - onChangeUrls(s, idx); - }} - errorMessage={errorMessages[`urls-${idx}`]} - /> - { - onClickRemoveUrls(idx); - }}> - - -
- ))} -
-
- -
- - -
- { + setWebCrawlingScope(val as WebCrawlingScope); + }} + disabled={disabledKnowledgeEdit} + /> +
+ +
+
+ {t( + 'knowledgeBaseSettings.webCrawlerConfig.includePatterns.label' + )} +
+
+ {t( + 'knowledgeBaseSettings.webCrawlerConfig.includePatterns.hint' + )} +
+
+ {webCrawlingFilters.includePatterns.map( + (pattern, idx) => ( +
+ { + onChangeIncludePattern(s, idx); + }} + /> + { + onClickRemoveIncludePattern(idx); + }}> + + +
+ ) + )} +
+
+ +
+
+ +
+
+ {t( + 'knowledgeBaseSettings.webCrawlerConfig.excludePatterns.label' + )} +
+
+ {t( + 'knowledgeBaseSettings.webCrawlerConfig.excludePatterns.hint' + )} +
+
+ {webCrawlingFilters.excludePatterns.map( + (pattern, idx) => ( +
+ { + onChangeExcludePattern(s, idx); + }} + /> + { + onClickRemoveExcludePattern(idx); + }}> + + +
+ ) + )} +
+
+ +
+
+
- - + + )} ); } diff --git a/frontend/src/features/knowledgeBase/types/index.d.ts b/frontend/src/features/knowledgeBase/types/index.d.ts index 54867ee4e..51c4ec8a0 100644 --- a/frontend/src/features/knowledgeBase/types/index.d.ts +++ b/frontend/src/features/knowledgeBase/types/index.d.ts @@ -1,4 +1,7 @@ +export type BedrockKnowledgeBaseType = "dedicated" | "shared" | undefined; + export type BedrockKnowledgeBase = { + type?: BedrockKnowledgeBaseType; knowledgeBaseId: string | null; existKnowledgeBaseId: string | null; dataSourceIds?: string[]; // only present after bot is ready diff --git a/frontend/src/i18n/en/index.ts b/frontend/src/i18n/en/index.ts index 3fad2a823..987a5c2b7 100644 --- a/frontend/src/i18n/en/index.ts +++ b/frontend/src/i18n/en/index.ts @@ -946,16 +946,19 @@ How would you categorize this email?`, }, }, advancedConfigration: { - existKnowledgeBaseId: { + existingKnowledgeBaseId: { label: 'ID for the Amazon Bedrock Knowledge Base', description: 'Please specify ID that your existing Amazon Bedrock knowledge base.', - createNewKb: { - label: 'Create New Knowledge Base', - }, - existing: { - label: 'Use your existing knowledge base', - }, + }, + createDedicatedKnowledgeBase: { + label: 'Create a dedicated Knowledge Base', + }, + createTenantInSharedKnowledgeBase: { + label: 'Create a tenant in a shared Knowledge Base', + }, + useExistingKnowledgeBase: { + label: 'Use your existing Knowledge Base', }, }, }, diff --git a/frontend/src/i18n/id/index.ts b/frontend/src/i18n/id/index.ts index 70ab979d7..73a9f4e73 100644 --- a/frontend/src/i18n/id/index.ts +++ b/frontend/src/i18n/id/index.ts @@ -726,16 +726,19 @@ const translation = { }, }, advancedConfigration: { - existKnowledgeBaseId: { + existingKnowledgeBaseId: { label: 'ID Amazon Bedrock Knowledge Base', description: 'Masukkan ID Amazon Bedrock Knowledge Base eksisting Anda.', - createNewKb: { - label: 'Buat Knowledge Base baru', - }, - existing: { - label: 'Gunakan Knowledge Base eksisting', - }, + }, + createDedicatedKnowledgeBase: { + label: 'Buat Knowledge Base berdedikasi', + }, + createTenantInSharedKnowledgeBase: { + label: 'Buat penyewa di Knowledge Base bersama', + }, + useExistingKnowledgeBase: { + label: 'Gunakan Knowledge Base eksisting', }, }, }, diff --git a/frontend/src/i18n/ja/index.ts b/frontend/src/i18n/ja/index.ts index b2efaca9d..1463be8b1 100644 --- a/frontend/src/i18n/ja/index.ts +++ b/frontend/src/i18n/ja/index.ts @@ -952,16 +952,19 @@ const translation: typeof en = { }, }, advancedConfigration: { - existKnowledgeBaseId: { + existingKnowledgeBaseId: { label: '既存のAmazon Bedrock Knowledge BaseのID', description: - '既存のAmazon Bedrock Knowledge Baseを使用することができる', - createNewKb: { - label: '新規のナレッジを作成する', - }, - existing: { - label: '外部のナレッジ(Knowledge Base)を利用する', - }, + '既存のAmazon Bedrock Knowledge Baseを利用できます', + }, + createDedicatedKnowledgeBase: { + label: '専用のKnowledge Baseを作成する', + }, + createTenantInSharedKnowledgeBase: { + label: '共有のKnowledge Baseにテナントを作成する', + }, + useExistingKnowledgeBase: { + label: '外部のKnowledge Baseを利用する', }, }, }, diff --git a/frontend/src/i18n/pl/index.ts b/frontend/src/i18n/pl/index.ts index 17a1b639d..ead60f506 100644 --- a/frontend/src/i18n/pl/index.ts +++ b/frontend/src/i18n/pl/index.ts @@ -727,16 +727,19 @@ Jak sklasyfikowałbyś ten e-mail?`, }, }, advancedConfigration: { - existKnowledgeBaseId: { + existingKnowledgeBaseId: { label: 'ID dla bazy wiedzy Amazon Bedrock', description: 'Proszę podać ID istniejącej Bazy Wiedzy Amazon Bedrock.', - createNewKb: { - label: 'Utwórz nową Bazę Wiedzy', - }, - existing: { - label: 'Użyj istniejącej Bazy Wiedzy', - }, + }, + createDedicatedKnowledgeBase: { + label: 'Utwórz dedykowaną Bazę Wiedzy', + }, + createTenantInSharedKnowledgeBase: { + label: 'Utwórz najemcę w udostępnionej Bazy Wiedzy', + }, + useExistingKnowledgeBase: { + label: 'Użyj istniejącej Bazy Wiedzy', }, }, }, diff --git a/frontend/src/i18n/pt-br/index.ts b/frontend/src/i18n/pt-br/index.ts index 7ab205bb2..77127f29d 100644 --- a/frontend/src/i18n/pt-br/index.ts +++ b/frontend/src/i18n/pt-br/index.ts @@ -906,16 +906,19 @@ Como você categorizaria este e-mail?`, }, }, advancedConfigration: { - existKnowledgeBaseId: { + existingKnowledgeBaseId: { label: 'ID para a Base de Conhecimento Amazon Bedrock', description: 'Por favor, especifique o ID da sua base de conhecimento Amazon Bedrock existente.', - createNewKb: { - label: 'Criar Nova Base de Conhecimento', - }, - existing: { - label: 'Use sua base de conhecimento existente', - }, + }, + createDedicatedKnowledgeBase: { + label: 'Criar nova Base de Conhecimento dedicada', + }, + createTenantInSharedKnowledgeBase: { + label: 'Crie um inquilino em uma Base de Conhecimento compartilhada', + }, + useExistingKnowledgeBase: { + label: 'Use sua Base de Conhecimento existente', }, }, },