<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kayne Rodrigo</title>
    <description>The latest articles on DEV Community by Kayne Rodrigo (@kayne_rodrigo).</description>
    <link>https://dev.to/kayne_rodrigo</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2761386%2F9b61e566-1deb-4039-bf88-be9907a111f4.png</url>
      <title>DEV Community: Kayne Rodrigo</title>
      <link>https://dev.to/kayne_rodrigo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kayne_rodrigo"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Kayne Rodrigo</dc:creator>
      <pubDate>Tue, 18 Aug 2026 13:49:10 +0000</pubDate>
      <link>https://dev.to/kayne_rodrigo/-3396</link>
      <guid>https://dev.to/kayne_rodrigo/-3396</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://dev.to/aws-builders/amazon-bedrock-service-tiers-priority-vs-flex-42dk" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgxdb2r26n1r8uu2d28rd.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://dev.to/aws-builders/amazon-bedrock-service-tiers-priority-vs-flex-42dk" rel="noopener noreferrer" class="c-link"&gt;
            Amazon Bedrock Service Tiers: Priority vs Flex - DEV Community
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            The Problem: Every Request Gets the Same Tier   When I first saw Bedrock's new service...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" 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%2F8j7kvp660rqzt99zui8e.png"&gt;
          dev.to
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Amazon Bedrock Service Tiers: Priority vs Flex</title>
      <dc:creator>Kayne Rodrigo</dc:creator>
      <pubDate>Tue, 18 Aug 2026 13:47:06 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-bedrock-service-tiers-priority-vs-flex-42dk</link>
      <guid>https://dev.to/aws-builders/amazon-bedrock-service-tiers-priority-vs-flex-42dk</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Every Request Gets the Same Tier
&lt;/h2&gt;

&lt;p&gt;When I first saw Bedrock's new service tiers, I thought the decision was going to be&lt;br&gt;
pretty straightforward: use Priority when latency matters, Default for normal traffic,&lt;br&gt;
and Flex for anything that can wait.&lt;/p&gt;

&lt;p&gt;But before putting that logic into a router, I wanted to answer a simpler question:&lt;br&gt;
does Priority actually make my requests faster?&lt;/p&gt;

&lt;p&gt;The pricing table makes the case seem obvious — Priority costs roughly 1.75× Default,&lt;br&gt;
Flex costs roughly 0.5× Default. The implication baked into that pricing is that you&lt;br&gt;
get something proportional in return. I didn't want to build a router on top of that&lt;br&gt;
assumption without testing it first. So I tested it.&lt;/p&gt;

&lt;p&gt;The results changed how I thought about the whole problem.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why This Experiment, Why Now
&lt;/h2&gt;

&lt;p&gt;AWS documentation tells you what each tier is intended for. Pricing tells you the&lt;br&gt;
obvious economic incentive. But neither tells you what happens with your specific&lt;br&gt;
workload, region, and traffic pattern.&lt;/p&gt;

&lt;p&gt;I didn't want to build another "Flex is for batch, Priority is for real-time" demo.&lt;br&gt;
I wanted to see what actually happened when I ran real requests through all three&lt;br&gt;
tiers, modeled the economics across different traffic distributions, and built a&lt;br&gt;
routing layer I could actually read, reason about, and change without guessing at&lt;br&gt;
the consequences.&lt;/p&gt;

&lt;p&gt;That is what led to this project: a measurable, reproducible experiment rather than&lt;br&gt;
a confident assumption presented as a tutorial.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architecture: Four Small Modules, One Clean Pipeline
&lt;/h2&gt;

&lt;p&gt;I intentionally kept the router boring.&lt;/p&gt;

&lt;p&gt;No LangChain. No LLM classifier. No abstraction layers. Just four small Python&lt;br&gt;
modules doing one thing each: validate the workload label, choose a tier, check&lt;br&gt;
whether that tier is available, and return a decision. I wanted to look at any&lt;br&gt;
routing outcome and immediately understand why it happened.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;classify → policy → capability check → routing decision
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4no58rgsubh9jpu3rf5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn4no58rgsubh9jpu3rf5.png" alt="aws-bedrock-service-tier-architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Component responsibilities:&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;Module&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;router/classifier.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates and normalizes workload category strings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;router/policy.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maps workload categories to preferred service tiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;router/capability.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks tier availability; selects deterministic fallbacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;router/router.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Orchestrates classify → policy → capability → decision&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why four modules instead of one function?&lt;/strong&gt; I split the concerns because the&lt;br&gt;
policy is the part I expect to change most often. By isolating it in a frozen&lt;br&gt;
dataclass, I can swap in a different policy — database-backed, A/B split,&lt;br&gt;
per-tenant — without touching the capability checker or the orchestrator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS services used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock Runtime&lt;/strong&gt; (&lt;code&gt;bedrock-runtime&lt;/code&gt;) — the &lt;code&gt;converse&lt;/code&gt; API with the
&lt;code&gt;serviceTier&lt;/code&gt; parameter. This is how you specify which tier to use; omitting
it defaults to Default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;boto3&lt;/strong&gt; — the AWS SDK for Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why not a framework?&lt;/strong&gt; The routing logic is a pure decision function. Adding&lt;br&gt;
LangChain or a similar framework would add indirection without adding value. The&lt;br&gt;
router is a component that plugs into your existing Bedrock call, not a&lt;br&gt;
replacement for it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Implementation Walkthrough
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Validate the workload category
&lt;/h3&gt;

&lt;p&gt;I wanted invalid workload labels to fail early rather than silently becoming a&lt;br&gt;
potentially expensive routing decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# router/classifier.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;CRITICAL&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;INTERACTIVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;interactive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;BACKGROUND&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;background&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;classify_workload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload_category&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;InvalidWorkloadCategoryError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Workload category is required.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;workload_category&lt;/span&gt;

    &lt;span class="n"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;InvalidWorkloadCategoryError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown workload category: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Expected one of: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;str&lt;/code&gt; enum base class means a &lt;code&gt;WorkloadCategory&lt;/code&gt; behaves like a string in&lt;br&gt;
comparisons and CSV output — no &lt;code&gt;.value&lt;/code&gt; everywhere.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Apply the routing policy
&lt;/h3&gt;

&lt;p&gt;Keeping the policy in its own module was deliberate. I expect this to be the part&lt;br&gt;
I change most often as I learn more about the actual workload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# router/policy.py
&lt;/span&gt;
&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RoutingPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RoutingPolicy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CRITICAL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PRIORITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INTERACTIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFAULT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BACKGROUND&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLEX&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;def&lt;/span&gt; &lt;span class="nf"&gt;preferred_tier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;UnsupportedPolicyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No routing policy exists for workload: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Check capability and resolve fallbacks
&lt;/h3&gt;

&lt;p&gt;This ended up being more important than I initially expected. Choosing a tier is&lt;br&gt;
one thing; actually being able to use it is another. The capability checker&lt;br&gt;
resolves to an available fallback using a configured chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# router/capability.py (fallback defaults)
#   priority → default
#   flex     → default
#   default  → no fallback (RuntimeError if unavailable)
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requested_tier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ServiceTier&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;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ServiceTier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;requested&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;normalize_tier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requested_tier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;       &lt;span class="c1"&gt;# (resolved_tier, used_fallback)
&lt;/span&gt;
    &lt;span class="n"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_fallback_order&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="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Requested tier &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is unavailable and no &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;available fallback is configured.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Compose in the Router
&lt;/h3&gt;

&lt;p&gt;At this point the router is intentionally simple: it turns a workload label into&lt;br&gt;
a concrete tier decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# router/router.py
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RoutingPolicy&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;capability_checker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TierCapabilityChecker&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&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="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;RoutingPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capability_checker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capability_checker&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nc"&gt;TierCapabilityChecker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;WorkloadCategory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RoutingDecision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;workload&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_workload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workload_category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;requested_tier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preferred_tier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;resolved_tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;used_fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capability_checker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requested_tier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RoutingDecision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;requested_tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;requested_tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;resolved_tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resolved_tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;used_fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;used_fallback&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;
  
  
  Step 5: Pass the resolved tier to Bedrock
&lt;/h3&gt;

&lt;p&gt;This is the part that makes the project more than a routing abstraction — the&lt;br&gt;
decision ultimately becomes a real &lt;code&gt;serviceTier&lt;/code&gt; parameter in a live Bedrock call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;decision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;background&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# → RoutingDecision(resolved_tier=FLEX, ...)
&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;modelId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apac.amazon.nova-pro-v1:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]}],&lt;/span&gt;
    &lt;span class="n"&gt;inferenceConfig&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;serviceTier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;decision&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resolved_tier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;   &lt;span class="c1"&gt;# ← this is the key param
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;serviceTier&lt;/code&gt; parameter was added to the &lt;code&gt;converse&lt;/code&gt; API alongside the tier&lt;br&gt;
launch. If you are on an older boto3 version (pre-1.34), it will not exist —&lt;br&gt;
upgrade to at least &lt;code&gt;boto3==1.43&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IAM permissions required&lt;/strong&gt; — no new permissions beyond standard model invocation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bedrock:InvokeModel"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:bedrock:ap-southeast-1::foundation-model/amazon.nova-pro-v1:0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tier selection is a parameter in the existing &lt;code&gt;converse&lt;/code&gt; call, not a separate API.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Gotcha: Two Things I Expected That Turned Out to Be Wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gotcha 1: Priority did not demonstrate lower latency in the experiment
&lt;/h3&gt;

&lt;p&gt;This was the assumption I was most confident about going in: Priority should be&lt;br&gt;
faster. The documentation describes it as the fastest tier and says it prioritizes&lt;br&gt;
requests over Default and Flex. I fully expected to see that in the data.&lt;/p&gt;

&lt;p&gt;The controlled tier experiment ran 300 requests across all three tiers in&lt;br&gt;
randomized order. Here is what came back:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Mean (ms)&lt;/th&gt;
&lt;th&gt;P95 (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Priority&lt;/td&gt;
&lt;td&gt;~803&lt;/td&gt;
&lt;td&gt;~1,080&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;~733&lt;/td&gt;
&lt;td&gt;~1,045&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flex&lt;/td&gt;
&lt;td&gt;~711&lt;/td&gt;
&lt;td&gt;~903&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Honestly, I was surprised. The documentation says Priority is the fastest tier,&lt;br&gt;
but the experiment didn't show a statistically significant latency advantage for it.&lt;/p&gt;

&lt;p&gt;I don't think those two things are actually contradictory — I think the experiment&lt;br&gt;
just didn't create conditions where the difference becomes visible. The requests&lt;br&gt;
were intentionally tiny: &lt;em&gt;"Explain Amazon SQS in exactly three concise sentences."&lt;/em&gt;&lt;br&gt;
That is roughly 10 input tokens and ~58 output tokens per request, running at 20 RPM&lt;br&gt;
under light load. There is no real demand contention for Priority to resolve in your&lt;br&gt;
favor under those conditions.&lt;/p&gt;

&lt;p&gt;My hypothesis is that the difference would become more observable with larger prompts&lt;br&gt;
and at higher request rates, where tiers would actually compete for inference capacity.&lt;br&gt;
I haven't tested that yet, so I won't claim it — but it is the next experiment I would&lt;br&gt;
run.&lt;/p&gt;

&lt;p&gt;For now, if I had to explain Priority tier to a stakeholder, I would frame it this&lt;br&gt;
way: you are paying for prioritized access, not raw speed. That distinction matters&lt;br&gt;
more when there is actual demand competing for capacity. For short, low-frequency&lt;br&gt;
requests, you are unlikely to feel it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Gotcha 2: Workload-aware routing can cost more, not less
&lt;/h3&gt;

&lt;p&gt;My second assumption was even simpler: if Flex is cheaper, sending batch workloads&lt;br&gt;
there should obviously save money.&lt;/p&gt;

&lt;p&gt;It does — but only if the rest of your workload does not contain too much Priority&lt;br&gt;
traffic.&lt;/p&gt;

&lt;p&gt;Here is the break-even rule I derived from the observed token profile:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;~1.48 Flex-routed background requests&lt;/strong&gt; are required to offset the cost of&lt;br&gt;
&lt;strong&gt;one Priority-routed critical request.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The policy sensitivity model confirmed the consequence. Of 231 simulated workload&lt;br&gt;
mixes, only &lt;strong&gt;96 (42%)&lt;/strong&gt; produced a cheaper result than All-Default. &lt;strong&gt;134 (58%)&lt;/strong&gt;&lt;br&gt;
were more expensive. The model makes the extremes concrete:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload mix&lt;/th&gt;
&lt;th&gt;Savings vs. All-Default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0% critical / 100% background&lt;/td&gt;
&lt;td&gt;+50.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100% critical / 0% background&lt;/td&gt;
&lt;td&gt;−75.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your batch workload is genuinely deferrable, Flex is a compelling default. But&lt;br&gt;
I wouldn't route something to Flex just because it's called "batch." The real&lt;br&gt;
question is how much latency and availability variance you are willing to trade for&lt;br&gt;
the lower cost. "Batch" tells you how the job runs; it doesn't tell you how tolerant&lt;br&gt;
that job is to degraded service. Those are different questions, and the routing&lt;br&gt;
decision should answer the second one.&lt;/p&gt;


&lt;h2&gt;
  
  
  Results: What the 100-Request Benchmark Actually Showed
&lt;/h2&gt;

&lt;p&gt;The benchmark ran 100 live Bedrock requests in &lt;code&gt;ap-southeast-1&lt;/code&gt; against&lt;br&gt;
&lt;code&gt;apac.amazon.nova-pro-v1:0&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;20 critical → Priority&lt;/li&gt;
&lt;li&gt;50 interactive → Default&lt;/li&gt;
&lt;li&gt;30 background → Flex&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;This was the part I most wanted to verify: did the router actually do what I told&lt;br&gt;
it to do?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Policy mapping accuracy&lt;/td&gt;
&lt;td&gt;100.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tier resolution accuracy&lt;/td&gt;
&lt;td&gt;100.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request success rate&lt;/td&gt;
&lt;td&gt;100.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fallback rate&lt;/td&gt;
&lt;td&gt;0.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this run, yes. Every workload resolved to the intended tier, every request&lt;br&gt;
succeeded, and the fallback path was never needed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Estimated router cost&lt;/td&gt;
&lt;td&gt;$0.019490&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All-Default baseline&lt;/td&gt;
&lt;td&gt;$0.019523&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Savings&lt;/td&gt;
&lt;td&gt;$0.000034 (0.17%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This was smaller than I expected. The Priority premium on 20 critical requests&lt;br&gt;
almost exactly cancelled the Flex savings on 30 background requests. The takeaway&lt;br&gt;
is not that the router fails — it is that workload composition matters more than&lt;br&gt;
simply "using tiers." The router gives you the lever; your traffic distribution&lt;br&gt;
determines whether that lever saves money or costs you more.&lt;/p&gt;
&lt;h3&gt;
  
  
  Token usage
&lt;/h3&gt;

&lt;p&gt;The benchmark deliberately used a small, standardized prompt so the tier comparison&lt;br&gt;
stayed controlled: 1,000 input tokens and 5,851 output tokens across 100 requests,&lt;br&gt;
averaging about 10 input and 58 output tokens per call. That also means the result&lt;br&gt;
should not be generalized to large-context workloads.&lt;/p&gt;


&lt;h2&gt;
  
  
  Lessons Learned: What I Would Do Differently
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Randomize request order from the start
&lt;/h3&gt;

&lt;p&gt;Running critical → interactive → background in contiguous blocks means workload&lt;br&gt;
category is correlated with request time. Any time-varying service behavior&lt;br&gt;
contaminates the comparison. In the next experiment, I randomize.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Don't leave classification until the end
&lt;/h3&gt;

&lt;p&gt;I built the router around an explicit label first because it made the experiment&lt;br&gt;
cleaner. But that exposed a gap: in a real application, something still has to&lt;br&gt;
decide whether a request is critical, interactive, or background. That problem is&lt;br&gt;
non-trivial, and I would solve it before calling this production-ready. A&lt;br&gt;
lightweight heuristic, or a Haiku-class model classifying on request metadata, is&lt;br&gt;
probably the next piece.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Instrument the workload mix before you ship
&lt;/h3&gt;

&lt;p&gt;This became obvious only after I saw the 0.17% result. The economic value of this&lt;br&gt;
router is entirely determined by your critical-to-background ratio. Without a&lt;br&gt;
dashboard showing that ratio in real time, you have no idea whether the router is&lt;br&gt;
saving or costing money on any given day. Add the metric before the feature,&lt;br&gt;
not after.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Stress-test Flex before relying on it
&lt;/h3&gt;

&lt;p&gt;Flex is the only tier with an explicit throttling caveat in the AWS documentation.&lt;br&gt;
I tested it at 20 RPM under light load and saw zero fallbacks. That is not a stress&lt;br&gt;
test. Before routing anything important to Flex, verify how it behaves at or above&lt;br&gt;
expected peak load.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Re-verify pricing per region before any billing claim
&lt;/h3&gt;

&lt;p&gt;The pricing table I used reflects &lt;code&gt;ap-southeast-1&lt;/code&gt; at project time. Cross-region&lt;br&gt;
inference profiles, other regions, and tier pricing can all differ. Any cost claim&lt;br&gt;
in production needs current figures from the AWS pricing page for the exact model&lt;br&gt;
and configuration.&lt;/p&gt;
&lt;h3&gt;
  
  
  6. Test different prompt sizes
&lt;/h3&gt;

&lt;p&gt;This is probably the biggest open question I have after finishing the experiment.&lt;br&gt;
Tiny prompts are good for controlled comparison but limit how much the latency&lt;br&gt;
result generalizes. A follow-up should stratify by token count to test whether tier&lt;br&gt;
differences become more observable as input and output sizes grow — which is also&lt;br&gt;
where the cost differences become more meaningful.&lt;/p&gt;


&lt;h2&gt;
  
  
  What This Is, and What It Isn't
&lt;/h2&gt;

&lt;p&gt;After running the experiments, this is the claim I am comfortable making:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A deterministic workload-aware router can successfully map critical,&lt;br&gt;
interactive, and background workloads to Priority, Default, and Flex service&lt;br&gt;
tiers respectively — with 100% routing accuracy in a 100-request benchmark,&lt;br&gt;
an explicit fallback mechanism, and a measurable economic model whose value&lt;br&gt;
is determined by workload distribution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is not a claim that the router universally saves money, that Priority is&lt;br&gt;
reliably faster, or that this policy is production-optimal. Those claims would&lt;br&gt;
require more evidence than one benchmark run against one model in one region&lt;br&gt;
at light load.&lt;/p&gt;

&lt;p&gt;I started this project thinking the tier decision would be straightforward.&lt;br&gt;
The experiments made that mental model considerably less simple.&lt;/p&gt;

&lt;p&gt;Priority didn't produce the latency advantage I expected under small-prompt&lt;br&gt;
conditions. Flex can reduce cost, but only when enough of the workload can safely&lt;br&gt;
tolerate it. And the economics can flip quickly once Priority traffic becomes a&lt;br&gt;
large share of the mix.&lt;/p&gt;

&lt;p&gt;That is ultimately why I think the router is useful — not because it magically&lt;br&gt;
makes Bedrock cheaper, but because it forces the tier decision to become explicit,&lt;br&gt;
measurable, and testable. Before building this, I was making implicit assumptions.&lt;br&gt;
The router turned those assumptions into a policy I can read, change, and validate&lt;br&gt;
against data.&lt;/p&gt;


&lt;h2&gt;
  
  
  Get the Code
&lt;/h2&gt;

&lt;p&gt;The full project — router, benchmark scripts, analysis, and results — is on&lt;br&gt;
GitHub: &lt;strong&gt;&lt;a href="https://github.com/kayndrigs/bedrock-tier-router" rel="noopener noreferrer"&gt;bedrock-tier-router&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To run the unit tests locally (no AWS credentials needed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/&amp;lt;your-org&amp;gt;/bedrock-tier-router.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-tier-router
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
pytest &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Questions, corrections, and production war stories about Bedrock tier behavior&lt;br&gt;
are welcome in the repo issues or in the AWS Community Builders Slack.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Kayne Rodrigo — AWS Community Builder, AI/ML Engineering Track&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix: Pricing Assumptions
&lt;/h2&gt;

&lt;p&gt;These are the analytical rates used in all cost calculations in this post.&lt;br&gt;
Re-verify against current AWS pricing before using for any billing estimate.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Input / 1M tokens&lt;/th&gt;
&lt;th&gt;Output / 1M tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;$0.80&lt;/td&gt;
&lt;td&gt;$3.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priority&lt;/td&gt;
&lt;td&gt;$1.40&lt;/td&gt;
&lt;td&gt;$5.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flex&lt;/td&gt;
&lt;td&gt;$0.40&lt;/td&gt;
&lt;td&gt;$1.60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Model: &lt;code&gt;apac.amazon.nova-pro-v1:0&lt;/code&gt;, Region: &lt;code&gt;ap-southeast-1&lt;/code&gt;,&lt;br&gt;
cross-region inference profile.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>aws</category>
      <category>bedrock</category>
    </item>
  </channel>
</rss>
