<?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>Forem: Martin Muller 🇩🇪🇧🇷🇵🇹</title>
    <description>The latest articles on Forem by Martin Muller 🇩🇪🇧🇷🇵🇹 (@mmuller88).</description>
    <link>https://forem.com/mmuller88</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%2F533828%2Ffa1d01e9-5d5a-400b-a657-6f3da3c6c3bb.jpeg</url>
      <title>Forem: Martin Muller 🇩🇪🇧🇷🇵🇹</title>
      <link>https://forem.com/mmuller88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mmuller88"/>
    <language>en</language>
    <item>
      <title>How I Use OpenClaw as My AI-Powered Personal Operating System</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Wed, 01 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-i-use-openclaw-as-my-ai-powered-personal-operating-system-58i</link>
      <guid>https://forem.com/aws-builders/how-i-use-openclaw-as-my-ai-powered-personal-operating-system-58i</guid>
      <description>&lt;p&gt;I've been running &lt;a href="https://openclaw.ai" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt; on a Hostinger VPS for about a month now, and it has fundamentally changed how I work. What started as "let me try this AI agent thing" turned into a powerful assistant that saves me hours every week by automating routine tasks — from GitHub issue triage to email management.&lt;/p&gt;

&lt;p&gt;OpenClaw doesn't do everything for me (yet), but it handles a lot of the repetitive work that used to eat up my day. Here's a deep dive into every use case I've discovered so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is OpenClaw?
&lt;/h2&gt;

&lt;p&gt;OpenClaw is an open-source AI agent platform that runs on your own infrastructure. You connect it to your chat channels (Telegram, WhatsApp, Discord), give it access to your tools, and it becomes a persistent assistant that remembers context across sessions. Think of it as your own self-hosted AI employee that's always on.&lt;/p&gt;

&lt;p&gt;My setup: Docker container on a Hostinger VPS, with &lt;strong&gt;Telegram as my primary interface&lt;/strong&gt;. Most of my interactions — voice messages, text, files — go through Telegram. It feels like texting a colleague who never sleeps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 1: Autonomous GitHub Issue Management
&lt;/h2&gt;

&lt;p&gt;This is the killer feature for me. OpenClaw monitors my &lt;a href="https://hallocasa.com" rel="noopener noreferrer"&gt;HalloCasa&lt;/a&gt; repositories every 2 hours via a heartbeat. When a new issue appears, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Notifies me&lt;/strong&gt; on Telegram&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generates a detailed implementation plan&lt;/strong&gt; using Cursor CLI with Claude Opus for planning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Posts the plan as a comment&lt;/strong&gt; on the GitHub issue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Waits for my approval&lt;/strong&gt; before implementing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implements the fix&lt;/strong&gt; using Cursor CLI with Composer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creates a branch, commits, pushes, and opens a PR&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the first week alone, it planned and implemented fixes for currency display bugs, locale changes, phone validation, and filter cleanup — all with minimal intervention from me.&lt;/p&gt;

&lt;p&gt;The workflow follows the "Factory" pattern I heard about at Agentic Conf Hamburg: don't just use AI to build features — build a production line that builds features for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 2: Email Management
&lt;/h2&gt;

&lt;p&gt;OpenClaw reads and sends emails via Himalaya CLI (IMAP/SMTP). I just tell it what to do on Telegram:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Tell the sports group leader we can't come today"&lt;/strong&gt; → Searched my contacts, found the match, sent a polite cancellation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Email my tax advisor that I need to reschedule"&lt;/strong&gt; → Found the firm in contacts, sent a professional German email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Reply to the kindergarten about scheduling a visit"&lt;/strong&gt; → Continued an existing email thread with context-aware reply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It sends HTML emails with a professional signature and handles German and English.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 3: Morning Briefing (Cron Job)
&lt;/h2&gt;

&lt;p&gt;Every morning at 7:00 AM (my timezone, Europe/Berlin), OpenClaw runs an automated check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scans unread emails from the last 24 hours&lt;/li&gt;
&lt;li&gt;Checks yesterday's emails for pending follow-ups&lt;/li&gt;
&lt;li&gt;Cross-references with my Google Calendar for today and tomorrow&lt;/li&gt;
&lt;li&gt;Sends me a concise summary on Telegram&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This runs on Claude Haiku (cheap) and costs almost nothing. I wake up to a briefing instead of manually checking three different apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 4: Calendar Management
&lt;/h2&gt;

&lt;p&gt;OpenClaw has full access to my Google Calendar via OAuth. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conference schedule&lt;/strong&gt; : I sent a PDF of the Agentic Conf Hamburg schedule with sessions I'd marked with red boxes. It extracted the image, identified the marked sessions using vision AI, fetched details from the conference website, and created 9 calendar events with full descriptions, speaker info, and links.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating meetings&lt;/strong&gt; : "Create a meeting with Phillip P. for today at 18:00 CET" → Done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Case 5: Contact Lookup
&lt;/h2&gt;

&lt;p&gt;Connected to my iCloud contacts via CardDAV, OpenClaw searches by name, organization, or context. When I say "email the sports group leader," it finds the match and uses the right email. No manual lookup needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 6: LinkedIn Post Drafting
&lt;/h2&gt;

&lt;p&gt;I've used it to draft LinkedIn posts for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Community Day Athens 2026&lt;/strong&gt; : Looked up all speakers from the conference website, compiled them alphabetically, created a professional announcement post.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agentic Conf Hamburg recap&lt;/strong&gt; : Searched for organizers' backgrounds, found LinkedIn profiles, referenced specific talks, and wove in my personal highlights about the "Factory" concept.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does the research, I do the personal touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 7: Research &amp;amp; Due Diligence
&lt;/h2&gt;

&lt;p&gt;Questions that would normally cost 15 minutes of Googling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Is there an app for biometric passport photos with QR codes?"&lt;/strong&gt; → Comparison of available apps, including recent regulation changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"There's a conference in my area tomorrow, what is it?"&lt;/strong&gt; → Found the event, full program, speakers, venue, and registration link.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Case 8: Document Generation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Birthday invitation&lt;/strong&gt; : Generated a themed HTML invitation card with embedded images and QR code, published as a shareable link.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application form&lt;/strong&gt; : Built a bilingual job application form, deployed to GitHub Pages so it works when shared via WhatsApp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kindergarten applications&lt;/strong&gt; : Generated PDF application forms from templates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email signatures&lt;/strong&gt; : Set up HTML signatures in Gmail (via API) and Apple Mail.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Case 9: Workspace &amp;amp; Config Management
&lt;/h2&gt;

&lt;p&gt;OpenClaw manages its own configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version-controls its workspace files in a Git repo&lt;/li&gt;
&lt;li&gt;Adjusts its own heartbeat intervals and model settings when I ask&lt;/li&gt;
&lt;li&gt;Maintains daily memory notes and long-term memory files for context continuity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Case 10: Global Brain via PeachBase
&lt;/h2&gt;

&lt;p&gt;OpenClaw is connected to &lt;a href="https://aws.amazon.com/marketplace/pp/prodview-mmaafgzgntjhk" rel="noopener noreferrer"&gt;PeachBase&lt;/a&gt; — a serverless vector database that acts as my persistent memory across all AI agents. Via MCP, OpenClaw can store and retrieve knowledge: personal info, project decisions, contacts, learnings.&lt;/p&gt;

&lt;p&gt;When I tell OpenClaw something worth remembering, it stores it in PeachBase. When I ask a question weeks later — even from a different agent like Cursor — the knowledge is there. It's the shared brain that ties everything together.&lt;/p&gt;

&lt;p&gt;I wrote a dedicated post about this: &lt;a href="https://dev.to/peachbase-global-brain"&gt;My Global Brain with PeachBase&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Case 11: Multi-Channel Communication
&lt;/h2&gt;

&lt;p&gt;I talk to OpenClaw primarily via Telegram, but it also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sends emails on my behalf (Himalaya/SMTP)&lt;/li&gt;
&lt;li&gt;Is connected to WhatsApp&lt;/li&gt;
&lt;li&gt;Can deliver cron job results to specific Telegram chats&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Case 12: Writing This Blog Post
&lt;/h2&gt;

&lt;p&gt;Meta moment: this very blog post was drafted by OpenClaw. I sent a voice message on Telegram (in German): "I want to write about OpenClaw and how I've been using it — go through all your history and find the use cases, in English please."&lt;/p&gt;

&lt;p&gt;It scanned 23 days of daily memory notes, extracted every use case, researched the blog repo format, and produced a full draft in the right Gatsby frontmatter format — all in one turn. I just reviewed and tweaked.&lt;/p&gt;

&lt;p&gt;This is the "Factory" idea in action: I didn't write a blog post. I told my agent to write one, and it had all the context it needed because it &lt;em&gt;was there&lt;/em&gt; for every use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security: It's a Spectrum
&lt;/h2&gt;

&lt;p&gt;Giving an AI agent access to your email, contacts, calendar, and GitHub is powerful — but it's also a security conversation you need to have with yourself.&lt;/p&gt;

&lt;p&gt;OpenClaw treats security as a spectrum, not a binary. You can start wide-open for convenience and tighten as you go. Here's what's available and what I use:&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandboxing
&lt;/h3&gt;

&lt;p&gt;OpenClaw supports &lt;strong&gt;Docker-based sandboxing&lt;/strong&gt; where tool execution (shell commands, file reads/writes) runs inside an isolated container instead of directly on the host. You can choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"off"&lt;/code&gt; — everything runs on the host (my current setup, maximum convenience)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"non-main"&lt;/code&gt; — only non-main sessions (group chats, webhooks) are sandboxed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"all"&lt;/code&gt; — every session runs sandboxed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still on &lt;code&gt;"off"&lt;/code&gt; because I'm the only user and I trust the agent boundary. But if you're running OpenClaw on a shared machine or exposing it to group chats, sandboxing is a must.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool Policy
&lt;/h3&gt;

&lt;p&gt;You can allowlist or denylist specific tools per agent. For example, you could disable &lt;code&gt;exec&lt;/code&gt; (shell access) entirely and only allow &lt;code&gt;read&lt;/code&gt;/&lt;code&gt;write&lt;/code&gt; — or restrict which commands can run. OpenClaw also has an &lt;strong&gt;elevated exec&lt;/strong&gt; model (think &lt;code&gt;sudo&lt;/code&gt;) where dangerous commands require explicit approval.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;One thing I'd do differently: my early setup had API keys in config files. OpenClaw supports environment variables and secret refs instead. Move your credentials out of plaintext config.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Human-in-the-Loop Rule
&lt;/h3&gt;

&lt;p&gt;My most important security measure isn't technical — it's a rule in my agent's config: &lt;strong&gt;always ask before sending emails.&lt;/strong&gt; After one incident where the agent sent an email I hadn't approved, I added a hard rule. The agent now shows me a draft and waits for "OK" before any outbound communication. This applies to anything that "leaves the machine" — emails, social posts, webhooks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Network
&lt;/h3&gt;

&lt;p&gt;The Gateway port (18789) should never be public. Mine is localhost-only inside Docker. If you need remote access, use Tailscale or an authenticated reverse proxy with TLS.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Take
&lt;/h3&gt;

&lt;p&gt;Security with AI agents is genuinely new territory. The threat model is different from traditional apps because the agent can be influenced by external content (prompt injection via emails, web pages). OpenClaw has tools to limit blast radius — sandboxing, tool policies, approval gates — but the most important thing is being deliberate about what you give access to and expanding gradually.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;After a month of use, here's what surprised me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost optimization matters&lt;/strong&gt; : Running on Claude Opus 4.6 burns through API credits fast. Switching heartbeats to Haiku and reducing intervals from 30min to 2h made a huge difference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory is everything&lt;/strong&gt; : The daily notes + MEMORY.md system means I never have to re-explain context. It knows my projects, my contacts, my preferences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML email was harder than expected&lt;/strong&gt; : Getting Himalaya to send proper HTML emails with MML syntax took some trial and error. Plain text signatures don't have clickable links.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog post automation&lt;/strong&gt; : Using OpenClaw to help draft and publish posts (like this one!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deeper GitHub integration&lt;/strong&gt; : Auto-implementing approved plans without manual trigger&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More cron jobs&lt;/strong&gt; : Weather briefings, social media monitoring, calendar reminders&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;OpenClaw isn't just a chatbot. It's a personal operating system that happens to be powered by AI. The combination of persistent memory, tool access (email, calendar, contacts, GitHub, file system), and multi-channel communication makes it genuinely useful — not in a "cool demo" way, but in a "I saved 2 hours today" way.&lt;/p&gt;

&lt;p&gt;If you're a developer comfortable with Docker and CLI tools, I highly recommend giving it a try. The learning curve is worth it.&lt;/p&gt;




&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openclaw.ai" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;OpenClaw GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/invite/clawd" rel="noopener noreferrer"&gt;OpenClaw Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinmueller.dev/peachbase-global-brain/" rel="noopener noreferrer"&gt;PeachBase — My Global Brain (Blog Post)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🚀 &lt;strong&gt;I'm also looking for beta testers for &lt;a href="https://forms.gle/a7SErMaLMYHMnmAn7" rel="noopener noreferrer"&gt;PeachBase&lt;/a&gt;&lt;/strong&gt; — the serverless vector DB I use as shared memory across all my AI agents. If you want to try it, sign up!&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you have questions about my setup, feel free to reach out. And if you'd like help setting up your own OpenClaw AI agent — whether it's configuration, tool integration, or building custom workflows — I'm available for consulting. Just drop me a message at &lt;a href="mailto:office@martinmueller.dev"&gt;office@martinmueller.dev&lt;/a&gt; or book a call at &lt;a href="https://calendly.com/martinmueller_dev" rel="noopener noreferrer"&gt;calendly.com/martinmueller_dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>ai</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Strands TypeScript SDK - Building Production AI Agents</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/strands-typescript-sdk-building-production-ai-agents-3c6e</link>
      <guid>https://forem.com/aws-builders/strands-typescript-sdk-building-production-ai-agents-3c6e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building AI agents that work in production requires more than wrapping an LLM API. You need tool execution, streaming responses, cost management, and integration with existing systems. After evaluating several frameworks for &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;ai-secure.dev&lt;/a&gt;, I chose the &lt;a href="https://github.com/strands-agents/sdk-typescript" rel="noopener noreferrer"&gt;Strands TypeScript SDK&lt;/a&gt; from AWS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Strands over alternatives?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LangChain&lt;/td&gt;
&lt;td&gt;Feature-rich, large ecosystem&lt;/td&gt;
&lt;td&gt;Heavy, complex abstractions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;crewAI&lt;/td&gt;
&lt;td&gt;Multi-agent orchestration, role-based agents&lt;/td&gt;
&lt;td&gt;Python-focused, heavier runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw Anthropic/OpenAI API&lt;/td&gt;
&lt;td&gt;Full control&lt;/td&gt;
&lt;td&gt;Too low-level, no tool orchestration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strands SDK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight, AWS-native, streaming-first&lt;/td&gt;
&lt;td&gt;Newer, smaller community&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Strands hits the sweet spot: enough abstraction to be productive, low enough to maintain control. It's what I used to build the security audit agent behind &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;ai-secure.dev&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agent Creation Basics
&lt;/h2&gt;

&lt;p&gt;Creating an agent requires three things: a model, a system prompt, and tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Agent, tool } from '@strands-agents/sdk'
import { z } from 'zod'

const agent = new Agent({
  model, // BedrockModel or custom provider
  systemPrompt: `You are a security auditor...`,
  tools: [httpSecurityCheck, dnsLookup, browserNavigate],
})

// Invoke the agent
const response = await agent.invoke('Audit https://example.com')

// Or stream for real-time updates
for await (const event of agent.stream(prompt)) {
  // Handle events: text deltas, tool calls, metadata
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK handles the agentic loop: model generates response → tool calls extracted → tools executed → results fed back → repeat until done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Tools
&lt;/h2&gt;

&lt;p&gt;Tools are functions the agent can call. The &lt;code&gt;tool()&lt;/code&gt; helper wraps them with Zod schema validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const calculatorTool = tool({
  name: 'calculator',
  description: 'Performs arithmetic. Params: operation, a, b',
  inputSchema: z.object({
    operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
    a: z.number(),
    b: z.number(),
  }),
  callback: (input) =&amp;gt; {
    let result: number
    switch (input.operation) {
      case 'add': result = input.a + input.b; break
      case 'subtract': result = input.a - input.b; break
      // ...
    }
    return `Result: ${result}`
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For domain-specific agents, design tools around your use case. My security agent has tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http_security_check&lt;/code&gt; - Headers, TLS inspection, redirect chain&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dns_lookup&lt;/code&gt; - SPF/DMARC/CAA records&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_navigate&lt;/code&gt; - Navigate and interact with pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;totp&lt;/code&gt; - Generate 2FA codes for authenticated scans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Complex tool example&lt;/strong&gt; (abbreviated):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const httpSecurityCheckTool = tool({
  name: 'http_security_check',
  description: 'HTTP security analysis: headers, TLS cert, redirects',
  inputSchema: z.object({
    url: z.string().describe('URL to check'),
    method: z.enum(['GET', 'HEAD', 'OPTIONS']).optional(),
    includeTls: z.boolean().optional(),
  }),
  callback: async (input) =&amp;gt; {
    // Make request, inspect TLS socket, check headers
    const securityHeaders = ['strict-transport-security', 'content-security-policy', ...]
    // ... implementation
    return JSON.stringify({ url, statusCode, securityHeaders, tls })
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tools are the agent's "hands" - design them for your domain, not as generic utilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Model Provider
&lt;/h2&gt;

&lt;p&gt;The SDK includes &lt;code&gt;BedrockModel&lt;/code&gt; for AWS Bedrock, but you can create custom providers. I built &lt;code&gt;AnthropicModel&lt;/code&gt; for direct Anthropic API access with features like message caching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class AnthropicModel {
  constructor(config: AnthropicModelConfig) {
    this.client = new Anthropic({ apiKey: config.apiKey })
    this.config = {
      modelId: config.modelId || 'claude-sonnet-4-5-20250929',
      maxTokens: config.maxTokens || 16000,
      enableMessageCaching: config.enableMessageCaching ?? true,
    }
  }

  async *stream(messages, options) {
    // Convert messages to Anthropic format
    // Add cache_control blocks for cost reduction
    // Yield SDK-compatible events
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Message caching&lt;/strong&gt; reduces costs by 90% on repeated context. Add &lt;code&gt;cache_control&lt;/code&gt; to strategic messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Cache system prompt (reused every call)
request.system = [{
  type: 'text',
  text: systemPrompt,
  cache_control: { type: 'ephemeral', ttl: '1h' }
}]

// Cache last tool definition
tools[tools.length - 1].cache_control = { type: 'ephemeral', ttl: '1h' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cost tracking&lt;/strong&gt; built into the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MODEL_PRICING = {
  'claude-sonnet-4-5-20250929': { input: 3.00, output: 15.00, cacheRead: 0.30 },
  'claude-haiku-4-5-20251001': { input: 1.00, output: 5.00, cacheRead: 0.10 },
}

function calculateCost(modelId, inputTokens, outputTokens, cacheReadTokens) {
  const pricing = MODEL_PRICING[modelId]
  return (inputTokens * pricing.input + outputTokens * pricing.output 
          + cacheReadTokens * pricing.cacheRead) / 1_000_000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Model Routing for Cost Optimization
&lt;/h2&gt;

&lt;p&gt;Not every request needs your most powerful model. Route simple tasks to cheaper models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function classifyTask(prompt: string) {
  const lower = prompt.toLowerCase()

  // Complex patterns → Sonnet
  const complexPatterns = [
    /security|vulnerabil|audit/i,
    /iso\s*27001|compliance/i,
    /investigate|analyze|assess/i,
  ]

  // Simple patterns → Haiku (10x cheaper)
  const simplePatterns = [
    /^(hi|hello|hey)/i,
    /^(thanks|thank\s*you)/i,
    /^(yes|no|ok)/i,
  ]

  for (const pattern of complexPatterns) {
    if (pattern.test(prompt)) {
      return { complexity: 'complex', model: 'claude-sonnet-4-5' }
    }
  }

  for (const pattern of simplePatterns) {
    if (pattern.test(lower)) {
      return { complexity: 'simple', model: 'claude-haiku-4-5' }
    }
  }

  // URLs always complex (security audits need full power)
  if (prompt.includes('http://') || prompt.includes('https://')) {
    return { complexity: 'complex', model: 'claude-sonnet-4-5' }
  }

  return { complexity: 'complex', model: 'claude-sonnet-4-5' } // Default safe
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log cost comparisons in production to validate routing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📊 Tokens: 15420 in, 2341 out | $0.0812 (sonnet-4-5)
   Alternative: $0.4102 (opus-4-5) → +$0.329 (+405%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Streaming Architecture
&lt;/h2&gt;

&lt;p&gt;For real-time UX, stream agent events via Server-Sent Events (SSE):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/invocations', async (req, res) =&amp;gt; {
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')

  const sendEvent = (type, data) =&amp;gt; {
    res.write(`data: ${JSON.stringify({ type, ...data })}\n\n`)
  }

  for await (const event of agent.stream(prompt)) {
    // Text streaming
    if (event.type === 'modelContentBlockDeltaEvent') {
      const delta = event.delta
      if (delta?.type === 'textDelta') {
        sendEvent('text', { content: delta.text })
      }
    }

    // Tool execution tracking
    if (event.type === 'modelContentBlockStartEvent') {
      const start = event.start
      if (start?.type === 'toolUseStart') {
        sendEvent('tool_start', { tool: start.name })
      }
    }

    if (event.type === 'afterToolsEvent') {
      sendEvent('tool_end', { tool: currentTool })
    }

    // Token usage
    if (event.type === 'modelMetadataEvent') {
      totalTokens += event.usage?.totalTokens || 0
    }
  }

  sendEvent('done', { usage: { totalTokens } })
  res.end()
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key event types:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modelContentBlockDeltaEvent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text/tool input streaming&lt;/td&gt;
&lt;td&gt;Real-time display&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modelContentBlockStartEvent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool call begins&lt;/td&gt;
&lt;td&gt;Show "Analyzing..."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;afterToolsEvent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tool finished&lt;/td&gt;
&lt;td&gt;Show result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;modelMetadataEvent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tokens counted&lt;/td&gt;
&lt;td&gt;Cost tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  OpenAI-Compatible Adapter
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why build this?&lt;/strong&gt; During development, I needed to chat with my agent without building a UI first. By exposing an OpenAI-compatible endpoint, I could use &lt;a href="https://github.com/cline/cline" rel="noopener noreferrer"&gt;Cline&lt;/a&gt; (VS Code extension) as my interface - instant chat UI for free.&lt;/p&gt;

&lt;p&gt;This let me iterate on tools and prompts rapidly before touching frontend code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function createOpenAIAdapter(config) {
  const router = Router()

  router.get('/v1/models', (_, res) =&amp;gt; {
    res.json({
      data: [{ id: config.modelName, owned_by: 'strands-agents' }]
    })
  })

  router.post('/v1/chat/completions', async (req, res) =&amp;gt; {
    const { messages, stream } = req.body
    const prompt = extractPromptFromMessages(messages)

    const { agent } = config.createAgent()

    if (stream) {
      // Stream SSE chunks in OpenAI format
      res.setHeader('Content-Type', 'text/event-stream')
      for await (const event of agent.stream(prompt)) {
        // Convert to OpenAI chunk format
        res.write(`data: ${JSON.stringify(chunk)}\n\n`)
      }
      res.write('data: [DONE]\n\n')
    } else {
      // Collect and return
      const response = await agent.invoke(prompt)
      res.json({ choices: [{ message: { content: response } }] })
    }
  })

  return router
}

// Mount the adapter
app.use(createOpenAIAdapter({ modelName: 'security-agent', createAgent }))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now point Cline at &lt;code&gt;http://localhost:8080/v1&lt;/code&gt; and it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Tips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Session management with TTL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sessions = new Map&amp;lt;string, Session&amp;gt;()
const SESSION_TTL_MS = 30 * 60 * 1000 // 30 min

setInterval(() =&amp;gt; {
  const now = Date.now()
  for (const [id, session] of sessions) {
    if (now - session.lastAccessedAt &amp;gt; SESSION_TTL_MS) {
      sessions.delete(id)
    }
  }
}, 60 * 1000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Issue tracking during scans:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const issueTrackerTool = tool({
  name: 'issue_tracker',
  description: 'Track problems during audit: auth failures, timeouts, etc.',
  inputSchema: z.object({
    type: z.enum(['auth_failed', 'access_denied', 'timeout', 'credentials_required']),
    title: z.string(),
    description: z.string(),
  }),
  callback: (input) =&amp;gt; {
    session.issues.push(input)
    return `Issue tracked: ${input.title}`
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include issues in the final report so users know what couldn't be tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Frontend │────▶│ Agent Server │────▶│ Tools │
│ (Next.js) │ │ (Strands SDK) │ │ (http, dns, │
└─────────────┘ │ │ │ browser) │
      ▲ └────────┬─────────┘ └─────────────┘
      │ │
   SSE Events ┌────▼────┐
                        │ Model │
                        │ Provider│
                        └─────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cost comparison (per 1M tokens):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Cache Read&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Haiku 4.5&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$0.10&lt;/td&gt;
&lt;td&gt;Simple queries, greetings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sonnet 4.5&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;td&gt;$15.00&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;Security audits, analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Opus 4.5&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$25.00&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;Complex reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With routing + caching, typical security audit costs ~$0.08-0.15 vs $0.40+ without.&lt;/p&gt;

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

&lt;p&gt;The Strands TypeScript SDK provides a solid foundation for building production AI agents. Key takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tools are everything&lt;/strong&gt; - Design domain-specific tools, not generic utilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache aggressively&lt;/strong&gt; - Message caching saves 90% on repeated context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route by complexity&lt;/strong&gt; - Not every request needs your best model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stream for UX&lt;/strong&gt; - Users need to see progress during long operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track costs&lt;/strong&gt; - Log token usage and compare models in production&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The SDK handles the agentic loop so you can focus on domain logic. For &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;ai-secure.dev&lt;/a&gt;, that meant security analysis - not prompt engineering infrastructure.&lt;/p&gt;




&lt;p&gt;Questions or building your own agent? Connect on &lt;a href="https://www.linkedin.com/in/martinmueller88/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>agents</category>
      <category>strands</category>
      <category>typescript</category>
    </item>
    <item>
      <title>AWS Bedrock AgentCore - AI Agent Development from Local to Cloud</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Sat, 17 Jan 2026 00:00:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-bedrock-agentcore-ai-agent-development-from-local-to-cloud-2bp1</link>
      <guid>https://forem.com/aws-builders/aws-bedrock-agentcore-ai-agent-development-from-local-to-cloud-2bp1</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building production-ready AI agents requires more than just a prompt and an LLM. You need infrastructure for state management, tool execution, and secure deployment. I recently built &lt;strong&gt;&lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;https://ai-secure.dev&lt;/a&gt;&lt;/strong&gt;, a SaaS for automated security compliance audits, using &lt;strong&gt;AWS Bedrock AgentCore&lt;/strong&gt;. This post explores how AgentCore simplifies the transition from a local prototype to a scalable cloud agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AWS Bedrock AgentCore?
&lt;/h2&gt;

&lt;p&gt;AgentCore is AWS's managed runtime for AI agents. Think of it as "Fargate for AI agents" - you bring your container, AWS handles scaling, networking, and infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Container-based&lt;/strong&gt; : Package agent as Docker image, push to ECR, deploy via CDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC networking&lt;/strong&gt; : Agents run in private subnets with NAT for outbound (Anthropic API, target websites)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AgentCore Browser&lt;/strong&gt; : Managed Chromium browser for web automation - no Playwright/Puppeteer infra needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory&lt;/strong&gt; : Built-in conversation memory across sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt; : SSE responses for real-time progress updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;https://ai-secure.dev&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A security compliance scanner that uses an AI agent to audit websites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User submits URL + selects security compliance framework (ISO 27001, NIST, SOC2, COBIT)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp22aczq4eeaiq7fhkr5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp22aczq4eeaiq7fhkr5b.png" alt="New Scan" width="800" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent navigates site using AgentCore Browser, shows real-time progress&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiimavrufy61qa895ubr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiimavrufy61qa895ubr7.png" alt="Scan Progress" width="800" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scan completes with summary&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e9cbwsqsix1t0obaiuf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e9cbwsqsix1t0obaiuf.png" alt="Scan Finished" width="800" height="814"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User views detailed report with findings + recommendations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r9n0cc7beoew0dr5ik5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r9n0cc7beoew0dr5ik5.png" alt="Scan Report" width="800" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐     ┌────────────────────┐     ┌─────────────┐
│ Frontend    │────▶│ AgentCore          │────▶│ AgentCore   │
│ (Next.js)   │     │ Runtime │ │ Browser│     |             |
└─────────────┘     │ (your container)   │     │ (Chromium)  │
                    └────────────────────┘     └─────────────┘
                           │
                     ┌─────▼──────┐
                     │ Claude     │
                     │ (Anthropic)│
                     └────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/strands-agents/sdk-typescript" rel="noopener noreferrer"&gt;Strands TypeScript SDK&lt;/a&gt; + Claude (Anthropic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser&lt;/td&gt;
&lt;td&gt;AgentCore Browser (cloud) / Playwright MCP (local)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js 16 + React 19 + Tailwind (Hosted on AWS ECS Fargate)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;AWS Cognito&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra&lt;/td&gt;
&lt;td&gt;AWS CDK → AWS ECS Fargate + AWS Bedrock AgentCore Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payments&lt;/td&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Agent tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;browser_navigate&lt;/code&gt;, &lt;code&gt;browser_snapshot&lt;/code&gt;, &lt;code&gt;browser_click&lt;/code&gt;, &lt;code&gt;browser_type&lt;/code&gt; - web automation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http_security_check&lt;/code&gt; - headers, TLS, redirects&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dns_lookup&lt;/code&gt; - SPF/DMARC/CAA records&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;totp&lt;/code&gt; - 2FA code generation for authenticated scans&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;issue_tracker&lt;/code&gt; - tracks problems during scan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Model routing for cost optimization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Simple tasks → Haiku (~10x cheaper)
// Complex tasks → Sonnet
const classification = classifyTask(prompt)
const model = classification.complexity === 'simple' 
  ? 'claude-haiku-4-5' 
  : 'claude-sonnet-4-5'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Agent development:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't build an agent from scratch initially. Validate your use case works with existing agents (Cursor, Claude Code, Kiro). If they can't solve it, your custom agent probably won't either. Your goal: beat them on speed, accuracy, cost for your specific domain.&lt;/li&gt;
&lt;li&gt;Develop locally first using Docker. Same container runs locally and in AgentCore.&lt;/li&gt;
&lt;li&gt;Use Server-Sent Events (SSE) streaming for real-time progress - users need to see what the agent is doing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Browser automation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AgentCore Browser is game-changer. No more managing Playwright/Puppeteer infrastructure.&lt;/li&gt;
&lt;li&gt;For local dev, use existing MCP Docker images (playwright-mcp). Your custom implementation won't be better.&lt;/li&gt;
&lt;li&gt;Browser tools need good error handling - pages don't always load, elements move, auth flows vary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost optimization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model routing: Haiku for greetings/simple queries, Sonnet for audits&lt;/li&gt;
&lt;li&gt;Message caching: 90% cost reduction on repeated context&lt;/li&gt;
&lt;li&gt;Disable extended thinking unless needed (~$0.15/call)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production gotchas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AgentCore logs are separate from your app logs. Use CloudWatch SDK directly for application logging.&lt;/li&gt;
&lt;li&gt;Use Infrastructure as Code (IaC) to deploy your agent to AgentCore.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;AWS Bedrock AgentCore provides a robust foundation for building complex, stateful AI agents. By offloading the heavy lifting of runtime management and browser infrastructure to AWS, I was able to focus on the core logic of &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;https://ai-secure.dev&lt;/a&gt; - creating high-quality security audits - rather than debugging infrastructure. If you're building agents that need to browse the web or maintain long-term state, AgentCore is a powerful accelerator.&lt;/p&gt;

&lt;p&gt;Please give me feedback on &lt;a href="https://www.linkedin.com/in/martinmueller88/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;. Either if you find the &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;https://ai-secure.dev&lt;/a&gt; useful and how I can make it better or if you need help with your own AI agent project. More and more builders are curious about how I build and deploy AI Agents in more specifics. So let me know if you would interested in more blog posts around this or perhaps a workshop/course around how I build my AI Agents?!&lt;/p&gt;

&lt;p&gt;As well I think my next post will be about &lt;a href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; as I'm exploring those to start another AI Agent idea where I want the agent helping me with marketing for &lt;a href="https://ai-secure.dev" rel="noopener noreferrer"&gt;https://ai-secure.dev&lt;/a&gt;. So make sure to subscribe to my blog posts &lt;a href="https://martinmueller.dev/rss.xml" rel="noopener noreferrer"&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>agents</category>
      <category>agentcore</category>
      <category>bedrock</category>
    </item>
    <item>
      <title>SST, AWS CDK, AWS CloudFormation migration to Terraform</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Mon, 26 Aug 2024 15:15:41 +0000</pubDate>
      <link>https://forem.com/mmuller88/sst-aws-cdk-aws-cloudformation-migration-to-terraform-390o</link>
      <guid>https://forem.com/mmuller88/sst-aws-cdk-aws-cloudformation-migration-to-terraform-390o</guid>
      <description>&lt;p&gt;Recently, a client approached me with an intriguing request: to migrate their SST (v.2 &lt;a href="https://v2.sst.dev/" rel="noopener noreferrer"&gt;https://v2.sst.dev/&lt;/a&gt;) project to Terraform. This task presents several interesting challenges, particularly in mapping SST and CDK constructs to equivalent Terraform resources. Additionally, certain components like Lambda functions and static S3 React buckets require extra attention, as they involve uploading additional files such as Lambda function code or React builds.&lt;/p&gt;

&lt;p&gt;Although I performed this migration specifically for SST, the process I'll outline is equally applicable to AWS CDK or AWS CloudFormation projects. If you're facing a similar challenge, this blog post aims to provide you with a solid starting point and valuable insights into the migration process.&lt;/p&gt;

&lt;p&gt;First, let's discuss some possible motivations and non-motivations for migrating from SST, AWS CDK, or AWS CloudFormation to Terraform.&lt;/p&gt;

&lt;p&gt;After that, I will describe the migration process in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivations
&lt;/h2&gt;

&lt;p&gt;My client's motivation was rather passive. As a newcomer to AWS, he used SST (v.2) to quickly deploy his application, which worked well for him. However, his company eventually decided to standardize on Terraform instead of SST. This highlights alignment as a motivation: if your company is using Terraform, you probably should too.&lt;/p&gt;

&lt;p&gt;Another commonly cited reason is Terraform's superior state management. While Terraform deployments are faster than SST or AWS CDK, the difference doesn't feel significant to me.&lt;/p&gt;

&lt;p&gt;There are undoubtedly more motivations; feel free to mention important ones I may have missed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-Motivations
&lt;/h2&gt;

&lt;p&gt;The following are reasons I wouldn't consider as motivations for migrating to Terraform:&lt;/p&gt;

&lt;p&gt;The biggest one is the degree of abstraction, like as for the CDK Constructs. You would lose that when migrating to Terraform. Defining many basic resources in Terraform isn't particularly enjoyable. To counter this somewhat, you can use an AI tool in your IDE to speed up the definition process. As well, Terraform modules give you a way to abstract definitions to modules, but it doesn't feel the same as the CDK Constructs.&lt;/p&gt;

&lt;p&gt;As SST and AWS CDK are already TypeScript-based, they make it really easy to define and handle the lifecycle of Lambda functions. In Terraform, you need to define helper functions to manage the Lambda function lifecycle, such as bundling the code, creating the deployment package, and so on. This was actually quite painful for my project, and I ended up with this rather inelegant script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda_function_payload.zip
&lt;span class="nb"&gt;cd&lt;/span&gt; ../functions
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist
npx tsc
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/ dist/node_modules
&lt;span class="nb"&gt;cd &lt;/span&gt;dist
zip &lt;span class="nt"&gt;-rFS&lt;/span&gt; lambda_function_payload.zip &lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; lambda_function_payload.zip ../../../terraform
&lt;span class="nb"&gt;cd&lt;/span&gt; ../../../terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This feels ugly and was no fun.&lt;/p&gt;

&lt;p&gt;Kind of similar is the deployment of a React app into a S3 bucket. In SST and AWS CDK you can use the &lt;code&gt;Bucket&lt;/code&gt; construct and let the framework handle the deployment. In Terraform, you need to manually deploy the React app to the S3 bucket and then invalidate the CloudFront cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;p&gt;In the next section, I will describe how the migration from SST, AWS CDK, or AWS CloudFormation to Terraform works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Deploy
&lt;/h2&gt;

&lt;p&gt;Before you start the migration, make sure your deployment works as expected. If you have an SST project, follow the instructions for deploying it like &lt;code&gt;npx sst deploy&lt;/code&gt;. After deploying, check the functionality of the CloudFormation stacks. That is super important as that will be your comparison to the Terraform deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Generate Terraform from the CloudFormation template
&lt;/h2&gt;

&lt;p&gt;Now that your deployment from step 1 created at least one CloudFormation stack, you can start the migration.&lt;/p&gt;

&lt;p&gt;Via the AWS Console, grab each generated stack and let a chat AI like Claude from anthropic.com or ChatGPT.com generate Terraform from the CloudFormation template. The prompt could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Resources": {
    "CustomResourceHandlerServiceRole41AEC181": {
      "Type": "AWS::IAM::Role",
      ...
}

Change the AWS CloudFormation to Terraform. Give back the full Terraform code!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through the output from all those answers into the main.tf file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Cleanup the main.tf
&lt;/h2&gt;

&lt;p&gt;Great now we have all the AWS resources in the main.tf file. But unlucky we have resources in the main.ts file which we don't want or which are not useful like those AWS CDK helper resources &lt;code&gt;CustomResourceHandlerServiceRole...&lt;/code&gt; and &lt;code&gt;CustomResourceHandler...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As well, there are might be other resources which you might consider removing. For example, if you have several stacks and used variable referencing between the CloudFormation stacks, the AI translation to Terraform usually uses &lt;code&gt;aws_ssm_parameter&lt;/code&gt; Terraform resources to replace them. But since all your resources are in one main.tf file, you don't need &lt;code&gt;aws_ssm_parameter&lt;/code&gt; and simply reference resources directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Make the Lambda's working
&lt;/h2&gt;

&lt;p&gt;Yeah, now comes the tricky part with making the Lambda's working. Look this totally depends on your project structure like where is the Lambda function code located and how is it structured. I think good advice is to keep it similar as possible to your SST or AWS CDK project. As well, use the &lt;code&gt;source_code_hash&lt;/code&gt; to make sure the Lambda function code is hashed and the Lambda function is recreated if the code changes. Like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"my_lambda_function"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;source_code_hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filebase64sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"my_lambda_function.zip"&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;What is left is the script to bundle the Lambda function code to the &lt;code&gt;my_lambda_function.zip&lt;/code&gt; file. For you, it could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; lambda_function_payload.zip
&lt;span class="nb"&gt;cd&lt;/span&gt; ../functions
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist &lt;span class="c"&gt;# the tsconfig.json has an "outDir": "./dist" configured where all the compiled js files will be stored&lt;/span&gt;
npx tsc &lt;span class="c"&gt;# compile the TypeScript code&lt;/span&gt;
npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; node_modules/ dist/node_modules
&lt;span class="nb"&gt;cd &lt;/span&gt;dist
zip &lt;span class="nt"&gt;-rFS&lt;/span&gt; lambda_function_payload.zip &lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; lambda_function_payload.zip ../../terraform
&lt;span class="nb"&gt;cd&lt;/span&gt; ../../terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the Lambda function and check with the AWS Console if it is working as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Make the S3 React bucket working
&lt;/h2&gt;

&lt;p&gt;Sure this is described for a React App but any other SPA or static site will work the same. First, you need to build the React app like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../frontend
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the build to the s3 bucket and invalidate the cloudfront cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xyz-react-site-bucket
&lt;span class="nv"&gt;DISTRIBUTION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;EZ8RBY8ZM1234
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;dist/ s3://&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt;

aws cloudfront create-invalidation &lt;span class="nt"&gt;--distribution-id&lt;/span&gt; &lt;span class="nv"&gt;$DISTRIBUTION_ID&lt;/span&gt; &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="s2"&gt;"/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Phew, that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Validating
&lt;/h2&gt;

&lt;p&gt;Validate the Terraform deployment with your reference deployment from step 1. Usually like when you have an Api Gateway our Lambdas, make sure they are working as expected when comparing the two deployments. The same goes for the S3 React bucket App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Considerations
&lt;/h2&gt;

&lt;p&gt;Sure, storing all those AWS resources into one main.tf file isn't Terraform best practice, but it is totally a starting point. If you made sure that your Terraform deployment is working, feel free to split the main.tf file into multiple files, as you are used to.&lt;/p&gt;

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

&lt;p&gt;Migrating SST to Terraform was interesting. With the power of AI, it was a quick process. Combined with my years of experience, I was able to quickly migrate. If you have a question or need otherwise help, please reach out to me.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>cdk</category>
      <category>migration</category>
    </item>
    <item>
      <title>AWS Bedrock Update from Claude v2.1 to Claude v3</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Sun, 21 Apr 2024 15:33:46 +0000</pubDate>
      <link>https://forem.com/mmuller88/aws-bedrock-update-from-claude-v21-to-claude-v3-3kle</link>
      <guid>https://forem.com/mmuller88/aws-bedrock-update-from-claude-v21-to-claude-v3-3kle</guid>
      <description>&lt;p&gt;Recently, AWS released Claude v3. It comes with the &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/03/anthropics-claude-3-haiku-model-amazon-bedrock/" rel="noopener noreferrer"&gt;Haiku&lt;/a&gt; and &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/03/anthropics-claude-3-sonnet-model-amazon-bedrock/" rel="noopener noreferrer"&gt;Sonnet&lt;/a&gt; flavors. Both are big improvements over the previous version. We recently updated our &lt;a href="https://martinmueller.dev/arcbot-eng" rel="noopener noreferrer"&gt;arcBot&lt;/a&gt; to use Claude v3 Sonnet and are very impressed with the results. Not only are the responses more intelligent, but the speed of the responses is much faster. In the next section I will describe how an update can go as smoothly as it did for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade
&lt;/h2&gt;

&lt;p&gt;As mentioned above, we upgraded from Claude v2.1 to Claude v3 Sonnet for our gen AI database tool &lt;a href="https://martinmueller.dev/arcbot-eng" rel="noopener noreferrer"&gt;arcBot&lt;/a&gt; (Try it out!).&lt;/p&gt;

&lt;p&gt;When you do this there is a high risk that your answers will not be as good as before. But we were prepared and the answers are even better now. But how did we do it?&lt;/p&gt;

&lt;p&gt;Simply with &lt;a href="https://martinmueller.dev/aws-bedrock-unit-testing" rel="noopener noreferrer"&gt;Unit Testing&lt;/a&gt;. I wrote a bunch of unit tests during the development with Claude v2.1 and they came in handy for the update to v3. It was basically just a matter of making those unit tests pass.&lt;/p&gt;

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

&lt;p&gt;Using AWS gen AI offerings like Claude is super fun. It can be challenging to make sure your answers are still as good as before. But with good test coverage, you can make sure that your answers are still as good as before, or even better. With our unit tests in place, we are confident that we can easily upgrade to a future version again, or if we wanted, change the LLM entirely. If you have any questions or thoughts about this, feel free to contact us :)!&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus - AB Picturer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Faws-bedrock-update%2Fab-picturer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Faws-bedrock-update%2Fab-picturer.png" alt="drawing" width="800" height="628"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Did you notice the cool blog title picture? It is actually one of two randomly selected pictures. I love writing blog posts and choosing nice pictures for them. But often I want to choose THE BEST picture. So to find the best picture I'm using AB Testing. If you are curious about it, have a look at my &lt;a href="https://martinmueller.dev/ab-picturer" rel="noopener noreferrer"&gt;AB Picturer Tool&lt;/a&gt; and provide me feedback or even better become an engaged tester :).&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>anthropic</category>
      <category>llm</category>
    </item>
    <item>
      <title>AI Softwareentwicklung mit Ninox und arcBot</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Fri, 16 Feb 2024 18:15:33 +0000</pubDate>
      <link>https://forem.com/mmuller88/ai-softwareentwicklung-mit-ninox-und-arcbot-4e40</link>
      <guid>https://forem.com/mmuller88/ai-softwareentwicklung-mit-ninox-und-arcbot-4e40</guid>
      <description>&lt;p&gt;KI beflügelt die Unternehmenslandschaft. Viele einfache und komplexe Prozesse im Unternehmen können durch AI stark vereinfacht oder sogar komplett automatisiert werden. Jakob mein Co-Founder und ich sehen es als spannende Herausforderung, wie wir den Zugang zu AI für kleine und mittelständische Unternehmen vereinfachen können. Wir glauben mit der Low Code Plattform &lt;a href="https://ninox.com" rel="noopener noreferrer"&gt;Ninox&lt;/a&gt; und unserem AI Chatbot &lt;a href="https://app.arcbot.de" rel="noopener noreferrer"&gt;arcBot&lt;/a&gt; einen spannenden Ansatz gefunden zu haben. In diesem Blogpost möchte ich euch diesen Ansatz vorstellen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ninox.com
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ninox.com" rel="noopener noreferrer"&gt;Ninox.com&lt;/a&gt; ist eine cloudbasierte Low-Code-Plattform zum Bau von Softwarelösungen für kleine und mittelständische Unternehmen. Ninox wurde 2013 in Deutschland von &lt;a href="https://www.linkedin.com/in/frank-boehmer/" rel="noopener noreferrer"&gt;Frank Böhmer&lt;/a&gt; gegründet. Hier einige Möglichkeiten, wie Ninox Unternehmen unterstützen kann:&lt;/p&gt;

&lt;p&gt;Automatisierung der Geschäftsprozesse: Mit Ninox können Unternehmen ihre täglichen Aufgaben und Prozesse automatisieren und so ihre Effizienz steigern.&lt;/p&gt;

&lt;p&gt;Anpassbarkeit: Da Ninox eine Low-Code-Plattform ist, können Unternehmen ihre Anwendungen ohne komplexe Programmierkenntnisse an ihre spezifischen Bedürfnisse anpassen.&lt;/p&gt;

&lt;p&gt;Integration: Ninox kann mit einer Vielzahl anderer Tools und Plattformen integriert werden, was einen nahtlosen Datenaustausch zwischen verschiedenen Systemen ermöglicht.&lt;/p&gt;

&lt;p&gt;Kosteneffizienz: Verglichen mit der Entwicklung einer benutzerdefinierten Software von Grund auf, kann die Verwendung einer Low-Code-Plattform wie Ninox zu erheblichen Kosteneinsparungen führen.&lt;/p&gt;

&lt;p&gt;Schnelle Implementierung: Mit Ninox können Unternehmen ihre Anwendungen schnell erstellen und implementieren, was zu einer schnelleren Markteinführung führt.&lt;/p&gt;

&lt;p&gt;Vor einigen Monaten habe ich mit Ninox ein MVP erstellt. Meine gesammelten Erfahrungen können &lt;a href="https://martinmueller.dev/ninox-mvp" rel="noopener noreferrer"&gt;hier&lt;/a&gt; nachgelesen werden. Im nächsten Kapitel erkläre ich, wie wir mit arcBot den Build für deine Softwarelösung in ninox vereinfachen.&lt;/p&gt;

&lt;h2&gt;
  
  
  arcBot
&lt;/h2&gt;

&lt;p&gt;Der arcBot ist eine ChatGPT-ähnliche KI zum schnellen Erstellen und Modifizieren von Ninox-Tabellen. Wir benutzen die AWS Bedrock API um die Ninox Tabellen Antworten zu erstellen. Ein Beispiel Prompt zum erstellen von Ninox Tabellen könnte wie folgt aussehen:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Create customer and product tables in a many to many relationship.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2FcreateTable.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2FcreateTable.gif" alt="Zeichnung" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool oder? arcBot erstellt viele Tabellen für Kunden und Produkte. Bemerkenswert ist, dass arcBot sogar die ninox-typischen inversen Felder setzen kann. Im nächsten Prompt wollen wir den Last Name zu einem Customer hinzufügen.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Add last name to customer table&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2FaddLastName.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2FaddLastName.gif" alt="drawing" width="1051" height="1095"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Für die Antwort verwendet arcBot das &lt;a href="https://docs.anthropic.com/claude/docs/claude-2p1-guide" rel="noopener noreferrer"&gt;Claude v2.1 model&lt;/a&gt;, das über die &lt;a href="https://docs.aws.amazon.com/bedrock/" rel="noopener noreferrer"&gt;AWS Bedrock API&lt;/a&gt; zur Verfügung gestellt wird.&lt;/p&gt;

&lt;p&gt;Wie stellen wir sicher, dass die Qualität der Antworten so hoch wie möglich ist? Wir haben eine Feedbackschleife eingebaut. Der Nutzer kann nach der Antwort Feedback geben. Dieses Feedback wird dann zur Verbesserung von arcBot verwendet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Ffeedback1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Ffeedback1.png" alt="Zeichnung" width="800" height="297"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Ffeedback2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Ffeedback2.png" alt="Zeichnung" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ich stehe auch in engem Kontakt mit anderen Gen AI Experten, die mir helfen, die Qualität der arcBot Antworten zu verbessern. An dieser Stelle möchte ich mich bei diesen Experten bedanken. Es sind hauptsächlich AWS Community Mitglieder und AWS Mitarbeiter die mir schon viele wertvolle Tipps gegeben haben. Vielen Dank 🙏&lt;/p&gt;

&lt;h2&gt;
  
  
  Ninox Connector
&lt;/h2&gt;

&lt;p&gt;Es ist cool, dass wir mit arcBot Ninox Tabellen erstellen können, aber wie können wir die Daten in Ninox weiterverarbeiten? Hier kommt der Ninox Connector ins Spiel. Der Ninox Connector kann Ninox Tabellen lesen und aktualisieren. Das folgende Bild zeigt den Ninox Connector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Fninox-connector.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fraw%2Fmaster%2Fcontent%2Farcbot%2Fninox-connector.png" alt="Zeichnung" width="800" height="1223"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Der Ninox Connector benötigt einige Informationen wie die Ninox Url, Team Id, Database Id und den Ninox API Key. Zurzeit is der Ninox Connector nur über den Early Access verfügbar. Dieser ist aber ganz leicht auf &lt;a href="https://app.arcbot.de/" rel="noopener noreferrer"&gt;https://app.arcbot.de/&lt;/a&gt; zu bekommen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discord Community
&lt;/h2&gt;

&lt;p&gt;Werdet Teil unserer &lt;a href="https://discord.gg/MMWZSHSrEQ" rel="noopener noreferrer"&gt;Discord Community&lt;/a&gt;. Wir haben bereits einige Member welche uns wertvolles Feedback und Feature Wünsche zu dem arcBot mitteilen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;In diesem Blogpost habe ich euch gezeigt, wie wir durch die Kombination von Ninox und arcBot das Erstellen und Aktualisieren von Ninox-Tabellen vereinfachen. Wir freuen uns auf euer Feedback. Vielen Dank fürs Lesen.&lt;/p&gt;

&lt;p&gt;Ich liebe es, an Open-Source-Projekten zu arbeiten. Viele Dinge kannst du bereits frei nutzen auf &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;github.com/mmuller88&lt;/a&gt;. Wenn du meine Arbeit dort und meine Blog-Posts toll findest, denke doch bitte darüber nach, mich zu unterstützen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oder&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Buy me a Ko-Fi" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Und schau doch mal auf meiner Seite vorbei&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ninox</category>
      <category>ai</category>
      <category>database</category>
    </item>
    <item>
      <title>Create a Next.js Server Component S3 Picture Uploader with SST</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Thu, 04 Jan 2024 09:44:04 +0000</pubDate>
      <link>https://forem.com/aws-builders/create-a-nextjs-server-component-s3-picture-uploader-with-sst-14d4</link>
      <guid>https://forem.com/aws-builders/create-a-nextjs-server-component-s3-picture-uploader-with-sst-14d4</guid>
      <description>&lt;p&gt;I recently started exploring &lt;a href="https://github.com/sst/sst" rel="noopener noreferrer"&gt;SST&lt;/a&gt; as an alternative to my favorite full-stack set consisting of &lt;a href="https://github.com/projen/projen" rel="noopener noreferrer"&gt;Projen&lt;/a&gt;, &lt;a href="https://github.com/aws/aws-cdk" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;, and React. I have been thoroughly impressed with the experience so far. In this article, I will demonstrate how to create a Next.js App Router S3 Picture Uploader using SST.&lt;/p&gt;

&lt;h2&gt;
  
  
  SST
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/sst/sst" rel="noopener noreferrer"&gt;SST&lt;/a&gt; is a powerful framework that simplifies the development of serverless applications. It offers a straightforward and opinionated approach to defining serverless apps using TypeScript. Built on top of AWS CDK, SST handles the complexity of setting up your serverless infrastructure automatically. SST is an open-source framework and is completely free to use.&lt;/p&gt;

&lt;p&gt;SST offers a variety of powerful constructs, including the NextjsSite construct. In the following section, I will provide more details about the NextjsSite construct, which greatly simplifies the process of deploying your frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  NextjsSite
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.sst.dev/constructs/NextjsSite" rel="noopener noreferrer"&gt;NextjsSite&lt;/a&gt; construct allows you to effortlessly create and manage &lt;a href="https://github.com/sst/open-next" rel="noopener noreferrer"&gt;open-next&lt;/a&gt;, which is a great alternative for hosting Next.js on Vercel. Being defined within an SST App, you can easily integrate other AWS services, making it incredibly powerful. However, if you're using the latest and most advanced features of Next.js, such as Server Components with the Next.js App Router, you may encounter some challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Components
&lt;/h2&gt;

&lt;p&gt;Server Components are a new way to build with Next.js. They allow you to write parts of your application using React components that are served from the server. This approach offers several advantages, such as faster page loading and simplified setup without the need to manage client states with useState, useEffect, and similar hooks. However, working with Server Components may require adjusting your workflow and learning new concepts.&lt;/p&gt;

&lt;h2&gt;
  
  
  S3 Picture Uploader
&lt;/h2&gt;

&lt;p&gt;In this section, I will demonstrate how to create an S3 picture uploader using the Next.js App Router and SST. We will utilize the NextjsSite construct to create the Next.js App Router and the S3 construct to create the S3 Bucket.&lt;/p&gt;

&lt;p&gt;You can find all the code in my &lt;a href="https://github.com/mmuller88/sst-nextjs-s3-picture-uploader" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialize SST NextjsSite
&lt;/h3&gt;

&lt;p&gt;All the steps are taken from the official &lt;a href="https://docs.sst.dev/start/nextjs" rel="noopener noreferrer"&gt;SST guide&lt;/a&gt;.&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-next-app@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mainly choose the defaults. Then switch to the app folder and open it via VS Code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;SST_PROJECT&amp;gt;
code &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run:&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-sst@latest
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before deploying to your AWS Account, ensure that you have set up the correct credentials. I recommend using the AWS Identity Service to obtain temporary AWS CLI credentials, but you can also set up IAM User credentials or profiles. Once you have the credentials in place, run the following 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 sst deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure that the AWS deployment is successful! View your CloudFront SiteUrl shown in the SST Output. Voila! You now have a running Next.js application on AWS with open-next 🤯. Let's take a closer look at what has been deployed in our AWS account because it's quite extensive!&lt;/p&gt;

&lt;p&gt;To inspect the resources created by SST, go to the AWS Console and navigate to CloudFormation. Click on the newly created stack to view the details. You will find a set of helper Lambda Functions, the Lambda Function and Lambda URL for the Server Component, a CloudFront Distribution, and an S3 bucket that serves the static Next.js files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add S3 Picture Uploader
&lt;/h3&gt;

&lt;p&gt;Ok let's go! We need to add an S3 bucket where we can upload the pictures to. Go to the &lt;code&gt;sst.config.ts&lt;/code&gt; file and add a Bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SSTConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sst&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextjsSite&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sst/constructs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_input&lt;/span&gt;&lt;span class="p"&gt;)&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sst-nextjs-s3-picture-uploader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="nf"&gt;stacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Site&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextjsSite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;site&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;

   &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOutputs&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;SiteUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;satisfies&lt;/span&gt; &lt;span class="nx"&gt;SSTConfig&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;permissions: [bucket]&lt;/code&gt; property gives the NextjsSite read and write access for the S3 bucket. With &lt;code&gt;bind: [bucket]&lt;/code&gt; You can use SST node variables in your React code like &lt;code&gt;Bucket.public.bucketName&lt;/code&gt;. Let's update the page.tsx for adding the upload button and S3 AWS SDK upload code. You might need to run &lt;code&gt;npm run dev&lt;/code&gt; so that the SST node variable &lt;code&gt;Bucket.public.bucketName&lt;/code&gt; gets recognized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sst/node/bucket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PutObjectCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-s3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;upload&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="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No file uploaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PutObjectCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;ACL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public-read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Uploaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to S3`&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="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex min-h-screen flex-col items-center justify-between p-24"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"image/png, image/jpeg"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Do you see how seaminess we can sneak in the AWS SDK s3 upload code? For me, that is mind-blowing if you like to compare it with a client-side variant where you couldn't do that so easily without exposing your AWS API credentials. But as server components are server side we are saved. It offloads a lot of complexity. Super cool!&lt;/p&gt;

&lt;p&gt;Let's deploy that! For more convenience let's add a new command in the package json &lt;code&gt;"deploy": "sst deploy",&lt;/code&gt;. Now run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the CloudFront SiteUrl. Now click on the upload button and check if you can see the file in S3.&lt;/p&gt;

&lt;p&gt;BTW. for faster development you could also run locally with &lt;code&gt;npm run dev&lt;/code&gt; but make sure to load your AWS CLI credentials before which allowing access to the S3 Bucket.&lt;/p&gt;

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

&lt;p&gt;I'm still super flashed at how nicely SST is orchestrating the frontend with the backend. In this post, I described how you can start with SST and how to create an S3 picture uploader. I hope you learned something new. If you liked my post or want to correct me please reach out to me :).&lt;/p&gt;

&lt;p&gt;I am passionate about contributing to Open Source projects. You can find many of my projects on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; that you can already benefit from.&lt;/p&gt;

&lt;p&gt;If you found this post valuable and would like to show your support, consider supporting me back. Your support will enable me to write more posts like this and work on projects that provide value to you. You can support me by:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Pateron" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmartinmueller.dev%2Fstatic%2F84caa5292a6d0c37c48ae280d04b5fa6%2Fa7715%2Fjoint.jpg" alt="drawing" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>sst</category>
      <category>devops</category>
      <category>s3</category>
    </item>
    <item>
      <title>How to Perform Unit Testing on your AWS Bedrock AI Lambda</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Sat, 30 Dec 2023 13:58:48 +0000</pubDate>
      <link>https://forem.com/aws-builders/how-to-perform-unit-testing-on-your-aws-bedrock-ai-lambda-3phn</link>
      <guid>https://forem.com/aws-builders/how-to-perform-unit-testing-on-your-aws-bedrock-ai-lambda-3phn</guid>
      <description>&lt;p&gt;Using the AWS Bedrock API for MVPs is incredibly enjoyable! I recently wrote an article on how you can make the LLM Claude respond in JSON. You can check it out &lt;a href="https://martinmueller.dev/aws-bedrock-validation" rel="noopener noreferrer"&gt;here&lt;/a&gt;. While it's a lot of fun, testing your LLM settings and prompts can become tiresome and frustrating. In this article, I will explain how you can effectively unit test your Lambda function that calls the AWS Bedrock API. By being able to unit test your prompts, you can iterate quickly towards your desired MVP or project state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda Unit Testing
&lt;/h2&gt;

&lt;p&gt;Unit testing a Lambda function is straightforward since it is essentially a function that executes code. To test it, you can simply call the function with the necessary arguments and verify the response. If you are using Node.js, Jest provides a convenient way to run your test code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda Streaming Response &amp;amp; Unit Testing
&lt;/h2&gt;

&lt;p&gt;The Lambda Streaming Response allows to leverage the streaming response from your LLM. Using the streaming response from your LLM is important as it gives your early feedback. That is pretty much what is happening when ChatGPT gives your those word by word streaming response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda
&lt;/h2&gt;

&lt;p&gt;Here I show you a bit from the Lambda Function I want to unit test later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&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;APIGatewayProxyEventV2WithRequestContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayEventRequestContextV2&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;responseStream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambdaStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResponseStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Context&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ninoxTables&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bedrockParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InvokeModelCommandInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;modelId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anthropic.claude-v2:1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`\n\nHuman: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\nAssistant:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`bedrockParams: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bedrockParams&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvokeModelWithResponseStreamCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bedrockParams&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;//InvokeModelWithResponseStreamCommandOutput&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="nx"&gt;responseStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&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;h2&gt;
  
  
  Unit Testing
&lt;/h2&gt;

&lt;p&gt;And here is the unit test in a file living next to my Lambda function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ArcbotLambdaInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/arcbot-stack.stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userInput: How is the weather?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;How is the weather?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="c1"&gt;//@ts-ignore&lt;/span&gt;
    &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// do some validation&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;I do not understand. Please rephrase!&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create a customer table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;mockEvent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create a customer table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="c1"&gt;//@ts-ignore&lt;/span&gt;
    &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// do some validation&lt;/span&gt;
  &lt;span class="nf"&gt;commonExpects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NinoxTable&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`body: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NinoxTableSchema&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ArcbotLambdaInput&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIGatewayProxyEventV2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ArcbotLambdaInput&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;event&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Unit testing your Lambda function that calls the AWS Bedrock API is crucial. It enables you to iterate quickly towards your desired MVP or project state. I hope this article has been helpful to you. If you have any questions or feedback, please don't hesitate to reach out to me.&lt;/p&gt;

&lt;p&gt;I am passionate about contributing to Open Source projects. You can find many of my projects on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; that you can already benefit from.&lt;/p&gt;

&lt;p&gt;If you found this post valuable and would like to show your support, consider supporting me back. Your support will enable me to write more posts like this and work on projects that provide value to you. You can support me by:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Pateron" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>ai</category>
      <category>lambda</category>
    </item>
    <item>
      <title>My first Experience with Powertools for AWS Lambda (TypeScript)</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Sun, 24 Dec 2023 14:16:50 +0000</pubDate>
      <link>https://forem.com/mmuller88/my-first-experience-with-powertools-for-aws-lambda-typescript-lp3</link>
      <guid>https://forem.com/mmuller88/my-first-experience-with-powertools-for-aws-lambda-typescript-lp3</guid>
      <description>&lt;p&gt;Hi there,&lt;/p&gt;

&lt;p&gt;I'm new to &lt;a href="https://docs.powertools.aws.dev/lambda/typescript/latest/" rel="noopener noreferrer"&gt;Powertools for AWS Lambda (TypeScript)&lt;/a&gt;. Though I hear a lot of my DevOps friends say that it's a great tool for building serverless applications, I've never had the chance to use it myself. So I decided to give it a try and see what all the fuss is about. What can I say, I totally love it. In the next sections I will explain what the.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Powertools for AWS Lambda
&lt;/h2&gt;

&lt;p&gt;Powertools for AWS Lambda is a collection of utilities, patterns, and best practices for writing AWS Lambda functions in Python, Typescript, Java and DotNet. It includes logging, tracing, custom metrics, and more. The goal of this project is to enable developers to build scalable and robust serverless applications easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am using AWS Lambda Powertools
&lt;/h2&gt;

&lt;p&gt;I'm a huge DevOps fanboy so I'm all over for techniques like Infrastructure as Code (IaC) or Serverless. Pretty cool about those that it helps me to focus on my business logic of my software product rather then wasting time in setting it up. Powertools feels pretty similar as it helps me to easy implement certain goodies as logging or tracing without the need to spend much time.&lt;/p&gt;

&lt;p&gt;Certainly my main motivation is the Powertools logger library. The logger library helps you to write my Lambda logs in specific format to enable features like log levels and log queries with AWS CloudWatch Logs Insights. So far that is amazingly great and it gives me a powerful insight into my Lambda.&lt;/p&gt;

&lt;p&gt;I'm using Powertools for me newest AI MVP. Part of the MVP is a Lambda where I call the AWS Bedrock API. Furthermore I automatically validate the LLM response. For more details see &lt;a href="https://martinmueller.dev/aws-bedrock-validation" rel="noopener noreferrer"&gt;https://martinmueller.dev/aws-bedrock-validation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to start?
&lt;/h2&gt;

&lt;p&gt;The Powertools GitHub repository offers nice examples to integrate and learn from. For example &lt;a href="https://github.com/aws-powertools/powertools-lambda-typescript" rel="noopener noreferrer"&gt;here&lt;/a&gt; are nice example written in AWS CDK. Following I will list the features I gained some experience with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logger
&lt;/h2&gt;

&lt;p&gt;The logger is super fun to use! It gives me a powerful insight into my lambdas. Though I'm still not super sure when to use the different log levels and when and what objects I should put into the object logger part. But I will keep learning from my DevOps friends and make my own experience. Ultimately I will know better when I truly need insight via Logs Insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parameters
&lt;/h2&gt;

&lt;p&gt;The parameters feature is super cool. It helps me to easily access the parameters I defined in my AWS CDK stacks. I can access them via the &lt;code&gt;get&lt;/code&gt; function. So far I only used it for getting secrets from the secrets manager but I'm super excited to use it for more like perhaps app configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency
&lt;/h2&gt;

&lt;p&gt;The idempotency feature seems super useful. It could help to reduce the quite high costs of using the AWS Bedrock API. But unfortunately idempotency isn't currently combinable with Lambda stream response. Hopefully that will change in future 🤞.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources to learn utilizing Powertools
&lt;/h2&gt;

&lt;p&gt;Here's a list of examples I found helpful to learn how to apply Powertools:&lt;/p&gt;

&lt;p&gt;Examples from the GitHub Repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-powertools/powertools-lambda-typescript" rel="noopener noreferrer"&gt;https://github.com/aws-powertools/powertools-lambda-typescript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Powertools has ana amazing Discord community. Make sure to not miss it!:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://discord.gg/82CkmT97ja" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/leegilmorecode" rel="noopener noreferrer"&gt;Lee Gilmore&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/leegilmorecode/embedded-aws-cloudwatch-dashboards/tree/main" rel="noopener noreferrer"&gt;https://github.com/leegilmorecode/embedded-aws-cloudwatch-dashboards/tree/main&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Working with AWS Lambda Powertools Typescript totally make sense and I love it. It will definitely be my default choice when developing my next Lambda. I still need to learn how to use it properly! Please if you have any feedback how I can utilize Powertools better, reach out to me!&lt;/p&gt;

&lt;p&gt;I am passionate about contributing to Open Source projects. You can find many of my projects on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; that you can already benefit from.&lt;/p&gt;

&lt;p&gt;If you found this post valuable and would like to show your support, consider supporting me back. Your support will enable me to write more posts like this and work on projects that provide value to you. You can support me by:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Pateron" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnwjl3sera1.execute-api.us-east-1.amazonaws.com%2F%3Fcb%3Drq_rand%284%29" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnwjl3sera1.execute-api.us-east-1.amazonaws.com%2F%3Fcb%3Drq_rand%284%29" alt="TitlePic" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>devops</category>
      <category>powertools</category>
    </item>
    <item>
      <title>Automatically validate your AWS Bedrock LLM Responses</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Mon, 18 Dec 2023 06:55:41 +0000</pubDate>
      <link>https://forem.com/aws-builders/automatically-validate-your-aws-bedrock-llm-responses-4fhe</link>
      <guid>https://forem.com/aws-builders/automatically-validate-your-aws-bedrock-llm-responses-4fhe</guid>
      <description>&lt;p&gt;Validating the response from your Language Learning Model (LLM) is a critical step in the development process. It ensures that the response is in the correct format and contains the expected data. Manual evaluation can quickly become tiresome, especially when making frequent changes to your LLM. Automating or partially automating the validation process is highly recommended to save time and effort. In this post, I will discuss and demonstrate some ideas how you can achieve this automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before
&lt;/h2&gt;

&lt;p&gt;When I refer to LLM, I am talking about the use of existing foundation models through AWS Bedrock, such as Claude, LLama2, and others. You can learn more about AWS Bedrock &lt;a href="https://aws.amazon.com/bedrock/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. There are techniques you can use to enhance the response, such as prompt refinements, RAG (Retrieval Augmentation using Vector Databases), or fine-tuning.&lt;/p&gt;

&lt;p&gt;Responses from Language Learning Models (LLMs) are often non-deterministic, meaning that different responses can be generated even with the same prompt. However, this behavior can be adjusted to some extent using LLM parameters such as temperature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ideas
&lt;/h2&gt;

&lt;p&gt;In the following sections I will present some ideas for creating automated tests for your LLM responses. I will also provide some examples of how I implemented these ideas in my own projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate the Shape
&lt;/h3&gt;

&lt;p&gt;In many cases, the response may contain deterministic parts that can be used to partially validate it. For instance, I rely on Claude to provide a JSON response. I have taught Claude the schema of the JSON response, and by performing a schema validation test, I can verify if Claude adheres to the schema. Verifying a JSON schema is very simple.&lt;/p&gt;

&lt;p&gt;Each programming language has a library that can be used to validate the schema. For instance, in TypeScript, I use the &lt;a href="https://github.com/colinhacks/zod" rel="noopener noreferrer"&gt;zod library&lt;/a&gt; to create and validate the schema. Which looks like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NinoxFieldSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictObject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;'&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;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;captions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NinoxField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;NinoxFieldSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NinoxTableSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictObject&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nextFieldId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;captions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NinoxTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;NinoxTableSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And as part of my unit tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;check schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="p"&gt;...&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NinoxTableSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;h3&gt;
  
  
  Validate Sub-Responses
&lt;/h3&gt;

&lt;p&gt;In my current AI application, I utilize multiple LLM calls to generate the final response. While validating the entire response may be challenging, I can easily validate some of the sub-responses. For instance, I have a deterministic response for which I can verify the response. The deterministic response classifies a user's intent into a specific category. For example, if the user asks to create a table, the intent is classified as "create_table". That will generate a deterministic sub-response in my AWS Lambda for the "create_table" intent. To test the accuracy of the classification, you can use well-known methods such as train-validation-test Split the training data into subsets for training and validation. I'll describe this technique more in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Train-Validation-Test Split
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://medium.com/@evertongomede/the-significance-of-train-validation-test-split-in-machine-learning-91ee9f5b98f3" rel="noopener noreferrer"&gt;train-validation-test split&lt;/a&gt; is very crucial to messsure the performance of the LLM. One methods of those splits is the k-fold cross validation. I try to explain this approach in easy words. Make sure to check the far more technical artical from &lt;a href="https://medium.com/@evertongomede/the-significance-of-train-validation-test-split-in-machine-learning-91ee9f5b98f3" rel="noopener noreferrer"&gt;Everton Gomede, PhD, The Significance of Train-Validation-Test Split in Machine Learning&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;For instance, you could use 90 percent of the data for training and 10 percent for validation. Then, you can use the validation data to test the accuracy of the classification. Additionally, you can use permutation to shift the 10 percent of the validation data. I implemented a simple algorithm in TypeScript which helps me to calculate the accuracy of the classification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@jest/globals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ArcbotStackStream&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/arcbot-stack.stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;call_bedrock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;generate_intent_identification_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;generate_table_identification_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;modify_table_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;relationship_json_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/arcbot-stack.stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;intentTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;modifyTableTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;oneToManyTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;tableIdentificationData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../src/training-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runEvaluation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&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;trainingData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;jestSpy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SpyInstance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="kr"&gt;any&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;promptRefinement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTrainingAndEvaluationPermutations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;trainingsData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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="c1"&gt;// Split trainings data into training and evaluation data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sliceTrainingsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;fromPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;toPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
   &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trainingsData&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evaluationSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fromPercentage&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;toPercentage&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trainingSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;evaluationSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&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="na"&gt;training&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;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;training&lt;/span&gt;&lt;span class="p"&gt;,&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="nx"&gt;trainingSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;evaluations&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;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluations&lt;/span&gt;&lt;span class="p"&gt;,&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt; &lt;span class="nx"&gt;evaluationSlice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;training&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;evaluations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trainingPercentage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validationPercentage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;trainingPercentage&lt;/span&gt;

  &lt;span class="c1"&gt;// permute the training and evaluation data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trainingValidationPermutations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="nf"&gt;sliceTrainingsData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;validationPercentage&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;validationPercentage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;validationPercentage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;trainingValidationPermutations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;sliceTrainingsData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;validationPercentage&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;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="s2"&gt;`trainingValidationPermutations: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;trainingValidationPermutations&lt;/span&gt;
   &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;trainingValidationPermutations&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;correctResponses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrongResponses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trainingRecords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTrainingAndEvaluationPermutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trainingData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trainingPermutation&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;trainingRecords&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`trainingPermutation=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trainingPermutation&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;jestSpy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mockImplementation&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;trainingPermutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;training&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;evaluationRecords&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;trainingPermutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evaluations&lt;/span&gt;
  &lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;evaluationRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intent_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;promptRefinement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_bedrock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intent_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;

    &lt;span class="c1"&gt;// trim to JSON string&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;received&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Expected &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;evaluationRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;\nReceived &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;received&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evaluationRecords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;received&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;correctResponses&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;wrongResponses&lt;/span&gt;&lt;span class="o"&gt;++&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;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;` correctResponses: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;correctResponses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n wrongResponses: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;wrongResponses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; \n &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
   &lt;span class="nx"&gt;correctResponses&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;correctResponses&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;wrongResponses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; accuracy`&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evaluate one to many&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ArcbotStackStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getOneToManyTrainingData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oneToManyPromptRefinement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;relationship_json_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;CUSTOMER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;EMPLOYEE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Employee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;INVOICE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="nx"&gt;userInput&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runEvaluation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;oneToManyTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;oneToManyPromptRefinement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evaluate intent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ArcbotStackStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getIntentTrainingData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generate_intent_identification_promptRefinement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;generate_intent_identification_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runEvaluation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;intentTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;generate_intent_identification_promptRefinement&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evaluate table identification&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ArcbotStackStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getTableIdentificationData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generate_table_identification_promptRefinement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_table_identification_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tableIdentificationData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runEvaluation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;tableIdentificationData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;generate_table_identification_promptRefinement&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evaluate modify table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;ArcbotStackStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getModifyTableTrainingsData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="nx"&gt;modifyTableTrainingsData&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modify_table_promptRefinement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;modify_table_prompt&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runEvaluation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;modifyTableTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;mockTrainingsData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;modify_table_promptRefinement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;true&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;I think the most interesting part here is the method interface &lt;code&gt;getTrainingAndEvaluationPermutations(trainingData)&lt;/code&gt; as that always expect the same format as input and gives you back a permuted test validation split of the input training data. The training data have to be in record string list shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the key represents the expected result / classification class / LLM output and the value represents possible inputs which leads to the result. The result will be this type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;training&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;
 &lt;span class="nx"&gt;evaluations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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;It is an Array representing the permutations. Each permutation has a &lt;code&gt;training&lt;/code&gt; and &lt;code&gt;evaluations&lt;/code&gt; slice.&lt;/p&gt;

&lt;p&gt;One traing data example would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;intentTrainingsData&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;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;create_new_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create table to store invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need to store my customers information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need a table for my employees&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;modify_existing_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customers table should also have an address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add address to the customer table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice should have a date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;link_two_tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer should have multiple invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Each employee should be responsible for multiple customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;do_not_know&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How are you today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is your name?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather today?&lt;/span&gt;&lt;span class="dl"&gt;"&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;This training set is to teach the model the intent recognition of the user. The permuted training validation would looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;training&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;create_new_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create table to store invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need to store my customers information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;modify_existing_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customers table should also have an address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add address to the customer table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;link_two_tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer should have multiple invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;do_not_know&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How are you today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is your name?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2 + 3&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="na"&gt;evaluations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;create_new_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need a table for my employees&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;modify_existing_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice should have a date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;link_two_tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer should have multiple invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Each employee should be responsible for multiple customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;do_not_know&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How are you today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is your name?&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;training&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;create_new_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create table to store invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need to store my customers information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need a table for my employees&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;modify_existing_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customers table should also have an address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add address to the customer table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice should have a date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;link_two_tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Each employee should be responsible for multiple customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;do_not_know&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How are you today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is your name?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather today?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2 + 3&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="na"&gt;evaluations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;create_new_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create table to store invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need to store my customers information&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I need a table for my employees&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;modify_existing_table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customers table should also have an address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add address to the customer table&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice should have a date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;link_two_tables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Customer should have multiple invoices&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Each employee should be responsible for multiple customers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;do_not_know&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What is the weather today?&lt;/span&gt;&lt;span class="dl"&gt;"&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;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;h3&gt;
  
  
  Golden Response
&lt;/h3&gt;

&lt;p&gt;This is an idea from the AI community that shows promise. Although I haven't personally tested it yet, the concept is to compare the response with a "golden response" to ensure its correctness. The golden response can be compared with the actual response with the same Language Learning Model (LLM). We can then determine if they are identical or very similar. This approach holds potential and I'm eager to try it out soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;I would like to express my gratitude to the AWS Community for their invaluable assistance in helping me.&lt;/p&gt;

&lt;p&gt;A special thanks goes to &lt;a href="https://www.linkedin.com/in/chris-t-miller" rel="noopener noreferrer"&gt;Chris Miller&lt;/a&gt; for giving me a lot of thoughts and feedback on my validation approach. &lt;a href="https://www.linkedin.com/in/neylsoncrepalde/" rel="noopener noreferrer"&gt;Neylson Crepalde&lt;/a&gt; for making me aware and explaining the golden response validation method.&lt;/p&gt;

&lt;p&gt;Once again, thank you all for your support and contributions.&lt;/p&gt;

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

&lt;p&gt;Working with AWS Bedrock AI is incredibly enjoyable. The field is constantly evolving, and there is always something new to learn. In this post, I explained how to partly validate your LLM responses.&lt;/p&gt;

&lt;p&gt;I hope you found this post helpful, and I look forward to sharing more with you in the future.&lt;/p&gt;

&lt;p&gt;I love to work on Open Source projects. A lot of my stuff you can already use on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;https://github.com/mmuller88&lt;/a&gt; . If you like my work there and my blog posts, please consider supporting me on the:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Buy me a Ko-Fi" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>ai</category>
      <category>validation</category>
    </item>
    <item>
      <title>AWS Bedrock Claude 2.1 - Return only JSON</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Thu, 07 Dec 2023 18:49:36 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-bedrock-claude-21-return-only-json-4pfk</link>
      <guid>https://forem.com/aws-builders/aws-bedrock-claude-21-return-only-json-4pfk</guid>
      <description>&lt;p&gt;Working with the AWS Bedrock API is an exhilarating experience! I came across an interesting business case where I needed to develop an AI MVP. The MVP generates JSON data based on a prompt and utilizes the &lt;a href="https://docs.anthropic.com/claude/docs" rel="noopener noreferrer"&gt;anthropic.claude-v2:1&lt;/a&gt; model in &lt;a href="https://aws.amazon.com/bedrock" rel="noopener noreferrer"&gt;AWS Bedrock&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I encountered an issue where the response I received was not pure JSON. It contained additional characters that I couldn't remove like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;" format: {\"one\":\"Supplier\",\"many\":\"Time\"}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeking help from the AWS Community, I was able to find a solution to this problem. In this post, I will share the solution with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Human: $YOUR_PROMPT . Answer in JSON formatAssistant:{,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This technique is known as "Put words in Claude’s mouth". It involves providing a prompt to Claude and letting it generate the rest of the response on its own. While there may be alternative approaches to solving this issue, I am currently happy with this solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;I would like to express my gratitude to the AWS Community for their invaluable assistance in helping me resolve this issue.&lt;/p&gt;

&lt;p&gt;A special thanks goes to &lt;a href="https://www.linkedin.com/in/corvus/" rel="noopener noreferrer"&gt;Corvus Lee&lt;/a&gt; for providing the advice that ultimately solved the problem.&lt;/p&gt;

&lt;p&gt;I would also like to thank &lt;a href="https://www.linkedin.com/in/metaskills/" rel="noopener noreferrer"&gt;Ken Collins&lt;/a&gt; for bringing the &lt;a href="https://docs.google.com/presentation/d/1tjvAebcEyR8la3EmVwvjC7PHR8gfSrcsGKfTPAaManw" rel="noopener noreferrer"&gt;Claude 2 docs sheet&lt;/a&gt; to my attention.&lt;/p&gt;

&lt;p&gt;Once again, thank you all for your support and contributions.&lt;/p&gt;

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

&lt;p&gt;Working with AWS Bedrock AI is incredibly enjoyable. The field is constantly evolving, and there is always something new to learn. In this post, I demonstrated how to obtain a pure JSON response from AWS Bedrock Claude 2.1. As AI technology continues to advance rapidly, you may not encounter this issue in the future. However, if you are working with Claude 2.1 or newer, be sure to refer to the &lt;a href="https://docs.anthropic.com/claude/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;I hope you found this post helpful, and I look forward to sharing more with you in the future.&lt;/p&gt;

&lt;p&gt;I love to work on Open Source projects. A lot of my stuff you can already use on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;https://github.com/mmuller88&lt;/a&gt; . If you like my work there and my blog posts, please consider supporting me on the:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Buy me a Ko-Fi" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>claude</category>
    </item>
    <item>
      <title>Java Spring CI/CD in AWS - The HalloCasa Journey</title>
      <dc:creator>Martin Muller 🇩🇪🇧🇷🇵🇹</dc:creator>
      <pubDate>Thu, 07 Dec 2023 18:39:57 +0000</pubDate>
      <link>https://forem.com/mmuller88/java-spring-cicd-in-aws-the-hallocasa-journey-13dn</link>
      <guid>https://forem.com/mmuller88/java-spring-cicd-in-aws-the-hallocasa-journey-13dn</guid>
      <description>&lt;p&gt;A fresh start can be daunting, especially when it comes to revamping an existing Java Spring application. This was my journey with the HalloCasa real estate platform, and here's how we took it from deployment nightmares to smooth, cloud-native operations. If you would like to improve your Java Spring CI/CD experience as well. Reach out to me!&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing HalloCasa
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hallocasa.com" rel="noopener noreferrer"&gt;HalloCasa&lt;/a&gt; is a dynamic Real Estate Platform designed to allow  real estate agents to create their own business card within a couple of minutes. It also serves as a directory and a referral network.&lt;/p&gt;

&lt;p&gt;My motivations to work on HalloCasa are that I had the chance to contribute to the development of a powerful product. My expertise in AWS could be applied effectively.&lt;br&gt;
Working with a co-founder who was not only business-savvy but also had a knack for generating compelling content in the real estate area. He has his own podcast &lt;a href="https://blog.hallocasa.com/podcasts/" rel="noopener noreferrer"&gt;https://blog.hallocasa.com/podcasts/&lt;/a&gt;. Make sure to check it out!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deployment Dilemma
&lt;/h2&gt;

&lt;p&gt;In the beginning, our deployment process could only be described as patchwork:&lt;/p&gt;

&lt;p&gt;Setting up Maven, Tomcat, and MySQL locally. Creating a war file and then SSHing into an EC2 VM to transfer it. Manually restarting the Tomcat server each time.&lt;/p&gt;

&lt;p&gt;Our NextJS frontend wasn't much different. We had to SSH into our EC2 VM and manually run “npm start”. And to make matters a bit more convoluted, our frontend was in BitBucket while the backend resided in AWS CodeCommit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying the New and Improved Way
&lt;/h2&gt;

&lt;p&gt;Here’s how I revamped our deployment:&lt;/p&gt;

&lt;h3&gt;
  
  
  Shifting to GitHub
&lt;/h3&gt;

&lt;p&gt;Migrated from AWS CodeCommit and BitBucket to GitHub.&lt;br&gt;
Leveraged GitHub Actions for test builds and introduced a &lt;a href="https://github.com/anc95/ChatGPT-CodeReview" rel="noopener noreferrer"&gt;PR AI reviewer&lt;/a&gt; for efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerization
&lt;/h3&gt;

&lt;p&gt;Deployed our configuration as code, which is both clean and manageable. With docker-compose, local development became hassle-free. Gone were the days of setting up Maven, Tomcat, and MySQL locally.&lt;br&gt;
Further utilized this to feed AWS ECS, making deployments consistent. Additionally health checks were introduced to ensure that the application is up and running. If the health check fails, the application will restart and in most cases that fixes the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  CDK Pipelines
&lt;/h3&gt;

&lt;p&gt;Implemented a CI/CD staging pipeline that first deploys changes onto QA and subsequently to PROD. Ensured robustness by integrating testing with Postman. We setup a dedicated AWS account for each stage, ensuring clear demarcation and management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fpipeline.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fpipeline.png%3Fraw%3Dtrue" alt="Pipeline" width="720" height="2238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  DB Migration with Flyway
&lt;/h3&gt;

&lt;p&gt;Adopted Flyway to empower our Java App to manage the database, eliminating the need to provide access to the database for schema modifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fdbmigration.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fdbmigration.png%3Fraw%3Dtrue" alt="dbmigration" width="606" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring Metrics
&lt;/h3&gt;

&lt;p&gt;Introduced plenty of metrics to monitor our backend. For an informed decision on crucial metrics, I sought advice from ChatGPT about the most essential metrics for a Java application.&lt;br&gt;
Our revamped production setup went live on 01/08/23, and the migration was seamless.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fmonitoring.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmmuller88%2Fmmblog%2Fblob%2Fmaster%2Fcontent%2Faws-spring%2Fmonitoring.png%3Fraw%3Dtrue" alt="Monitoring" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflecting on the Journey
&lt;/h2&gt;

&lt;p&gt;The refurbishment of HalloCasa reaffirmed several beliefs:&lt;/p&gt;

&lt;p&gt;Docker is a lifesaver. The portability and consistency it brings to applications are unparalleled. CI/CD is a game-changer. It automates manual tasks and ensures faster, reliable deliveries. AWS CDK is powerful. It simplifies cloud resource provisioning and management. CDK Pipelines streamline deployments. With it, managing multiple stages of deployment becomes a breeze. One revelation that stood out was how the lines between infrastructure and application seemed blurred in our setup. And this convergence was a positive one, indicating tight integration and consistency.&lt;/p&gt;

&lt;p&gt;The HalloCasa journey is a testament to the power of continuous learning, adaptation, and innovation. Our users now enjoy a more robust platform, and we can sleep better at night, knowing that our deployment is smoother than ever. If you have a Java application which needs a bit polishing, reach out to me!&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post and I look forward to seeing you in the next one.&lt;/p&gt;

&lt;p&gt;I love to work on Open Source projects. A lot of my stuff you can already use on &lt;a href="https://github.com/mmuller88" rel="noopener noreferrer"&gt;https://github.com/mmuller88&lt;/a&gt; . If you like my work there and my blog posts, please consider supporting me on the:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ko-fi.com/T6T1BR59W" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6uulkfn093g044mmvc6q.jpg" alt="Buy me a Ko-Fi" width="606" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/bePatron?u=29010217" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2m5uxtzh1so3y31q999.jpg" alt="Buy me a Ko-Fi" width="500" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And don't forget to visit my site&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinmueller.dev/resume" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vhsl3eps3qryy8jzgg7.jpg" alt="martinmueller.dev" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
