AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Amazon Transcribe Live Call Analytics with Agent Assist - Agent Assist
  Setup
Parameters:
  LCAStackName:
    Type: String
    Description: LCA Stack Name
  AISTACK:
    Type: String
    Description: AISTACK Stack ARN
  QNABOTSTACK:
    Type: String
    Description: QNABOT Stack ARN
  AgentAssistLLMBedrockModelId:
    Type: String
    Default: anthropic.claude-3-sonnet-20240229-v1:0
    AllowedValues:
    - anthropic.claude-3-haiku-20240307-v1:0
    - anthropic.claude-3-sonnet-20240229-v1:0
  BedrockKnowledgeBaseID:
    Type: String
    Default: ''
    AllowedPattern: ^(|[0-9a-zA-Z]{10})$
    Description: "Provide the *id* (not name) of an existing Bedrock Knowledge Base\
      \ to be used for Agent Assist bot.\n"
  BedrockKnowledgeBaseS3BucketName:
    Type: String
  LexAgentAssistBotId:
    Type: String
    Description: Lex Bot Id used for Agent Assist.
    AllowedPattern: ^(|[0-9a-zA-Z]{10})$
  LexAgentAssistAliasId:
    Type: String
    Description: Lex Bot Alias ID used for Agent Assist.
    AllowedPattern: ^(|[0-9a-zA-Z]{10})$
  LexAgentAssistLocaleId:
    Type: String
    Description: Lex Bot Locale ID used for Agent Assist.
  QnaAgentAssistDemoJson:
    Type: String
    Description: Location of QnABot agent assist sample/demo file (in JSON lines format)
  QnaBotSettings:
    Type: String
    Description: JSON string specifying key/value pairs to configure new defaults
      for QnAbot Settings
  WebAppBucket:
    Type: String
    Description: The LCA Web App Bucket Name.
  CloudFrontDistributionId:
    Type: String
    Description: The LCA web app CloudFront distribution id
  LexAgentAssistIdentityPoolId:
    Type: String
  CloudFrontDomainName:
    Type: String
  FetchTranscriptFunctionArn:
    Type: String
  TranscriptSummaryFunctionArn:
    Type: String
  TranscribeLanguageCode:
    Type: String
  TranscriptLambdaHookFunctionArn:
    Type: String
  TranscriptLambdaHookFunctionNonPartialOnly:
    Type: String
  DynamoDbExpirationInDays:
    Type: String
  EndOfCallTranscriptSummary:
    Type: String
  EndOfCallLambdaHookFunctionArn:
    Type: String
  CallAudioSource:
    Type: String
  ComprehendLanguageCode:
    Type: String
  AgentAssistOption:
    Type: String
  AgentAssistExistingLexV2BotId:
    Type: String
  AgentAssistExistingLexV2BotAliasId:
    Type: String
  AgentAssistExistingLambdaFunctionArn:
    Type: String
  AgentAssistWebCrawlURLs:
    Type: String
  AgentAssistWebCrawlSyncScope:
    Type: String
  IsSentimentAnalysisEnabled:
    Type: String
  SentimentNegativeScoreThreshold:
    Type: String
  SentimentPositiveScoreThreshold:
    Type: String
  Version:
    Type: String
  CloudWatchLogsExpirationInDays:
    Type: Number
    Default: 14
    Description: The number of days log events are kept in CloudWatch Logs.
Conditions:
  ShouldConfigureQnabot:
    Fn::Not:
    - Fn::Equals:
      - Ref: QNABOTSTACK
      - ''
  ShouldConfigureBedrockKB:
    Fn::Not:
    - Fn::Equals:
      - Ref: BedrockKnowledgeBaseID
      - ''
  ShouldConfigureBedrockLLM:
    Fn::And:
    - Condition: ShouldConfigureQnabot
    - Fn::Not:
      - Condition: ShouldConfigureBedrockKB
  HasSummaryLambdaFunction:
    Fn::Not:
    - Fn::Equals:
      - Ref: TranscriptSummaryFunctionArn
      - ''
  IsBedrockKnowledgeBaseS3BucketName:
    Fn::Not:
    - Fn::Equals:
      - ''
      - Ref: BedrockKnowledgeBaseS3BucketName
Resources:
  GetLowercaseFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      InlineCode: "import cfnresponse\nimport time\ndef handler(event, context): \
        \                                                   \n    output = event['ResourceProperties'].get('InputString',\
        \ '').lower()\n    responseData = {'OutputString': output}               \
        \                             \n    cfnresponse.send(event, context, cfnresponse.SUCCESS,\
        \ responseData)\n"
      LoggingConfig:
        LogGroup:
          Fn::Sub: /${AWS::StackName}/lambda/GetLowercaseFunction
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/lca/0.9.4/lca-agentassist-setup-stack/fd217df20ad195225569159238dfa985
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Customer can use VPC if desired
        - id: W92
          reason: Customer can choose reserved concurrency based on their requirement.
    DependsOn:
    - GetLowercaseFunctionLogGroup
  GetLowercaseFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /${AWS::StackName}/lambda/GetLowercaseFunction
      RetentionInDays:
        Ref: CloudWatchLogsExpirationInDays
  LowercaseStackName:
    Type: Custom::GetLowercase
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - GetLowercaseFunction
        - Arn
      InputString:
        Ref: LCAStackName
  LambdaHookSummarizeCallRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: 2012-10-17
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - Fn::If:
        - HasSummaryLambdaFunction
        - PolicyName: InlinePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
            - Action: lambda:InvokeFunction
              Effect: Allow
              Resource:
                Ref: TranscriptSummaryFunctionArn
        - Ref: AWS::NoValue
  QNASummarizeCallFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName:
        Fn::Sub: QNA-SummarizeCall-${LCAStackName}
      Role:
        Fn::GetAtt:
        - LambdaHookSummarizeCallRole
        - Arn
      Handler: qna_summarize_call_function.handler
      Runtime: python3.12
      Timeout: 900
      Environment:
        Variables:
          TRANSCRIPT_SUMMARY_FUNCTION_ARN:
            Ref: TranscriptSummaryFunctionArn
      Code:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/lca/0.9.4/lca-agentassist-setup-stack/c2c6f0a75b809bc0ffac764c41f4aded
      LoggingConfig:
        LogGroup:
          Fn::Sub: /${AWS::StackName}/lambda/QNASummarizeCallFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Customer can use VPC if desired
        - id: W92
          reason: Customer can choose reserved concurrency based on their requirement.
        - id: W58
          reason: Managed policy already provides access to CloudWatch logs.
    DependsOn:
    - QNASummarizeCallFunctionLogGroup
  QNASummarizeCallFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /${AWS::StackName}/lambda/QNASummarizeCallFunction
      RetentionInDays:
        Ref: CloudWatchLogsExpirationInDays
  Boto3Layer:
    Type: AWS::Lambda::LayerVersion
    Properties:
      Content:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/lca/0.9.4/lca-agentassist-setup-stack/103fdd76c56cc087d1d295474f511d79
      CompatibleRuntimes:
      - python3.12
  QNABedrockKnowledgeBaseFunctionRole:
    Condition: ShouldConfigureBedrockKB
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - bedrock:Retrieve
            - bedrock:RetrieveAndGenerate
            Resource:
              Fn::Sub: arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:knowledge-base/${BedrockKnowledgeBaseID}
          - Effect: Allow
            Action:
            - bedrock:InvokeModel
            Resource:
              Fn::Sub: arn:aws:bedrock:${AWS::Region}::foundation-model/${AgentAssistLLMBedrockModelId}
        PolicyName: BedrockPolicy
      - PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - s3:GetObject
            Resource:
            - arn:aws:s3:::*-importbucket-*/*
            - Fn::If:
              - IsBedrockKnowledgeBaseS3BucketName
              - Fn::Sub: arn:aws:s3:::${BedrockKnowledgeBaseS3BucketName}/*
              - Ref: AWS::NoValue
        PolicyName: S3BucketPolicy
      - PolicyName: InlinePolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Action: lambda:InvokeFunction
            Effect: Allow
            Resource:
              Ref: FetchTranscriptFunctionArn
  QNABedrockKnowledgeBaseFunction:
    Condition: ShouldConfigureBedrockKB
    Type: AWS::Lambda::Function
    Properties:
      FunctionName:
        Fn::Sub: QNA-${LCAStackName}-BedrockKB-LambdaHook
      Role:
        Fn::GetAtt:
        - QNABedrockKnowledgeBaseFunctionRole
        - Arn
      Handler: qna_bedrockkb_lambdahook_function.handler
      Runtime: python3.12
      Layers:
      - Ref: Boto3Layer
      Timeout: 900
      Environment:
        Variables:
          FETCH_TRANSCRIPT_FUNCTION_ARN:
            Ref: FetchTranscriptFunctionArn
          KB_ID:
            Ref: BedrockKnowledgeBaseID
          MODEL_ID:
            Ref: AgentAssistLLMBedrockModelId
      Code:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/lca/0.9.4/lca-agentassist-setup-stack/c2c6f0a75b809bc0ffac764c41f4aded
      LoggingConfig:
        LogGroup:
          Fn::Sub: /${AWS::StackName}/lambda/QNABedrockKnowledgeBaseFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Customer can use VPC if desired
        - id: W92
          reason: Customer can choose reserved concurrency based on their requirement.
    DependsOn:
    - QNABedrockKnowledgeBaseFunctionLogGroup
  QNABedrockKnowledgeBaseFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /${AWS::StackName}/lambda/QNABedrockKnowledgeBaseFunction
      RetentionInDays:
        Ref: CloudWatchLogsExpirationInDays
  QNABedrockLLMFunctionRole:
    Condition: ShouldConfigureBedrockLLM
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - bedrock:InvokeModel
            Resource:
              Fn::Sub: arn:aws:bedrock:${AWS::Region}::foundation-model/${AgentAssistLLMBedrockModelId}
        PolicyName: BedrockPolicy
      - PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - s3:GetObject
            Resource: arn:aws:s3:::*-importbucket-*/*
        PolicyName: S3ImportBucketPolicy
      - PolicyName: InlinePolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Action: lambda:InvokeFunction
            Effect: Allow
            Resource:
              Ref: FetchTranscriptFunctionArn
  QNABedrockLLMFunction:
    Condition: ShouldConfigureBedrockLLM
    Type: AWS::Lambda::Function
    Properties:
      FunctionName:
        Fn::Sub: QNA-${LCAStackName}-BedrockLLM-LambdaHook
      Role:
        Fn::GetAtt:
        - QNABedrockLLMFunctionRole
        - Arn
      Handler: qna_bedrockllm_lambdahook_function.handler
      Runtime: python3.12
      Layers:
      - Ref: Boto3Layer
      Timeout: 900
      Environment:
        Variables:
          FETCH_TRANSCRIPT_FUNCTION_ARN:
            Ref: FetchTranscriptFunctionArn
          MODEL_ID:
            Ref: AgentAssistLLMBedrockModelId
      Code:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/lca/0.9.4/lca-agentassist-setup-stack/c2c6f0a75b809bc0ffac764c41f4aded
      LoggingConfig:
        LogGroup:
          Fn::Sub: /${AWS::StackName}/lambda/QNABedrockLLMFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Customer can use VPC if desired
        - id: W92
          reason: Customer can choose reserved concurrency based on their requirement.
    DependsOn:
    - QNABedrockLLMFunctionLogGroup
  QNABedrockLLMFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /${AWS::StackName}/lambda/QNABedrockLLMFunction
      RetentionInDays:
        Ref: CloudWatchLogsExpirationInDays
  SetupFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: 2012-10-17
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyName: InlinePolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Action:
            - cloudformation:DescribeStacks
            - cloudformation:DescribeStackResource
            Effect: Allow
            Resource:
            - Ref: AISTACK
          - Action:
            - lambda:GetFunctionConfiguration
            - lambda:UpdateFunctionConfiguration
            Effect: Allow
            Resource:
            - Fn::Sub: arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LCAStackName}*
          - Action:
            - iam:ListRolePolicies
            - iam:PutRolePolicy
            Effect: Allow
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/*
          - Action:
            - s3:GetObject
            - s3:PutObject
            Effect: Allow
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:s3:::${WebAppBucket}
            - Fn::Sub: arn:${AWS::Partition}:s3:::${WebAppBucket}/*
          - Action:
            - cloudfront:CreateInvalidation
            Effect: Allow
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistributionId}
          - Fn::If:
            - ShouldConfigureQnabot
            - Action:
              - cloudformation:DescribeStacks
              - cloudformation:DescribeStackResource
              Effect: Allow
              Resource:
              - Fn::Sub: arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${LCAStackName}-QNABOT*
            - Ref: AWS::NoValue
          - Fn::If:
            - ShouldConfigureQnabot
            - Action:
              - ssm:GetParameter
              - ssm:PutParameter
              Effect: Allow
              Resource:
              - Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/CFN-DefaultQnABotSettings*
            - Ref: AWS::NoValue
          - Fn::If:
            - ShouldConfigureQnabot
            - Action:
              - s3:GetObject
              - s3:PutObject
              Effect: Allow
              Resource:
              - Fn::Sub: arn:aws:s3:::${LowercaseStackName.OutputString}*
              - Fn::Sub: arn:aws:s3:::${QnaAgentAssistDemoJson}
            - Ref: AWS::NoValue
          - Fn::If:
            - ShouldConfigureQnabot
            - Action:
              - lambda:InvokeFunction
              Effect: Allow
              Resource:
              - Fn::Sub: arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${LCAStackName}*
            - Ref: AWS::NoValue
  SetupFunction:
    Type: AWS::Lambda::Function
    Properties:
      Role:
        Fn::GetAtt:
        - SetupFunctionRole
        - Arn
      Handler: setup_function.handler
      Runtime: python3.12
      Timeout: 900
      Code:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/lca/0.9.4/lca-agentassist-setup-stack/c2c6f0a75b809bc0ffac764c41f4aded
      LoggingConfig:
        LogGroup:
          Fn::Sub: /${AWS::StackName}/lambda/SetupFunction
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Customer can use VPC if desired
        - id: W92
          reason: Customer can choose reserved concurrency based on their requirement.
        - id: W58
          reason: Managed policy already provides access to CloudWatch logs.
    DependsOn:
    - SetupFunctionLogGroup
  SetupFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /${AWS::StackName}/lambda/SetupFunction
      RetentionInDays:
        Ref: CloudWatchLogsExpirationInDays
  SetupFunctionResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - SetupFunction
        - Arn
      AISTACK:
        Ref: AISTACK
      QNABOTSTACK:
        Ref: QNABOTSTACK
      LexAgentAssistBotId:
        Ref: LexAgentAssistBotId
      LexAgentAssistAliasId:
        Ref: LexAgentAssistAliasId
      LexAgentAssistLocaleId:
        Ref: LexAgentAssistLocaleId
      QnaAgentAssistDemoJson:
        Ref: QnaAgentAssistDemoJson
      QNASummarizeCallFunction:
        Ref: QNASummarizeCallFunction
      QNAAgentAssistLambdaHookFunction:
        Fn::If:
        - ShouldConfigureBedrockKB
        - Ref: QNABedrockKnowledgeBaseFunction
        - Ref: QNABedrockLLMFunction
      QnaBotSettings:
        Ref: QnaBotSettings
      LexAgentAssistIdentityPoolId:
        Ref: LexAgentAssistIdentityPoolId
      TranscribeLanguageCode:
        Ref: TranscribeLanguageCode
      TranscriptLambdaHookFunctionArn:
        Ref: TranscriptLambdaHookFunctionArn
      TranscriptLambdaHookFunctionNonPartialOnly:
        Ref: TranscriptLambdaHookFunctionNonPartialOnly
      DynamoDbExpirationInDays:
        Ref: DynamoDbExpirationInDays
      EndOfCallTranscriptSummary:
        Ref: EndOfCallTranscriptSummary
      EndOfCallLambdaHookFunctionArn:
        Ref: EndOfCallLambdaHookFunctionArn
      CloudFrontDomainName:
        Fn::Sub: https://${CloudFrontDomainName}/
      CallAudioSource:
        Ref: CallAudioSource
      ComprehendLanguageCode:
        Ref: ComprehendLanguageCode
      AgentAssistOption:
        Ref: AgentAssistOption
      AgentAssistExistingLexV2BotId:
        Ref: AgentAssistExistingLexV2BotId
      AgentAssistExistingLexV2BotAliasId:
        Ref: AgentAssistExistingLexV2BotAliasId
      AgentAssistExistingLambdaFunctionArn:
        Ref: AgentAssistExistingLambdaFunctionArn
      AgentAssistWebCrawlURLs:
        Ref: AgentAssistWebCrawlURLs
      AgentAssistWebCrawlSyncScope:
        Ref: AgentAssistWebCrawlSyncScope
      IsSentimentAnalysisEnabled:
        Ref: IsSentimentAnalysisEnabled
      SentimentNegativeScoreThreshold:
        Ref: SentimentNegativeScoreThreshold
      SentimentPositiveScoreThreshold:
        Ref: SentimentPositiveScoreThreshold
      Version:
        Ref: Version
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
