<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wildo Monges</title>
    <description>The latest articles on DEV Community by Wildo Monges (@wildomonges).</description>
    <link>https://dev.to/wildomonges</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1235382%2F0293cbb8-209a-473e-bb74-9ba01bac8054.jpeg</url>
      <title>DEV Community: Wildo Monges</title>
      <link>https://dev.to/wildomonges</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wildomonges"/>
    <language>en</language>
    <item>
      <title>Building a Christmas-Themed Chatbot: My ServerlessGuru Hackathon Journey</title>
      <dc:creator>Wildo Monges</dc:creator>
      <pubDate>Sun, 17 Dec 2023 19:59:20 +0000</pubDate>
      <link>https://dev.to/wildomonges/building-a-christmas-themed-chatbot-my-serverlessguru-hackathon-journey-3ojg</link>
      <guid>https://dev.to/wildomonges/building-a-christmas-themed-chatbot-my-serverlessguru-hackathon-journey-3ojg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I recently came across an exciting opportunity: the &lt;a href="https://hackathon.serverless.guru/"&gt;Serverless Holiday Hackathon&lt;/a&gt; organized by &lt;a href="https://serverlessguru.com/"&gt;Serverless Guru&lt;/a&gt;. The hackathon, taking place throughout the first half of December 2023, challenges participants to showcase their creativity and skills by developing the best holiday-themed chat application, utilizing any LLM (Language Model).&lt;/p&gt;

&lt;p&gt;As I delve into the world of serverless development and explore the possibilities of creating a holiday-themed chat application, I'm excited to share my journey and insights with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo and Architecture Explanation Videos
&lt;/h2&gt;

&lt;p&gt;I recorded a quick video showing how the Frontend application works before to deep dive into the Backend implementation.&lt;/p&gt;

&lt;p&gt;Please see the video shared on Google Drive entering in the next url:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1gk3uVF41EYiyXFzKgWFWHEz0tHOCBhA9/view?usp=sharing"&gt;demo video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additional to the Demo video, you can see a Video about my explanation of the Architecture Design that is behind the Chatbot in the next link&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1_-QnwIXrIbkV9D2Z4b7k5QLme66HTYq5/view?usp=sharing"&gt;architecture video&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Github Repo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Github:&lt;/strong&gt; &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot"&gt;https://github.com/wildomonges/christmas-theme-chatbot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This my Journey
&lt;/h2&gt;

&lt;p&gt;I started investigating the following new concepts and technologies for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LLM (Large Language Model)&lt;/code&gt;: Serverless Guru gaves us a great introduction to LLM by sharing the next &lt;a href="https://www.youtube.com/watch?v=QHaQB-9Vf9k&amp;amp;t=3865s"&gt;Youtube Video&lt;/a&gt;. So I found that LLM stands for and advanced Machine Learning model designed to understand and generate Human-Like text at large scale.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS Bedrock&lt;/code&gt;: This is an AWS Service to build and scale generative AI application with foundation models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After to have been familiarized (at basic level) with these technologies I commenced to choose the stack of technologies to develop the application.&lt;/p&gt;

&lt;p&gt;For the Backend I decided to use&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS SAM (Serverless Application Model)&lt;/code&gt;: To manage the AWS Resource definition for the serverless application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ruby 3.2&lt;/code&gt;: As the programming language to code the business logic.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Docker&lt;/code&gt;: For development to isolate my environment project from others I have in my local machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;I chose this stack as per my experience building Serverless Projects and due of the lack of time I had during the Hackathon to try with another programming language as Javascript, Python or another IaC (Infrastructure as Code) like Serverless Framework, Terraform, etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before to jump straight to the coding part I designed the Application Architecture using LucidChart in order to have a general overview of the AWS Services I required to achieve the goal of having a functional Chatbot.&lt;/p&gt;

&lt;p&gt;The first design was just a single Lambda Function connected to AWS Bedrock&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftadtk11hddf62zlo5xbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftadtk11hddf62zlo5xbp.png" alt="first-design" width="598" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I had that Lambda -&amp;gt; Bedrock integration in my mind, I started initializing a basic project structure to start coding the first &lt;code&gt;sendMessageHandler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;christmas-theme-chatbot/
 -backend/
     - app/
         - functions/
         - layers/shared/ruby/
              - gems
              - lib
         - template.yaml
         - Gemfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I filled the &lt;code&gt;template.yaml&lt;/code&gt; with the following code snippet&lt;/p&gt;

&lt;p&gt;template.yaml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2010-09-09"&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Chatbot resource definition&lt;/span&gt;

&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   
    &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;256&lt;/span&gt;
    &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby3.2&lt;/span&gt;
    &lt;span class="na"&gt;Tracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Active&lt;/span&gt;
    &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;25&lt;/span&gt;
&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
  &lt;span class="na"&gt;Service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;christmas-theme-chatbot&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;sendMessageHandler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app/functions/send_message/&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Handle the message received from the client side and forward to Bedrock&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;handler.ChristmasThemeChatbot::Functions::SendMessage.handler&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Service}-${Env}-send-message-handler"&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bedrock:*'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sendMessageHandler&lt;/code&gt; I placed into&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- functions/
    - send_message/
         - handler.rb 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 1 - Able to connect to Bedrock within a lambda function using Ruby
&lt;/h3&gt;

&lt;p&gt;In order to interact with Bedrock I had to add the following gems to my Gemfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# frozen_string_literal: true

source 'https://rubygems.org'

ruby '&amp;gt;= 3.2.2'

gem 'aws-sdk-bedrockruntime'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having all the dependencies in place I initiated to write a basic  Ruby code to send messages to Bedrock and get a response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'aws-sdk-bedrockruntime'&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ChristmasThemeChatbot&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Functions&lt;/span&gt;
    &lt;span class="c1"&gt;# This class implements the handler function to receive the message&lt;/span&gt;
    &lt;span class="c1"&gt;# from the client side and forward to bedrock api&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendMessage&lt;/span&gt;
      &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
           &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Aws&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BedrockRuntime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
           &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_model_with_response_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;model_id: &lt;/span&gt;&lt;span class="no"&gt;MODEL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;content_type: &lt;/span&gt;&lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;accept: &lt;/span&gt;&lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="ss"&gt;event_stream_handler: &lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
        &lt;span class="kp"&gt;private&lt;/span&gt;

        &lt;span class="no"&gt;ACT_AS_SANTA_CLAUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'You are Santa Claus, a friendly old man who talk with people about Christmas'&lt;/span&gt;
        &lt;span class="no"&gt;MODEL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'anthropic.claude-v2'&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;
          &lt;span class="n"&gt;event_stream_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Aws&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;BedrockRuntime&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EventStreams&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ResponseStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
          &lt;span class="n"&gt;event_stream_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_chunk_event&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;response_event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
            &lt;span class="n"&gt;chunk_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'completion'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="nb"&gt;print&lt;/span&gt; &lt;span class="n"&gt;chunk_response&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="n"&gt;event_stream_handler&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After to experiment with few models like &lt;a href="https://aws.amazon.com/bedrock/llama-2/"&gt;Meta Llama 2&lt;/a&gt; and &lt;a href="https://aws.amazon.com/bedrock/claude/"&gt;Anthropic Claude-V2&lt;/a&gt; I found that this last one gave me better dialog given the text I provided to it.&lt;/p&gt;

&lt;p&gt;Here is where the first challenge pop up to &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Be able to send data to the Bedrock api using &lt;a href="https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/BedrockRuntime/Client.html"&gt;aws-sdk-bedrockruntime&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Find a good prompt value in order to get a nice answer from the api. The one who worked well for me is the next prompt&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACT_AS_SANTA_CLAUS = 'You are Santa Claus, a friendly old man who talk with people about Christmas'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Be able to retrieve as a stream response using the sdk because in my mind I already had that I wanted to stream the Santa's answer to the Frontend app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This first phase took me around 2 days working 4~5 hours per day. But after that time I was able to get a basic coherent response from the api.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2 - Expose an API for the client to interact with my Bedrock handler code.
&lt;/h3&gt;

&lt;p&gt;The simplest approach would be just to add an &lt;code&gt;Http API Gateway&lt;/code&gt; connected directly with &lt;code&gt;sendMessageHandler&lt;/code&gt;, however this could end in a bad user experience having the client to wait the &lt;code&gt;sendMessageHandler&lt;/code&gt; to fully have the &lt;code&gt;Bedrock API&lt;/code&gt; response before to send  back to it.&lt;/p&gt;

&lt;p&gt;Having that in mind I decided to expose a Websocket which allows to the handler function to stream the Bedrock message near to the real time to the client.&lt;/p&gt;

&lt;p&gt;I went back to LucidChart and added the new components of the application as follow&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6va3yrya0tkidd5pj6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6va3yrya0tkidd5pj6c.png" alt="second-design" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I updated &lt;code&gt;template.yaml&lt;/code&gt; in order to add the new resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;onConnectHandler&lt;/code&gt;: It receives the $connect request and stores the connectionId into a Dynamodb  table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onDisconnect&lt;/code&gt;: It receives the $disconnect request and removes the connectionId from the table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection table&lt;/code&gt;: It stores connection ids to establish a connection between the Client and the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I updated &lt;code&gt;template.yaml&lt;/code&gt; defining the new resources as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="c1"&gt;####################&lt;/span&gt;
  &lt;span class="c1"&gt;# Websocket API&lt;/span&gt;
  &lt;span class="c1"&gt;####################&lt;/span&gt;

  &lt;span class="na"&gt;webSocketApi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Api&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Service}-${Env}-websocket-api"&lt;/span&gt;
      &lt;span class="na"&gt;ProtocolType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;WEBSOCKET&lt;/span&gt;
      &lt;span class="na"&gt;RouteSelectionExpression&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$request.body.action"&lt;/span&gt;

  &lt;span class="na"&gt;webSocketApiLogGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Logs::LogGroup&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;LogGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/aws/apigateway/${webSocketApi}/${Env}"&lt;/span&gt;

  &lt;span class="na"&gt;Stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Stage&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;StageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Env&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Env}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;stage"&lt;/span&gt;
      &lt;span class="na"&gt;DeploymentId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
      &lt;span class="na"&gt;AccessLogSettings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DestinationArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;webSocketApiLogGroup.Arn&lt;/span&gt;
        &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"requestId":"$context.requestId","ip":"$context.identity.sourceIp",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"requestTime":"$context.requestTime",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"httpMethod":"$context.httpMethod",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"routeKey":"$context.routeKey",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"status":"$context.status","protocol":"$context.protocol",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"responseLength":"$context.responseLength"}'&lt;/span&gt;

  &lt;span class="na"&gt;Deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Deployment&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;connectRoute&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sendMessageRoute&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;disconnectRoute&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;

&lt;span class="c1"&gt;###################&lt;/span&gt;
  &lt;span class="c1"&gt;# Routes&lt;/span&gt;
  &lt;span class="c1"&gt;###################&lt;/span&gt;

  &lt;span class="na"&gt;connectRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Route&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
      &lt;span class="na"&gt;RouteKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$connect&lt;/span&gt;
      &lt;span class="na"&gt;OperationName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;connectRoute&lt;/span&gt;
      &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;integrations'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;connectIntegration&lt;/span&gt;

  &lt;span class="na"&gt;connectIntegration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Integration&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Connect Integration&lt;/span&gt;
      &lt;span class="na"&gt;IntegrationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS_PROXY&lt;/span&gt;
      &lt;span class="na"&gt;IntegrationUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${onConnectHandler.Arn}/invocations"&lt;/span&gt;

  &lt;span class="na"&gt;disconnectRoute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Route&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
      &lt;span class="na"&gt;RouteKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$disconnect&lt;/span&gt;
      &lt;span class="na"&gt;OperationName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disconnectRoute&lt;/span&gt;
      &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Join&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;integrations'&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;disconnectIntegration&lt;/span&gt;

  &lt;span class="na"&gt;disconnectIntegration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::ApiGatewayV2::Integration&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
      &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Disconnect Integration&lt;/span&gt;
      &lt;span class="na"&gt;IntegrationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS_PROXY&lt;/span&gt;
      &lt;span class="na"&gt;IntegrationUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${onDisconnectHandler.Arn}/invocations"&lt;/span&gt;

&lt;span class="c1"&gt;#######################&lt;/span&gt;
  &lt;span class="c1"&gt;# Function Permissions&lt;/span&gt;
  &lt;span class="c1"&gt;#######################&lt;/span&gt;

  &lt;span class="na"&gt;onConnectPermission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Permission&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;onConnectHandler&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apigateway.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;SourceArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${webSocketApi}/${Env}/$connect"&lt;/span&gt;

  &lt;span class="na"&gt;onDisconnectPermission&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Permission&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;webSocketApi&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lambda:InvokeFunction&lt;/span&gt;
      &lt;span class="na"&gt;FunctionName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;onDisconnectHandler&lt;/span&gt;
      &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apigateway.amazonaws.com&lt;/span&gt;
      &lt;span class="na"&gt;SourceArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${webSocketApi}/${Env}/$disconnect"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this step is where the second challenge pop up! I had issues trying to invoke the &lt;code&gt;onConnect&lt;/code&gt; and &lt;code&gt;onDisconnect&lt;/code&gt; handlers when testing using &lt;code&gt;wscat&lt;/code&gt;. This was due of lack of permissions on the lambda functions. Unfortunately &lt;code&gt;sam deploy&lt;/code&gt; is not creating automatically the &lt;code&gt;permissions&lt;/code&gt; that you can see at the &lt;code&gt;Permissions&lt;/code&gt; section, that's why I had to manually add them into the template.yaml fie.&lt;/p&gt;

&lt;p&gt;To test and debug the integration I used &lt;a href="https://github.com/websockets/wscat#readme"&gt;wscat&lt;/a&gt; as follow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wscat &lt;span class="nt"&gt;-c&lt;/span&gt; wss://WEBSOCKET_API_ID.execute-api.us-east-1.amazonaws.com/dev/
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; connected

&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"action"&lt;/span&gt;: &lt;span class="s2"&gt;"sendMessage"&lt;/span&gt;, &lt;span class="s2"&gt;"data"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello Santa!"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that I fixed the issue that was preventing the api gateway to invoke the onConnect function I started the phase 3&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3 - Saving and removing connections
&lt;/h3&gt;

&lt;p&gt;To interact with the Dynamodb table I used a gem called &lt;a href="https://github.com/Dynamoid/dynamoid"&gt;dynamoid&lt;/a&gt;. I created a &lt;code&gt;Connection&lt;/code&gt; model in order to use it like &lt;code&gt;Connection.create(connection_id)&lt;/code&gt; and &lt;code&gt;Connection.find(connectionId).delete&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dotenv/load'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'dynamoid'&lt;/span&gt;

&lt;span class="no"&gt;Dynamoid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt; &lt;span class="c1"&gt;# to avoid having the prefix dynamoid_ as part of the table name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ChristmasThemeChatbot&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Layers&lt;/span&gt;
    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Shared&lt;/span&gt;
      &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Models&lt;/span&gt;
        &lt;span class="c1"&gt;# This class is used to interact with dynamodb connections table&lt;/span&gt;
        &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;
          &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Dynamoid&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Document&lt;/span&gt;

          &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'CONNECTIONS_TABLE'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

          &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:connectionId&lt;/span&gt;

          &lt;span class="n"&gt;validates_presence_of&lt;/span&gt; &lt;span class="ss"&gt;:connectionId&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;once defined this module, I used it as follow within the &lt;code&gt;onConnect&lt;/code&gt; and &lt;code&gt;onDisconnect&lt;/code&gt; handlers&lt;br&gt;
&lt;code&gt;Eg.&lt;/code&gt; onConnectHandler&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# frozen_string_literal: true&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'models/connection'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ChristmasThemeChatbot&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Functions&lt;/span&gt;
    &lt;span class="c1"&gt;# This class implements the handler function to connect to the websocket&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OnConnect&lt;/span&gt;
      &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ChristmasThemeChatbot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Shared&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;  

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
          &lt;span class="n"&gt;connection_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'requestContext'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'connectionId'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="no"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;connectionId: &lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;statusCode: &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="s1"&gt;'Successfully created a new connection'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Note&lt;/code&gt;: &lt;em&gt;At this point I was able to connect to the websocket, invoke the Bedrock api, receive Santa's message and disconnect from the api successfully&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4 - Improving the code
&lt;/h3&gt;

&lt;p&gt;In order to have a cleaner code I started &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring the code, adding log information and encapsulating the methods just exposing the handlers one.
The full code you can see at my repository &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Adding some unit tests&lt;/li&gt;
&lt;li&gt;Reusing code using Lambda Layers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 5 - Work on the Frontend application building the Chat UI
&lt;/h3&gt;

&lt;p&gt;I chose React as the library to implement the UI and I used some libraries like &lt;a href="https://github.com/FredrikOseberg/react-chatbot-kit"&gt;react-chatbot-kit&lt;/a&gt; for the chat component and &lt;a href="https://github.com/robtaussig/react-use-websocket"&gt;react-use-websocket&lt;/a&gt; to manage the connection with the backend API.&lt;/p&gt;

&lt;p&gt;It is a simple UI which I created using the next command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app christmas-theme-chatbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn1w7y1peovk59vq0or.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn1w7y1peovk59vq0or.png" alt="frontend-app" width="334" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;src&lt;/code&gt; folder contains the files to render the Chatbot Interface to chat with Santa.&lt;br&gt;
The major files are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;App.js&lt;/code&gt;: it's the main wrapper, it imports the &lt;code&gt;Chatbot&lt;/code&gt; component provided by &lt;code&gt;react-chatbot-kit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MessageParser.js&lt;/code&gt;: Handles the user messages and trigger the &lt;code&gt;action&lt;/code&gt; to execute with the message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ActionProvider.js&lt;/code&gt;: Using the &lt;code&gt;react-use-websocket&lt;/code&gt; library, it creates a websocket connection to the &lt;code&gt;serverless backend api&lt;/code&gt; and sends the input message. It receives the api response and update the messages state to render in the UI Santa's chat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ActionProvider implements the &lt;code&gt;handleSendMessage&lt;/code&gt; function which uses the &lt;code&gt;sendJsonMessage&lt;/code&gt; built in function to send the message to the websocket api as follow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sendJsonMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socketUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTokens&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prevTokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prevTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as a response is received the &lt;code&gt;onMessage&lt;/code&gt; will update the &lt;code&gt;tokens&lt;/code&gt; list to render automatically the Santa's message in the UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 6 - Deploying the React App on Amplify Hosting
&lt;/h3&gt;

&lt;p&gt;I chose &lt;a href="https://aws.amazon.com/amplify/getting-started"&gt;AWS Amplify&lt;/a&gt; to host the React application. I have setup it manually on AWS Console connecting my Github account to Amplify and selecting the Github repository I want to build and deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4mprem37fwa353u74i1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4mprem37fwa353u74i1.png" alt="Image description" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The production way to do this is to add the trigger to deploy the Frontend application from the CI/CD pipeline on merging code to &lt;code&gt;main&lt;/code&gt; branch with a step like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; aws amplify start-job --app-id &amp;lt;&amp;lt;parameters.amplify_app_id&amp;gt;&amp;gt; --branch-name &amp;lt;&amp;lt;parameters.amplify_branch_name&amp;gt;&amp;gt; --commit-id $CIRCLE_SHA1 --job-type RELEASE        
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However I decided to kept it simple and manually configure the amplify application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 7 - Feature to discover Gift and Child name and store into a new table
&lt;/h3&gt;

&lt;p&gt;After to have a basic version of the chat working, I thought of adding a new feature to helps Santa Claus to figure out the Gifts requested by each Child.&lt;/p&gt;

&lt;p&gt;There I went back to LucidChart and I introduced new resources like &lt;code&gt;messageAnalyzerHandler&lt;/code&gt;, &lt;code&gt;giftsQueue&lt;/code&gt; , &lt;code&gt;giftRegistrationHandler&lt;/code&gt; and &lt;code&gt;GiftsTable&lt;/code&gt; as follow&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjkbr22ytve3kqpcqh42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjkbr22ytve3kqpcqh42.png" alt="Gifts Analyzer Integration" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How it works?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The input message is sent by &lt;code&gt;sendMessageHandler&lt;/code&gt; to a SQS queue called &lt;code&gt;messagesQueue&lt;/code&gt;. This stores messages in the next format
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hi Santa! My name is Wildo, and I would like  Tshirt for Christmas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connection_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"THE_CONNECTION_ID"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code which sends the message from the handler is &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot/blob/main/backend/app/functions/send_message/handler.rb#L57"&gt;this one&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;SendQueueMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;connection_id: &lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;queue_url: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'MESSAGES_QUEUE'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I implemented a service class called &lt;code&gt;SendQueueMessage&lt;/code&gt; which is a wrapper to encapsulate the business logic around the invocation of the api method &lt;code&gt;send_message&lt;/code&gt; of the &lt;code&gt;aws-sdk-sqs&lt;/code&gt; gem. &lt;br&gt;
The service code can be found &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot/blob/main/backend/app/layers/shared/ruby/lib/services/send_queue_message.rb"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The lambda &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot/blob/main/backend/app/functions/message_analyzer/handler.rb#L61"&gt;messageAnalyzer&lt;/a&gt; consumes the message from the SQS and invokes Bedrock API with a special prompt
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ACT_AS_GIFT_DISCOVER = 'Given the next message sent by a child to Santa, extract the name of the gift and the name' \
                               ' of the child  in the next format ' \
                               ' "the name of the gift is \"GIFT_NAME\" and the name of the child is \"CHILD_NAME\"'
 MODEL_ID = 'anthropic.claude-v2'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Bedrock responds with a &lt;code&gt;completion&lt;/code&gt; like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "completion": " the name of the gift is \"I would like to\" and the name of the child is \"Wildo\"",
    "stop_reason": "stop_sequence",
    "stop": "\n\nHuman:"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing this payload to a regular expression I was able to get the &lt;code&gt;Child&lt;/code&gt; and &lt;code&gt;Gift&lt;/code&gt; name. For reference code &lt;a href="https://github.com/wildomonges/christmas-theme-chatbot/blob/main/backend/app/functions/message_analyzer/handler.rb#L82"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Then, It sends a new payload to another queue called &lt;code&gt;giftsQueue&lt;/code&gt; in the next format
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    gift: 'Tshirt',
    username: 'Wildo',
    connection_id: 'THE_CONNECTION_ID'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The previous message is consumed by &lt;code&gt;giftRegistrationHandler&lt;/code&gt; which using the &lt;code&gt;Connection&lt;/code&gt; model saves the record into &lt;code&gt;giftsTable&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gift.create(connectionId: data['connection_id'], username: data['username'], gift: data['gift'])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 8 - Protecting the Websocket API
&lt;/h3&gt;

&lt;p&gt;In this step I was stuck for 2 days (~ 10 hours) trying to implement a &lt;code&gt;lambdaRequestAuthorizer&lt;/code&gt; to &lt;code&gt;allow&lt;/code&gt; and &lt;code&gt;deny&lt;/code&gt; access to the &lt;code&gt;$connect&lt;/code&gt; route checking the &lt;code&gt;Authorization&lt;/code&gt; header, however when I was able to make it work at the backend side, I figured it out that using &lt;code&gt;react-use-websocket&lt;/code&gt; does not support to set new &lt;code&gt;headers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, I ended in removing all the resources and code provisioned for the authorization feature and implementing a &lt;code&gt;workaround&lt;/code&gt;. It works as follow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendMessage&lt;/span&gt;
      &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;
        &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ChristmasThemeChatbot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Shared&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Models&lt;/span&gt;
        &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ChristmasThemeChatbot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Layers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Shared&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Services&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
          &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Handle sendMessage. Event: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

          &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
          &lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'accessToken'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="n"&gt;request_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'requestContext'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;request_context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'domainName'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;request_context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'stage'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
          &lt;span class="n"&gt;connection_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request_context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'connectionId'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ACCESS_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Access unauthorized'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="no"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;
            &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Connection &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; deleted!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;delete_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;connection_id: &lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;statusCode: &lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="s1"&gt;'Unauthorized'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="k"&gt;end&lt;/span&gt;

          &lt;span class="c1"&gt;# Continue execution&lt;/span&gt;
          &lt;span class="n"&gt;user_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SendMessageHandler expects to receive in the &lt;code&gt;body&lt;/code&gt; the &lt;code&gt;accessToken&lt;/code&gt;. If a valid &lt;code&gt;static&lt;/code&gt; token is provided, it continues the normal flow, in another hand, it finds the connection and delete it from the db as well as It notifies to the client using the code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;   &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;delete_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;connection_id: &lt;/span&gt;&lt;span class="n"&gt;connection_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Architecture Design
&lt;/h3&gt;

&lt;p&gt;This is the final design&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flce53r5xuauoech2xisv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flce53r5xuauoech2xisv.png" alt="final-design" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  MVP - PoC
&lt;/h3&gt;

&lt;p&gt;The current application is just a &lt;em&gt;Proof of Concept&lt;/em&gt; to play with &lt;em&gt;AWS Bedrock&lt;/em&gt; to see if is possible to build a &lt;em&gt;Christmas Theme Chatbot&lt;/em&gt;, even if it's online &lt;a href="https://main.d84kg9vv2z5z9.amplifyapp.com/"&gt;here&lt;/a&gt; It's far away to be on &lt;em&gt;production&lt;/em&gt; &lt;br&gt;
mode to be shared with the public in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improvements I would like to have added
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provide a better UI/UX (for multi devices like mobile and tables)&lt;/li&gt;
&lt;li&gt;Add login page for Santa or Elf to be as an admin of the app&lt;/li&gt;
&lt;li&gt;Add page to allow Santa to visualize the list of &lt;code&gt;gifts&lt;/code&gt; grouped by &lt;code&gt;childs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add better unit tests&lt;/li&gt;
&lt;li&gt;Add integration tests&lt;/li&gt;
&lt;li&gt;Add CICD with multi stages like &lt;code&gt;dev -&amp;gt; qa -&amp;gt; staging &amp;gt; prod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Improve the &lt;code&gt;prompt&lt;/code&gt; used to &lt;code&gt;generate the conversation&lt;/code&gt; as well as the one used for &lt;code&gt;gifts and child&lt;/code&gt; discover.&lt;/li&gt;
&lt;li&gt;Improve the &lt;code&gt;log&lt;/code&gt; format using &lt;code&gt;json&lt;/code&gt; objects.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Bugsnag&lt;/code&gt; or &lt;code&gt;Rollbar&lt;/code&gt; to monitor &lt;code&gt;errors&lt;/code&gt; in the application.&lt;/li&gt;
&lt;li&gt;Build monitoring dashboards to track informations like &lt;code&gt;Amount of Requests to the Chatbot&lt;/code&gt;, &lt;code&gt;Gifts Most Requested&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Add an incident management system like &lt;code&gt;PagerDutty&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Loading Testing&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provide a &lt;code&gt;Swagger&lt;/code&gt; documentation about how to interact with the &lt;code&gt;Websocket api&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a better &lt;code&gt;Authentication and Authorization&lt;/code&gt; mechanism.&lt;/li&gt;
&lt;li&gt;Set a reserved and provisioned concurrency analyzing the behavior of each lambda function&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It was a great journey coding a Christmas Theme Chatbot, I learned how to use AWS Services like &lt;code&gt;Websocket API&lt;/code&gt; and &lt;code&gt;AWS Bedrock&lt;/code&gt; in a basic way. I challenged myself trying to deliver a functional chatbot knowing the lack of time I had because of my Full Time job as a &lt;strong&gt;Senior Lead Software Developer at Decisiv Inc&lt;/strong&gt; and other personal project I had to maintain during the Hackathon.  &lt;/p&gt;

</description>
      <category>serverless</category>
      <category>ruby</category>
      <category>aws</category>
      <category>bedrock</category>
    </item>
  </channel>
</rss>
