bookly/static/architecture.html
Cody Borders 3947180841 Harden security/perf, add literate program at /architecture
Security and performance fixes addressing a comprehensive review:

- Server-issued HMAC-signed session cookies; client-supplied session_id
  ignored. Prevents session hijacking via body substitution.
- Sliding-window rate limiter per IP and per session.
- SessionStore with LRU eviction, idle TTL, per-session threading locks,
  and a hard turn cap. Bounds memory and serializes concurrent turns for
  the same session so FastAPI's threadpool cannot corrupt history.
- Tool-use loop capped at settings.max_tool_use_iterations; Anthropic
  client gets an explicit timeout. No more infinite-loop credit burn.
- Every tool argument is regex-validated, length-capped, and
  control-character-stripped. asserts replaced with ValueError so -O
  cannot silently disable the checks.
- PII-safe warning logs: session IDs and reply bodies are hashed, never
  logged in clear.
- hmac.compare_digest for email comparison (constant-time).
- Strict Content-Security-Policy plus X-Content-Type-Options,
  X-Frame-Options, Referrer-Policy, Permissions-Policy via middleware.
- Explicit handlers for anthropic.RateLimitError, APIConnectionError,
  APIStatusError, ValueError; static dir resolved from __file__.
- Prompt cache breakpoints on the last tool schema and the last message
  so per-turn input cost scales linearly, not quadratically.
- TypedDict handler argument shapes; direct block.name/block.id access.
- functools.lru_cache on _get_client.
- Anchored word-boundary regexes for out-of-scope detection to kill
  false positives on phrases like "I'd recommend contacting...".

Literate program:

- Bookly.lit.md is now the single source of truth for the five core
  Python files. Tangles byte-for-byte; verified via tangle.ts --verify.
- Prose walkthrough, three mermaid diagrams, narrative per module.
- Woven to static/architecture.html with the app's palette
  (background #f5f3ee) via scripts/architecture-header.html.
- New GET /architecture route serves the HTML with a relaxed CSP that
  allows pandoc's inline styles. Available at
  bookly.codyborders.com/architecture.
- scripts/rebuild_architecture_html.sh regenerates the HTML after edits.
- code_reviews/2026-04-15-1433-code-review.md captures the review that
  drove these changes.

All 37 tests pass.
2026-04-15 15:02:40 -07:00

2162 lines
474 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Bookly</title>
<style>
html {
color: #1a1a1a;
background-color: #fdfdfd;
}
body {
margin: 0 auto;
max-width: 36em;
padding-left: 50px;
padding-right: 50px;
padding-top: 50px;
padding-bottom: 50px;
hyphens: auto;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
font-kerning: normal;
}
@media (max-width: 600px) {
body {
font-size: 0.9em;
padding: 12px;
}
h1 {
font-size: 1.8em;
}
}
@media print {
html {
background-color: white;
}
body {
background-color: transparent;
color: black;
font-size: 12pt;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3, h4 {
page-break-after: avoid;
}
}
p {
margin: 1em 0;
}
a {
color: #1a1a1a;
}
a:visited {
color: #1a1a1a;
}
img {
max-width: 100%;
}
svg {
height: auto;
max-width: 100%;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 1.4em;
}
h5, h6 {
font-size: 1em;
font-style: italic;
}
h6 {
font-weight: normal;
}
ol, ul {
padding-left: 1.7em;
margin-top: 1em;
}
li > ol, li > ul {
margin-top: 0;
}
blockquote {
margin: 1em 0 1em 1.7em;
padding-left: 1em;
border-left: 2px solid #e6e6e6;
color: #606060;
}
code {
font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
font-size: 85%;
margin: 0;
hyphens: manual;
}
pre {
margin: 1em 0;
overflow: auto;
}
pre code {
padding: 0;
overflow: visible;
overflow-wrap: normal;
}
.sourceCode {
background-color: transparent;
overflow: visible;
}
hr {
border: none;
border-top: 1px solid #1a1a1a;
height: 1px;
margin: 1em 0;
}
table {
margin: 1em 0;
border-collapse: collapse;
width: 100%;
overflow-x: auto;
display: block;
font-variant-numeric: lining-nums tabular-nums;
}
table caption {
margin-bottom: 0.75em;
}
tbody {
margin-top: 0.5em;
border-top: 1px solid #1a1a1a;
border-bottom: 1px solid #1a1a1a;
}
th {
border-top: 1px solid #1a1a1a;
padding: 0.25em 0.5em 0.25em 0.5em;
}
td {
padding: 0.125em 0.5em 0.25em 0.5em;
}
header {
margin-bottom: 4em;
text-align: center;
}
#TOC li {
list-style: none;
}
#TOC ul {
padding-left: 1.3em;
}
#TOC > ul {
padding-left: 0;
}
#TOC a:not(:hover) {
text-decoration: none;
}
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
html { -webkit-text-size-adjust: 100%; }
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ background-color: #f8f8f8; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ef2929; }
code span.an { color: #8f5902; font-weight: bold; font-style: italic; }
code span.at { color: #204a87; }
code span.bn { color: #0000cf; }
code span.cf { color: #204a87; font-weight: bold; }
code span.ch { color: #4e9a06; }
code span.cn { color: #8f5902; }
code span.co { color: #8f5902; font-style: italic; }
code span.cv { color: #8f5902; font-weight: bold; font-style: italic; }
code span.do { color: #8f5902; font-weight: bold; font-style: italic; }
code span.dt { color: #204a87; }
code span.dv { color: #0000cf; }
code span.er { color: #a40000; font-weight: bold; }
code span.ex { }
code span.fl { color: #0000cf; }
code span.fu { color: #204a87; font-weight: bold; }
code span.im { }
code span.in { color: #8f5902; font-weight: bold; font-style: italic; }
code span.kw { color: #204a87; font-weight: bold; }
code span.op { color: #ce5c00; font-weight: bold; }
code span.ot { color: #8f5902; }
code span.pp { color: #8f5902; font-style: italic; }
code span.sc { color: #ce5c00; font-weight: bold; }
code span.ss { color: #4e9a06; }
code span.st { color: #4e9a06; }
code span.va { color: #000000; }
code span.vs { color: #4e9a06; }
code span.wa { color: #8f5902; font-weight: bold; font-style: italic; }
</style>
<style>
:root {
--bg: #f5f3ee;
--panel: #ffffff;
--ink: #1a1a1a;
--ink-muted: #6b6b6b;
--accent: #2e5b8a;
--border: #e2ddd2;
--code-bg: #f0ede4;
}
html, body {
background: var(--bg);
color: var(--ink);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
font-size: 16px;
line-height: 1.6;
margin: 0;
}
body {
max-width: 820px;
margin: 0 auto;
padding: 48px 32px 96px;
}
h1, h2, h3, h4 {
color: var(--ink);
letter-spacing: -0.01em;
line-height: 1.25;
margin-top: 2.2em;
margin-bottom: 0.6em;
}
h1.title {
font-size: 44px;
font-weight: 700;
margin-top: 0;
margin-bottom: 0.2em;
letter-spacing: -0.02em;
}
h1 { font-size: 28px; font-weight: 700; border-bottom: 1px solid var(--border); padding-bottom: 0.3em; }
h2 { font-size: 22px; font-weight: 600; }
h3 { font-size: 18px; font-weight: 600; color: var(--ink-muted); }
p { margin: 0 0 1.1em; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 0.88em;
background: var(--code-bg);
padding: 0.1em 0.35em;
border-radius: 4px;
border: 1px solid var(--border);
}
pre {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px 20px;
overflow-x: auto;
font-size: 13.5px;
line-height: 1.55;
}
pre code {
background: transparent;
border: none;
padding: 0;
font-size: inherit;
}
.sourceCode { background: transparent; }
#TOC {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px 28px;
margin: 28px 0 40px;
}
#TOC ul { list-style: none; padding-left: 1.1em; margin: 0.3em 0; }
#TOC > ul { padding-left: 0; }
#TOC li { margin: 0.25em 0; }
#TOC a { color: var(--ink); }
.mermaid, .diagram, figure {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
margin: 24px 0;
text-align: center;
}
figure img, figure svg, p img { max-width: 100%; height: auto; }
blockquote {
border-left: 3px solid var(--accent);
margin: 1.2em 0;
padding: 0.2em 1em;
color: var(--ink-muted);
background: var(--panel);
border-radius: 0 6px 6px 0;
}
header#title-block-header {
margin-bottom: 0.4em;
}
@media (max-width: 720px) {
body { padding: 28px 18px 72px; }
h1.title { font-size: 34px; }
}
</style>
</head>
<body>
<header id="title-block-header">
<h1 class="title">Bookly</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#introduction" id="toc-introduction">Introduction</a></li>
<li><a href="#the-four-guardrail-layers" id="toc-the-four-guardrail-layers">The four guardrail layers</a></li>
<li><a href="#request-lifecycle" id="toc-request-lifecycle">Request
lifecycle</a></li>
<li><a href="#module-layout" id="toc-module-layout">Module
layout</a></li>
<li><a href="#configuration" id="toc-configuration">Configuration</a></li>
<li><a href="#data-fixtures" id="toc-data-fixtures">Data
fixtures</a></li>
<li><a href="#tools-layer-3-enforcement" id="toc-tools-layer-3-enforcement">Tools: Layer 3 enforcement</a></li>
<li><a href="#agent-loop" id="toc-agent-loop">Agent loop</a>
<ul>
<li><a href="#system-prompt" id="toc-system-prompt">System
prompt</a></li>
<li><a href="#runtime-reminders" id="toc-runtime-reminders">Runtime
reminders</a></li>
<li><a href="#layer-4-output-validation" id="toc-layer-4-output-validation">Layer 4 output validation</a></li>
<li><a href="#session-store" id="toc-session-store">Session
store</a></li>
<li><a href="#the-tool-use-loop" id="toc-the-tool-use-loop">The tool-use
loop</a></li>
<li><a href="#run_turn" id="toc-run_turn">run_turn</a></li>
</ul></li>
<li><a href="#http-surface" id="toc-http-surface">HTTP surface</a>
<ul>
<li><a href="#security-headers" id="toc-security-headers">Security
headers</a></li>
<li><a href="#sliding-window-rate-limiter" id="toc-sliding-window-rate-limiter">Sliding-window rate
limiter</a></li>
<li><a href="#session-cookies" id="toc-session-cookies">Session
cookies</a></li>
<li><a href="#apichat" id="toc-apichat">/api/chat</a></li>
<li><a href="#architecture" id="toc-architecture">/architecture</a></li>
</ul></li>
</ul>
</nav>
<h1 id="introduction">Introduction</h1>
<p>Bookly is a customer-support chatbot for a bookstore. It handles
three things: looking up orders, processing returns, and answering a
small set of standard policy questions. Everything else it refuses,
using a verbatim template.</p>
<p>The interesting engineering is not the feature set. It is the
guardrails. A chat agent wired to real tools can hallucinate order
details, leak private information, skip verification steps, or wander
off topic and the consequences land on real customers. Bookly defends
against that with four independent layers, each of which assumes the
previous layers have failed.</p>
<p>This document is both the prose walkthrough and the source code. The
code you see below is the code that runs. Tangling this file produces
the Python source tree byte-for-byte; weaving it produces the HTML you
are reading.</p>
<h1 id="the-four-guardrail-layers">The four guardrail layers</h1>
<p>Before anything else, it helps to see the layers laid out in one
picture. Each layer is a separate defence, and a malicious or confused
input has to defeat all of them to cause harm.</p>
<p><img role="img" aria-label src="data:image/png;base64,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m1FyKSIiIiI2o+RSRERERGxGyaWIiIiI2IySSxERERGxGY0WF7nFosKukBiTWdFhSDlcTTVVdAgiInctJZcit9C9rauQHJ9Dbs4fL1lZsGA+ubl5DB06tKJDsTmjEe5pU6WiwxARuStpbXERuSEdOnTAZDKxf//+ig5FRETuIOpzKSIiIiI2o+RSRERERGxGyaWIiIiI2IySSxERERGxGSWXIiIiImIzSi5FRERExGaUXIqIiIiIzSi5FBERERGbUXIpIiIiIjaj5FJEREREbEbJpYiIiIjYjJJLEREREbEZJZciIiIiYjNKLkVERETEZpRcioiIiIjNKLkUEREREZtRcikiIiIiNqPkUkRERERsRsmliIiIiNiMkksRERERsRkllyIiIiJiM0ouRURERMRmlFyKiIiIiM0ouRQRERERm1FyKSIiIiI2o+RSRERERGxGyaWIiIiI2IySSxERERGxGSWXIiIiImIzSi5FRERExGaUXIqIiIiIzSi5FBERERGbUXIpIiIiIjaj5FJEREREbEbJpYiIiIjYjF1eXl5eRQchIne2Nm3aULlyZattaWlpAEVu379//22LTURE7iyGig5ARO58VatWJTExscjP8pPMgvuKiMifl5rFRaRUI0eOxMnJqdT9nJycGDly5G2ISERE7lRqFheRMunXr1+xtZf5qlatyrp1625TRCIicidSzaWIlElptZeqtRQREVDNpYiUQ0m1l6q1FBERUM2liJTDyJEjcXR0LLRdtZYiIpJPNZciUi5F1V6q1lJERPKp5lJEyuX6vpeqtRQRkYJUcyki5Vaw9lK1liIiUpBqLkWk3PJrL1VrKSIi11PNpYjckH79+gGo1lJERKwouZSbtmVRHEd3pFR0GCJymzTv4EbPp2pWdBgicofS2uJy8/KgTf8a+LZ2r+hIROQWO304lbS4zIoOQ0TuYOpzKSIiIiI2o+RSRERERGxGyaWIiIiI2IySSxERERGxGSWXIiIiImIzSi5FRERExGaUXIqIiIiIzSi5FBERERGbUXIpIiIiIjajFXrkTyUiIpzDh/fSuXMv6tSpW9HhlEtCQjyHD++ld++BN1zGrl1bOHhwDxkZ6dSocQ/du/ejQYNGNoxSRET+7JRcyp9KREQYgYFTadTo3rsquYyLu8y77/6dOnXq3nByuWhREN9//w0AHh6eJCcnkZgYz9tvf3RTscXERLN69WIee+xZPD2r3lRZfyZXr6axZs0yGje+F3//NhUdjoiIzahZXOQOFxq6lpdffpLo6KgbLiMrK4sFC37Cy8ub4OBNzJ+/nlmzlvDEE8NuOr5Nm0JYsGA2OTnZN13Wn8np0yf5/vtvSExMqOhQRERsSjWXIgXk5uZy5Mh+wsKOkpaWSsuW7WnZsh0Gg/lbZfXqJbi7e9C5c0+r40JCluLu7kGnTj0Ac/P7wYO7uXo1DW9vH7p06Y2LiwsA+/btJDY2hq5d+7BjRygXLpyjW7c+3Htv80LxHDmyn/HjP6BVq/YcPLinyJgPHdrLyZP/5eGHn8TVtVKR+1y+fJHk5CR69HiQKlXcAPDyqgdASkoyW7eux9f3Ppo29bMck7+9SZNmNGp0L2FhRzl8eC9Vq1anefMW1K/fkFOnTnDixDEA1q9fiZubOw880Jr69RsCkJmZyfbtGzl37gzu7p74+7fB17cpALGxlzhwYDe9eg0gNHQN0dFR1K1bn65d+5CUlMCePdvIycmmZ8/+1KxZq9hnFht7iT17ttG4cVPCwo6SlJRAvXoN6NKll+V+REae4tixQ3Tq1IPjx48QERGGr+99dOvWx3IP//vf38jLy6Nx46a0b98FOzs7ALZu3YCXlzcAO3duxtHR8X/XcR+7d28lIiKMZs38ad26g+U92bp1AyZTDh4eVTl8eB+VK1fh/vtbcd999wOQmJjAzp2hABw4sIu0tFS8vLxp1ap9sdcpInK3UHIpUsD69SuZNOkzy9eLFgVRvXpNZsyYT+XKVTh0aA9bt24gOHiTJUmLjDzF5MnjGDbsFTp16sH8+T8xa1aAVblz537Pv//9PTVq3MPGjasJDV1DSMgSIiLCARgw4NEi46lSxZ133x1Hz54P0r9/2yL3+fTT/yMjIx1Pz2r06/dwkfvUrVsfo9GVtWuX07//EHx8Gls+q1SpMjNnTqN+/YZMmfKTZfu2bRsICJjAxInf8e23X7FyZTBGoysZGekALF++g9OnT/Dbbwf/9/V8DAZHPD2rUb9+Q2JjLzFmzGtER0dZHTdixOsMHTqCCxfOMXnyOObP/5GYmGjLeX/5ZSYxMdGWY2bNCmDs2K/p2LF7kdd24cI5AgImAFidZ/bsb5k0aSb33FObsLCjBARMYO3aZZZ7Pnr0xwBMnvw5ISFLLF0FADp27M5HH03EwcGBRYvmculSNMnJSVble3v7EBUVidHoyq+//oiPT2O+++5XABYtmkt4+LFCMQ0f/irPPvsSKSlJrFmzDIDt2zdx6NBeevbsr+RSRP4Q1CwuUkDXrn0YNuwVvvvuV5Yt287QoSOIj49l06YQAPr1GwzAtm0bLcds327+d/fufTl+/AizZgXQqVMP5s5dxYoVO3nnnU+IiYkulHDWrl2XhQs3smTJFurW9S4ynoYNfenVq7+lFq0oTzwxjKZN/fDza1nsPvb29rzxxrtkZKTz6qtDmTx5HLGxlwAwGAwMHvwU4eHHiIqKtByzaVMIHh6eNG16PytXBuPl5c2iRaHMmxfCJ598hbOzM/37P8KTTw4HYNq0OQQFraZLl14ATJ36BdHRUXz66TcEB29i1qwl+Pm14Mcfp1udp0GDxixbtp1ly7bTtKkfMTHRvPDCGyxZsoXvv19giaU0zzzzIkuWbGHevBD++te/Ex8fy+zZ31rtk56eTmBgMMuX76B374Hs3r2VkJAl9O37EPPmrWHhwo307/8Iu3ZtYcuWdZbjkpOTmDDhW5Yu3crYsV//r6yrzJsXwoIFG+jf/xEiI09x9uxpyzFGoytz564iOHgTEyZ8i49PY37++TvOnz+Lj09jPvtsMgBvvvkBQUGreemlUaVeo4jI3UDJpUgBrq6uPPvsS1y9mkZo6BpLM2d+f8eWLdvh4eFJSMgSAPLy8ti4cTVNmjSjXr0GliSoS5deJCTEcebMSby9ffDw8OTgwd1W53ryyeG4ubkX25RdVs899zJTpvxU6gClPn0GMW3azzRv7k9IyFL+9renOHJkPwB9+z4EwIYNqwBzU/Px40fo338Izs7OeHv7EB0dxebNa/HwqGpp/i9OfHws+/btpHlzfzw8PDl1KpwrV1Jo0cJc+5pfqwfQu/dAXFxccHFxsZTbtWsf7OzsqF+/Ic2b+7Nv385S70PDhk2ws7OjWrUaPPPMi/j4NGbnzs1W+wwa9Dj16jXA2dkZg8HA1q0bLNsdHBxwc3Nn8OCnAdi7d4flOC8vb0vsHTp0A8zvQrVqNXBycqJHjwcBOH78iOWYqlWrU6PGPRgMBlq0aMvzz78OYLnnIiJ/VGoWFykgPT2d9957zZL81K7tBUBmZgZgruV7+OGnmDNnBpGRp8jJySEmJtoyMCYyMgKAiRM/LlS20ehq9bWv73237DqK06RJMyZNmsmuXVuYMOEj/vnPV1m1ajf16jXAz68Fa9cuY/jwV9m+fROAJWn64IPxjB07mokTPyYoKJAxY8bRpEmzYs9z/vxZwJxsvfnm84U+v3btWplj9vSsBkSU4yrN6tdvSGTkKdLSrli2+fm1sNrn0iVzc3zBZ5HfX7S4AVRF1SK7u3sCkJeXW2w8+e/S5csxZQlfROSupeRSpIDZs/9DePgxRo16n969B/6v6de6r2OfPoOYM2eGVVNtly69AXBxMf6vnOWFBqFcn5SU1NR9q3Xs2J3HHnuGoKBAzpyJoEmT+xg06HEmTPiIAwd2s2nTanx8Glv6ZjZo0IjAwEVs3Liab7/9ipEjh/Pjj0uLrS3Nvw8DBgxh1Kj3C31ub29f7AAlWzlx4rhVLObzOljtk//Z1atpuLt7AOaBTIClT62tnDljTpCvr6nOy8uz6XlERCqamsVFCli7djleXt4MGvQYLi4uZGVlAdY1bbVq1aF16w6sWLGQdeuW07Fjdzw8zDVX+TVg27dvxN7e3urPrUomExMTCAs7WmqSkpmZafX1pUsXAbh2zQRAp049MRpdmTFjEhER4fTvP8Syr8lkwmAw8OCDgxk5cgyApZnf2dkZMA+syVevXgMANm9eR3Z2dqF7caudP3+WmJhofHwaW7o2FMXHxxewbqY/fvyw1We2smfPNgDLpPVOTk4AXLhw1qbnERGpaKq5lD+lkJAlHDq012pb58496d69L2vWLGPTphAqV3bj119nAeYRvW+8McYynVD//kM4cGA3GRnp9OkzyFLGo48+w7Jl8/nhhylcuHCO++9vxb59Ozl79hTTps3B0dHR5tcyevQLxMRE8+GHE+jatXeR++Tl5fHaa8/QpEkz7rvvfsLCjhIauobWrTtYpsdxcXGhd++BrFwZDJj7PeZ7//03aNjQFz+/loSGrgGwTM/Ttm1nfvhhCnPnfk9KSjI5Odn06/cww4e/ys8/f8dbbz3PwIGPkZGRzoYNqxg6dMRNrTJUnBUrFpKXl0dY2G+EhCwF4JlnXirxmMGDnyI4eA5ffvk+Q4eOoE6dekyfbh55/sgjT99UPNHRUQQHz6VKFTfWrVvOsWOH8fb2oW3bzgA0btwUDw9P1q5dTo0atcjOzuLBBx+xvGMiIncrJZfyp1Jw7sLr1avXgCeeGEZMTDQTJphXrenf/xH8/Foyf/5PnDhxzLKSSv6UMUajK23adLKU4eHhyTffzGLatC8JCVlKSMhSjEZXunfvS3Z2Fo6OjjdVg1lUrV/NmrWJiYn+X9/Eol29msa99zZn164tluSwdesOjBz5ntV+7dt3ZeXKYNq27US1atUBc61lzZq1CA1dw5Ilv1C7thfDhr1Cy5btAHMfxWeffYnFi+cxfvwHGI2u9Ov3ME8//TwGg4FZswKYPn0iYJ6+J78pOv8+FLym/H8XvEdlrek8d+40n39urlU1Gl0ZPfpjyzyWxd3ze+6pzaRJM/n660/58cfpgDlp/vjjryzdGhwcHHBwcCjy+Hz55V9/nrlzv7dMQ9S2bSfefvsjS02qwWDg9dffZcaMSUyePA4Af/82Wo5TRO56dnnq8CM3aUtwHM5uTvi2dq/oUGwmMTEBNzd3SyKQmpqCq2sly9dJSYkMHdqPQYMeZ9So94osIz39KpmZmVStWnzSZwu5ublcvZpWpj6C165d49Kli3h6Vi1ylPru3Vv55JPRjBnzOT17Pmj1WV5eHomJCZak83rp6elkZ2fh7u5hlWTl5uaSmBiPm5uHpSnYlg4e3MN7773Ohx9OoFWr9mRlZd3QPb9yJRWwTV/LN998nitXUgkMDC712k0mEykpSbi7e5bYhH+nOH04lbS4THoNrVnRoYjIHerO/0kmUgGuT07c3MyJc05ODhs2rGLNGnOz64MPDi62DFfXSjc9zVBZ2NvblzkhcnBwsKzMU1BCQhybNoUQHDwHDw9Py3Q7BZmn+Sk6sQTzNE6urq6Fttvb21O9+u1JRCpVqkylSpVv6FhbD+CBsl27wWCgWrUaNj+3iEhF0YAekXJISkpg8uRxpKQkMXbs10Uu2Xg3+u23gwQGTqVWLS++/nomRqOx9INERESKoGZxuWl/xGbx4mRnZ5OSkkSNGvdUdCg2lT8XZOXKVSo4kvK7ejWNqKhI6tatf0tqH2/EuXNnuHbtGg0b2nbE+Z1AzeIiUho1i4uUg5OT0x8usYS7M6nMV6lSZcuI9ztF/kTsIiJ/RmoWFxERERGbUXIpd6zlyxdYrdV8vaysLF5++Ul++WXWbYknNTWF+fN/Ijb20m05340q7b7ls/X1XLx4gZdffpKNG1fbpLzSbN26gZdffpJz587clvOJiEjZKLmUO1JCQhzTp0/kl19mFrtPXl4eUVGRJCUl2PTcx48fYdasgELbDx/ex6xZAWzZss5y/tDQtaxevcSm578ZRd23sl7PzTKZcoiKiiQ1NcUm5ZUmLe0KUVGRZGdn35bziYhI2ajPpdyRqlWrwRdfBFC7dtFrV99KP/30H+LiLvHCC29YbW/fvisff/xvWrQwrzV+7do1xo//gEGDHr/tMRanqPtW1usRERGxBSWXcsdJSkpk+/aNALi4GKlT5/dE6ezZ0+zfv5Ps7GzLajkF5eXlsXfvDk6cOIaDg4HGjZvSrl1nq5V5ateui4uLC3v2bOPKlVT8/dtYVtzZvXsbcXGXSE5OYsWKhQD07NmfmJhowsOPAubJtitVqszq1YsBOH36BCtWLMTZ2YX27buydet6fH3vo2lTP0tcKSnJbN26niZNmpV5+qKcnBx2797KuXNnyMvLo0mTZrRu3QGDwcC+fTuJjY2ha9c+7NgRyoUL52jZsi0xMdFW960815PvyJH9RESEkZKSjI+PLx07dsdoNJKbm8uRI/sJCztKWloqLVu2p2XLdjc18XdqagpbtqyjadP7ycrK5ODB3Tg6OvHoo8+wfv2KQvdx48bVuLgY6dy5Z7FlZmZmsn37Rs6dO4O7uyf+/m3w9W1q+Twy8hQHDuzm2jUTfn4tadKkWbmX5SypjJKeW/6xBw/uISUlCW9vHzp37mWZ+iks7CinToVz773N2bdvJ7m512jU6F46dOhmtVJRae+5iEhFUnIpd5zExHgCAszrOz/11F9p3twfgNDQtYwf/wEA1avXZPbsb62OM5lMjBv3Lrt2bbHa3rZtJ8aOnYTBYGDRormkpCSRnJwEQEZGOr/++iP//Odn9O49kF27NlsStF9//REw1/CdOHHMEtO//jWFWrXqsHhxEADh4ceIj4+levWa9Oo1gJkzp1G/fkOmTPnJEsO2bRsICJjAxInflekeXLp0kbFjRxMZeQoPD0+ysrLIyEjntdf+wSOPPM3GjasJDV1DSMgSIiLCAWjevEWh+1ae68nJyWHixI/YunUDRqMrlSpVJj5+Nl269OKjjyayfv1KJk36zBLjokVBVK9ekxkz5t/waPPk5EQCAibg69uUiIhwjEZXOnXqQVpaKgEBExg27BWr5DIoKBAPD89ik8vY2EuMGfMa0dFRGI2ulqUXR4x4naFDR3Do0F7GjHnN6phJk2Za3rGyKKmM0p7b+vUr+eqrsRiNrjg7O5OcnMS8eTP5979nUK1aDfbt20FQUCCAVfytW3fgs88mYzAYyvSei4hUJP0UkjtOo0ZNWLhwI08+2duyLTb2EuPHf4CXlzfjx/+HmjVrcezYYd555yXLPosXB7Fr1xaGD3+VRx/9CyaTiSfyCI8AACAASURBVLlzv2fZsvmsX7+SAQOGABATE82//jWFdu06c+ZMBH//+19YvnwBvXsP5O23P+LixQvExV3ip5+WWcp+6KEnqF69Jp98MtqyLTBwEYMGdSi0BOTgwU8xf/5PREVF4u3tA8CmTSF4eHji59eyTPdg6tQviIw8xYcfTqBrV/N9OHRoL82bt7Dar3btunzxxXQMBgOurpUK3bfyXM/ixUFs3bqBxx57hhdfHIXBYODs2dMYjeZVd7p27UNc3GU6d+5J7dp1+eWXmfz6649s2hTC4MFPlem6ihMREc6XX06nRYu2ZGVlcfXqlRsqZ+rUL4iOjuLTT7+hTZuOXL4cw6RJ5nXDO3XqwfLlCwAIClpNpUpV2LVrS7knwi+pjJKeW0JCHF99NRZvbx8mTZqJi4uRNWuWEhAwgTlzZvDWWx9azjFu3FTatu1EZOQpfv11Fps3r2PjxtU8+ODgMr/nIiIVRQN65K6wbt0KAB5//Dlq1qwFQOPGTa32yW+m9vdvQ1RUJBcvnqdly3YAHD9+2LKfl5c37dp1BqBhQ1+8vX1sOuK4b9+HANiwYRVgToyPHz9C//5DcHBwKPX42NhLHDiwGx+fxpYEBaBly3aF1qd+8snhuLm522SZyfykaejQFyy1Xw0aNOKee2oD5uUdn332Ja5eTSM0dI1ln+joqJs+d/v2XWnVqj329vY3vDpQfHws+/btpHlzfzw8PDl1KpwrV1IsfUrDw49Rq1YdAJYu/ZW8vDx69epf7pq+4soo7bnt378LML8fVaq44ejoyMCBj2E0urJ5s/WgqoYNmwDg49OY119/F4D9+3cCZX/PRUQqimou5a5w7txpADp06Frk55mZmZbm34K1mflMJlOxZXt7+xAVFWmDKM3q1WuAn18L1q5dxvDhr7J9+yYAevR4sEzH5ydrnTv3KnVfX9/7bjzQAjIzM4mPj6V5c3/c3T2K3Cc9PZ333nuN8PBjANSu7fW/YzNu+vwPPND6pss4f/4sYB4d/+abzxf6/Nq1azz77MucP3+WhQt/ZuXKYEaOHEPv3gPLdZ7iyijtuV2+fBGAZs1+b4J3cHDAz68F+/btJD39apHHubm5YzS6EhNz4abecxGR20XJpdxVUlNTqFatRqHt+bVPtWt7MXPm4kIDG0oa6FBwoES+nJycMseUm5tbaNugQY8zYcJHHDiwm02bVuPj0xgfn8ZlKs/JyRmgTFMslXUAR2nXk3//EhPji91n9uz/EB5+jFGj3qd374E4OzvTv79tRpqXpUa3NC4u5hrPAQOGMGrU+4U+z3/O48ZN5ciR/UyZ8gUTJ36MnZ0dvXoNKPN5KleuUmQZ99xjrtEs7rnlx3flivVUTRcvXgB+f+7Xi429REZGOq6ulW/qPRcRuV3ULC53hfy+i3v3bi/yc4PBQJMmzYiJiSYiIgx7e3urP+X5T9fZ2Zn4+FjS09NL3C+/zKiowk3qnTr1xGh0ZcaMSUREhNO/v7kfnMlk4vjxI2RlZRVbbr16DQBzP01b1ESV5XoMBgO+vk2JiYnm9OmTVp/l5eUBsHbtcry8vBk06DFcXFws13Dt2rX/lWEeLR0fH3vTMVeubF4j/OTJ/1q2JScnWSW/+aOzExLigN/v2+bN68jOzi70DsDvNXv+/m348svpAFYDYy5fjim1Fru4Mkp7bt7e5iUhC05wn5AQR3R0FD4+jYttnj9wYDcAjRvfW673PCIinKSkxBKvRUTkVnAYO3bs2IoOQu5u5/6bjsHZgWp1XGxWZlZWFgsX/kzz5i1o1ao9Xl7eLF4cxKFDe4mKOoPJZGLVqkWcOhVO06Z+tG3bGS8vb9avX8nu3VvIyEjn4sUL/PDDZI4fP0yHDt0AWLNmKdnZWTzyyNOWc23btoFz584wbNjfAMjIyGDPnm0kJyeQlZVFQkIcderU5cKFc2zevJZevQbg5eWNvb09Fy6cZd++nTg5ORETcwFHR0fc3T0xGAzExV3mwAFzP7vRoz/G1dWVxYvn8eWX7xMfH0unTj2KvHZnZxdMphwOH95HaGgI2dnZhIUd46uvxlKtWg28vX3Yvn0TZ8+essRc3H0rz/Xcc08dNm5czaZNIaSkJBEfH8uMGd9w5sxJ2rTpREzMBQ4d2ouXVz3i4mKZMuVz4uIuExNzgSFD/oKnZ1VCQ9fw228HcHPzKFOTfUpKMitWLKRNm05W64MbDAYuXDjLjh2h2NnZER19nunTJxAXd5maNWvx4IOP4OJiZNmy+Zw5c5L69Rvi7e2Dvb09Bw7sYs+ereTm5nL48D6mTPmcSpUq07ChL/PmzWTx4nnY2zuwY8cmwsKO0rVrb/z925CTk8OTT/ZmxYqF9OzZHzc39yJjLq6Mdu06l/jc2rXrwvbtG9m1awvJyYnk5eXyww9TuHw5htdf/yf16zfkyJH9HD16kKysTLKyMpk3L9Ayyv+9976gcuUqZXrPT5w4zsiRw9m8eS1Dhgwtsnb+RiVdyiI73YSP38338xWRPyY1i8sdKb8GJv/v6tVr8vXXgXz11Sds3bqBfft2Wpoy85tU/f1bM3bs1wQETLBM5+Lh4WnVn8/BwaHUJthu3frw22/7Wbt2OWvXLqdnz/60bt0BOzt7q5gAnn56BHFxly0r4Lz55geWWtb27buycmUwbdt2olq16gD06NGPuXO/JzU1ucQYhg17BWdnFxYsmM3MmdMwGl1p27aTpem0uJrY6+9bea6ndesOfPLJV0yfPpHFi+f97562oVYtc9/KJ54YRkxMNBMmfARA//6P4OfXkvnzf+LEiWP4+7fhb397i6CgQIKD5zBw4KMlXmPBcxeV/Dz99Aiys7OZM2eG5fwF+3d6edVj+PBXWbjwZ3bsCMXfvw1PP/08BoOBWbMCmD59ImCu9c5vknZxcSEqKpLPPx+D0ehK374P8dhjzwLmmtDBg59i+fIFJY5WL6mMkp6bwWDgiy8C+Oabf7FyZTArVwZjNLry1lsf0q1bH6tzbNy4mpUrgy3xjx79sWVgVVne80aN7qVJk2acPPlfS82ziMjtYpennzxyk7YEx+Hs5oRv66JremztypVUjEbXEkf5JiUl4uTkZDU5eHmlpCRjMBjKVEZCQhyVKlXBxeX32tvdu7fyySejGTPmc3r2/H0wzxtvPEe3bn156qm/llpuXl4eiYnxeHhUvel+ieW5npSUZJydXayuJ19iYgJubu6W+5+amoKrayWr55GRkcGVKymkpBSfRDs4ONCwoW+psZT2vDMzMzGZcqzm2szNzSUxMR43N49CI+zB/H5UqeJWqMw9e7bz8cdvsXLlrlInVi+uDCj9uWVmZnL1aprll458P//8HUFBgcybtwZ7e/tS3+GS3vOZM6dx+vQJvvii8NKfN+P04VTS4jLpNbSmTcsVkT8O1VzKXadKFbdS9/H0rHrT5ylu1HRRCg4ySkiIY9OmEIKD5+Dh4WlpqgRYuTKYpKRESx/M0tjZ2RU5gOlGlOd6Stq3atVqVl8X1XxsNBr54YfJrFq1qNhyatf2spp7szilPW9zAmydBNvb21O9evHJT1HvR1JSIoGBU/j73/+vTCv2lPSOlfbcXFyKTtzLWn5p+4SHH2PFioWMH/+fUssQEbE1JZciNvbbbwcJDJxK06Z+/OMfn1nN2xgVFcnUqbOL7c/3RzJq1HtWk8vf6c6dO82QIX9h0KDHKjqUmxYWdpTPP59mtbqRiMjtomZxuWm3u1n8TpeWZu6vd6NLIsqfV3x8LHFxl/H1ve+OXcZRzeIiUpo786eXyF1MSaXcqOrVa5bYnC8icjfQPJciIiIiYjNKLuWudvz4EV5++UkOHtxzy86xfPkCq4mv7yTvvPMS//nPv622RUaeIjh4bokTtd9JTCYTQUGBjB79Ih9+OIrLl2Nu6fm2bt3Ayy8/adP15G+Hixcv8PLLT7Jx4+qKDkVEpERKLuWulpWVSVRUJFevpt1UOTEx0cycOa3QiiYJCXFMnz6RX36ZeVPl3yqnT58kNvaS1bbVqxfzww+TOXPmZDFH3VmWLv2Vn3/+DlfXSnh7+9hsdHxx0tKuEBUVSXZ2don7FfdOVBSTKYeoqEhSU1NK37mA48ePWOZhFRG5HdTnUgTzkn0LFszm4YeftNperVoNvvgigNq161ZQZOU3dOgLtG7dgSZNmlV0KGWybdsGWrfuwLhxUys6FCvFvRN3m59++g9xcZd44YU3KjoUEfmTUHIpt9zJk2EcO3aIBx8cbDXZ86ZNa7h69YrlP+/4+Fh27drC5csx1KpVhw4dulkGN6SmprBlyzqaNr2frKxMDh7cjaOjE/fe2xyAS5eiCQ6ey5UrKTRqdC+dOvWwGm0bGXmKQ4f2kpAQR/XqNenQoRu1a5tXnomICOfEiWMArF+/Ejc3dx54oDVubh5s374RABcXI3Xq1CU29hIHDuymV68BhIauITo6irp169O1ax+SkhLYs2cbOTnZ9OzZn5o1axV7T2JjL7FnzzYaN25KWNhRkpISqFevAV269MLV9fdl9XJycti+fRORkRGWuEpKGvfs2U5srLlZOTc31zKBd05ODrt3b+XcuTPk5eXRpEkzWrfuwLp1K3B396Bz555W5YSELMXd3aPYJSoLiow8xbFjh+jUqQfHjx8hIiIMX9/7LKvORESEc/Dgbq5eTcPb24cuXXrj4uLCxYsX2LlzM+Hhx2jbthMrViyka9c+uLgYWb9+Bb6+91lNpbNx42pcXIyWWCMjT3HgwG6uXTPh59eSJk2a4ejoSGZmJnv3buf06RM4ODjQsWMPfH2blnodBRX3TtSvb14fPDMzk+3bN3Lu3Bnc3T3x929jOceNvCMmk4mQkCV4eXmTmBjP2bOnqVmzFm3bdra8p0XJzc3lyJH9hIUdJS0tlZYt29OyZTvLu7979zbi4i6RnJzEihULAejZs79l0FlJ33MiIjdKyaXcchkZV5kxYxKVK1ehX7+HAUhIiGfChA/p3XsgDz9s7jP52Wf/ICMjHaPRlYyMdKZNG8+XX06nVav2JCcnEhAwAV/fpkREhGM0utKpUw9LchkYONVyHEDz5v589tlkKleuwoULUbz66lCrmL777msmT/6R++67n9OnT/DbbwcBWL58PgaDI56e1ahduy4BARMAeOqpv9K8uT8XLpxj8uRxzJ//IzEx0ZbyfvllJjEx0ZYYZs0KYOzYr+nYsXuR9+TChXOWsgvGPXv2t0yaNJN77qnNlSupvP/+G5w8+V9q1/aynO/ll9/iiSeeK7LcDRtWsnXrBgB69RqAo6Mjly5dZOzY0URGnsLDw5OsrCwyMtJ57bV/cOzYIbZu3UBw8CbLZOWRkaeYPHkcw4a9UqbkMizsKAEBE1i7dhkREeGAeS11gPnzfyrUJDt37vf8+9/fc+HCOX74YTIA+/btZN++nfj5taRKFTcCAiYwbNgrVsllUFAgHh6edO7ck0OH9jJmzGtW5U6aNJPmzf2ZOvULq36JQUGBdOvWhw8+GF/qteQr7p2oX78hsbGXGDPmNaKjo6ye3YgRrzN06IgbekdMJpPlfbjeuHFTadu2U5GfrV+/kkmTPrN8vWhRENWr12TGjPlUrlyFXbs2W2LIX6O8ffuuVK5cpdTvORGRG6U+l3LL3XffAxiNrqxZs9SybfPmtQAMHvwUV66kMmHCh3h4eBIQMJclS7YwadJMjEZXJkz4kGvXrlmOi4gI58svp7N48WZGjvx9gu7hw19l6dKtBAYG07fvQxw/foR588zrLtet683LL7/FhAnfsnLlLj755CsAFiyYDZjXyH7yyeEATJs2h6Cg1XTp0otGjZqwcOHGIq+pQYPGLFu2nWXLttO0qR8xMdG88MIbLFmyhe+/XwCYm1VL88wzL7JkyRbmzQvhr3/9O/Hxscye/S0Av/wyi5Mn/8vbb3/ETz8tIzAwmCZNmvHDD5O5ePFCkeV98MF4Hn/cOvGcOvULIiNP8eGHE5g/fz1Ll25l/Pj/MGDAo/TrNxiAbdt+v8782tru3fuWGn9B6enpBAYGs3z5Dnr3Hmjp69epUw/mzl3FihU7eeedT4iJiWbWrADatevMnDkrAfPzW7t2Pz4+jct0ruXLzfc4KGg1S5du4913x1l+0Xj88ed4/fV/Mm9eCPPmraF16w5s3bqB06fL3ge1uHcCzPczOjqKTz/9huDgTcyatQQ/vxb8+ON0oqIiLWXcyDvi79+GZcu2s2TJVt577wsAJk8eR05OTpFxdu3ah2HDXuG7735l2bLtDB06gvj4WEu5b7/9EQ880Jratb0IClpNUNBqatasVer3XG5ubpnvlYjI9ZRcyi3n5OTEQw89wfHjRzh//ix5eXmsWrWIJk2a0bSpHwcO7CY5OYkOHbpx7ZqJEyeO4+DgwAMPtCI5OYlLly5aymrfviutWrXH3t7eauUbb28fAOrVa8CLL44CYPXqJZbPn3jiOapVq8HWretJTIwHuKnRwr17D7Qs4Zdfu9e1ax/s7OyoX78hzZv7s2/fzlLLadiwiWWpwGeeeREfn8bs3LkZgI0bVwHmGsj8a+vb9yEADh/eV6Y485tofXwa07Vrb8v2li3b4eTkRMuW7fDw8CQkxHyv8vLy2LhxNU2aNKNevQZlvBtmgwY9Tr16DXB2dsZgMFgSnC5depGQEMeZMyfx9vbBw8OTgwd3l6vs69WqVQcwDwbKy8ujV6/+lqbgRo2a0K/fYE6fPsnOnaGWrhgxMUUn5OURHx/Lvn07ad7cHw8PT06dCufKlRRatGgLmJddzHcj70iDBo1wcXHB1dWVHj368cQTw4iPj+XChXNFxuPq6sqzz77E1atphIausdyD6OioEq+jtO+5/O8REZEboWZxuS0GDHiUhQt/JjR0Da1bdyQ6OooxYz4H4Pz5swAsWfILS5b8UujY3Nzfay4feKB1qefy9KxKq1btOXhwD+npV3F1rcT06RMttV1eXt6AeaT5reLpWQ2IKPdx9es3JDLyFAkJcSQnJ9GqVXucnJwsn+f3t7x0Kbq4IqzkJxmdO/cq8nODwcDDDz/FnDkziIw8RU5ODjEx0TzxxLByx+7n18Lq68hI8/VPnPhxoX2NRtdyl1/Qs8++zPnzZ1m48GdWrgxm5Mgx9O49EIAzZyJ4773XSE5OwsPD03KMLZ53/rt6/PgR3nzz+UKfF6xlL01Z3pE6deoB5l8Siup7mZ6eznvvvWZJavP3yczMKLHc0r/nVHMpIjdOyaXcFl5e9WjfvgurVi0iNvYSHh6elmZGZ2dnAP75z8/o2bN/oWPt7e0tzY35A1RKk7+fi4uR7ds3sXz5AgYMeJQRI17H3d2D99573aoJ805x4sRxACpVMg+4SEpKsPo8Pj4WKPsqQE5OzkWWU1CfPoOYM2eGVRNtly69i92/OPb21s/GxcVcszx79vJCg5vs7OzKXX5BlStXYdy4qRw5sp8pU75g4sSPsbOzo1u3vkya9CnJyUlMmjST++67n7Cwo4we/eJNnS9f/jUNGDCEUaPeL/S5vb29TedcPX36BIDVQLiCZs/+D+Hhxxg16n169x6Is7Mz/fu3LbTf9c3qZfmeExG5UfoJIrfNQw89SXJyEuvXr2TIkL/g6OgIQKNG9wIQGroGe3v7Qn/K68qVVI4dO0zz5v7Y29uzZcs6wNyvz93dAzDXYplMv/+Hm/+fbXHNj7fD+fNniYmJxsenMS4uLvj4NCYy8hRXrqRa9jlyZD8A3t7mUcvOzs4l1mLmN21v2hSCyWQqcp9aterQunUHVqxYyLp1y+nYsbulxi8iIvyG53n09b0PMPfhvP6ZlpRcVq5sHlh08uR/Lduub6rNvxZ//zZ8+eV0AHbt2sL582eJiAjnkUeetjz/7GzzZPL5tYr5711CQlyJ8Rf1TuTfz82b15GdnX3T72pJcnNz2bFjE2CubTcYzHHn/4IBsHbtcry8vBk06DFcXFwsE+cXrEF1dnYmPj6W9PR0yzZbf8+JiBSknyJy27Ru3cHSbPfgg49Ytrds2Q5//zbs27eTjz9+i/XrVzJjxiSefrov0dHny1T2/Pk/snnzOjZsWMU//vE3MjLSee65vwHQoUM3AFauXMiRI/uZPPlzjh8/QnJykmV0c9u2nQHzSObQ0LWsW7fCZtddkhUrFrJ16wZmzJjEyJHmASTPPPOS1d+jR7/IunUrWLx4HsuXL8Db24c2bToC0K1bXyIjT/H9998UuSKPm5s7Q4eOICMjnZdeepwFC2YTHDyXF154jO3bN1n2699/CBkZ6SQnJ9GnzyDAXIv6xhvP8cYbz5WruTffo48+g9Hoyg8/TGHy5HFs3Lia8eM/5NVXhxY7QAXAxcWFHj36sWfPNubO/YGQkKWMGfN3y6hsMI98Hjv2HbZu3cCyZb8C5n633t4+1K7txf79u9izZzuhoWsZP/4DAEtf1mbN/AGYOXOqJVkvSlHvROXKVRg+/FUyMtJ5663nWb58AfPn/2SzlXN27Ahlw4ZVLFgwm5deeoLk5CQGD34KT8+q1KlTFy8vb1atWmTpT9y9e1+io6PYtCmEvXt38P77rwOwffsmMjPN3QA6djT39/zuu6/YvHkdBw7stsn3nIhIcdQsLreNg4MDgwc/zZkzJ6latZplu729PR9+OIHvvvuajRtXs2fPdsCcjOb3t8yv6SqqVsVodMXNzYMvvzQ3U1avXpNPPvnKMp1K58696Nt3L0FBgQQFBeLn14K33vqQyZPHsWfPNnx9m1K/fkOeffYlFi+ex/jxH2A0utKv38OW817/d8E48v9dsDaurLU/586d5vPPx1iuY/Tojy3zQ3br1oe0tA/4/vtv+PrrTy335O23P7IM3Bg8+CliYi6waFEQjz8+DGdn50LxDBv2Cs7OLixYMJuZM6dhNLrStm0nS5M5YLlXRqMrbdqYp71p1OhemjRpxsmT/yUvL6/YayiuFtLDw5NvvpnFtGlfEhKylJCQpRiNrnTv3pfs7CwcHR0tsV5/v55+egTZ2dnMmTMDgCeeGGbVj9DFxYWoqEg+/3wMRqMrffs+xGOPPYuDgwN///s/mDFjEh9//BZGoyvPPfc3Dh7cza5dW8jMzMTLqx7Dh7/KwoU/s2NHKP7+bYqMv7h34umnn8dgMDBrVgDTp08EzIltfpP5zbwjV6+m8e9/f2L5+rHHnuH551+3fP23v71FUFAgwcFzGDjwUZ54YhgxMdFMmPARYB7l7ufXkvnzf+LEiWP4+7ehW7c+/PbbftauXc7atcvp2bM/rVt3KPV7TkTkRtnllfS/hkgZbAmOw9nNCd/W7qXum5WVhcmUU2wfspycHFJSkvD0rFam/pVZWVlcvZpG1arVuHo1DZPJZGn6vt6VK6k4Ojrh4uICmJtWTSaT5WswD5DIzs7C3d3jpvsFluTgwT28997rfPjhBFq1ak9WVpZVwn29hIR4KlWqbBVrQZmZmcV+li8vL4/ExHg8PKoWurdJSYkMHdqPQYMeZ9So36d4mjlzGqdPn+CLL25u+cD09KtkZmaWeI1FuXIlFaPR1WpC/OvjrlLFrdDn5mtNoGrVapbnmJAQR9Wq1S1fZ2aau0aU1n+1uHciNzeXxMR43Nw8rAZd3YjMzEweeaQLjzzyNC+99CapqclUrVq92F9SMjIyrGZLSExMwM3N3XIfUlNTcHWtZHVfUlKSMRgMhb73yvs9d/pwKmlxmfQaqsnWRaRoqrmU28rZ2dnSl60ojo6O5VohpGB5xSWs+fInCc9nMBgKJSWurq64ut7cSObyqlSpcqmxV6tWvcTPS0ssAcuURwXl5OSwYcMqyxykDz442PJZePgxVqxYyPjx/ym17NK4ulayWnmorK5/Ztfz9Kxa5HbztVrfs+uv3XzPSr9vxb0T9vb2t2Q1Gycnp1LLLZhYAoWSdje3wr/oFfdLV3m/50RESqM+lyJ/YklJCUyePI6UlCTGjv3aMhE5mFfe+fzzaVar5IiIiJRGzeJy08rTLC5mmZmZXL58kRo17rmhGj1bMZlMpKVdsZoPUm6/vLw8zp8/S+XKbuXuOnC7qVlcREqjZnGRCuDi4kL9+g0rOgwMBoMSyzuAnZ2dZZUpEZG7nZrFRURERMRmlFyKiIiIiM0ouRQRERERm1FyKSIiIiI2o+RSRERERGxGo8XFJq4k5hB7LqP0HUXkrnYlIYdbt3aViPwRKLmUm+ZR05EzR68SviurokOR2ygqKgoAb2/vCo5Ebjef5hU3N6uI3Pk0ibqI3JAOHTpgMpnYv39/RYciIiJ3EPW5FBERERGbUXIpIiIiIjaj5FJEREREbEbJpYiIiIjYjJJLEREREbEZJZciIiIiYjNKLkVERETEZpRcioiIiIjNKLkUEREREZtRcikiIiIiNqPkUkRERERsRsmliIiIiNiMkksRERERsRkllyIiIiJiM0ouRURERMRmlFyKiIiIiM0ouRQRERERm1FyKSIiIiI2o+RSRERERGxGyaWIiIiI2IySSxERERGxGSWXIiIiImIzSi5FRERExGaUXIqIiIiIzSi5FBERERGbUXIpIiIiIjaj5FJEREREbEbJpYiIiIjYjJJLEREREbEZJZciIiIiYjNKLkVERETEZpRcioiIiIjNKLkUEREREZtRcikiIiIiNqPkUkRERERsxi4vLy+vooMQkTtbmzZtyrX//v37b1EkIiJyp1PNpYiUytnZuUz75eXllXlfERH5Y1JyKSKlGj58OPb2pf+4cHZ2Zvjw4bchIhERuVMpuRSRUj3zzDM4OjqWuE9eXh52dnY888wztykqERG5Eym5FJFSValSpdTay/xayypVqtzGyERE5E6j5FJEyqSk2kvVWoqISD4llyJSJiXVXqrWUkRE8im5FJEyK6r2UrWWIiJSkJJLESmzomovVWspIiIFKbkUkXIpWHupWksRccbcrAAAIABJREFUEbmekksRKZeCtZdOTk6qtRQRESta/lFEyu3KlSv0798fgDVr1ii5FBERCyWXckc5viuVYztTKjoMkT+cZu3duL+Le0WHISJ/AoaKDkCkoLRkE+73uNC4pVtFhyLyh3HmSCppKaaKDkNE/iSUXModx1jZAc9azhUdhsgfhmukAcit6DBE5E9CA3pEROT/2bvzuKrq/I/jL+CyunDBXRQlcSd3gVRcMdxKrSwrc8bGlmmf+tVYWjpWkzplljhqbpmYu7ig4gZq4oZLmqaGiqKIC6vKooL8/mC4eQMB7epFfD8fDx91z/o59/iQN9/v+X6PiIjFKFyKiIiIiMUoXIqIiIiIxShcioiIiIjFKFyKiIiIiMUoXIqIiIiIxShcioiIiIjFKFyKiIiIiMVoEnW5L8XEHOHnn3fRvn1XatasZe1yinXhwjl27NjCsWNH8PZuROvWj+DhUfuOjrVq1RIuXDjHtWtXsbW1o2rV6nTt2pMKFe7dW4127txKevplunbtec/OaS0rViykXr2GNG3a/I6PcfjwL4wfP5pXX32P1q39LVidiEjpo3Ap96WYmMNMn/4t9eo1LPXh8sKFc7z55gukpqZgNLqxdu0KAAYMGMzQoW/d9vGWLJlLfHyc2bJZsybx1lsf0bVrD4vUDJCQEM/q1Ut54onncXNzN1s3dep44uPj6NixOwZD2f1nJCnpIpMmjaNt23Z89tm3d3yczMwM4uJiychIt2B1IiKlk7rFRe4yOzs7atWqw9ixk1mwYD0LF27A2dmFRYt+ICsr646OWblyVZYv38r06Yt5550RZGZmMGPGnYefwkRErGHhwtlcv36twLoRI8YyfvyMMh0sASpVqsK//x3Ma699YO1SRETuG2X7J4M8sG7cuMH+/bs5fPgXrly5RMuWfrRs6YvBYCAtLZUtW9ZTv35jGjXyMe2Tv7xBgyY0bNiU3Nxcdu2K4ujRg9jZGfD2boSvb3tsbGwAiI7exoULCQQEBBIVFcmZM6fo2DGQhg2bmtVSqVIVvvpquumzq6sRH58WREdvIz39Mk5OTgDs27eL3377lcceG4CLS7kir8/e3h4nJydq165L7dp1+emnDezZs4PTp08C8PPP0fj7d6RKlWoAZGZmsmFDGN7ejWjc+GEOH/6FtLQUGjduxtatESQknKFu3Xp07hyEwWAgJuYIR48eBGD9+jAqVnSlWbPW1KnzEKtWLeHGjRu4uroBkJ2dzZo1oQQEBHL06CEOHtxH5cpV8fMLoFy58kRFRXLhQgL+/h1p0KCJ2XUkJl5g+/bNnD+fQPXqNfH370jlylVved2xscc4eHAf7dp15tCh/cTEHKZ+/cZ07BhY7P3asmUDHh6eAGzbtgl7e3uaN29D/fqN2bFjCzExh2nSpDmtW/tjMBhISUlm69aNADg5OVOzZi3TtbZr14UzZ05x4MAe7Ozs6NChK56eXma1/vxzNL/8shej0R2Dwb7AtWRlZbF160ZOnTqBq6vb/2ppZFofHr4cN7dKeHh4sn37Ji5fvkS/fs/i6OjI/v27OXLkIF5e9fHxaWG6zyIipYHCpZRJ69eHMX78aNPnJUvmUrlyVaZOXUC5cuWZMWMideo8xDfffG/a5qefNhAcPJZx46aQnZ3NZ5/9k+3bN5sdt23bdowaNR6DwcDGjauJjAxnzZpQYmKOANCzZ/9ia7t27RoHD/6M0ehGpUpVTMv/9a//IzMzAze3Sjz66GMlvtbs7GyOHz8KQJUq1dmxYwvBwWPx9PQyhY6MjCsEB49l0KCXaNz4YaKjo5g7dzoeHp4kJyeSmZkBwC+/7OUf//iY48ePcuDAXgBWrFiAwWCPm1sl6tR5iGnTviEzM4P69RvRsWMg2dnZBAePZcmSEBIS4nF2diEzM4NZsybh6OhIamoKAHPnTufJJwfx8svvALB3705Gj36fzMwM0z4TJ47hiy8m0aqVX6HXevjwLwQHj2Xt2uWm7/zddz8p0f1asiSEc+fiSU1NMZ0PwNPTi7i4WJydXZg/fxZeXt5MmTKf5OREgoPHAvD003+hadPmpmtdtWoJsbHHTMeZPXsy06cvpnbtuuTm5jJ58pcsX74AAKPRzfQd5Ltw4RzDhr1GfHycWS1DhrzOwIFDAJgy5SuzfcuVK89zzw3lzTdfMNWbmZlBq1Z+fPHFpBL/fRERudvULS5lUkBAIC+88ApTpsxn+fKtDBw4hMTEC0RErMFgMPD4409z5MhB4uJiTftERKzBaHTDx6clS5fOZfv2zQwe/CqhoZtZtGgjffs+Q3T0NtavDzM7V40atVi0aCOhoZupVcuz2NrWrl1OZmYGTzzxvNnyp556gUaNfPDxaVnsMVJTU9i4cTXz5s3k44/fJjU1hd69nzS1gpZUYGBvFi+OYP78dXh6ehEevpyMjHR69OjLgAGDAZg4cQ5z566mQ4euACxbtgUvL+9CjzdnThhLl27iqadeIDMzg8aNm7FiRRSLFm3E09OL1auXkp2dzeXLlxg7dgRGoxvBwSGEhm5m/PgZODu7MHbsCHJycoqsOyMjg+nTF7NiRRTduvUq8f1KTU1h7NjJLFu2hVGjvvrfsdL58cc1LFy4gR49+hIbe4yTJ49Tr14DFi3aWOj5s7IymTkzlKVLN/HWWx8CeX9/AKKiIlm+fAEdOwayfPlWFixYz1//+prZ/t9++2/i4+P417++ZvHiCGbODMXHpwWzZk0y+zuZkBDPwIFDWLw4gmnTFnHs2BHi4mLp3ftJli3bwuTJ8xg06OUivysRkXtN4VLKJBcXF55/fijp6VeIjAw3PRuYPxCme/c+AGzYsArIa0k6dGg/PXr0w87OjtWrlwLQvHkb4uJiOXv2NC1b+gJw6NDPZucaMGAwFSu6FtuVDZCSksyMGRMxGt147LGnzdYNGvQS33zzfYkGKGVmZjBu3Cd8//1/2bt3J4MHv8rrr9/+c4FBQX0xGAy4ubmbRjEnJV287eMA+Pp2oGrV6tja2tKuXWcA2rfvgqOjIxUrutKxYyCZmRnEx8exZ88OUlNT8PfvSE5ONkePHsLOzo5mzVqRmprCuXNnizxX795PUrt2XRwdHTEYDCW+Xx4enrRo0RYAf/+OALRs6UulSlVwcHCgc+eg/+2zv9hr9fCo/b9r7QLkBUGA0NAfARg06GVT2L/5UYnExAtER2+jadPmGI1uHDt2hMuX00x1HTly0LSts7MLTzzxPAaDAReXctSo4QHAzp0/cfjwLzz0UP0/NYpdRORuULe4lEkZGRl8+OFrph/U+T+Us7IyAahduy4+Pi1Yu3Y5gwe/ytatEQB07hxEVlaWKSi8997QAsfOzs42+1y/fuMS1zVlypdkZmbw7ruf4OLicvsX9j+VK1dl4sQ5RESsYdq0CaSnX8bOzu6OjwdQs2be1EjXr1//U8e5lfxnNG/cuGF6NjQ0dB6hofMKbHvjRtEtlz4+LUz/f7v3K1/+s5iF1Zibe6PI898sfyR9/sCn48d/o0YND+rUeajQ7fOv/dCh/bz99l8LrL+51bZZs1Y4ODiYPleqVIXhw8cwfvxo3nlnCH5+Abz99nAqVapc4npFRO42hUspk2bP/i9Hjhzkrbc+olu3Xjg6OtKjR1uzbXr3fpKxYz9mz54dRESsxsvLGy8vb1MYqVHDgxkzlhYIIcV9vpVt2zaxadM6/PwC6Ngx8E9cXd6AHnf3SvTv/yybN69jyZK5dO7cgwYNGpObm3tHxyzpdViCo6MjAB98MJouXQpOn2RrW3Sniq3t70E6v1W6pPfrbnB2Nv9FITU1hezs7EJH0zs5OQPQs2c/3nrrowLrb772m68zX8eOgbRs6cvChbNZuHA2b701mJCQVff0/omIFEXd4lImrV27Ag8PT3r3fgInJyeuXr0KmLcKtWvXBWdnF6ZOHU9MzBF69OgH5IWVBg2akJAQT0zMYWxtbc3+3MkP8cuXL/HNN58D8MYb/yx0m+TkJA4f/uW2wqGdnR3vvDMCgPHj/0V2drZpEM/x47+ZtstvLbsd+QHwzJlTt71vcerVawhAZGR4ge+3uGD5R3fjfv0ZDz1Un8zMDI4ePVTo+tq16wKwadM6rl27dtvXnp2dTYUKFfnb396kR4++JCZe4OTJ4xa9BhGRP0Mtl3JfW7MmlH37dpkta9++C506dSc8fDkREWsoX74i8+fPBGDr1gjeeGMYTk5OODk50a1bL8LCFgN5g4DyDR36Nh988AojR/6D3r2fpEqV6kRGhlOzZi1TmLsd06ZNIDU1hRo1PFi5cpFpubt7Zfr3fxaAd999kYSEeEaMGEtAQLcSH7tevQYMGDCYRYt+YNmy+fTrNxCj0Y0lS0JwcHAgIyOdGTMm3nbNbdu2Z9q0bwgJ+Y60tFSuX792W6PYi9KypS/Nm7chOnobn3zyDgEBgZw48RsREWsYP37mbb+9yNL368946qkXOHRoP8OHv0n37n1o2rQFK1cuNK0vX74Cgwe/yg8/TOGdd/5Kr15PkJmZwYYNqxg4cAjduvW65bHT06/w8stP06/fQCpXrsb+/btxdnbB3V3d4iJSeihcyn3p5rkL/6h27bo89dQLJCTEM3bsxwD06NEXH5+WLFjwPUePHqR58zYA+PkFEBa2mLZt25k9t9a8eWtGjfqK4OCxzJ2bN0el0ehGs2atC9RQnJiYI6a38iQkxLNw4WzTOi8vb1O4rFq1BgkJ8bi5VSryeHZ2dgWeSXzuuaFERoYzbdoEOnXqzttvD2fevJlMnDgGo9GNIUNeZ9asSdjY2JrVbt4Fa76uTp2HeP75oSxd+iNjxgzH2dnFFC4NBoPpWH/c/4//X9jxbW1tGTFiLFOmfMXGjavZuXMrAK1b+xf5vOWtvvOS3C87O7tin0vNP/6t/lvU9eVr164zb745jOnTv2XFioXs2bPDNLo+f79nnvkrBoOBmTODmTRpHJA3JVJ+l/mtrjc1NYV69RoyfXrehPkNGjThb397C1dXY5HXJSJyL9nk3ukDWiJ3wc41yWRdBZ8A9+I3LoHk5CQqVnQ1Pft26VIaLi7lTJ937NjCyJHvMmzY53TpElToMVJSknFwcKBcufIWqelWbty4QXr6FYu+IzwlJRmj0e1PdQ1nZGRw7dpVXF2Nd6WL+fr166SlpeDmVulPD0qCe3e/ipObm8ulS2lFBr8bN26QnJxIxYpGs4E7xbl+/ToZGeklDpWHt6dgZ3ODR3oX/YuLiIglqOVSyjR3d/MfphUrugJ50+1ERKxh8eI5GI1upmlpCvPH92rfLba2thYNlmCZ2l1cXP7UyPbi2NvbF/lWntt1r+5XcWxsbIoNf7a2tnd07fb29mqtFJFSSwN65IF04MBepk//lurVPfjqqxk4OzsXv5OIiIgUSy2X8kBq27YdS5ZEUr58BWuXIiIiUqYoXMoDSaFSRETk7lC3uIiIiIhYjMKlSCFiY4+xeHGIafL1u73fvTRu3CeMGvWetcsQEZEySuFSpBCrVy9l2rQJnDjxW/EbW2C/oqSnX2HJkrns37/bIsc7dy6euLhYixyrNMjNzSUyci2rV4dauxQREUHhUqRQAwe+yL/+NZ4GDZrck/2Kcvz4b3z33dckJydZ7JhlSU5ODmPGDOfYsSPWLkVERNCAHpECdu7cyoULCUDeJNd2dnZs2bKBGjVq4eTkxM6dP3H58iWaN29Dq1Z+pv1WrVpqervM+fMJ1KxZy7QuJuYIe/fuID39Cp6eXnTo0A0nJyfT+uvXr7NjxxZOnTpBbm4uDRo0oXVrfy5dSmPbtkgA9uzZzpUrl/Dw8DSdNzc3l127ojh69CB2dga8vRvh69vebLLzlJRktm3bxPnzZ2nZ0pdr10rWZX+rmnfs+ImzZ08TFPS42UTlERHhpKdf5rHHBgCQmHiB7ds3c/58AtWr18Tfv6NpTsdLl9LYvHkdjRo9zNWrWezdu4OcnBwqV65K/fqNadTIx3TctLRUtmxZT4MGTWjYsGmBOlevXgrA8eNHWblyEY6OTjz66GOcPn2Sn3+Oxt+/o+l965mZmWzYEIa3dyMaN36Y6OhtXLiQQEBAIFFRkZw5c4qOHQO5ceMGaWkpNG7cjK1bI0hIOEPduvXo3DnINAF/UZKSLpKUdNGiv2SIiNwvFC5F/mDDhjDTayW7du2Jvb09S5aEkJaWQmpqCgCZmRnMnz+LDz4YbXoX9A8/TDatHzXqK1O4XLDge2bODDY7R0jId/znP99RpUo1zp07y6hR7xIbewyj0Y2rV6+SmZnBa6+9T7NmrQkPXw7kvRd9375ddOnSg1at/MjOzuazz/7J9u2bzY7dtm07Ro0aj8Fg4MSJGD788DVSU1MwGt1YsOB7ADw8PIv8DoqqOS0thalTx1O+fAXT6yCTkhIZO3YE3br14rHHBrB3705Gj36fzMwMnJ1dyMzMYOLEMXzxxSRatfIjNTWZ4OCx1K/fiJiYIzg7u+Dv35HlyxdQp85DfPPN96bz/vTTBoKDxzJu3JRCa126dC4AR44cJDHxApUrV+XRRx/j+PHfCA4ei6enlylcZmRcITh4LIMGvUTjxg+zceNqIiPDWbMmlJiYvJbPnj37ExGxmrlzp+Ph4UlyciKZmRkA/PLLXv7xj4+L/O4AoqIiWbduBcHBIcVuKyJS1qhbXOQPhg8fw5NPDiqwPCEhno8++oJly7YwefI8AFasWGhav2DBej799BuzfQ4d2s/MmcG0a9eZkJBVrFy5jffeG0lCQrwpvH377b+JjT3GiBFjWbBgPcuWbWHMmP/Ss2d/vLy8GT16AgBvvz2cuXNXM3ToW0BeqNq+fTODB79KaOhmFi3aSN++zxAdvY3168PIzs5m3LiPSU1NYdy4qSxYsJ4ffwzHaHQr8vqLq7lTp0dxdnYhPHyZaZ9Nm9YC8PjjT3P58iXGjh2B0ehGcHAIoaGbGT9+Bs7OLowdO4KcnN/fHR4Tc4QvvpjE0qWbePvt4Tz++NMcOXLQ7JnQiIg1GI1u+Pi0LLTe6dOXANC795PMnbvaLJiWVI0atVi0aCOhoZupVev34B0Y2JvFiyOYP38dnp5ehIcvJyMj/baPLyLyIFG4FCkhDw9PfH3bA/DQQ/Xx9PTi1KkTRe4TEbEGgA4dupKUdJETJ37D09MLo9GNvXt3cOHCOfbs2YGXlzcBAd1M+7Vs6Vvsu6bzu4ObN29DXFwsZ8+epmVLXwAOHfqZQ4d+Jjb2GJ07P0rz5q0BqFSpcrGtlsXV7OTkRJ8+T3Ho0H5Onz5Jbm4uq1YtoUGDJjRq5MOePTtITU3B378jOTnZHD16CDs7O5o1a0Vqagrnzp01ncvPL4BWrfywtbXF2dmZ7t37ALBhwyoALlw4x6FD++nRo59F3jt+KwMGDKZiRVdcXMqZLQ8K6ovBYMDNzZ3Wrf2BvC7vwly6lMaECZ8zYcLnrF8fxpkzcabPO3duvWu1i4iUNuoWF7lDnp5exY66jo2NAfKm//kjZ2cX4uPjAGjfvuttnTsrK4uEhHgA3ntvaIH12dnZnD59EoBHHul8W8curmbI6zpetOgHIiPDad36EeLj4xg27HMA03lDQ+cRGjqvwDHyn0sFaNastdm62rXr4uPTgrVrlzN48Kts3RoBQOfOQbd1Dberfv3GxW5Ts2ZtIO/52MLcuHEDyAXy3hmeJ+9zbm7un65RROR+oXApcod+DxC35uSU987y2bNXULVqdbN1NjY2/PrrAQBSUoofCX5zQMkfVFKjhgczZiw1G8CTf+xVq/K6i69cuVTssW+nZgAPj9r4+XVg1aolXLhwDqPRjQ4d8gKyo6MjAB98MJouXXoUOL6tra0plBfWGtm795OMHfsxe/bsICJiNV5e3nh5eRdbd164+93tBLo/fn93so3R6MY774wA8h6XWLduhemziMiDRN3iIndRfovY1q0bsbW1NftjY2ND7dp1gbyu6Ozs7EKPkd89fubMSdMyg8FAgwZNSEiIJybmcKHHzu/+3rZtk0VrztenzwBSU1NYvz6Mfv2exd7eHoB69RoCEBkZXmD/kgTydu264OzswtSp44mJOUKPHv2K3D6/prg480cU8gfxHD/++5yj+a2qIiJy96jlUuQu6t//OZYvX8C0ad9w5swpHn64FdHR2zh58hgTJ86hYkVXBg4cwvz5sxg69El69XoCW1s7Vq9eyosvvkGHDl3x9m6E0ejG2rUrqFKlOteuXSUoqC9Dh77NBx+8wsiR/6B37yepUqU6kZHh1KxZi3feGUGzZq3x8vJmz54dvPvu3+jWrTdXrlzi0KH9RT53WVzN+SGydWt/atTwICEhnqCgvqb9W7b0pXnzNkRHb+OTT94hICCQEyd+IyJiDePHz8TDo3aR35mTkxPduvUiLGwxAAEBgUVub2dnR+fOj7Jp0zoWLPged/fKNGzYlEaNfDAa3ViyJAQHBwcyMtKZMWNiSW/dn+LnF6BpiETkgaVwKVKI/Ba2/FYxOzu7Eg0o+WNXrNHoxtdfz2TixC9Ys2YZa9Ysw9nZhU6dunPt2lXs7e154YVXcHR0YuHC2cyYMRFnZxfatm2Hg0Ne97LBYOD11//J1KnjmTDhMyBvEE/z5q0ZNeorgoPHMnfudNP58p9jtLOzY/ToCXz55Sj279/NoUP76dgxsNjR4iWpOf/4jz/+DCdO/Ia7eyWz727EiLFMmfIVGzeuNg1mad3a3/S8Zf73equWTD+/AMLCFtO2bTsqVapc7Pf+zDNDuHjxvGkE/ttvD8fT04u33x7OvHkzmThxDEajG0OGvM6sWZOwsTG/v39UWH1//DtRlGrValCtWo1itxMRKYtscvWkuZQiO9ckk3UVfALcrV3KHfnuuwksWRLChAmzaNz4YbN1GRnpZGVlmQWxm+Xm5pKcnIjR6F5okM3OziYtLQVXV7cCE3mnpCTj4OBgNqm5+bkzsLW1NZu4vSSKq/nq1atkZ1+/5XmvX79OWloKbm6Vbmu0944dWxg58l2GDfucLl1KPpgnKeki5cpVKHCdKSnJGI1uJQqGZdHh7SnY2dzgkd6F30cREUtSy6WIBezfv5uTJ4+zevVSjEY36tSpV2AbF5dyBaa6uZmNjQ2VKlW55XqDwXDL9W5uRYdxFxeXItffer+ia3Z0dDQN4CmMvb296a08JZGUdJGIiDUsXjwHo9ENf/+Ot1XvnX4/IiJiOQqXIhYwcuS7ZGZmUL9+I15//Z93HOYedAcO7GX69G9p1MiH998fjbOzs7VLEhGR26RucSlV7tdu8bi4WMqXr4ibm/sD2/VqCVeuXAagfPkKVq6kbFG3uIjcS2q5FLEAT08va5dQJihUiojc/zTPpYiIiIhYjMKliIiIiFiMwqXcd7Zs2cBLLw3g1KkTxW8sVrFz51YiItZYu4xSady4Txg16j1rlyEictfomUu571y5cpm4uFiuXbtm7VIeaOnpVwgPX463d0OaN29jtm7q1PHEx8fRsWP3AnNyPuji4+O4fPn23vcuInI/UculiNyR48d/47vvviY5OanAuhEjxjJ+/AwFSxGRB5D+5ZcyIzb2GHv37iQtLQVPTy/at+9qmifx8OFfSEtLoXHjZmzdGkFCwhnq1q1H585BZgHo8OFf2LNnBzY2NjRr1po5c6YycOAQWrXyK/LcqakpREdHcfr0SSpWdMXHpyWNGvmY1u/bt4tffz1Abm4u3t6N8PPrYJqyaMuWDdSoUQsnJyd27vyJy5cv0bx5G9M5V65chL29Az16/P7+7uvXr7Ny5SJq1qxlmmg8JuYIe/fuID39Cp6eXnTo0M30ppro6G1cuJBAQEAgUVGRnDlzio4dA6lXryGHD//Czz/vwt29Mk2btqBOnYewsbEhKSmRXbu2Eh8fR4UKFfHxaUnTps0BSE5OYtu2SAD27NnOlSuX8PDwpFUrP1atWsKNGzdwdTV/zaQl7s8fhYcvx82tEh4enmzfvonLly/Rr9+zuLtXIjHxAtu3b+b8+QSqV6+Jv3/HYid0v3HjBvv37+bw4V+4cuUSLVv60bKlr6mGCxfOER0dRVBQX376aeP/Xn1ZmcDA3lSoUNHsWPv37yYm5jBpaal4edXnkUc6mc3bGRt7jF27tnLt2lXatGlX4I1ORX1fsbHHOHhwH+3adebQof3ExBymTZtHCrQgi4hYg8KllAnr14fx5ZejcHZ2wdHRkdTUFH78cQb/+c9UKlWqQnR0FHPnTsfDw5Pk5EQyMzMA+OWXvfzjHx8DsGzZfCZP/hIAZ2cXfvhhCgCDB79a5Ln37t3J6NHvk5mZQeXKVUlMvABAcHAI9es3YsKEz1mzJhSj0Y3U1BQAHnmkEx9/PA47OzuWLAkhLS3FtC4zM4P582fxwQej6datF4cP/8LGjat5+OFWeHjUBiA6OoqpU8fz7rufALBgwfem92rnCwn5jv/85zuqVKnGxo2riYwMZ82aUGJijgDQs2d/Jk/+krCwxTg7u5i+kxUrorC3t+e115411ZTv1Vffo3//Z0lLSyE8fDkAW7dGsG/fLrp06UGrVn5Mm/aNaUL5jh0DLXZ/CjNlyldm32u5cuUZPPhVs3uSf20TJ47hiy8mFfmLwvr1YYwfP9r0ecmSuVSuXJWpUxdQvnwFzpw5xbfffsHKlYuIjT1mOvbKlYv47ruFGAwGrl+/zrhxH7NlywacnV0oV648iYmz6dChKx9/PA7I6xp/9dWBpv1DQqbx1VfT8fFpUaLv6/DhXwgOHsvatctN9/Phh1vd8rpERO4ldYvLfS8p6SJffjkKT08v5swJIyRkNW+88U/i4+OYM2eq2baBgb1ZvDiC+fPX4enpRXj4cjIy0snIyGDy5C+pUcODhQs3MH/+OlMI8fDwvOW5L1++xOjR7+Po6MjUqQuYO3c1K1du44svJlG/fiN27NjCmjWhdO/hlPSJAAAgAElEQVTehx9/DGfRoo306NGX7ds3s3nzOtNxEhLi+eijL1i2bAuTJ88DYMWKhQA89tgAACIiVpu2Dw9fjrOzC506PcqhQ/uZOTOYdu06ExKyipUrt/HeeyNJSIgvEDhr1KjFokUbCQ3dTPXqNQkLW4yHhydLlkTy449rGDnySxwdHbG1teWNN4bx4Yf/JjR0C1OnLsBodGP27Mnk5ubi5eXN6NETAHj77eHMnbuaoUPfAmDZsi14eXlb9P4UJSEhnoEDh7B4cQTTpi0iMzODsWNHYDS6ERwcQmjoZsaPn4Gzswtjx44gJyfnlscKCAjkhRdeYcqU+SxfvpWBA4eQmHihwOCkqlVrEBq6mfnz19G9ex/i4+PYv383AEuXzmXLlg088cRzLF4cwdy5q5k6dQEvv/wPs2N8+uk3LFu2hYkTfwBgy5b1t/19ZWRkMH36YlasiCq2dV1E5F5RuJT73u7d2wHo3r0PFSpUxN7enl69nsDZ2YVNm9aZbRsU1BeDwYCbmzutW/sDeT/Mz58/C+S1KLq6GnFycqJDh24AHD16EMh7NWFY2GLCwhabwkZ09DYyMzPo1q03devmvU/cwcHB9IN+y5YNAPTu/SR2dnZUrOjK448/A8CuXVGmujw8PPH1bQ/AQw/Vx9PTyzQavnHjh6lfvxFhYYvJyckhMfECO3f+RN++z+Dk5GSqpUOHriQlXeTEid/w9PTCaHRj794dZtc/YMBgKlZ0xcWlHAaDAU9PL+Lj49i0aS1Gozvt2nU2bRsQ0I1mzVqzZ892DhzYg5tbJTIzM0hJSb7n96cozs4uPPHE8xgMBlxcyrFnzw5SU1Pw9+9ITk42R48ews7OjmbNWpGamsK5c2fJysoy3cuwsMWcPn0SyHsH+/PPDyU9/QqRkeGm7vD4+Dizc3bv3gcXl3I4OTnxyCOdgLwuc/j9l4KBA1807V+3bj2qVath2v/m+92gQROaNm1u+mXjdr6v3r2fpHbtujg6Our5VhEpNfSvkdz38oNhkybNTcvs7Ozw8WlBdPS2W7Z81ayZ18V8/fp1ateuC+R1w+bk5GBnZ8fhwwcAaNgw79nJn37aYAoONWp40LVrT+LjTwGYgsIfnTsXD0D9+o1Ny+rUeQgoGFhu5unpRVxcrOlz374D+fLLUabn+CAvWADExsYAeVPc/JGzs/k7zm+uA2D48DGMGvUu48Z9wty50xk27DMaNGgCwOrVoXzzzeem683ver56NeuWdRfGEvenKM2atcLBwcH0OT8ohobOIzR0XoHtb9zIIT39ChMnjjEtGzbsc2rXrktGRgYffvgaR47k/UJRo4YHAFlZmbc8f40atf5X5zWysrJITLxA06bNcXU1Fln3zSpVqsKhQ/uB2/u+8rvRRURKE4VLue85OeUNcrh8Oc1s+dmzZwBwcHAsdL+b3wFub2/PCy+8wpw5U/nb357A1dWNI0cO0qVLD9zc8t5z/uKLbzJo0MsA2Nramh37Vq15+bWlp18xhY20tFSAAgNAbpZ//HwBAYFMmjSOdetWcuTIL3TsGEjVqtXNzjF79grTssKusbDPdevWY/r0JWzcuJrJk7/kzTcHM2vWMrKyMvnmm89p0KAJw4Z9jodH7UKf6wTIzc295XXcXN+fuT9FsbW1M/vs6Jh3vA8+GE2XLj0K2d6W3NxcFi7cYFrm4lIOgNmz/8uRIwd5662P6NatF46OjvTo0bZEdQCm1sPk5MQS7/NHt/N9/fHaRURKA3WLy33H3t4e+L271NMzryUwv+Unf118fBxeXt4l7i709W1vaukrV648b731If/3f6NM652dnXF1NeLqajQFQy+v+sDvz8vlyw9c+evzW8Ly6vzZbF1JODk58fjjTxMZGU5CQjx9+w40rctvjdy6dSO2trZmf4oLaNnZ2RgMBoKCHufNN4cBsHfvDo4ePQTA00//xTSI6Nq1q0DeiGrA1Fp45szJIs9hqftTUvXqNQQgMjK8wPeRH9ptbGxM99LV1Wj6O7V27Qo8PDzp3fsJnJycuHo175qLek7zZgaDgfr1G5GQEM/x47+ZrSsuhOe719+XiIil6V8pue/kdxfOmPEtzs7OtGnzCJ6eXixa9AOZmRm0bduOJUvmAvDcc0NLfNwxY/IGgfTs2Z/z5xM4ffok+/fvpnnzNrf8gd6mzSM0bdqcbds28d57QwkM7MOZM6fYsCGM//73Rx5//GkWL57DF198xMCBQ6hZszaTJo0FoG/fZ27runv06MeCBd/j5eVt1h3av/9zLF++gGnTvuHMmVM8/HAroqO3cfLkMSZOnGMKToX56KM3eOih+vj4tCQyMhzIex6wevW87uCIiDVUqFCRY8eOEBIyDYBdu7bSv/+zeHs3wmh0Y+3aFVSpUp1r164SFNTXNP3Rzd+RJe5PSbVs6Uvz5m2Ijt7GJ5+8Q0BAICdO/EZExBrGj59pCsuF6dSpO+Hhy4mIWEP58hWZP38mkDci/o03hpXo/EOGvMFHH73Be+8NpWfPftSpU4+NG1dTr14DXn21+Dfz3OvvS0TE0tRyKfcdD4/aDB78KhcvnicqKhKDwcC//x1M69b+hIUtZuTId4mJOcw774wwTYWT34J3c3fzza1YkPcMY2pqCvPmzSQm5jCRkeF89NEbptBVGFtbW0aO/IquXXty8ODPTJjwGZs2reWRRzpz5cplqlWrwfjxM3B3r8ysWZP4/PNhlCtXgfHjZ5i6sO3s7LCzK757s2bNWvj5BfDkk4PMlhuNbnz99UyaNm3OmjXLGDfuE3bs2ELDhk1NrY2FtWBmZ2dTtWp1IiPD+fTTDzhz5hQvvPAKLVv6UqOGBy+++Ab79u3in//8O6Gh8/jgg9FUrlyVn37K6042GAy8/vo/AZgw4TP++9//mJ4xNRgM2NjYmv7fEvfnVv643tbWlhEjxtKtWy927tzKl1+OYunSH6lXryE3bhTdAvnUUy/QvHkbxo79mI8/fpvatevyzDN/JTMzg6NHDxZZZ/5/W7f2Z+TILylXrjxLl/7I119/io2NjSmwF3a/7ex+/+Xldr4vEZHSyCa3pH01IvfAzjXJZF0FnwD3YrfNysoiO/s65ctXMFuWnn6FSpUq3/a5r1+/btbKl52dTe/e/jRv3oZx46YUu392djZpaam3PHf+K/+KetayOJcvX8LZ2eWWLakZGelkZWXh7l6pxMfMzc0lOTmp0LqzsrK4ejXLbHDK5cuXzK4h77pTcHV1K7bL9s/cnztx/fp10tJScHOrVKIAny85OYmKFV1N13PpUppphP3tSEtLxdHRqUBrbklZ6vs6vD0FO5sbPNK75H8vRETulLrF5b6V9wPbqcCyO/1BPmbMcC5dSqNXr7zn7aKjtwHg7d2oRPsbDIYiQ8CfCZUlPYaLSznT4JSSsrGxuWXdhX2ff6wh77qrlOhcf+b+3Al7e/ti38pTmD+G84oVXe/o/LczYrww9/r7EhGxBIVLEfJa3zw9vfjxxxkcOLAHgMqVq9Kv30D++tfXrFydiIjI/UPd4lKq3E63uIiUjLrFReRe0oAeEREREbEYhUsRERERsRiFSxERERGxGIVLEREREbEYhUsRERERsRhNRSSlzo2cXK5fvWHtMkTKjOzrudg5WLsKEXlQKFxKqWJwsCFmSyoxu9OsXYoUIycnGzB/daGUXi27/LkJ3UVESkrzXIrIHfH39yc7O5vdu3dbuxQRESlF9MyliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjMKliIiIiFiMwqWIiIiIWIzCpYiIiIhYjE1ubm6utYsQkdKtTZs25ObmYmNjU+R2+dvs3r37HlUmIiKljVouRaRYQUFBGAyGYrczGAwEBQXdg4pERKS0UsuliBTr7NmzPP7440W2XuavW7FiBTVr1rzHFYqISGmhlksRKVbNmjWLbb3Mb7VUsBQRebCp5VJESqSo1ku1WoqISD61XIpIiRTVeqlWSxERyaeWSxEpscJaL9VqKSIiN1PLpYiUWGGtl2q1FBGRm6nlUkRuy82tl4BaLUVExIxaLkXkttzceqlWSxER+SO1XIrIbctvvQTUaikiImYULkXuU+lp2exck2ztMqyqbZA7FdyKf3OQiIjcO/pXWeQ+lZl+g1NHMvAJcLd2KVbxa1QyPu1dFS5FREoZ/assch9zdLHloRYVrV2GVRzbl2btEkREpBAa0CMiIiIiFqNwKSIiIiIWo3ApIiIiIhajcCkiIiIiFqNwKSIiIiIWo3ApIiIiIhajcCkiIiIiFqNwKSIiIiIWo0nURR4Aubm57NoVxdGjBzl37ixubpVo0qQZfn4BGAwGZsyYyL59OwkODrmrdVy9epU33hhE1649efbZF+/quURExDoULkXKuIyMDL7+ejRbtmwAwNnZhczMDAAWLtyAq6uRixfPExNz5K7XkpubS1xcLCkpSXf9XCIiYh0KlyJl3HffjWfLlg107vwof//7+xiNbmRlZXH06EFcXY3WLk9ERMoYhUuRMuzUqROsWbMMLy9vPvjgU+zs7ABwcnKiefM2Re4bG3uMfft2kZR0kcqVq+Lv35EaNTxM6zduXI2joxMdOnQ1Ldu/fzdxcbH06vWE6VwnTx5n9+5tXLt27ZbnjIk5wt69O0hPv4KnpxcdOnTDycnpz16+iIhYgcKlSBm2Z88OAAYMGGwKeyVx5kwcr7460GzZlClfMWHCLBo3fhiAOXOmUqlSFbNwuXVrBCtWLKR798ews7MjMnItY8YMB6By5arMnj25wLkWLPiemTODzZaFhHzHf/7zHVWqVCtxzSIiUjpotLhIGRYfHweAl1f929qvVi1PXnrpHcaOnUxY2HZGjvwSgIULZ5f4GBcunGPMmOF4eHgyZ04Yc+eu5quvppttc+jQfmbODKZdu86EhKxi5cptvPfeSBIS4gsEThERuT+o5VKkDLt0KRWA8uUr3Pa+Tz01iNOnT7Jly3rTAKBTp06UeP9161YC8OSTg6hatToA3t6NzLaJiFgDQIcOXUlKukhS0kU8Pb0wGt3Yu3fHbdcsIiLWp3ApUoZVr573jGRS0kVTwCupSZPGsWLFQgA8PDwBuHo1q8T7nzp1HAB//4BbbhMbGwPAuHGfFFjn7OxS4nOJiEjpoXApUobVrFkbgOjoKNOzkiWR/+xkz579GTLkdVxdjXz44evExcXedg2XLqVRqVKVQtc5OTkDMHv2igLh18bG5rbPJSIi1qdnLkXKsICAbjg7uzB37nROnIgxW7drVxTXrl0DwGDI+z0zLS2vG33z5nUADB78qmm6oqtXs8jOvm7av0aNWhw//hvZ2dkA5OTkcPLkcdN6T0+v/51n6y3rq1+/MQBbt27E1tbW7I/CpYjI/clu1KhRo6xdhIjcvozLORw/cAXvVq633MbBwZEKFVzZufMnVq1aQmZmBvHxcYSGzmPmzGDs7e1p1qwVV65cZvv2zcTFxdKo0cM4OjoRFRVpmg5o/vxZbNu2iaysLPz9O1KpUmWuXr1KVFQEFy+eIysrkzlzprJnz3YAnn32RerUeYilS+eyb98u4uJOkJ2dzapVSzh27AiNGvnQtm17vLzqs2rVEnbu3EpS0gXS06+wcOFsQkK+Iyiob5Ej3I//fImHfMpRzlUdMCIipYnCpUgpEhYWRm5uLpUrVy5225KES4AGDRpTr14DfvvtELt2RREdHcXJk8fo3ftJBg4cgqOjI1Wr1iAlJYktWzbg49MSf/9OJCVdYNmy+axfH4aTkxPPPTeUHTu2UKlSFZo1a0316jXJzMxg8+Z1bNq0jtq16+Lp6cXp0yd59tkXqVjRlZYtffnllz0cOrSfvXt38NBDDYiJOUzTps1p0+YRnJyc8fMLIDY2hm3bNhMVFcn582dp3dqfFi3a4uDgcMvrUrgUESmdbHJzc3OtXYSIQNu2bXFyciIzMxNHR0e8vb1p3749DRo0oHXr1lSoYD7iO/HsNdaFnCPob54lPselS2lkZKRTtWp1bG0LPhVz+fIlHBwccXR0NH22t3cwtWBmZ2eTnZ1tNsF5VlYWN27k4OJS7pbnvXz5Es7OLqbu98JkZKSTlZWFu3ulEl3Lulmn6fZMVarWdizR9iIicm/oV36RUsLBwYHMzEwgL7AdPHiQI0eOYGNjQ3Z2NpUqVaJRo0b4+fnRsGFD6tQo+QCdfBUrulKx4q1bOitUqFjkZ4PBUCAgluRNOn88TmFcXMoVGVBFROT+oHApUkpERUXRpk3e6xHzB7Pk5OSY1iclJbF161b27t1LZmYmRhdPXuwzwSq1ioiI3IpGi4vcR2xsbMjIyMBgMNCwYUNrlyMiIlKAwqVIKXH+/PlCn4O8WW5uLvb29nTr1o1//etf96iy0unGjRu8//77BAcHc+jQIWuXIyIi/6NucREruXjxIrt372bPnj3s3r2bM2fOkJubW+T8jjY2NgwZMoRXXnmFxLPX7mG1pU9OTg4JCQl8//02vv/+e6pWrUqnTp3o2rUrbdu2tXZ5IiIPLI0WF7lHEhMT2b17tylQnj592mx9rVq18PPzY/HixbcMmCNHjuSxxx7LO94djBYvS9bNOk2LQDv2HIxgw4YNHDx40LTO1dWVgIAAunTpQqdOnaxYpYjIg0fhUuQuSU5ONguTp06dMlvv7u6Or68vbdu2xd/fn2rVqgGYBvXkyx+hPWPGDLPnLBUuzaciOn/+POvXry8QNJ2cnGjfvj1dunShQ4cOlC9f3loli4g8ENQtLmIhV65cITo6mt27d7Nz505Onjxptt7Z2ZnWrVvTpk0b/Pz8qF+/fqHHsbOzM40St7Ozo2bNmnzxxRcawFOMatWqMWjQIAYNGlQgaG7cuJGNGzcC8Mgjj9ClSxc6d+6Mu7u7lasWESl71HIp8ifs2rXL9OfXX38tsL5Fixb4+fnRtm1bWrRoUaJj+vn5kZOTg6OjIzVq1GDWrFkFJlAHtVyWdBL1W7VoQt79CQwMpFu3blSpUuVulisi8sBQuBS5DQcPHmT37t3s2LGD3bt3F1jv7e2Nn58fvr6+tG7dukQTjBfG19eX7t27M2zYsEKDJeSFyxVT4mnga7yjc9zvYnan0ftvNW7rDT1FBc1mzZoRGBhI9+7dFTRFRP4EhUuRIpw8edLUMrl7926uXLlitt7Dw4O2bdvi6+uLr68vRuO9C3oZl3LY/1PqPTtfadSsg5FyrnZ3tO/FixfZsGEDGzZsYP/+/WbrfHx8TEEz/1lYEREpGYVLkZtkZmayc+dOtm3bxrZt2zh37pzZejc3N1OY9Pf3p3r16laqVCwpMTHRFDR//vlns3VNmjQhMDCQRx99VPdbRKQEFC7lgRcbG0tUVBRRUVFER0ebrcsfhJMfKG81CEfKjuTkZFPQ3Lt3r9m6Jk2a8OijjxIUFKSucxGRW1C4lAdOVlYW0dHRpkCZkJBgtr5evXq0a9eO9u3bF5gWSB4sycnJRETkzaP5x2dsW7RoQVBQEN27d7+nj0OIiJR2CpfyQEhLSyMyMpJNmzaxa9curl37/e02zs7O+Pr60r59ezp06EDVqlWtWKmUVvktmuvWrSvQde7r68ujjz5K165dqVixopUqFBEpHRQupcw6d+4cGzZsYNOmTQXCQN26denQoQPt2rXD19fXShXK/Sr/79batWs5fPiw2bqOHTvSs2dPOnbsiKNjyUeyi4iUFQqXUqYcOXKETZs2sWnTJo4dO2a2rkWLFnTu3JnAwEANzBCLiY+PZ+3ataxbt87s75yLiwtdu3alR48e+Pv7W7FCEZF7S+FS7nu//vqraQDG2bNnTcsdHBzw9fWlc+fOdOnSBVdXVytWKQ+C2NhYVq1axdq1a82e5XV3d+fRRx+lZ8+eNG3a1IoViojcfQqXcl+6VaAsX748AQEBdOrUiQ4dOtzxJOYif9bPP/9MeHg4GzZsIDX19/lIa9WqRc+ePenVqxe1a9e2YoUiIneHwqXcN86cOUNoaCjh4eGcP3/etNzJyYnu3bvTrVs3OnToYMUKRQq3detWwsPD2bRpE1lZWablPj4+9OnThx49elC+fHkrVigiYjkKl1LqhYeHExoayp49e0zLHB0dCQgIMI3QFbkfZGVlsWnTJsLDw9m6davZusDAQPr06aNfkETkvqdwKaXSyZMnCQ0NZfny5WavXPT19aVnz550795dXd5yX0tLS2P16tWsXLmS3377zbTc3d2dXr160bdvX7y8vKxYoYjInVG4lFIlLCyM0NBQs3c9V65cmccee4wnn3xSo7ylTIqNjSU0NJQ1a9aQkpJiWt64cWP69+9Pjx49cHFxsWKFIiIlp3ApVnfixAkWL17M6tWrzVop27dvT//+/encubMVqxO5t7Zs2UJYWBgRERGmZU5OTgQFBdG/f398fHysWJ2ISPEULsUqsrKyCA8PZ9myZRw8eNC0vFq1ajz++OP0799fb8qRB1paWhphYWEsW7aM2NhY0/J69erRv39/evfuTYUKFaxYoYhI4RQu5Z66cuUKISEhzJ8/36yVslOnTvTv31+DGUQKceDAAUJDQ1m/fr1ptLmDgwNBQUEMHDiQhg0bWrlCEZHfKVzKPZGWlsacOXNYuHAhGRkZANSsWZN+/frRr18/3N3drVyhSOmXkZFhmj3h5tdO+vj48PTTT9OrVy8rVicikkfhUu6qpKQkfvjhB5YsWWJqcfH29mbo0KEEBgZauTqR+9fRo0eZN28e69at49q1awC4ubnx9NNPM2DAAIxGo5UrFJEHlcKl3BWJiYnMmDGD5cuXm37wNWrUiKFDh2qAjogFpaWlsWzZMhYvXmx65aSDgwO9evVi0KBB1K1b18oVisiDRuFSLOrcuXNMnz6dZcuWmZb5+PgwdOhQPU8pcpdFREQQEhLCgQMHTMvat2/P888/j6+vrxUrE5EHicKlWERmZiZTp04lJCTEtKxly5YMHToUPz8/K1Ym8uD59ddfmT17Nhs3bjQta9KkCa+88grt27e3YmUi8iBQuJQ/LTw8nAkTJpCYmAjkvUVn6NChtGrVysqViTzYEhISmDt3LsuXLyczMxPI60l49dVX8ff3t3J1IlJWKVzKHTt58iSffvqp6W06tWrVYvjw4bRt29bKlYnIzS5fvsy8efOYP38+ly5dAqBVq1Z89NFHeiZTRCxO4VJuW3p6OpMnT2b+/PlA3ttDhg4dyl//+lcrVyYiRcnMzGTp0qXMmjWL1NRUAJ555hn+/ve/U758eStXJyJlhcKl3JawsDC+/fZbkpOTAQgMDOS9996jSpUqVq5MREoqPT2dqVOn8uOPPwJ5Uxi9/vrr9OvXz8qViUhZoHApJXL8+HE+/fRT06sa69Spw7Bhw9QFLnIfi4uL47PPPmPv3r0AtGjRgjFjxlC5cmUrVyYi9zOFSynWDz/8wLfffguAs7MzL730EoMHD7ZyVSJiKWFhYfznP/8hPT2dChUq8Omnn2rqMBG5YwqXckvp6el8+OGHbNu2DYDu3bvz7rvvqgtcpAy6cOECw4cPZ9++fQAMGDCAf/7zn1auSkTuRwqXUqhjx47x7rvvcvbsWVxcXPj000/p1KmTtcsSkbssJCSECRMmAHnTFgUHB2uwj4jcFoVLKSA8PJwRI0YAULduXb755hs8PDysXJWI3Cu//vorb7zxBpcuXcLb25spU6boXeUiUmIKl2Lms88+M726sUePHowYMQInJycrVyUi99qZM2d4+eWXuXDhArVr12bGjBm4u7tbuywRuQ8oXAqQ907wd955h2PHjuHg4MAHH3ygaUlEHnCJiYm89tprnDhxggYNGpimLhIRKYrCpRAfH89f/vIXUlNTqVmzJuPHj8fb29vaZYlIKZCamsozzzxDUlISzzzzDO+//761SxKRUs7W2gWIdSUmJvLKK6+QmpqKr68v8+bNU7AUEROj0cinn34KwIIFC0yzR4iI3IrC5QMsLS2Nl156iXPnzuHj48PXX39NuXLlrF2WiJQyvr6+/OUvfwHgu+++s3I1IlLaKVw+oDIyMnjllVc4ffo03t7e/Pe//8XR0dHaZYlIKfXiiy/i4uLCwYMHOXDggLXLEZFSTOHyAXT16lVee+01jh07Ru3atZk6dSouLi7WLktESrFy5crxxBNPADBnzhwrVyMipZkG9DyAXnvtNXbt2kW1atWYPXu23iMsIiVy/vx5evfuDcDu3butXI2IlFYKlw+Yt99+m6ioKIxGI7Nnz9bk6CJSJD8/P7Kzs4vdzt7enh07dtyDikSktDNYuwC5d6ZPn05UVBTly5dn2rRpCpYiUqycnBwAbGxsityuJAFURB4MeubyAREbG8uUKVMA+Pzzz/Hy8rJyRSJyPwgKCsJgKLodwmAw0L59+3tUkYiUdgqXD4iPPvoIgD59+uiHgIiU2Ouvv05OTg5FPUGVnZ3NCy+8cA+rEpHSTOHyATB79mxiYmJwc3Pj//7v/6xdjojcR2rWrEmfPn2KnKqsadOmtGnT5h5WJSKlmcJlGXfmzBkmTpwIwKhRoyhfvryVKxKR+83LL7+MjY1Noa2XBoOBN9980wpViUhppXBZxqk7XET+rJo1azJ48OACrZe5ubk0bNhQrZYiYkbhsoJApN0AACAASURBVAybO3cuv/76q7rDReRPe+655wqMGLe3t2fIkCFWqkhESiuFyzIqISGBSZMmAeoOF5E/r0KFCgwePNg0cjw3Nxc3Nzc6d+5s5cpEpLRRuCyjgoODuXbtGr1791Z3uIhYxHPPPYe9vT0AdnZ2vPbaa1auSERKI4XLMujChQusXbsWyHsjj4iIJVSoUIG///3vOBjKYTQaeeyxx6xdkoiUQnpDTxk0f/58AHr16oW7u7uVqxGRsqSTb3/c/96VHn+pbu1SRKSUUstlGZOVlcXixYsBNKmxiFhU/LFMtq9KpqG/O4smnLF2OSJSSilcljHLly8nIyOD1q1bU79+/f9n777jqqz//48/EJADgoLgREncK1FxoJkrZ65yZJ+0YVofP2m5vp8+Vub6aGWpaVLu0o/iJFEcuHeKO00TBUVRRgICso6C8vvj/K4rkSEocHEdXvfbzVtxznWu63WGxyfvqXU5QggzER6SyrHtd+k01JWyLqVp0tlFAqYQIlsSLs2Mj48PYBp4L4QQBUEJlp2Huqq3ubgaJGAKIbIl4dKMHDx4kIiICKpWrUqHDh20LkcIYQayC5YKCZhCiOxIuDQja9asAWDIkCEaVyKEMAe5BUuFBEwhxJMkXJqJ4OBgzp49i52dHf369dO6HCGEzuUlWCokYAohHifh0kxs27YNgAEDBmAwGDSuRgihZ/kJlgoJmEIIhYRLM3HixAnAtLalEEI8q2cJlgoJmEIIkHBpFuLi4ggJCcHe3l6WHxJCPLPnCZYKCZhCCAmXZiAwMBCAdu3aaVyJEEKvCiJYKiRgClGySbg0A0qXuJeXl8aVCCH0qCCDpUIJmBu+l4ApREkj4dIMHD16FICXXnpJ40qEEHpTGMFS4eJqwOMVCZhClDQSLnXu+vXrxMfHU7NmTZycnLQuRwihI4UZLBUSMIUoeSRc6pzSJd66dWuNKxFC6ElRBEuFGjDnSsAUoiSQcKlzEi6FEPlVlMFS4eJqoGkXCZhClAQSLnXu9OnTAHh6empciRBCD8JDUjm+o2iDpcJZAqYQJYKESx0LCgrCaDTSuHFjbG1ttS5HCFHMKcGy05CiD5YKCZhCmD8Jlzp29epVAOrWratxJUKI4q44BEuFBEwhzJuESx1TwqXsyiOEyE1xCpYKCZhCmC8JlzoWGhoKSLgUQuSsOAZLhQRMIcyThEsdCwsLA6BGjRoaVyKEKI6Kc7BUSMAUwvxIuNSxyMhIABwdHTWuRAhR3OghWCokYAphXiRc6tSNGzcAmcwjhMhKT8FSIQFTCPMh4VKnlFbLypUra1yJEKI40WOwVEjAFMI8SLjUqTt37gBQsWJFjSsRQhQXeg6WCgmYQuifhEudio6OBqBChQoaVyKEKA7MIVgqJGAKoW8SLnVKCZfScimEMKdgqVAC5noJmELojoRLnVK6xaXlUoiS7Xaw+QVLhbOrgWZdJWAKoTcSLnXq7t27ADg7O2tciRBCK7eDUwkMMM9gqXCuKgFTCL2RcKlTCQkJAJQrV07jSoQQWigJwVIhAVMIfZFwqVNKuJQxl0KUPCUpWCokYAqhHxIudSoxMRGDwaB1GUKIIlYSg6VCAqYQ+iDhUofi4+MB2fZRiJKmJAdLhQRMIYo/CZc6lJSUBICDg4PGlQghiooEy79JwBSieJNwqUNKuCxTpozGlQghioIEy6wkYApRfEm41KGUlBQA7OzsNK5ECFHYJFjmTAKmEMWThEsdUsKltFwKYd4kWD6dBEwhih8Jlzok3eJCmD8JlnknAVOI4kXCpQ4ZjUYAWYpICDMlwTL/JGAKUXxIuNQhCZdCmC8Jls9OAqYQxYOESx26f/8+AKVLl9a4EiFEQZJg+fzUgDlHAqYQWpFwqUNKy6WNjY3GlQghCooEy4LjXNVA824SMIXQioRLHVJaLiVcCmEebgencmKnBMuCVF4CphCakXCpQxIuhTAfSrDs+JYEy4ImAVMIbUi41KEHDx4AYG1trXElQojnIcGy8EnAFKLoSbjUofT0dACsrKw0rkQI8awkWBYdCZhCFC0Jlzok4VIIfZNgWfQkYApRdCRc6tDDhw8BCZdC6JEES+1IwBSiaEi41CFpuRRCnyRYak8CphCFT9KJDkm4FEJ/CjJYPnjwgC1b1uV6jK2tHb17D3yu60yYMIJaterx0Uf/fq7zPKvo6L84d+4kly79Tt26jWjbtiNOTuWf+7yPB8zBE6oVQKVCiMdJOtGhR48eAVCqlDQ8C6EHBd1imZb2gGXLfsj1GBeXis8dLq9du4qDQ7nnOsfz8PNbw6+/+tCokQc7d25h48aVLF++CUtLy+c+twRMIQqPhEsdysjIAMDCwkLjSoQQT1MYXeF2dmXw9d2v/rx8+QICAvz46ac1VKxYGTCPXz6HDPmAoUM/xM6uDLt2+TN37nTOnAmkVauXCuT8EjCFKBwSLnVIwqUQ+lBYYywtLCxwcCir/mxrawuAg0PZTLeHhoZw9uwJEhLicHNz56WXOqvHAqSlpXH06H5CQ4MpW7YcTZp4Urduw3zVsm/fDmxsDLRr11m97fz504SFhfLqq/2xtLQkJuYOZ84E8tdfEdSv/yKNGnlQpow9YPo+O3nyN65cuYilpRW1a9enVauXsLCwUI9RnnNhkIApRMGTcKlDSre4hEshii+tJ+/s2bON2bOnYmtrh42NDfHxcaxZs5zvvluMs3MFEhPv8fnno7l69U+qVHElMjIcgA8+GMvAgUPzfJ1Vq0znezxcHj26H3//DXTt2oeUlGSGDHkVMI0DTU1N4f33RzN48Hukp6czY8Z/OH78UKZztmzZlqlT56rjyqOiIvjpp+9wdXWjadOWz/vSZKEEzA1zb/HG+OoFfn4hShr995uUYBIuhSieUpIeahosY2OjmT17Km5u7qxatY3Vq3cwevR/CA8PY9WqxQCsXfszV6/+ybhxX7JixRaWLfOlbt2GLF06j4iIgptJfeDATgAmTpzJr78e4KuvvOnYsTsAmzb5cPz4Id55ZyR+fofYuHEf/foN5tSpY+zZsw0wLb323XeTsbGx4b//nU/p0qULrLbHla9qoFm3CgSsiCqU8wtRkki41CHpFheieLt8IhFDGe06hk6fPg5A1669cXAoi7W1Na++2h9bWzsOHtwNwL592wHo3LknANWr16Br194A/P77qQKrxcWlIgC7d/sTGxuNp6cXlSpVAWDHjk0AeHi0ICwslIiIWzRr1gqAS5d+ByAw8DAXL/7OqFH/wdW1cFsVbWwtibl9v1CvIURJIN3iOqR0iwshiifPVxy5dj6JY5uiaNu/cpFf/6+/IgBo2NBDvc3S0pLGjZty6tQx4uJiiY+Po3nz1plaApXxllFR4QVWS5s2HRg8+D3Wr1/B22/3ZvDg9xg69EMePXqkdsVPmDAiy+OUJddu3boBQKtW7QqspuwkxaVxeF0Eb096oVCvI0RJIOFSh5QWS6UFUwhR/NTyME1G0SJgGgymSTuJiQmZble6u21tywAQFxeb6f6YmDsA2Ns7FFgtFhYWvP/+aLp378fixXNZv34F9+7FM3r0RACqVHFl+fJNWXpilJ9ffLE5r732JgaDocBqepIESyEKlnSLCyFEIanlYU/DVvYc21S04/jc3GoCcOnSefW22NhowsPDcHevjcFgwN29NqGhISQm3lOPOX/+dKbH29jYPLUVs0qValy7dlVtaXz48CE3blxT78/IyODhw4e4ulZn2rS5uLhU5ODB3VhZWVG3bkMiI8MJDr5MqVKlMv1RwmWtWvVo27YTRqOxAF6ZrCRYClHwLKdOnTpV6yJE/mzfvp3w8HD69OlDlSpVtC5HCJGL8pVLY2UFZ/fepXoD+6c/4BmcOXOcy5f/oH//tyhTxp7KlV05enQfx48fIj7+LhkZj1i6dD5//RXJqFGf8sILNSlb1pEjR/YSGHgYg8GW8+fPsGbNMtzc3Pnww7GUKlWKO3eiOHHiCKmpyTRu3DzbXcHu37/Pb7/tJzo6CqMxlVWrFnPmjGnM5z/+8T5//nmeyZPHYTDYcuXKJQ4c2Em9eg3p3r0frq5u7NmzjcDAQ6SmphARcZulS+dx6dLveHm1B2Dx4rl4e88iKekerVsXbNe4BEshCod0iwshRCEr7C5ypZVP+a+VlRVffeXN99//l23bfNm2zRdbWzvGjp1E+/ZdAGjfvgtJSV+wZMn3zJkzDQBPTy/GjftSDZF9+75BZORtfv3VhwED3sbGxibLtV9++RWuX7/Kvn072LNnG23adKBt244cO3YQMIVPB4ey6jVat27He++NAsDDw5OpU+fg7T0LH59lADg6OtGkiad6/goVKgF/TwwqKBIshSg8FhkycE93PvroI06ePMmSJUto3ry51uUIIfLo2vkk/jyZVKRjMI1GI8nJSTg7u+R4TGxsDGXK2Oc4rtFoND51zKPRaOTRo4fY2ZXJ9v7k5CQsLa1yPE9c3F1Kly6daeF0xb17CZQtW3DbUEqwFKJwSculEEIUES0m+RgMhqcGw9yCp3KOvFwnN9mFxsc5OZXP8T4JlkLoi0zoEUKIIqTVJB8hwVKIoiLhUgghipgEzKKXFJ/GIQmWQhQJCZdCCKGBWh72NGhpzzE/CZiFLSk+jUNrI3hHgqUQRULCpQ7Jto9CmIfaTe1p0EICZmGSYClE0ZNwqUOyQ48Q5kMCZuGRYCmENiRc6piESyHMgwTMgifBUgjtSLgUQohiQAJmwZFgKYS2JFzqkIy5FMI8ScB8fhIshdCehEsdkjGXQpgvCZjPToKlEMWDhEsdknAphHmTgJl/EiyFKD4kXOqQhEshzJ8EzLyTYClE8SLhUsckXAph3pSAeXyzBMycJMencWiNBEshihMJlzokE3qEKDlqN7Wnvqc9v22K0LqUYic5Po2DayJ450sJlkIUJxIudUi6xYUoWQ6f3cCvO+dzxPe21qUUGxIshSi+rLQuQAghRPYCAwP5+uuvCQ8PB+Cl5Jc4vtmKNq9V1rgybUmwFKJ4k3CpQ9JyKYR5i46O5rvvvmP//v0AVKlShc8//5w2bdoQ8nsSxzdHldiAKcFSiOJPwqUOSbgUwnytWLGCZcuWYTQasbGxYdiwYYwYMUK9v3ZTe4ASGTAlWAqhDxIudUzCpRDm49SpU3zzzTfcvHkTgPbt2/Ppp59SuXLWAFkSA6YESyH0Q8KlDslscSHMR3R0NHPmzGHv3r1A5i7w3JSkgCnBUgh9kXCpQ9ItLoR5WLlyJcuWLSM1NZXSpUvz3nvv8eGHH+b58SUhYEqwFEJ/JFzqkIRLIfTt7NmzzJw5U+0C9/Ly4vPPP6dq1ar5PlftpvZkZMAxvyjavm5eATM5IY0DayJ4V4KlELoi4VKHJFwKoU8xMTHMmTOHPXv2AFC1alX+7//+j/bt2z/Xees0M7VgmlPATE5I44CPBEsh9EjCpQ5JuBRCf1atWsWSJUtITU0FYMSIEYwcObLAzm9OAVOCpRD6JuFSh0qVMm2s9OjRI40rEUI8TUF2gT+NOQRMCZZC6J+ESx1SWi4lXApRfMXFxfH999+zY8cOwDQLfMKECXTs2LFQr6vngCnBUgjzIOFSh5SWS+kWF6J42rx5M/PmzSMpKQkwdYEPGzYMGxubIrm+HgOmBEshzIeESx2SbnEhiqfr168zbdo0Ll26BECzZs2YMmUK1apVK/Ja9BQwJVgKYV4kXOqQTOgRongxGo0sXLgQHx8fAJydnRk3bhw9evTQtC49BEwJlkKYHwmXOmRlZXrb0tPTNa5ECHHw4EFmz55NVFQUAAMHDmT06NHY29trXJlJcQ6YEiyFME8SLnVIusWF0F5ERARfffUVgYGBANSvX59JkyZRv359jSvLqjgGTAmWQpgvCZc6JLPFhdDWsmXL+Pnnn3nw4AEODg6MHj2aAQMGaF1WropTwJRgKYR5k3CpQzJbXAhtnDp1ipkzZ3L79m0A+vTpw5gxY3B0dNS4srxRAqaWe5Enx8uWjkKYOwmXOiTd4kIUrejoaL777jv2798PgLu7O1OmTKFx48YaV5Z/dZrZY2GhTcBMjk/joARLIcyehEsdktniQhSdlStXsnTpUoxGI3Z2dowcOZK33npL67KeS+2mRd+CqQTLdyRYCmH2JFzqkLRcClH4nty2sVu3bowfPx4XFxeNKysYRRkwJVgKUbJIuNQhCZdCFJ6YmBjmzZvHzp07AahWrRqTJ0+mefPmGldW8IoiYEqwFKLkkXCpQ5aWlgA8fPhQ40qEMC/r1q1j4cKFJCcnYzAYGD58OMOGDdO6rEKlBMzCmEWeFJ/GIQmWQpQ4Ei51SFouhShYFy9eZMaMGYSEhADQoUMH/v3vf1O5cvFYE7KwFUbATIpP49DaCDoPsyyQ8wkh9EPCpQ4p4VJaLoV4PvHx8fzwww/4+/sDUKVKFT7//HPatGmjcWVFryADphIsVx/+gB823+X06dMFUaIQQidKaV2AyD+lW1xaLoV4dps2baJ///5qsBw+fDhbt24tkcFSUbupPQ1a2HPML+qZz6EEy3cmvUD58uUBuHTpUkGVKITQAQmXOiTd4kI8u6CgIIYOHcpXX33FvXv3aNGiBZs3b+Zf//qX1qUVC88TMB8PlgCNGjUCTMMOhBAlh3SL65BM6BEi/5KSkvD29sbX1xeAChUqMGHCBLp06aJxZcXPs3SRJ8WlcWjd38ESoHHjxmzZskVaLoUoYSRc6pCESyHyZ9u2bcyfP5+4uDgAhg4dysiRIzEYDBpXVnypAXNTFG375x4wk+LSOPxEsATUHYyk5VKIkkXCpQ5JuBQib65fv86MGTO4cOECAE2aNGHSpEnUrFlT48r0oXZT01aRuQVMJVi+PSnrckN16tTBxsaGsLAwkpKSsLe3L+yShRDFgIRLHZJwKUTuUlJSWLx4MT4+PgA4OTkxZswYevfurXFl+lPLI+cWzNyCpaJhw4acO3eOixcv4uXlVai1CiGKB5nQo0NKuExPT9e4EiGKn927d9O/f381WA4cOBA/Pz8Jls+hloc9DVvZc2zT35N88hIs4e+ucaX1WAhh/qTlUoesrExvm7RcCvG327dvM23aNM6dOwdA/fr1mTRpEvXr19e4MvPweAtms24ueQqW8PeM8T///LNQ6xNCFB8SLnVIusWF+Nv9+/dZunQpK1asAKBs2bKMGjWKAQMGaFyZ+VEC5rldd/IULMHULQ6y1qUQJYmESx2SbnEhTA4ePMjs2bOJijJ11/bp04cxY8bg6OiocWXmq5aHPVVr2ub5+KpVq2Jvb09cXBx//fUXlSpVKsTqhBDFgYRLHbK2tgYkXIqSKyIigq+++orAwEAA3N3dmTJlijq+TxQuW4f87Rf+4osvcvz4cf78808Jl0KUADKhR4eUMZcSLkVJtHTpUgYOHEhgYCB2dnaMHz+ejRs3SrAsxpSucRl3KUTJIC2XOqSEy7S0NI0rEaLonDp1iunTpxMZGQlAt27dGD9+PC4uLhpXJp6mQYMGgIRLIUoKCZc6JN3ioiSJjo7m22+/5cCBAwBUq1aNyZMn07x5c40rE3klk3qEKFkkXOqQtFyKkmLlypUsXboUo9GIwWBg+PDhDBs2TOuyRD5VrFgRJycn4uLiuH37NtWqVdO6JCFEIZIxlzokYy6FuTt79iwDBgxgwYIFGI1GOnTogK+vrwRLHVPGxErXuBDmT1oudah06dKAtFwK8xMTE8P8+fMJCAgAoHLlykycOJF27dppXJl4Xg0bNuTIkSMEBQXRrVs3rcsRQhQiCZc6JOFSmKO1a9eyaNEikpOTARgxYgQjR47UuCpRUJSdkq5evapxJUKIwibhUodkzKUwJ+fPn+err77i2rVrAHh5efHpp5/i5uamcWWiINWtWxeQbnEhSgIJlzokLZfCHMTHxzN//ny2bt0KmCZ9jB8/ni5dumhcmSgMlSpVomzZsty7d4+YmBhZQkoIMybhUockXAq98/X15ccffyQxMRGAt99+m3/+858YDAaNKxOFqV69epw6dYqrV69KuBTCjMlscR1SwuX9+/c1rkSI/AkKCmLo0KF88803JCYm0qxZM3x9fRkzZowEyxJA6RqXcZdCmDdpudQhCZdCbxITE/H29ubXX38FwNnZmbFjx9KzZ0+NKxNFSQmXV65c0bgSIURhknCpQ0oLj4RLoQf+/v788MMPxMfHAzB48GD+9a9/YW9vr3FloqjVq1cPkJZLIcydhEsde/DggdYlCJGj69evM23aNHXLv0aNGjFp0iTq1KmjcWVCK7Vr1wbg5s2bpKamYmtrq3FFQojCIOFSp8qUKUNycjL379/HxsZG63KEUKWkpPDTTz+xbt06ABwdHRk9ejSvvfaaxpWJ4qB+/foEBQVx7do1ddceIYR5kQk9OiVd46I42rlzJ/3791eD5euvv46fn58ES6GqVasWACEhIRpXIoQoLNJyqVMSLkVxcuPGDWbOnMm5c+cAU/fn1KlT1V1ZhFC4u7sDpmETQgjzJOFSp5SucKPRqHEloiQzGo0sXbqUlStXAmBvb8+oUaMYNGiQxpWJ4qpmzZoAhIaGalyJEKKwSLjUKaXlUsKl0Mr+/fuZM2cOf/31FwC9evVi7NixODk5aVyZKM4kXAph/iRc6pSdnR1gmjwhRFGKiIhg+vTpnD59GjB1c06aNAkPDw+NKxN6UK1aNQCioqJISUlRv8uEEOZDJvTolIRLoYVFixbRt29fTp8+jZ2dHWPHjmXjxo0SLEW+KIupS+ulEOZJwqVOlSlTBpBwKYrG8ePH6dOnD8uWLQOga9eubNq0iaFDh2pcmdAj6RoXwrxJt7hOKbubJCYmalyJ0FJGRgbJycmFcm47Ozvu3LnDd999x6FDhwBTl+bkyZNp3rx5oVxTmJ9Hjx5l+SW4W7duODs7Y2dnR1JS0lPPYWlpKQuuC6EjEi51qly5cgDcu3dP40qElgozXO7evZu5c+diNBoxGAwMHz6cYcOGFcq1hPl69OhRls9ovXr11K0g8/L5tbKyknAphI5IuNSpsmXLAhIuReHZuHEjRqORzp07M378eCpXrqx1SUIIIXRAwqVOKeEyISFB40qEuXJxceGTTz6hTZs2WpcihBBCRyRc6pS0XIrC9v3332NlJV8RQggh8kdmi+uUtFwKIYQQojiSZgmdkgk9AiAtLY1t27ZRp04ddYIEmH7pOHLkiHp7RkYGp0+f5sqVK1haWlK7dm1atGiBhYUFYJpUceHCBa5cuYK7uzsNGzbE2dlZq6clzExQUBAJCQnUr1+fY8eOERkZyQsvvECHDh0ytY7HxcVx4sQJIiIiqFSpEi1btqRixYoaVi6EeBYSLnVKusUFmJZoWbFiBW5ubsydO1e9/ejRoyxcuJCvv/6a9PR0vv76awIDAzM9tkWLFnz55ZcATJgwgVu3bmFra0tqairNmjVj4cKFRfpchPk6ffo0a9euxdXVlbt375KamgrAxYsXGTNmDAAhISFMnjyZhIQEXFxciImJwdbWVpa+EkKHpFtcp5RWJekWL9lKlSpF7969uXLlCmFhYertBw8epFy5cjRq1IjNmzcTGBjI0KFD2bhxI2vXrqVPnz6cPn2affv2ceXKFW7dusWrr76Kr68v3t7evPXWWxo+K2GuOnfuzLp16/Dx8aF69ers3r2blJQUHj58yIIFC0hISODHH39k5cqVfP311wB4e3uTnp6uceVCiPyQcKljSiuTKNleeeUVAPbv3w/AnTt3+PPPP+nevTuWlpYEBAQA0KRJE27dukVkZCRNmzYF4NKlS+oSQydPniQoKEjtFheioHXr1g0rKyscHR3V1sjY2FiioqIICQmhefPm1KhRAzB9Xps2bUp4eDhRUVFali2EyCfpFtex8uXLEx4ezp07d2RcUglWvXp1GjVqxO7duxk6dCjHjh0DoEOHDhiNRvUf5k8//TTLYx8+fIizszMTJ05k/vz5TJgwgVatWvHxxx/LmEtRqKpWrQpAeno68fHxALz44ouZjmnQoAHHjx8nNja2yOsTQjw7CZc6VqFCBcLDw4mOjpZwWcL17NmT2bNnc+7cOQ4cOECNGjWoUaOG2p1YuXJllixZok7gUSg/v/zyyzRt2hRfX198fX0ZN24c27ZtK/LnIUomZfedJ8eQx8TEZLpfCKEP0i2uYy4uLgBER0drXInQWps2bbC1tWXp0qWEhITQvXt3wLRtXp06ddRux1KlSmX6o4TL9PR0HBwcGDZsGN26dSMmJkZtTRKisFWpUgWAP/74I9PtZ86cAZDdoYTQGQmXOlahQgXg79/uRcllMBjo1KkT4eHhALRr10697/333wdg2rRprF69ml27dvHZZ5/xww8/AKZliIYPH46vry+HDh3iwoUL2NraYjAYiv6JiBKpXLly9O7dm5CQEKZMmcKxY8f46aefCA8Pp3fv3urqGEIIfZBucR1TwqW0XAqAVq1asWPHDlq0aEH58uXV25s0acKXX37JwoULWbt2LWD6x1wZ35aQkEDNmjX55ZdfAKhTpw7Dhg2TcCkKjNJCXqrU3+0Zyv8r9w0bNgwLCwu2bt3K6dOnAejbty/vvvtuEVcrhHheFhkZGRlaFyGeTUBAAF9++SV9+/Zl8uTJWpcjNODn50fbtm0BOHHiBNOnT+fTTz+lQ4cO2R4fHx+PtbU1ZcqUyXJfWloaKSkp6gL9zs7Osv2jeG7p6en5mpCjTPBxdHRUP39WVlYywUwIHZF/OXRMabm8c+eOxpWIohYUFMTUqVOJiYmhXr16HDhwgE2bNlGuXDlat26d4+McHR1zvM/a2loNlkJoxcrKSh1PLoTQJwmXOqZ8AcuYy5IjKSkJb29vfH19AXB3d+fixYv88ssv1KtXjwkTJkh3thBCCE1JuNQxZfkhGXNZMmzfvp158+YRFxcH6/vIjgAAIABJREFUwODBg/noo49ISUmhU6dO2NnZFej1Hh8fJ8SzsrS0xN7ePtv7AgMDOXPmDG3btqVZs2Y5nkM+i0Loi4RLHbOzs8NgMMgWkGbuxo0bTJ8+nQsXLgBQv359pkyZQp06dQCyHT8pRHFhYWGR42f09u3bbNiwAQcHh0wrHAgh9E3Cpc65urpy7do1wsLCcHNz07ocUYDu37/P0qVLWbFiBQAODg6MGjWKgQMHalyZEAXDwcEBgMTERI0rEUIUJAmXOle9enWuXbvGrVu3JFyakUOHDvHdd9+pWzf26tWLsWPH4uTkpHFlQhQcJVwmJSVpXIkQoiBJuNS5atWqAabuJaF/UVFRzJgxg8DAQABeeOEFJk+ejIeHh8aVCVHwpOVSCPMk4VLnlHB569YtjSsRz+uXX35h+fLlGI1GDAYDI0aM4L333tO6LCEKjTLRJzU1VeNKhBAFScKlzlWvXh2Qlks9O3v2LDNnzuTmzZsAdOjQgX//+9+yn7Iwe8oKB0ajUeNKhBAFScKlzinhUlou9ScuLo65c+cSEBAAQKVKlZg0aRJt2rTRuDIhikbp0qUB0+Q1IYT5kHCpc1WrVgVQW72EPmzcuJEff/xRncgwbNgwRowYgY2NjcaVCVF0lM+7hEshzIuESzPwwgsvcPPmTSIiItSwKYqnoKAgZsyYQVBQEADNmjXjiy++oEaNGhpXJkTRk5ZLIcyThEszUL16dW7evMmtW7ckXBZTT27bWL58ecaNG0fPnj01rkwI7UjLpRDmSfbUMgNKq1doaKjGlYjs7Nixg/79+6vBctCgQWzatEmCpSjxDAYDIBN6hDA30nJpBmrXrg1ASEiIxpWIx924cYOZM2dy7tw5wLRt46RJk6hfv77GlQlRvEi4FMK8SLg0A0q4DA4O1rgSAVm3bbS3t2fUqFEMGjRI48qEKH7s7OxISUnRugwhRAGSbnEzoLSEXbp0SeNKzNvixYtp0aJFrsccP36c/v37q8GyZ8+e+Pn5SbAUIgdWVqY2DgmYQpgPabk0E8qM8bCwMNljvBBERESwdOlSALy8vNTtGRVRUVF8++23HD58GDC9H1988QXNmzcv8lqF0JNSpUxtHI8ePdK4EiFEQZFwaSZq167NzZs3CQ4OlnBZCD7//HMsLS15+PAh6enpme5buXIlCxYsUH8eNWoUw4YNK+oShdAlCZdCmB/pFjcTtWrVAmTcZWHYunUrQUFBPHz4UL3Ny8uLixcvMmjQIDVYenl5sW3bNgmWQuSDhEshzI+0XJoJmTFeOBITE/n222+ztFampaXx3nvvAeDi4sLEiRPp2LGjFiUKoWuWlpaAhEshzImESzOhhEtl5xdRMBYsWJCpxVJhYWFBRkYGQ4YM4aOPPlLX6xNC5I+FhQUg4VIIcyLd4mbCzc0NGxsboqKi1P2qxfM5ffo0mzZtynH3EAsLC9avXy/BUojnoLRcZvdLnBBCnyRcmpHGjRsDcPHiRY0rMQ9ff/01GRkZastKduQfRCGejzLmMiMjQ+NKhBAFRbrFzUiDBg04c+YMQUFBeHl5aV2Ori1evJiIiIhcg6WidevWnDhxogiqEkLfWrRoga2trdpaCX+vb/nGG2+oQRMgKSmJ06dPF3mNQojnJy2XZqRRo0aAjLt8XhEREfzvf/8jLS0t2/szMjIytbI8fPjwqYurCyFM31GpqakkJSWpf5SxlikpKeptiYmJGlcqhHgeubZcGpMfcj9VBlnrhVuV+jgYKnEzJJqEmOyDkXi6779dTGkLR0obTFvTPXr0KMvexwaDgcqVK2NtbU316tX55JNPisVrXqasFValn97aKoQWPv74Y0aOHPnU42xsbOjWrVsRVCSEKAwWGbkMdDnkG03w70lY20gDp16kpaVRqlSpTN1OIv/u37+vdtE93lVXnF9XY1I63d6pjHujMlqXIkSO3n333Vy3qlXGOfv7+1O1atUirEwIUVCeOubyxfblqe1ZrihqEUI8h6O+kVqXIMRTPa31Umm1lGAphH5Jk6QQQogi06JFC3V8+JMyMjJ48OABH374YRFXJYQoSBIuhRBCFKmPP/4429ttbGzo3bu3tFoKoXMSLoUQQhSp7FovpdVSCPMh4VIIIUSRe7L1UlothTAfEi6FEEIUucdbL6XVUgjzIuFSCCGEJpTWS2tra2m1FMKMSLgUQgihCaX1Mj09XVothTAjsre4EKLYiLphxPeH21qXIYpQS+cvadkeNs1OAUK0LkcUkWYdHXmpr4vWZYhCIuFSCFFsZGRAxeoGXnmnmtalCCEKSdCJeDLS0rUuQxSi5w6Xx48f4vbtm7z++ltYWRX/rBoVFcGePVv5669ImjTxpFWrdjg6OuXrHFevXub8+VO5HuPmVpPWrds9c50REbeZMmUcb745jFdeefWZzxMaGsKZM4H06TMIGxubbI/x99/A1q0bmT17KeXKOT7ztQpKcHAQGzasICoqgg4dujJw4NtalySEEEKIPHruNHjo0B4OHNhJnz5v6CJczpw5katX/8TW1o49e7YB8MUX39C+fZc8n+PKlYssW/ZDrsf06NHvucJlenoaYWGh3LuX8MznANixYxP+/hto1MiDBg1ezPaY+Pi7hIWFkp6u/W+SDx48YPToodja2tG9e1/c3GpqXZJmIiPD2bFjE/37D8HJqbzW5QghhBB5UvzTYAEbP34yaWkPqFu3IefPn+bTT0eyYsVP+QqXPXq8RseO3dWfx4x5j/v3jSxatE69zdq6dIHW/azefPN9PD29qFu3odal5Mkff5wFYObMBTRq5KFxNdravz+ADRtW0qfPIK1LEUIIIfKs0MOl0Wjk5MmjXLt2BUtLS9q06UidOvUBSEiI5/DhPdSp04D69Rurj1Fur1u3IfXqNSIjI4OTJ3/jypWLWFpaUbt2fVq1egkLCwsAdu7cgpOTM66ubhw/fpDExHu89to/KF/eOUs97u611f/38GiBra0dycmJmY7ZscOPUqVK0aNHv2yfk7W1NdbW1urPlpaWADg4lM103LlzJ/nzzwtkZGRQu3Z9Wrdup9YMcPduLIGBh4mIuEXlylVp1aodFStWztPr+rjk5CTOnz9NUNBF3N3r0LhxUypUqMSJE0e5cycSgEePHql1pqWl8dtvB7h27Qp16jQgMfFelnPGxNzh+PFD/PVXJJUrV8XLqz0uLhVzrSO39yk9PZ2AAD/atu3E7ds3uXDhDJaWlrRr1xk3N3cADhzYxZ49WwEIDr5MUlKi2vqblpbG0aP7CQ0NpmzZcjRp4pkpMOf2GUhLSyMw8DA3b14nIyODunUb4unppba05/Zc79yJ4syZQDp37smBAzsJDw+jWrUXePnlLsTFxXLixBHS0h7QqVOPLO9dcHAQZ88GkpychJubO+3avYLBYADg8uU/SEiIo0GDJhw9up/IyNvUqFGLjh27Y2VlRXBwEFeuXARgz55t6nN+4YWS25IrhBBCHwo9XP7ww1fs27dD/dnHZxnt23fhiy++oUwZe5YvX8ALL9Rk/vwV6jFHjuzF23sW3367iPT0dGbM+A/Hjx/KdN6WLdsydepcrKysWLRoDo6OTsTHxwFQpow977wzMte6Hj16xMmTv5GampJpTOPNm9eZP38mAC++2BxX1+rP9LznzZtJQIBfprratOnAl19+i6WlJcHBQUya9DHx8XG4uFQkJuYOtrZ2TJs2Fw+PFnm+Tnp6OmPHDiMsLBRbWztSU1No3rw1X3/9I3v3buPw4b0AdO7cE2traxIT7zF16nguXvwdW1s7AFJTUzKd8+zZE0yf/m9SU1PUcy5Y8A1ff/0jzZu3zrGO3N6n9PR0vL1nsX37r4SGhqjnXblyIcuW+VK9eg02b15LUJApUC1cOJvOnXvSunU7EhPv8fnno7l69U+qVHElMjIcgA8+GMvAgUMBcvwMREVFMHXqeEJDQ3B0dOL+/fukpqbw0Uf/pl+/wU99rrdv32TevBmsX/+Lel2AtWuXExkZrj7m55+9mTp1Dm3adABg/foV/Pyzd6bXYvXqJXz33RIqVKjEqVO/4eOzDFdXN+7ejVHfgz/+OMu4cV9y7doVLlwwteL6+6/HysoaJydnCZdCCCGKvUJf53LAgKGMGvUpa9YEsGbNTjw9vTh8eC/Xrl3FysqKvn3fICjoImFhoepj9u8PwNHRicaNm7Fpkw/Hjx/inXdG4ud3iI0b99Gv32BOnTqmjpkE0/i0N98chq/vfpYu3fjU8Z/jxr3PlCnjaNLEk/fe+0i9vWrV6rRu/TJt23akUqUqz/ScAwMPExDgR9euvVmzZicbN+6jR49+HD9+iEOHdvPw4UPmz59BfHwcixatw8dnB99+uwiA+fO/ytfYR+W169VrAJs3H2bhwrUMHWpaL+6LL75hwIChmY5fvXoJFy/+zgcfjMHP7xC+vvtp2bKten9i4j1mzZqEo6MT3t6r8fM7xNy5y7G1tWPWrEk8fPgw2zry+j4Zjan8/LMfmzYd5JNPPgNM77fpua+gV68BAGzfHsh//vNfANau/ZmrV/9k3LgvWbFiC8uW+VK3bkOWLp1HRMTfy9Zk9xn44YevCA0NYdKkWaxfv4fNmw/zzTc/0bPn6/l6rjVq1GbLlqNs2XKU+vUbExkZzvvvj8bP7xBLlmzI9DwuXTrPzz9707ZtR1av3s7WrceYMGEKkZHhWQJnly698PXdz7p1u3Fzc2fnzi2kpCTTo0c/Bg16B4AFC1bh47ODdu065+kzIYQQQmip0MNlrVp16datL9euXeXYsQOUKWMPQGSkKRR07dobgL17twOmbshLl87To8drWFpasmPHJsDUhR0WFkpExC2aNWsFwKVLv6vXsbW1o3//IVhZWWFnV+apdSnj+YKDL3Phwhn1dmtra6ZP/54pU2Y/8wQlpbWwV68BWFpaUrZsOfr2HQzAyZO/ERkZTnBwEJ6eXmo3vYdHC5o1a0V4eBgREbeyPW94+C22bfNV/6SkJFOliisAJ04c4fLlP6hZs06OYxWNRiObN6/D0dGJ119/CwsLC6ysrDJ1L585E0h8fBxeXu15+DCdK1cuYWlpSZMmzYmPjyMqKiLbc+f1fWrVqh2urtUpVaoUbdt2AsjUIpidfftMn43OnXsCUL16DfVz8/vvf8/af/IzoHRpu7vX5uWXX1GPa9asFaVLl87Xc33llVcxGAwYDAbatu0IwMsvd8HCwoIXXqhJo0YenDp1DPg7ZLZr15nY2GiuX7+Km5s7jo5OnD0bmOm5de/eDysrK5ycyuPp6QVAbGx0rq+HEEIIUZwVerf49evBfPbZR8THx2Va8uf+fSNgCgqNGzdl164tvPPOSI4e3Q9Ax47dMRqNavCYMGFElnM/3sLXpElzSpfO+ySaDz8cx5AhH/D++68zffq/2bLlqDoe7nlFRZlqrlOngXqb0p0ZHh6mjoNs0sQz0+MaNfLg2LGD/PVXZLatpteuXWHBgm/Un5Uxml988Q1z505n7NhhtG79MmPGfIGzc9bFaZVA367dK+r4yyfdunUDAD+/tfj5rc1y/6NHWVsu8/M+PU6ZAZ2W9iDb+5Vzx8fH0bx560zvrxKIldcasn4GwsPDAHjppexb/J7luebEyckZCAYgNNT032+/nZzlOGUoQnaqVjUNwUhLS8vzdYUQQojiplDDZXp6OnPnTiM+Po65c5fToMGLXL78B+PHD890XK9eA5g160vOnAlk//4duLvXxt29thpKqlRxZfnyTZkmwwCZfi5VKvuwlJsyZezp0qU3vr6r+PPP8zmOJ8wvg8EWME20UdaNTEiIB0yTfpSAce9efKbHRUf/BYC9vUO2523TpgMbNuxVfy5bthwA7dt3oVmzVmzYsJING1byySfvsHr19iyvl+LJ6z5OWQvz00+n06lTjyz3lyqVtbFbaeHN7X26f/9+ttfLLWw9fu64uNhMt8fE3AEyv1ZPfgZKl7bJ9rGKvDxX5T3JD+X9X7nSP8skn5zek6fdJ4QQQuhFoXaL37p1g+DgIPr1G0yjRh6UKlWKBw9MIePx8Wxt23bC1taOxYvnEhwcRI8erwGoXbambuTLlCpVKtOfZ/nHODk5KdPP169fBVC76wHCwkLVVq1n4e5eB0CdnAJ/dw27u9dRW6ge744H1G7VqlWrY2Vlmo2uhCgwddmXK+eo/lGef3p6Og4OZRk+/GN69OhHTMwdbty4lqWuSpWqAqZu+5zCXq1a9QA4cGBnltc7u2AJhfM+PX5ud/fahIaGZJrVfv78aYBc18GsXr0GYOqmzq719Fmea14oLdZHj+57rtdCCb+3b9985lpE/iUkxLNx4//UCVV6FBCwWf2F9llcuXKJdet+YfbsqaxZs5yzZ08UYHX6c/jwXj74YBA3b15Xb7t3L4H161dw505Ujo+7fPkPPvhgEGfOBOZ4TF7l5XpCFBcF1nK5cuVPaiBS9Ov3JlWquHL69HFOnDhKSkoyixbNBuDYsYN069YHAIPBwCuvvMq2bb6AaSybYsSIMXz66T+ZMmUcvXoNoEKFyhw4sJOqVasxduykfNVoNBoZNWoITZu2om7dhpw7d4KzZ0/g4dGCevUaAaYxnx98YFpXcM2aAJydK+T7tejb9w18fVfx9def8+abw6hatTo//jjr/78mgylXzpG+fd/A338DkyZ9Qs+er3P27AnCw8Po2/cNNTy6urqxffuvuLq68eqrr2d7reTkJD788A1ee+1NXFwqcf78aWxt7ShfPmu3uJ2dHf37v8WmTWv45z/foFu3vri4VGT79l/VY5o1a4WHRwtOnTrG5MljefnlLly/fpX9+wOYO/fnHGfPF+T79KS33hrBzJkTGT9+OIMGvUNSUiL+/htwc3OnRYs2OT6ubNlyvPnmMNat+4URIwbw6qv9KVXKNI73/fdH07Ztx2d6rk/z+utvsWXLepYunc/t2zd58cXmnDp1jBs3QliwYFWmZaxy07LlSyxdOp/Vq5eQkBBPWtoD9e+MKDwJCXEsW/YDQ4aMoEmT5lqXk2+7dvkzb94MXnyx+TPtuLVly3p++uk7AHU1BIDu3fvy0Uef5nn4UEZGBgcP7iY1NSXH76/nkZycxM6dW6hdu16+Vth4FklJiYSFhfLgwd9DeH7//RQ//+xNqVKl1Ml3T0pNTSEsLJSUlOQ8X+vSpfOcOHGE998fnen2vFxPiOLiucOl0hKzadOaLPf16PEa//rXv1m8eC6TJ4/F1taOoUM/5OzZQI4fP4TRaFS/qFq3fplt23xp2bJtpvGCHh6eTJ06B2/vWfj4LAPA0dEpy3jFvLQIPXr0kKZNWxEQ4EdAgB8AzZu35qOP/q0eY2trp3bVKt2bT2NpaZkpMFSqVIW5c5czZ840fvnlRwBcXd2YPHm22k06fPgnWFhYsGXLerXF8rXX3mTYsL+/UD78cCw+Psvw9V2V45dzfHwctWrVU3cMqlu3IcOHf6L+o6K0wCmvz7vvfsSDBw/Yts2XlSsX0qSJJ66ubsTHx6mta5MmzWLRojns27eDEyeOAuDp6ZXrGMS8vk9PaxHM7n1s374LSUlfsGTJ98yZM02tZ9y4LzNNusrusW+//U9sbAxs2LCS5csXYGtrR8uWbSld2iZPz1U55+N1P/maPnm/o6MT33//MwsWfE1AwGYCAjZja2tHhw5defDgPtbW1nk67wsv1GTIkBFs2rSGb775AltbOwmXIlcXL/7O3LnTn+vxP/30HXXq1OfLL7+jUqUqxMZGM3/+THbt8sfNzT3P27E+fPiQb775Ql0BoqBdu3aVJUu+Z+LEmYVy/qdp3fplJk/+jqZNWxboeVes+Ino6Kgs4bKwridEYbDIyMjIyOnOQ77RGMqWprZnuee6SEZGBnfvxlK+vLP6D2dsbDTly7uoPwcGHmbKlPFMnDiTTp26Z3ueuLi7lC5dOlMX9rNIT08nLi6WsmUds91v22g0TTYqiAk+SlfukwusP15LfPxdHB3L5zg7PTU1FVvb3INuWloaKSnJeW6pSE9Px2hMzXF8p3LOhIQ4nJycc5wAlJ2Cep+yExsbQ5ky9vl+b0yfwRgcHctn+1ye9bk+TUpKMkajMdsF/fN+jhQePLifaShEdo76RtKkXVncGz19tYTiKjLUyJHNMbzyTjXNaggLC+WDDwYxZMiIHNfLDQ0N4dy5k8TGRuPiUhEvr/bqyg07dvhRrpwjL73UKdNjAgI2U66co7raQG6L7IeGhnDx4jnatu3IpUvnCQ6+TJ06DXLdSeyvvyL55z8Hqy2Ny5dvolo1NwBu3w7j4MFddO7ck6pVc35tP/98NGfOBPLzz36ZWu6NRiNvvtkNgHXrdgOwZ8/WLBtg7Nu3A4PBlpde6oS//wZ+/PFb6tdvTJcuvbCxMdCtWx/u3InixIkj1K5dn8uX/yAuLpbq1WvQrl1ndaWPW7du8Pvvp/Dyak+FCpUA0/fg3r3bqF27PpUqVWXDhhX4+a2la9fe1KvXCFdXt2zHzQcGHiEi4hbdu/fN9J20f/9OkpMT6dNnELGxMZw8eZTw8DAcHMrSuHGzTKtu7Njhx/z5M/H2Xk2dOvUJDg4iKOgPwNTDULlyVfXY338/xR9/nP3/3+nWzJs3g0mTZvHyy6/w6NEjzp8/zeXLf5CUdI9mzVrTrFkr9bs/MPAIixbNJj4+juHDPwagU6ceREaGZ3u93DaXyMvGFVoJOhFPRlo67fpl7WET5qFItn+0sLDIMntZ6W6OjY1m//4AfH1X4ejohJdX+xzPU1D7K1tZWalfWNkpqFnjkHOofLyWp+1887RgCX+Px8wrKyurXIOlcs6n1ZadwtwHO7tZ8Hlh+gzmPMThWZ/r09jZlcnT0li5n8MOO7vcJz6JonP7dhgjR76Z6bZFi+Ywb94vNGjwIufOneDw4b34+u5X//6HhoYwb94M3n77n7Rt2/Gpi+xfvvwH3t6z2LVrC8HBQYBp69qcpKamMmXKOGxsbDINMVKsWrWIgwd3ExUVzv/939Rsz5Gens6ZM4E0auSRZUiIwWCga9fe+PtvIDLyNg4OZfH2nsXbb/8zU7j08VmGo6MTL73UiU2bfADT2POYmDu4uFSkW7c+3L59E29v0zChx7vdV65cyNy5y6lUqQrXrl3F23sWbm7u6nd1SkoS3t6zGDr0A9q1e4WdO7cAcPTofs6dO0mnTj2yDZcJCXEsXjwXe3sHteU/NjaGWbMm8corr9Kr1wA++ugf6gYMipEjJ/D66//I9rW6cuWi+hz++9/5VK5clYyMDBYunM2WLesBMm3qoNizZ1umluVff/XBxaUiixevx97egePHD6orb6xb9wtgarHM7npP21wiLxtXCFFYCn2dy6e5cOEsy5b9QOXKrsyZszxPQUoIIbRSrZobH3wwllmzFrJt23GmTDGNI9+wYSUA3br1BeDIkX3qY44eNf1/hw5d87XIfkpKCsuW+eLv/1umncQe9+jRI+bOnUZoaAhTp85VW1Af17Fj9/+/3mvOLZ/KygjKhMMnKcuj5XVCybJlprHcvXoNwMdnR6Zd2ADeems4fn6HWLMmgHff/RcxMXdYuXJhns7t7l6b6dPnATBmzBf4+OxgxIhPsj22Q4du2NrasXPnZvW2gwd3Aabx8aVKlWL06Il89tlX+PkdZvHi9Tg6OrFy5UJy6tjr3Xsg06bNzXTbb78dYMuW9bRv34UtW46yfv2eTBt0gGk+wdtv/5NFi9axZctR3nxzGDExd9S1cceN+5ImTTypUsUVH58d+PjsoGLFytleL6+bS+S2cYUQhUXzcNmyZVt+/fUA8+evULtwhBCiOBs4cCjOzhU4fHgPd+/GAKgziZs1a4Wjo5M6rjsjI4N9+3ZQt25Dqlevka9F9nv1GkD16jWwsbHJcdjMmjXLOXx4L//5z39p0ODFbI9p06YDixato3Xrdjk+p/R00/qqyhrET1KW9srp/vyqWbOu2qPw1lvDcXevzbFjBwvk3I8zGAz07j2QS5fOc+vWDTIyMti+/Vfq1m2otrq+/PIrNGniyZkzx7lw4QxOTs6kpqYQF3c3z9fx8zPNOxg69EO190uZKKqws7NjyJARJCcnceDATvU9VdbkzY+8bi7xLBtXCPG8iqRbPDdP65oVQoji5scfv8Xf37Ttp6ur6ZdiJXRZWVnRp88brFq1mNDQENLS0oiMDFcnwuRnkf3GjZvmWsdvvx1g1arFuLhUVFdRUJZQ2r9/B9269c00HjA3ygoTFy6cISMjI8v4XmVZtCfXbi0oL7xQk9DQEJKSEgv83D17vs7Gjf/jwIGdeHq2ITw8LNNEIGVMJZjW61W6s/MTpK9du0qVKq7qhhnZSUlJ4bPPPlKXqVNamY3G1Hw9n/xsLvG4vGxcIURB0DxcCiGEnhw9uh9//w307Pk6w4aNolw5Rz77bBRhYaHqMV269GLVqsWZuh/btTNtQZqfRfaftjnEpUu/4+joRHp6Gj4+SwHUYLRp0xo8PFrmOVyWKWOvjhOMiLidadxlRkYGJ0+aVlOoWLFKvnavevToUZ6Ou3LlEmB6fXKZZ5pFXo51da1O69bt2L79V+7cicLR0Yl27Uw7d12/Hsz8+TOpW7chEyfOxNW1erZjYvMiPj6O9PT0HFuZV678iaCgi3zyyee88sqr2NjY0KNH1tnfT9ulKz+bSzzpaRtXCFEQNO8WLw78/Tdw6dJ5rcsoVBMmjFDXrlOEhobg67s6xwXVwfTafPDBoOdakDk/1xOiuDt0yDRb+p13RqqT6O7fN6rdygCVK1fF09OLrVs3snu3P23adFC3vy2oRfbBtI3t+vV7Mv354IMxAHh7r8bDw7QUmNFo5Pz5M0/9u9e//xAAdu3akun2/fsDCA0NoX37LpQv74y9vWmi0tWrf6rHxMfHqUME4O+gHBZ2nae5desGkZGH/G1qAAAgAElEQVThuLvXzjTh8tq1q5mOeZzSYnf7dt42vOjdexDx8XHs2bON1177h7p8nBJq33jjXTVQK5t9KMFYOTY2NjrH89esWYfU1BT1fNnZtcsfV1c3evXqj8FgUN+PxzcVsbGxISbmDikpKTme53k2lxCiKJT4lsvY2Gh+/PFbWrZsy4wZPxTptSMjw9mxYxP9+w8p1BnWYPqSdnDIvKTUjh2b8PffQKNGHjmO1YqPv0tYWGiO+4M/KbeFk/NyPSGKi1OnjmVpQXJ3r4OXV3sOH97Ltm0b8fBowYEDu9RfToODg6hTpz5gWuf3zJlAUlNT6NKll3qOglpkPz+WLPme7dt/pV+/wZnW9X1Sr14DCAjwU3eCadDgRa5du8KuXf7qOsVgGsfYsWM3Dh7czerVS3F2rsCWLevUmd9gWv9XOWb9+hWUL++SaQzi1q0bycjI4PLlCwQEmCbbvPXWCADq12+Mo6MTv/66mtKlS5OSkszy5Qsy1Vq7dn0cHZ3YtcufChUq8+DBfbp375fjah+enl7qrOru3fuptyvrRu7fH4CDQ1lCQoJYvdrUCnzy5FFef/0fNGxoWpZo+fIfsLW1zXbR9oED3+bSpfN88cXHdO3am0aNmrJ164ZMx3To0JWdO7ewf38A9vZlWbfuZ8DUGj569EQMBgNt2nTk1KljLFo0m+bNvXBwKIunp1eW6z1tc4m8fmcLURhKfLh0dq7AV195U6VK0a+rt39/ABs2rKRPn0FFfm2AN998H09PL3WcTkHIbeHkwrieEAVNaXG7evXPTC1zAF279mb06Il07XoSH59l+Pgso3HjpowdO4l582Zw4sQRNVwqy+LY2trRokVb9Rz5WWT/2erPusi/ssxWbkuwgak7dd68FcydO40DB3Zy4MBOAJo08WTs2EmZusoHDx7GgwcPWLVqMWAKV0+OHRw8eBjR0X+pXcxjxnyhdtPfvHmNmTMnAqbXaPz4yeo6nlZWVowZ8wVr1/7MggXf4OjoxLBho/jllx/V52dlZcWoUf9h8eK5zJs3AwAPjxbUqFEr2+dmaWlJ376DuX79aqZ1Z6tUceX990ezdu3PHDt2EBeXinz66XR+/tmbI0f28vrr/8DVtTrvvDOSjRv/x2+/HcDDo0WW17lt2458/PFEli37AX//DZw5E4i7e23g7w0SBg58m8jIcGbN+hKAHj360bhxM9avX8GVKxfx8GhB+/ZduHDhNLt2+bNrlz+dOvXA09Mry/WetrmEEi6fZytbIZ5VkSyiXlzFxd1VlwipWbMujRp5cOdOFKdO/Ub37v04cmTf//8icqFLl17qmnWXL/9BSEgQ9eo14tSpYzx69JBaterh5dVe/Yu8b98ObGwM6rgeMHVZhIWF8uqr/bl+PZhVqxZx4sRR3nlnpLoAbnaDwbdu3Yi1dWl69Pj7t+20tDS2bt1I1arV8PJqn+uizgCvvdaepk1bMnXqHABOnDjKnTuRgKmFRWktSUtL47ffDnDt2hXq1GnAH3+cxd9/A2vW7MTZ2eWpiw3ntHByTte7ezeWwMDDRETconLlqrRq1U4dh5aX9yI3sbHRxMZGl5gwK4uoF63ExHtYW5dWW8rS09NJT09Xf46Lu8ubb3ajV68B6hIwTyqIRfbz6t69BMqWzft3+YMHD4iO/ouKFSvn2pqamHgPW1u7HMcZgunvYpkyDhgMBs6ePcFnn41i0qRZNG/emvv37+f6/OPi7uLo6JRj4E5PTychIY5y5ZxyrQHg/v37pKenZbvBg9Fo5P59Y6b1ghMT72X6rjEaTcMfchvTmJGRwb17CbmuO3z3bixly5ZT6713LwE7uzKZ6k9IiMfKyipPm1E86+YSWpFF1M1fiW65vHs3Rl2Y9o033qVRIw9u377JDz98zdatGzMtPLt160aWLNmAlZUVp079pm5x+PgiwJ6eXkyfPg8rKytWrVqMs3OFTOFSmQjQtWsfrl27os7q9Pdfj5WVNU5OztmGy8uX/2Dfvh28+GJzteXg1KnfWLx4LuPHT37qos7Z2bt3G4cP7wVMS1lYW1uTmHiPqVPHc/Hi7+qg78e7uR49evTUxYZzWjg5u+sFBwcxadLHxMfH4eJSkZiYO9ja2jFt2lw8PFrk6b3IzW+/HWD3bn+8vVfnepwQz+LJX3CsrKywsrIiLS2NvXu3q+sqdu/eN8dzFMQi+3mVn2AJpjGNTy6mnp28/KKX0wYGZcr8P/buOyyqa2vg8A8YaaKAHVHsJcFILNglFow1GkvUzxgTY7zRFGtuotGosQQ1ajRCxB4LlohiwV4xNsQuVlQUBRSlI0UH+P6YO0fawKAoxfU+T57IzJmz9znDwGKXtSxyDJ5yWjKkUqmyLZCQlomJSZZV2UAz1Z8xOMt4bZrnsw/gDAwMcixokTGYzuq9yU1RjJctLiHE6/JWj5fXqFGbTZsOZvlcuXI2eHn5sGHDPjp06EZwcJCyWFpr+vQ/2br1KO7uG2jT5kPOnj3FwYO79Gq7U6cefPLJIAAWLlyDh8eudIFoWtpp80OHXpx7z55t/5tK+zDHpM5ZmTBhJr17D0z32Nq1S/D3v8DQoSPx8vLB0/MQjo4vpvP0STasK3FyxvaSk5NZsGA6UVGRuLtvwMNjF7NnuwOwYMFv6dYL6fNeCFFQREaGM3/+dKKjI5kyZW6mXIdCCFHUvdXBZXY6dOiGuXnx/y2w/gDIXJmievXagKZaxLff/gTAmTMn8rwv77zzHrVq1cXb25Pk5GSePAnD1/dfevTop/ylnV1SZ30kJiaydesGrKys6dlzAAYGBqhUqkxTynmRbBg0m5kCAq7TqFEzZV2Sg0NjGjRoQnBwECEh95Vj9XkvtGJiopk/fwbz589g/35vHjwIUr729T2Wqz4K8TKsrEqxdu1O/v57m/L9Kl6oU8ee+fNXKhtphBBFz1s9La4v7Waf7BLPlixpiZmZOaGhD3Qe8yp69OjPnDlTuHjxDAEB1wDSbZrJLqmzPrT9btWqPUZGunPr5UWyYUBZf1m/fqN0j9vbO3DixBEePQrNsh85vRea1CGaUdQXC9k1X+cmd54QL8vY2DjHjTNvs+LFLSRbhBBFnASXeSQs7CEJCfGYm+e8+PpltG7tjJvbbPbt28H165dxcnJWNr7ok9RZXzExuvNZ5ibZcE6Jk7VrOjO2p61vbGFRIt16T31ZWVkzatREQLO5aN++7crXQgghhHj9ZFo8j5w9q6kJXLNmHUAzwnb79k1l7WBycjJ3795O9xrtwvIHD+7leH5TU1O6d+/L4cN7CA0NpkePFxt49EnqbGJiorMkGED58pr0IEePHtCZaFmfZMP6Jk6uWFHz+kuXzqZ73M/vRLrnhRBvn4zFG2JiopXcm/nlypWLDB36CefO+RbqNoR4EyS4fAXr1i3j6NEDuLj8rORZ0wZ9rVq1JyEhnvnzp3Po0G6mTfsxUyDl6NgS0GykOXx4L/v27ci2vU6dPgY0azzT1hxu1swJAG/vTVy8eIb582dw5cpFoqIiCQi4DoCTUwcCA2+xZMkfWQaP5ubm9Oo1AICvv+7LunXL2bdvBzt3blaOSZts+MIFPzw916RLNgwvEidfuXKRjRv/Zv9+7yxHUC0trejeve//doyP4PjxwyxcOJPg4CC6d++bq52SujRt2prvvhv3yucRQrxZGYs3XLjgx4oVrsof0vkhKSmRoKBAnj6NK9RtCPEmvPXT4tqRtoz/T5t4VvvvjMloDx7chbe3JwB2dtUYM2YS5cvbAJqNL3fu3OTgwV3s3+9N8+Yf0KJFG06cOKK8vkqV6nz66Vds2bKOmTMnYGZmzocffqSzrxUrVqJp09a0bt0+3eMtW7bLMalz9+59CQ19wObNHvTu/RkmJibK9Wiv+fPPv+HZs2d4e3uyatUi6tdvhK2tHVFRkRgaGuqVbBiyTpxsZ1ctU3tDhozAwMCAbds2KiOWH3/cn8GDv8v1e5GV8uVtlPdDCFF4NW3amkmTfpdNQEIUEm91EvWXtXq1Ox4ey1i3bg+GhoYYGxvrzNWWmJhISkpytrns4uPjefYsCUtLqxwrc2SXsDinpM7a/uSUaFetVpOYmKAzUbA+yYYhfeLknNqLiorAyqpUjrkrhW6SRP3N8PM7QVhYKK1bO3P8+GEePLiHk5MzderYk5qayunTx7lxwx8jIxU1a9alSZOWyuc6JSWFEyeOcPPmVcqXr4itbWWWL1/IL7/MVtZQP3kSxsmTPjx6FEqFChVp1sxJqbCzZ882UlJS6Nixu7Lhzd//AoGBATRu3CJd4YS0rl27zNmzpzAwMKB+/UasWbOY/v0H07Bh02yv5/nz5xw7dojAwACl0IM2g0RiYiL79++gVq13qFu3ntLWwYO7MDU1o2XLtly7dpno6Ejeeac+x44dIjT0AVWr1qBNm47pPuvZFW+IiHjC9euXAc1sT4UKFfU+b3bXrUtUVCR+fse5f/8uJUtaUq9eA+rWrackf//pp2mAAYGBAZQvb4Ozc7d0P+Ny+h7Qp42JE2cpgwiHD+8lLi6Gtm07YWFRgsDAW5w9e4rkZDX16jWgdu13X0vJ0NdJkqgXffKb/BXllOBXn4oJ5ubmmJub69VedgmLdSV1zm1/VCpVthUo9Ek2DLoTJ2fVnvaXpxAF3cGDuzh8eA+7d3spy046d+6JWq1m+vSfOHnSJ93xjo4tmDJlHkZGRsyYMY5jxw6lK75ga2unBB7nzvkydep/SUiIV45ZuHAmLi5uNGzYlPj4pyxePA+1+jndu/clJOQBY8d+Rd269ejcuWeW/d26dQOLFmly35qZmbN6tSaf7KBBw7K9ntjYGH7++Ttu3ryq1OQGGDp0FH36DCQuLgZX11l89tnX6YJLD49lWFlZ07JlW6XghK2tHRERT5Rrvnz5HKNHa0og5lS84cYNf6XYxbRpC6hQoaJe583purOS9v5rCzsA6QoxaEs3ah0/fhgXFzeAHL8HVCqVXm1oeXt7snDhTHr1GoCFRQnOnz/NuHHfpDtm3rzl6aqkCVEQyJpLIYR4CTY2ldi06SBeXj5UqmTHli0enDzpw6BBw/Dy8mHTpoP06NEPP78T7N/vzcWLZzh27BBt23bin38OsG7dbszMzDEyMqJs2fLExsYwa9ZErKyscXVdi5eXD/PmLcfMzJxZsyaSnJxMjx79qFfvfdzcZnP//l1cXH7GzMycn392yXLUPz4+nkWL5mBjY8s//xxgw4Z9yqidNmWZrutZv34FN29eZfToX/j7720sW+ZJ7drvsnTpfEJCcpdyzdm5K56eh9iwYR92dtXYs2cb8fFPgZyLN3Tr1odff52Xq/Pm5rq1YmNjmDr1v5iYmLB48UY8PHaxY8cJXFzclHrxoJmi37LlCNu2HaNt206cO+dLeLgmt3BO3wP6tgGacsELF86kadNWDBkyAkBJN+fhsYutW//lp5+mS5J+USBJcPkSunTpxfz5K/Nk04kQonD65JNBlCxpqSx52bVrC6ApBhAUFEhIyH0aNGgCwJUrF5SsEE5OzhgbG1O6dFmaNm1FUFAgMTHRnD17iqioSJo1cyI5Wc2NG1cwMjKifv2GREVF8vBhCEZGRvzww68AfPVVH27evMpPP03Tubb40aMQAJo3/wBLSytMTU1p1Uoz3Xrjhn+213Pw4E5AU64VoHLlqnTo0A3QbLDJjY4de6BSqbC2LkWjRs0AzbIZfYs35Pa8ubluLT+/EyQkxNO+fVeqVq0BaHKWZpxC79Chm1LHW7tG/vx5ze7unL4H9G0jODiIyZPHYGNjy08/TVf+cKhQQZPVY+vWDaSmptKuXSdZSiQKJPmufAllypSTaVwh3nK1ar2j/DsxMVGZNh479qtMx6rVamrU0KQp8/c/T4sWbVCr1fj7X6BatZqULGnJ/ft3AfDyWo+X1/pM50hJSQZQNtatWOFK27adsq0CVKlSFUAzXZycnIyRkRHXrl0CoE6deumOzXg9UVGRNGzYFGNjY+VxbdCXXVqznGjTjD1//lzv4g25PW/lylUB/a5bKzhYE/w3adJS7zatrTU1wp8+jdPre0DfNlau1EyzJyTEExcXq6zp//TTody/f5dNm1bj7e3J99+Po337Lnr3V4g3RYJLIYR4CWk3aGhHj2xsbFm+fEumjXkGBgYkJyfTsGFTNm/24Pp1fx49CuXJkzA+/XQo8CLv7Y8/TqVt206Z2tNmSEhMTOTAAc2o4qlTRwkJeUDFillvgCpWrBifffY1a9YsZsiQXlhaWnP9uj9t23bKtF48q+uJjAxPd4x2fWB2a7JzktWmxeyKN7zMeXNz3VrGxpr7n9tStlr6fA/8888qvdooU6YcY8dOZvz4b3Fzm8XUqfMBzX2fPv1PLl48w4IFvzF79iQMDAyU0WUhCgqZFhccPXqAoUM/yVUt8oJg9uxJTJkyNr+7IYQylRsaGkxAwDUMDQ3T/aed7nV27gpAfPxTatd+FxcXN7p00WzE0Y5sHj68J9Pr06beWrnSlaCgQIYM+Z6EhHimTv2BxETd5VebNGmpbJQpXtyCESPG88MPU3K8nmrVahIYeIvY2Bjl8YsXzwBgZ1cdCwvNJr6bN68qz0dFRRIR8UTv+6ZP8YaXldvrrlat1v/6sj/d4/qWjdXne0DfNoYOHUXDhk354otv8PU9xtGjBwCUvJ8ODo2VTURpNw89ehT6UpXZhMhrElwWEKmpqRw+vJddu7zeeNtxcbEEBQXy7Jnu2ukF0cOHwfKDVBQYX301EoDJk0ezerU7u3dv5ccfhykFFqKjo5g9exKOji1o1ao9JUqU5OrVS8oO7QYNmuDg0Bg/vxNMmjSK/fu9Wbx4Hv36dSA4+D4Ap08fZ+vWDXTo0I2+fT9n3LgZBAbewtV1ps5+zZyp2STUuXNPKlSw5f79u1y8eEYJVHQZMEAztTtmzBD27dvBli3r2L79H+zsqtG4cXNMTU1p0+ZDfH3/Ze3apezevZVx44bnqmyrPsUbXlZur7tx4+bY2ztw4sQRxo79it27t7J06QL69/+Q8PDHerWZ0/eAvm1olwj07j0QW1s73NxmERsbw4YNK5kyZSxHjx5g27YNgCbHMmiWAwwa9BFDh37CgwdBL3fThMgjMi1eQCQnJzNz5gS6du2d310RQmRDVy5aB4dGTJkyF1fXWXh4LAM0te7r128EQMmSljg5OXP06AECAq5RoYIte/duZ82axaxY4YWtbWUmTpyFu/tcDh7cha+vpupVo0bNSElJJjY2ht9+G4+VlTVffz0GgLZtO3L58jl27txMvXoN6NSpR6Z+de3amzVrFrN+/QoqV65KWFgoXl7r+eGHKXTo0E3n9Tg5ORMXN4ElS/5g7txflb6MHv2LMgXcr99gnj17xpo1iwHo0+czEhMTMt2rrAoh6Fu8QXNs+tfoc96crjsjQ0NDJk+ei7v7XA4d2o2//wXKlClH8+ZtiIuLzbJNbRCofS6n74HctmFsbMyYMZMYO/YrVq92p3x5G4KCApkxYxxmZuZ06NCNXr0+BTRLAbp378v27f/w9Glslu+pEG+KJFHXk1qtZvduLyWv2t27tylXrgKOji2VxMUxMdH4+Oyjbt33SEpK5Ny5UxQrZsz//d+XAAQG3uLcOV+ioyOxs6tGy5btMDMzAzQpJtzcZlO3bj2cnbtiYmKarlrP+fOnuXr1EqmpqdSsWZemTVul+6Xw/PlzTp06yr17d0hNTaV27Xdp1KiZ8ksgu7Z37fJiwYIZuLquzZQOI63sEkAnJSVy4YIfzZo5UbZseQASEhI4cMCbmjXr8s4775GSksLFi2e4du0ycXExNGjQlAYNmih9zOn+RUZGcOLEER49CqFBgyYsX/4n8fHxrFixJU/e48JOkqgXHJGREVkWV3j+/Hm6hNfXr/szcuQXfP75cAYMGJLuuOjoSKytS7/yRpeMbarVarp2bYaDQ2Nmz3bX6xzh4U+UHdJZya64g75yKt6QW69y3Wq1mujoKEqXfvkk37q+B/KijcjICEqUKJnpfvv6HmPSpFF4e58s0InVJYl60Scjl3pSq9VKIt+Mpk//E0fHFkRFReDqOotateoSEHAdMzNzWrRoA8D+/d7MmTMFMzNzTExMiIqKZN265fz++2JKly7Lli0egOaXzZMnYZQpU04JLufPn8Hu3V5YWVkTFRUJaFJs/PLLbIyMjHj4MIQpU8YQGHgLKytrkpKSSEiI55tv/kuPHv1ybFsfqamp2SaAvn37Jq6us7Czq6YEl/Hxcbi6zmLgwKG888577N/vzbx5U5Vzbt7sQZky5Vi8eCMWFiWyvX937gQwfvw3REVFYmVlzcaNfyvtC1HQZLVp5OnTOP7zn740btycZs2cSEiIZ8+ebQBUr14r3bHFihXLs4wUM2dOICYmmi5demFqaqqUWq1ZU/cfkhnlFABlV9xBXzkVb8itV7lulUr1SoEl5Fxg41XayOrckZERLFu2gOHDfyjQgaV4O0hwmUsODo2ZOnU+KSkpnD59DBeXn5k/fzp//71NOSYg4DouLm68/74jSUlJhIc/Zs6cKdjZVWPevOWYmpqxZ89WXF1nsWbNYkaNmsiyZZvp2rUZXbv2ZsSI8cq5Tp06yu7dXnTo0I3Ro3/h6dM4li//kz17tuHjs4927Trz55+/ERh4K13JsPPnT2Nv/75ebesjbQLoMWMmERsbzZAhvZUE0Ppo3dqZx48f0bJlW2xsKrF+/XI2bFjJoUO76d69r877p1armT37F6KiIpk9ezEODo0ID3/CN9/8n17tClEQJCYmUr9+I/bs2aYElbVq1eXbb3+kWTOn19KmWq3Gzq4a69Yt59Kls4BmJ/LHH/fniy++yeHVhdfbeN337t3m44//j65de+V3V4SQ4DK3qlatoUwNtWnzIQEB1/D0XMODB/eU6aumTVsrSXHNzMyUnYEdOnRT/sLv0qUXy5cv5MiRfdkGeNpdgl27agK5kiUt6d69H3v2bOP06ePUq9eAs2dPUa1aTSWwBJTEvWfOnHzpttPSlQD6yJF9xMRE63UOc3NzPv30K65cucjhw3uUKZ3g4PSLzzPev4sXzxAYeIs2bT7EwUGzdql06TLKuiwhCoPSpcvw00/TGDVqIiEh97GyKpXj6NarUqlUfP75cAYO/A+PHoWSmpqKrW3l19pmQfA2Xvf77zvy/vuO+d0NIQAJLl+ZNnFvWNhDZe2ldvG2lrZaxLvvvqj/amRkRL167+Pnd4L4+KdKjrWMtMmK0yY4rlKlOqAJyrSBWcuW7bJ8vT5t6yOnBND6iI+PZ/z4b7h+XVMhQ3u/0m4AgMz3T5tcunnzNnq1I0RBZmJiQrVqNd9om0ZGRjpzYRZlb+t1C5HfJLh8Rbdv3wBIt2g74wJ8U1PNxpnY2PQjfNr6vGkDy5SUlCxf+/RpnFJuMjpak3C4RImSaRL/pk92/DJtZ6dWrXeyTQCtTy64Vav+4vp1f0aM+Jn27btgYmJCp06Z/9LWtYEhLi4my8eFEEIIUXBInstXkJKSwvHjh4DsN5bY2WlGGq9cuag8Fh7+mODgIKpVq4lKpVJ2fgcFpU9krk26qx3t05zngvKctszZoUO7s8zfpk/b2sXf2eVyyykBtHbd5e3bN5XXaEcctfbu3Y6trR1du2oW2GuTJicnJ+tsF17c2xMnjmR7nBCvw8sk69++/R+GDv1E+UOwoPLyWs/p08df+TxqtRoPj2WMGTOEiRNH8OhRaLaP5wdf32McOrQ7z8739Gkca9YszjHX7o4dm9i/31v5esuWdf9LP+XKpk2r8fU9Rlxc7lMH6dt+RteuXWbo0E84e/ZUrtsUQl8ycplLx48fpnbtd4mIeMKePduIioqke/e+WFuXyjQ6qNW4cXPs7KqxadNqEhLicXRswebNmt3h2kTFRkZGtGnzIUeO7GPjxr8pVaoMderY0717Xzw91+Di8jP9+w+mYsXKuLlpdq336NGPkiUt6d9/MBs2rOSrr3rTpUsvDA2N2LVrC19++R3Nmjnl2LZ2ynz58j8xMzPDwaFxpmtImwC6bt33CAsL5erVS5QoYUmtWnWpW7ceVlbWbN68FmNjY+Ljn7J8+cJ05/jggw7s2bONQ4d2Y2FRkg0bVgBw7NghvvtunM57Xr9+I6pVq8nZs6cYM2YI7dt3JS4uhitXLspucfHaBQcHpatSo4+oqAiCggJzTFSen0JCHuDuPpdff/3jlc+1desGVq92x9GxBXZ21ZQsFLoez8qVKxfx9f2XL7/87pX7k5XFi+cRHByEk1OHV0qZpHXypA9r1y7lo4/6Znvc2rVLKFu2vJJbc/Vq90yJ5q2srBkzZjJNm7bK8/YzSkiIJygoUO8lUfD63xtR9EhwmUtPn8bx+++Tla979RrAF198C2Sd2Bc0o36//ebKH39Mw9vbE29vT8zMzBk1aiJOTs7Kcf36Debx40esWOEKwMiRE+jSpSfz5i1n7txfWblSU+7L1taOSZPmUK5cBQA+++xrTExM+eefVSxfvhAzM3McHVtgbGyiV9u2tpUZNGgYmzat5vjxw1kGl/okgB45cgLr169g4cKZWFlZM3jwt6xc6aYkQO7T5zNCQ4OZNesXADp16kG9eg3YuPFvbtzwp1SpMlnePyMjI6ZOnc+cOVO4ePEMV65cxMnJGSsr65d5C4UQaEoQmpmZK5vnXsW//x6gUaNmTJ/+p16PZ+Xvv//i8eOHry2AmThxFgkJ8XkSWAIcPLiL5s0/eKmfQ7Vq1WXevBXExkZz8eJZ/vzzNyZNGsWiReszpaV6He3n1ut+b0TRI8FlLn344Ud89dVIYmKiKFWqTLpAqHLlquzdeybL15UtW57ffnMlMTGRp0/jssxvVr16LebNW054+GOKFy+h7Eq3t3dgxYotyuhJxpxyKpWKAQOG8H//9yUREYEUMOUAACAASURBVE+wsiqVbt2iPm1/+ulX9O49ELX6eZb9NzAw4McfpzFhwosyc9oE0D4++xgwYAgtWrShRYs2REZGYGVljYGBAf37D053f2bPdiciIpySJS2VH/J9+nyGuXlxVCqVzvtXrlwFZs92Jz4+HkNDQ53JnEXRFxb2kLNnT9GuXWcOH95DcHAQlSpVoXVrZyIjw/H1/Zfnz5/Rtm0n5Q8w0BQSOH/+NOHhjylTphzNmjkpm8oA/PxOEBYWSuvWzhw/fpgHD+6l++NPKyTkAWfPnsTW1k4JzJ4/f87x44e5ffsGtWq9k+VIp65CBnfuBHDhgh+Oji2UZS5qtZrTp48peV5Bk6Lr8uVztGnTEX//89jYVMLU1BRf33+JjY3BwaGx3oFiamoqe/ZspUOHbhgbG+d4vK4iDiEhDzhx4gjXr/vj6NiCHTs20bq1M/HxT7N8XFcgdOrUvzx+/JCoqEh27NgEQNu2nZS8lxER4Zw6dZSQkPtUqFCRJk1aKe9tWNhDfH3/pWbNuly7dpnIyHAqV65Kq1btMDfXFBTYuXMzKSkpWFqmbz8qKhI/v+Pcv3+XkiUtqVevAXXr1svxfjx+/Ihz53zT/TzMDSMjlZJ1o127TpQvb8OYMUNwd5+rV2L73LZ/4YIfly+fw8qqFCpV+hyYORW3yOm9efIkjJMnfXj0KJQKFSrSrJlTnuVoFYWXBJcvwdjY+KU/PKampjkGRrqmjnJKVGxgYJDttFNObWuey/r53CSAzim9SqlSpdN9re9uc9CkMxJvtwcP7jF//nQ2blxJaGiw8vj69csJDQ1WkvyvWOHKlClzad78Ax48CGLYsP7pzuPuPpf581fyzjvvAZqRoMOH97B7t5dS77tz557pXhMbG8PEiSOIiHiCm5uH8tiUKWPw97+AmZnm+zPjtGd2hQwMDAxYvHgejx8/VMo6nj17il9//QFPz0PK537dumWcP3+arl17s3nzWqKjI5VUXAkJ8WzYsJIff5xK+/ZdcryHN29eJTQ0mDZtOuZ4bHZFHB48uMfSpfMBTXDu53eCevUa8Pjxoywf1xVcnjx5RHkvN2xYCWhSkllYlCAg4DoTJ35PVFQkZcqU48mTMMzMzPn113k4ODTmwYN7SoGLtAUeVq1axLx5yylf3oalSxeQkBBPrVp1lT8Yzp3zZerU/5KQEK+cF8ixUhmAj48mvVyTJvpPY2fH3t6BWrXqKrXPcxpd1bf91NRUFi2aw7ZtGwHSvYdaORW3yO69SXsPtfd+4cKZuLi45cmIuCi8ZEOP0EvaBNBTpoxl1qxfiI+Pe60JoIXITtWqNdm27Rjbth2jbt16hIYG8+WX3+Hl5cOSJf8AKBs4KlWyY+jQUcyatQhv75NMnjwHgH/+WZXpvDY2ldi06SBeXj5UqvRiTa9arcbF5WeCg4OYNm2Bkjdx7dol+PtfYOjQkXh5+eDpeQhHxxbK69IWMlizxpu1a3fx3Xc/ERwcxJo1i6lWrSZ2dtU4dGi3ki1i505PAA4c2AloAtgTJ47Qpk1HTEw0GR5CQ4P5+WcXtm49yqJF6wHNRiJ9HDmylzJlyvHuu/WzPS5tEYd16/awadNBOnXqwcmTPvj47KNJk5asWaPZrDJo0DD27j1DtWo1dT6uy+jRv1C/fiNsbGzx8NiFh8cuypWrQHJyMgsWTCcqKhJ39w14eOxSRvYWLPgt3ZrWAQOG4OXlw7p1u/n88+E8eRLGqlWLANi69Wi69mNjY5g69b+YmJiwePFGPDx2sWPHCVxc3HIMLAH27t1Gp0498nQGRbt+PCzsYZ61f/z4YbZt24iTkzPbth1j48b9mZLIt27tzGeffY27+wa2bTtG//6DefIkTPns6HpvYmNjmDVrIlZW1ri6rsXLy4d585ZjZmbOrFkTM2U+EW8XCS71ZGJiwoIFf9O798D87kq+0CaA3r79OO7uG9iwYR+urmvTVdYR4k1q376LMhqvnT5u3doZAwMDqlSpjr29g1LyD6BPn4GULl2Wo0f3ExHxBIB79+5kOu8nnwyiZElLZUpVa/nyPzl79hTffz+O995rAGj+6Nq6dQNWVtb07DkAAwMDVCoVtWu/q7wuYyGDYsWK0aVLL8zMzDlyZJ/yXFRUJDduXOHhwxB8fY8BsG3bBlJSUjh50geAtm1fjDTa2trRpElLQDN7YGdXLcvryUitVrN791Y6d+6prBPXRVcRB+Cld5kfPrxXWf998WLWy2C0QkODCQi4TqNGzZTg0MGhMQ0aNCE4OIiQkPvKsdWr11ZmbwYMGEK1ajV1Zpjw8ztBQkI87dt3pWrVGoBmRkqf0bY7dwIICgqkXbvO+l6yXrTBmIlJ9gFjbtr38loHwMCB/1EC0Tp17NMdoy1u8fRpXLbFLTI6e/YUUVGRNGvmRHKymhs3rmBkZET9+g2JioqUAhdvOZkW15OBgYFea3GKuvxIAC3Ey7C2Lg0EKF+7uc1WRva0o0RJSYmZXpe2YIFWcHAQW7ZoflGnTbEVGqrJF9uqVXud+Vn1KWTQurUzy5cv5Pjxw8oayPHjf8PF5WfOnz/N4cN7sLKyxt7+fZ3Xa2dXTa+0NOfO+ZKQEM8HH3TI8diciji8jDVrFiuv7dq1d5YbCLXCwjSpizIWVrC3d+DEiSM8ehSq875XqVKdwMBbxMXFZqpZHhysqTimDc5zQ/te1KvXINevzY72+yrjsqFXaf/27ZvY2Ngq71lW9C1uoau/Xl7r8fJan+n51FQZuXybSXAphCjyjh07xPbt/9C5c08GD/4WS0srxo//NstgTNdo3uefD+fRoxC2bt1A27ad0v2xGROjO5+lPoUMbGxssbd3YP/+HSQlJdG1a28++KADHh5LWbp0PoGBt+jT57Ns1+JlzLKgy6FDu6ld+11l81B2ciri8DIWLPhbGaXLWMTh+fP0Gwq1a1gz3t/Hjx8BYGFRItP6Vq0bN66ku4a0XhSfiMhV39VqNfv2badjxx46g9qXcfv2TQIDb1G3br1sR5Nfpv2oqMhs13HqW9wi43ujXZ7x449Tadu2U6bj9f1+FEWTvPtCiCLPx0cz/Txo0DAlSEpKStSZHSEjGxtbBgwYwpAhIzAzM2fu3F9Rq9WUL18R0Ewfa4sCZKRPIQOAdu06ExUVSUJCPN269cHAwIDevQcSGHgLQK+Rxpxopz4//PAjvY7PqYjDyyhRoiSWllZYWlphZvYi8DMxMeHJkzDi418Ei9ryupcunU13Du1yB+3zGd2/f5fQ0OB09zctbd+PHt2f7vGcKo1dvnyOqKhIvTZC6SsiIpz586cDKBXP8qr96tVrkZAQrwTaWdGnuEVW7422JPDhw3swNDTM9J94uxWp74CQkAcMHfoJBw/uSvd4biszBAbewtNzrfIh27Zto17VNrZv/0f5BZJXfdHXlSsXGTr0E86d833lcxWkqhpC5AXtpjNv701cvHiG+fNncOXKRaKiIpWd4dkxNNSMEpUsacmIET8TFBTI5s1rMTc3p1evAQB8/XVf1q1bzr59O9i5c7Py2rRFFBYunMmpU0eZOXMi8KKQAUCLFm0BzZpCbQaGNm06YmZmjo2NbZbT9bmlXYPYsmU7vY7Xrql2cfmZDRtWcvTogXRFHPJS8+aadbPu7nM4cmQfZ8+ewtLSiu7d+/5vx/gIjh8/zMKFMwkODqJ7977KHwqgqYRz9OgBFi+ex/ffDwLS39+0Gjdurkytjx37Fbt3b2Xp0gX07/9htpXKDh7cRbVqNfXORanLvXt3cHObzYwZ4/jyy57cvHmVbt365DhNn9v2+/T5DIAJE77HzW02R47sw8NjabpjPvigA8HBQRw6tJvTp4/z88+avM3Hjh0iMVGzbCSr96ZBgyY4ODTGz+8EkyaNYv9+bxYvnke/fh0IDr6PeLsVqWlxtfo5QUGBxMSkn37KbWWGXbu2sH37P9jbO/DOO+8RHR2ZY7WN8PDHuLnNxtGxBdOn/6l3X0JDg9m1awu9en2aYwqf7CQlJRIUFMjTp3EvfQ6t3FTVKIjy6p6KgimrYgXaf6edUkz7fMuW7ejQ4TQeHsvw8FhGvXrvM2rURObPn46v77/UqlVX53SkkZFRuinItm07sn//DlascKVNm458/vk3PHv2DG9vT1atWkT9+o2wtbUjKioSQ0NDvYsolCpVGkfHFnTt2lt5zNTUlB49+mFsbJKufxn7pK/Dh/fQtGmrHNf1aZUvb5NjEQftfc44WqXrcV2cnJy5dOkMe/duZ+/e7bRt24lGjZoxZMgIDAwM2LZtozJi+fHH/Rk8OH1C73v3bjNjhqbSl5mZOWPGTEp3fzVldl/0afLkubi7z+XQod34+1+gTJlyNG/ehri42Cx/5iUlJbF/vzdDh47S63rS0rarlZAQz/bt/2BnVw1HxxZ07twzx81EL9N+ixZt+P77cSxb9ifbt//D2bOnlDXz2vclp+IWDg6Ndb43EyfOwt19LgcP7lI2oTVq1IyUlOxL+oqizyA1m3kAH8/HmJY0pmYj/fMQ5qegoECGDv2EYcPG0rPn/ymP37kTQEJCPPb2Dtm8+oXw8CcEBFzF0bElRkZGrF7tjofHMtat25NlAnKts2dPYWNTiYoVK+ndFw+PZaxe7c6aNd7pEj7n1rlzvowf/y0TJ86idev2L30egJEjv6B4cQt++831lc6TX/LqnhY2xzxDqd+qJNXsi+d8cAEVGpjIv1uf0H5Qpddy/tjYGIoVM1Z2zqrVatRqdZ6klFGr1SQmJmTaPJJWdoUMAGJioile3CJd4KgdPcrvwgG6ijjktejoKFQqFcWLW6R7XK1WExUV8b9E4C8GCdL+7GvYsClJSUl6B8/a80ZHR2X7s72wS01NJSYmOt1Ib0YZi1vExEQrxS20dL03z58/Jzo6Emvr0nr90XPdN4rU52pa9Si69/xtV+hGLnNbDSCrygwpKSmcOHGEmzevUr58RWxtK7N8+UJ++WU2gYG3lB2KKSkp6T4oN274c+dOAIaGhtjbv4+Dg2YHY2RkBMeOHQQ0i8crVsz6F2PGvgQEXOfGDc1apv37vSlZ0pL69Rvx6FEoYWGhdO7cM137p08f59GjELp27a1zNODhw2A8PdcSGxtNjRp1aNGiTbofDqmpqZw+fZwbN/wxMlJRs2ZdmjRpmW21DW3iY10VRrTP+fufp0WLNly5cpGAgGvUqvWOMnIQEHCdc+dO8fRpHHZ21WjVqn22vyz37NmGtXVpbG3tOHnyCLGxMXz88f9RqlRpEhMTOXbsIPfu3cHS0hoHh8ZKbjpd97RKleocPLgLExNTWrV6MSV48eIZgoIC6dKlF0+fxuHjs4+6dd8jKSmRc+dOUayYMZ98Mojdu71o0aItDx7c49KlsxgZGdGqVTvs7KrpvAZR8GRV3SqvygGqVKpsA0vIuZBBVgUF8juo1HrdQaWWrgBIpVLlWLyieHGLTIFPTlQqVZEOLEEzop9dYAn6FbfQdY5ixYpJVR6RTqEKLl+mGkDGygypqanMmDGOY8cOpavmYGtrh4GBAQcOeCu53dq160yxYi9KZf366w/pXtO9e1++/fZHIiKeKBUi+vb9XOcIaca+3L59g0uXzgGwfftGVKpiWFuXJioqgoULZ1KxYmXlutRqNb/9Np4aNWrz0Uef6LxHy5b9ma6P9vYOTJ06HwuLEqjVaqZP/0nJmafl6NiCKVPm6ay2YWVlnW2FkdKly3Lt2mVcXWexd+82ZQ3bmDGTANi48W+lXrrW2rVL+P33JZQtWz7L63B3n5uumkTx4hYMGjSMsLCHjBv3DcHBQemuc/Dgb+nff7DOe1qlSnXWrNH0NW1wqd1F3KHDR0RFReDqOotateoSEHAdMzNzWrRog1qtxtV1Fjt3biYw8JbS7qpVi1i2zFOvXbdCCCHE26LQbOjJqRpA2p1taWWszHDx4hmOHTtE27ad+OefA6xbtxszM3OMjIwoW7Y8EybM1JkoffbsxWzZcoTff19C7drvsn37P5w86UONGrXZtOlgjteQsS+dOvXgk080C88XLlyDh8cuWrVqh5OTZlfovn07lGMvXz5HQkI8HTpkv8tz0KBhbN16lGXLPOnQoRtXrlxk3bplAGzZ4sHJkz4MGjQMLy8fNm06SI8e/fDzO8H+/d46q2rkVGEkrfj4eJYt82T79uO0b9+FK1cusmKFKy1atGHt2p3s2HGCsWMnExoanCngzCg0NJj+/Qfj6XmIpUs3oVKp+PPP3wgODuLXX//A0/MQK1Z4Ua/e+6xc6UZQUKDOe5obAQHXcXFxY8uWI3z//Xjl8cTEBFas8GLLliOMGKF5/HVszhJC6O/ddx1YsuQfGjVqlt9dEUL8T6EJLnOqBvDwYYhe53nwQJM818nJGWNjY0qXLkvTpq2y3HyTUaVKVTA0NKR+/YYMGDAEeFHBIi+VLGlJ+/ZdOHx4j9InbS3Zli3bZvta7TRt5cpVGTJkBAC7dnn97/9bAM1u1KCgQEJC7tOgQRPgRXqRrOhTYUSra9feVK5cFRMTE1QqlRJ8tWrVjvDwx9y5cxM7u2pYWVlz7typbK/FzMycXr0+RaVSYW5enCdPwvDzO4G9vQNWVtbcunWd2Nho3n9fk5MtbbqUV9G0aWsaNmyKoaFhulQpTZq0wta2MoaGhsrO3rS1rYUQb56pqSlVqlTPVFFJCJF/Cs20eE7VAPTdnabNzaVdH6hWq/H3v0C1ajWzXGOiS4MGmulqbbCa1zp27M7Bg7s4enQ/H37YnSNH9uLk5JyrdU/W1qVo2LAp5875EhkZrgRCY8dmTs+R3U54fSqMaNWrl76CSGCgpkLK7NmTMp1XmyBZl/r1GyrVSuDF98CVKxcZOfKLTMfrGr3OrYzVQLKi3YX+/PmzPGlTCCGEKCoKTXCpTzUAfUqf1ar1Dg0bNmXzZg+uX/fn0aNQnjwJyzF5bUbaoEdTYi7vvfdeQ2xsbNmzZyvlytkodXBzS7shqHhxzUYDGxtbli/fkintSnZVIfSpMKKlzQeY8bWrVm3PtHM7p7rGus7VufPHjBjxcxbH581AvL4pXnIKjoUQQoi3UaGZFtenGoBKpdl88+RJmM7zqFQqnJ01QVp8/FNq134XFxc3unTpmav+XL6s2TSi3aWcuZ2c+wIvguaMI6CGhoZ07tyTgIDruLvPxczMPNdrimJjY/D3v4C9vQPGxsbUrv0uoaHBBARcy3T/sgv09K0wkhVt4udjxw7mqs2saDfOHDmyj2fPnumsCKHrntrYVOL27ZvKKG1ycjJ3797OVR+EEEIIkb1CM3KZsRpA69bO3Llzk0OHdjNv3gpsbStTsWIlbG3t2LlzM7a2dlkGjNHRUcyePQlHxxbUrfseYWGhXL16iRIlLHUGilpLlsyjTZuOPHwYwqpVizAzM+ejj/pmeaw+fQFwdGzJ0qULWLt2CdHRUTx//kwpzdauXWdWrHAlODiIjz/un27nui4bN64kOTkZtfo5np5rSEiIZ+DA/wDw1Vcj+fHHr5k8eTRdu/ambNkKHD68h4oVKzFq1ESd50xbYSQhIR5HxxZs3uwB6K6AodWz5wC2bdvI0qULePDgHu+91xA/vxPcvXuLhQvX6HVNWhYWJRg0aBirV7szatQXdOnSi4SEeA4c2En//oNp374LoPuetmrVnnPnfJk/fzoNGzbl6NEDmcrKCSGEEOLVFJrg0tDQUK9qAP/5zyg8PJbh6blGCejSVmYoWdISJydnjh49QEDANSpUsGXv3u2sWbOYFSu8lA0bkH7a1s6uGgEB15UNLLVrv8uYMZOUHJDaY9O+Jqe+AFSpUp1PP/2KLVvWMXPmBMzMzJXgsmzZ8kpanKyWAmRkZmZOyZJWuLhopozLlCnH5MlzlHRGDg6NmDJlLq6us/Dw0Owgt7KyTrfGMKuqGvpUGNE1CmllZc0ff6xg4UIXdu/eyu7dWzEzM+eDDzrw7FlStsFlVufs1+8LVCoVK1a44uY2G9C8N9opc9B9T1u3bs+dOzc5eHAX+/d707z5B7Ro0UYpiZdV5Ze0pF6uEEIIkbNCWaFHn2oACQkJ6Xb6Znx92qDm+nV/Ro78gs8/H67sAk8rNjYGQ0NDihe3ICIiHDMzc53nzm1ftOLj43n2LAlLSyslyElJSWHEiEHExcWycuXWbKeRk5KSePo0jlKlSvP0aRxqtTrbpLmRkREYGxvnOuFwThVGshMf/5TExMRcVc/QJSUlhYiIJ5QsaZVu00/69jLfU9BcQ0pKcpHbXVpUKvQc3BBGo46Fq+SoEEJ/QdfiKG5hIBV6irBCM3KZlj7VAHQFc0+fxvGf//SlcePmNGvmREJCPHv2bAOgevVaWb4m7Q7tlwmM9AlEzc3NMTd/sUHk1Kl/OXJkLwEB1xk+/Icc1yeamJgoaw31CRhftuZ2ThVGsmNuXjzPAjpDQ8Mcvwcy3lOtglLxRGRmYmZI8ZJGXD8Zkd9dKdAiIiIICwvD1NSUqlUliX9BEB4eTlhYGCVKlMDGxualar+/TSpVz76alSjcCmVw+SoSExOpX78Re/ZsU4LKWrXq8u23P9KsmVM+9+4Fd/c5REVFMmjQMD7+uH9+d0eIN6JUBWN6fmub390osB4+fMiECRO4eFGzue6bb76h55ct87lXAuDcuUeMH/874eHhlCpVimnTptG0adaV44Qo6grltLgQIrOiMC0udNu6dSvz5s0jPj4eW1tbZs2aRd262W9CFG9WbGwskyZN4t9//wXg448/ZuzYsblaRiVEUSA7FIQQogCLjIzkm2++Yfr06cTHx9OjRw82btwogWUBVKJECf744w8mTpyIqakpW7dupU+fPvj75031MCEKCwkuhRCigPLx8aF3796cPn0aCwsL5s2bxy+//CLrhgu4jz/+mA0bNlCnTh0ePXrEF198wYIFC/K7W0K8MRJcCiFEARMbG8vEiRMZO3YsMTExNGjQAE9PT5ycCs66cJG9SpUq4eHhweDBgwFYs2YNvXv35vZtKdwgij4JLoUQogDx8/OjV69e7NmzB4BRo0axdOlSypSRtC2F0bfffsuSJUsoU6YM9+7do1+/fixfvjy/uyXEayXBpRBCFBALFixg+PDhREZGUrlyZTZs2MDAgQPzu1viFTVs2BBPT0/atm0LwKJFi/jqq694+PBhPvdMiNcjx93ihiYqqtUvqesQIUQBcdr7EQ3aWMpu8ULowYMHjBs3juvXrwPQp08fRo8ereSuFUWHt7c3M2fOJDExEQsLC8aPH0/Hjh3zu1tC5Klsg8tTu8K56hv7JvsjhHgFHT4tT+XakvakMPHy8mLu3LlKsPHbb7/RokWL/O6WeI1CQkL4+eeflV3kXbt25aeffsqy6IMQhVG2waUQQojXI2NOxAYNGuDi4iJrK98i7u7uLFu2DAAbGxt+++033nvvvXzulRCvToJLIYR4wy5evMiPP/5IeHg4ACNGjGDQoEH53CuRHy5cuMCECRN49OgRAF9//TVDhw7N514J8WokuBRCiDco7WhV5cqV+f3336lZs2Y+90rkp7i4OKZPn86BAwcAqFevHnPmzJFRbFFoSXAphBBvQHh4OD/99BMXLlwAoFu3bowfP1427QiFt7c3s2fPJj4+nhIlSuDi4kKzZs3yu1tC5JoEl0II8ZpduHCBH374gaioKIyNjfn111/p0KFDfndLFEAhISGMHTuWgIAAAAYMGMCYMWPyuVdC5I4El0II8RqlnQavUqUKc+fOpWrVqvncK1HQzZkzhw0bNgBQq1Yt5s6dS8WK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" alt /></p>
<p>Layer 1 is the system prompt itself. It tells the model what Bookly
is, what it can and cannot help with, what the return policy actually
says (quoted verbatim, not paraphrased), and exactly which template to
use when refusing. Layer 2 adds short reminder blocks on every turn so
the model re-reads the non-negotiable rules at the highest-attention
position right before the user turn. Layer 3 lives in
<code>tools.py</code>: the tool handlers refuse unsafe calls regardless
of what the model decides. Layer 4 lives at the end of the agent loop
and does a deterministic regex pass over the final reply looking for
things like fabricated order IDs, markdown leakage, and off-topic
engagement.</p>
<h1 id="request-lifecycle">Request lifecycle</h1>
<p>A single user message travels this path:</p>
<p><img role="img" aria-label src="data:image/png;base64,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0jmOKw2mouNcbZ248lKjKw0d+mKx58EXjl+07OuVIVLoyqfumEwm5/pT2FgsXLdKW69KW4/LMnmoCK6c3yuXyZWfiy7jqx0nrpX8e3p6AlC9egAtWrS6av1Ff6+il69f62RW9+6/o3PnHsXGdWWerjWPFU2V64G43agHQqDq9EDcztQDIa5QD8StVx49EJVBefdAyPVVpB6I8o9ARERERERuG0ogRERERETEZUogRERERETEZUogRERERETEZUogRERERETEZUogRERERETEZUogRERERETEZUogRERERETEZUogRERERETEZWX2amizWW+EvClmk/MV6Lc7w9B6cPNMcMvfGQ8GBugnuikmswmDqrT4qtbc/lou78+13G4lsxkt4ptQVm0Ng8v7S7lxFak9WCYJhIenCYfNhsnNTJm0gioJu93AzWRgdivvSH4dZrMJdwvk5dlxt5gvZxRyXQbgsNnw8PK45XV5eJpxWK04tK3eELvVwGIxLjdcqggfXzcuJdkwmdzRuuIak8mENc+GX2AVWlHKgZePmdQUG54+7hgOrZsuMYE1z0712re+weHubsKEgS3Pjtm94jSIKzrDMDDsdiyet74t4AqTYZRNK85uM8jOcGC3OcqiOpc5HJCRbCXgDkt5h1KCm8WEX3W3SnXW3jAMMtPs2KxgqmCNjowUG77+7rhVsFXBbDbhW90Nd0vZrAc2q0F2hh2HvWL9Pg47ZKVZqVargv1AgNkdqgW4V7mzarnZdvJyHBUuf8jLdmBg4O1bsc6+mMwmPL1NFS6uyig3y05BHjgcFavNkZtpx+xmxtOnYu0rzG5mPL3By6ds1k2HwyArzY7dVsF2HkBa0uXjTEU7IWRyM+Ff3Q23CpJ0ldklTG7uJqrVdAMq1o7TZjVIu5hPjdoVr1FSGZlMJqrVKLPV7oZkJFvxC3DDy7eC7TXKmLvFRPVaFe83KshzkJFSoG21AvH2dauQjeHk8wUYBlpXqjBvPze8/aCitTms+Q4sHqYqv26azSaq1ax4xxmAlMR8qtdyL7OTdrerqt1SEhERERGRG6IEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYEQkREREREXKYE4ibl5eWRlJREQUFBseE2m42E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2rRpw8qVKxkyZAjp6enMmTOHF154gddeew0vLy9++9vf0qdPH8aOHcsdd9yB3W6/arnh4eFcvHiRESNGMGXKFDp16sS4ceN4+eWXrzqNyWRy/u/v78+jjz7Kc889R15eHq+99hr33XcfX3zxBfXr1+fcuXOYTCb69evHU089RUhICDVq1KBv375MfmsKX2Z8RVC9mvztb38rlgj885//ZOvWrURFRVGtWrWr1l+aNm3asGzZMgYPHozVauWpp55iyJAhjBw5kqZNmzovi+revTszZszgiSeeYMqUKSxdupTq1avzt7/9ja5du3L69GkCAgJ48sknnYlHp06dmDp1Kv7+/iQkJNC9e3dn3H5+fiWWxd13333NWEVERCoCk1F4yq2KslkNTh/OplHLkpc+SNVy+nAOQfW98PKtHFf2FRQU4OHh4fycn5/vPPsNl8+22+12Z8+B1Wp16SZewzAoKCjA09OTvXv38t577zF37ly+++47du3a5RzPw8ODESNGlFqGzWbDbDY7G9M2mw13d3fsdjsmkwmz2ewc5pyfPAerlsXQtmMT6tWrd935vVFX1mez2XBzcyuWgBRdhlcuz8LlV5iAFfbeFJZ75fhXWxbyyyWfL8AwDALrlVzeIuXpYnw+Fg8zNYL0wISK6sS+LMKa+uJuufbJp6pOPRAildSVjekrG68mk6lYg9nVJwCZTCY8PT2xWq2sX7+et99+G4D27dvTrl27YuNdzZWXOxV+LnrJVNFxZs6cyfx588nKzoL3oW7duowePZrOnTs7x/klycO1Yiqq6DK8cnkWLr8rL/sqLKe05OFq9YiIiFRkOuUlIjfFYrEwZswYQkNDgcsNYU9PT+ffL23QF5o0aRIzZ868nDz8n/PnzzNmzBhWrFjxq9QhIiIirlMCISIV1ubNm4mOjgYu9zBMnTqVN954w9m78eabb5KZmVmeIYqIiFQ56jsXkQqraA9Djx492LNnD126dKFLly5s3LgRgAkTJjhf3maz2SgoKMBmszkf6err64ufnx9BQUHUr1+f8PDwYk9XEhERkRujBEJEKqyivQvR0dH07NmTVq1a8c477ziHe3p60rZtW0wmExaLxfkHkJWVVexv9erVnDx5ksaNGxMREUHHjh3x9/cv8/kSERG5nSmBEJEKq2jjvlWrVsTFxTFjxgwGDRrEW2+9BUDLli1p0aKFS+X17t0buPx27JiYGF5//XXuv/9+Bg4c+OsHLyIiUknpHggRqbD69Onj/N/f358ZM2bw8MMPs3z5cuDyuxSKjuOqoKAgnnjiCT755BPq1avHyJEjr/n+CxEREfkf9UCISIXVuXNnevfuTXR0NFu2bGHHjh3YbDbnG6NHjx79iy9B6ty5M82aNePFF1/kn//8568RtoiISKWmHggRqdAmTZrE0KFD8fP1w2q1YhgGd955J++///5N9T6Upnbt2jz77LPMnTv3VylPRESkMlMPhIhUeMOHD+e5Z4eyZeM+ght5ExER8avX0aRJEzZs2PCrlysiIlLZKIEQkdtGeNidhEfcmkewJiQkXPVt0SIiIvI/uoRJRATYvn073bp1K+8wREREKjwlECJy20pKSvpVynn77be57777uOeee36V8kRERCozXcIkIreV+Ph4Dhw4wMaNGzlx4gRff/31TZWTmZlJTEwM3377Lf3793f5XRIiIiJVnRIIEbktHD9+jMmTp5KSdoHk5GQMw8BkMrk8fU5ODidOnODnn38mJSWFnTt30qlTJ/7617/i5eV1CyMXERGpXJRAiMhtoW7detS+oy6nzhwDwGQyYRgGQ4YM4a677iItLQ2LxYKHhwfu7u74+Phw8eJFMjMzyc7O5o477qBGjRqEh4fTpk0bnnvuuXKeIxERkduTEggRuS34+vrylxcmcOzsD8yYMYP4+HjCwsKYMWMGKSkpWK1WCgoKKCgowGazYTKZ8PX1xd/fHz8/Pz1hSURE5FeiBEJEbis9evSgadOmTJgwgdzcXCwWC0FBQeUdloiISJWhBEJEbjuhoaHMmTOnvMMQERGpkvQYVxERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBERERERcZkSCBG5aQcOHCiXeu12O3l5eTc1rcPhwDCMm5r22LFjFBQUFBuWm5tb6rg2m+2m6hAREanolECIyE0bN25cudQbGxvL4sWLb2rahQsX3nTj/uOPPyY1NdX5OS0tjfHjx5c67vnz59m8efNN1SMiIlKRKYEQqcK2bNnCyZMnmTt3Lg6Hg4MHD7JgwQISEhIA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" alt /></p>
<h1 id="module-layout">Module layout</h1>
<p>Five Python files form the core. They depend on each other in one
direction only there are no cycles.</p>
<p><img role="img" aria-label src="data:image/png;base64,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" alt /></p>
<p>The rest of this document visits each module in dependency order:
configuration first, then the data fixtures they read, then tools, then
the agent loop, then the HTTP layer on top.</p>
<h1 id="configuration">Configuration</h1>
<p>Every setting that might reasonably change between environments lives
in one place. The two required values the Anthropic API key and the
session-cookie signing secret are wrapped in <code>SecretStr</code> so
an accidental <code>print(settings)</code> cannot leak them to a
log.</p>
<p>Everything else has a default that is safe for local development and
reasonable for a small production deployment. A few knobs are worth
noticing:</p>
<ul>
<li><code>max_tool_use_iterations</code> bounds the Layer-3 loop in
<code>agent.py</code>. A model that keeps asking for tools forever will
not burn API credit forever.</li>
<li><code>session_store_max_entries</code> and
<code>session_idle_ttl_seconds</code> cap the in-memory
<code>SessionStore</code>, so a trivial script that opens millions of
sessions cannot OOM the process.</li>
<li><code>rate_limit_per_ip_per_minute</code> and
<code>rate_limit_per_session_per_minute</code> feed the sliding-window
limiter in <code>server.py</code>.</li>
</ul>
<div class="sourceCode" id="cb1" data-chunk="config-py" data-file="config.py"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;Application configuration loaded from environment variables.</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">Settings are read from `.env` at process start. The Anthropic API key and</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">the session-cookie signing secret are the only required values; everything</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">else has a sensible default so the app can boot in dev without ceremony.</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> secrets</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pydantic <span class="im">import</span> Field, SecretStr</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pydantic_settings <span class="im">import</span> BaseSettings, SettingsConfigDict</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Settings(BaseSettings):</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> model_config <span class="op">=</span> SettingsConfigDict(env_file<span class="op">=</span><span class="st">&quot;.env&quot;</span>, env_file_encoding<span class="op">=</span><span class="st">&quot;utf-8&quot;</span>, extra<span class="op">=</span><span class="st">&quot;ignore&quot;</span>)</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="co"># Required secrets -- wrapped in SecretStr so accidental logging or repr</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> <span class="co"># does not leak them. Access the raw value with `.get_secret_value()`.</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> anthropic_api_key: SecretStr</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="co"># Signing key for the server-issued session cookie. A fresh random value is</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="co"># generated at import if none is configured -- this means sessions do not</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> <span class="co"># survive a process restart in dev, which is the desired behavior until a</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> <span class="co"># real secret is set in the environment.</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> session_secret: SecretStr <span class="op">=</span> Field(default_factory<span class="op">=</span><span class="kw">lambda</span>: SecretStr(secrets.token_urlsafe(<span class="dv">32</span>)))</span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a> anthropic_model: <span class="bu">str</span> <span class="op">=</span> <span class="st">&quot;claude-sonnet-4-5&quot;</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a> max_tokens: <span class="bu">int</span> <span class="op">=</span> <span class="dv">1024</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a> <span class="co"># Upper bound on the Anthropic HTTP call. A stuck request must not hold a</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a> <span class="co"># worker thread forever -- see the tool-use loop cap in agent.py for the</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a> <span class="co"># paired total-work bound.</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a> anthropic_timeout_seconds: <span class="bu">float</span> <span class="op">=</span> <span class="fl">30.0</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a> server_host: <span class="bu">str</span> <span class="op">=</span> <span class="st">&quot;127.0.0.1&quot;</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a> server_port: <span class="bu">int</span> <span class="op">=</span> <span class="dv">8014</span></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a> <span class="co"># Session store bounds. Protects against a trivial DoS that opens many</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a> <span class="co"># sessions or drives a single session to unbounded history length.</span></span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a> session_store_max_entries: <span class="bu">int</span> <span class="op">=</span> <span class="dv">10_000</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a> session_idle_ttl_seconds: <span class="bu">int</span> <span class="op">=</span> <span class="dv">1800</span> <span class="co"># 30 minutes</span></span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a> max_turns_per_session: <span class="bu">int</span> <span class="op">=</span> <span class="dv">40</span></span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a> <span class="co"># Hard cap on iterations of the tool-use loop within a single turn. The</span></span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a> <span class="co"># model should never legitimately need this many tool calls for a support</span></span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a> <span class="co"># conversation -- the cap exists to stop a runaway loop.</span></span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a> max_tool_use_iterations: <span class="bu">int</span> <span class="op">=</span> <span class="dv">8</span></span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a> <span class="co"># Per-minute sliding-window rate limits. Enforced by a tiny in-memory</span></span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a> <span class="co"># limiter in server.py; suitable for a single-process demo deployment.</span></span>
<span id="cb1-51"><a href="#cb1-51" aria-hidden="true" tabindex="-1"></a> rate_limit_per_ip_per_minute: <span class="bu">int</span> <span class="op">=</span> <span class="dv">30</span></span>
<span id="cb1-52"><a href="#cb1-52" aria-hidden="true" tabindex="-1"></a> rate_limit_per_session_per_minute: <span class="bu">int</span> <span class="op">=</span> <span class="dv">20</span></span>
<span id="cb1-53"><a href="#cb1-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-54"><a href="#cb1-54" aria-hidden="true" tabindex="-1"></a> <span class="co"># Session cookie configuration.</span></span>
<span id="cb1-55"><a href="#cb1-55" aria-hidden="true" tabindex="-1"></a> session_cookie_name: <span class="bu">str</span> <span class="op">=</span> <span class="st">&quot;bookly_session&quot;</span></span>
<span id="cb1-56"><a href="#cb1-56" aria-hidden="true" tabindex="-1"></a> session_cookie_secure: <span class="bu">bool</span> <span class="op">=</span> <span class="va">False</span> <span class="co"># Flip to True behind HTTPS.</span></span>
<span id="cb1-57"><a href="#cb1-57" aria-hidden="true" tabindex="-1"></a> session_cookie_max_age_seconds: <span class="bu">int</span> <span class="op">=</span> <span class="dv">60</span> <span class="op">*</span> <span class="dv">60</span> <span class="op">*</span> <span class="dv">8</span> <span class="co"># 8 hours</span></span>
<span id="cb1-58"><a href="#cb1-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-59"><a href="#cb1-59" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-60"><a href="#cb1-60" aria-hidden="true" tabindex="-1"></a><span class="co"># The type ignore is needed because pydantic-settings reads `anthropic_api_key`</span></span>
<span id="cb1-61"><a href="#cb1-61" aria-hidden="true" tabindex="-1"></a><span class="co"># and `session_secret` from environment / .env at runtime, but mypy sees them as</span></span>
<span id="cb1-62"><a href="#cb1-62" aria-hidden="true" tabindex="-1"></a><span class="co"># required constructor arguments and has no way to know about that.</span></span>
<span id="cb1-63"><a href="#cb1-63" aria-hidden="true" tabindex="-1"></a>settings <span class="op">=</span> Settings() <span class="co"># type: ignore[call-arg]</span></span></code></pre></div>
<h1 id="data-fixtures">Data fixtures</h1>
<p>Bookly does not talk to a real database. Four fixture orders are
enough to cover the interesting scenarios: a delivered order that is
still inside the 30-day return window, an in-flight order that has not
been delivered yet, a processing order that has not shipped, and an old
delivered order outside the return window. Sarah Chen owns two of the
four so the agent has to disambiguate when she says “my order”.</p>
<p>The <code>RETURN_POLICY</code> dict is the single source of truth for
policy facts. Two things read it: the system prompt (via
<code>_format_return_policy_block</code> in <code>agent.py</code>, which
renders it as the <code>&lt;return_policy&gt;</code> section the model
must quote) and the <code>check_return_eligibility</code> handler (which
enforces the window in code). Having one copy prevents the two from
drifting apart.</p>
<p><code>POLICIES</code> is a tiny FAQ keyed by topic. The
<code>lookup_policy</code> tool returns one of these entries verbatim
and the system prompt instructs the model to quote the response without
paraphrasing. This is a deliberate anti-hallucination pattern: the less
the model has to generate, the less it can make up.</p>
<p><code>RETURNS</code> is the only mutable state in this file.
<code>initiate_return</code> writes a new RMA record to it on each
successful return.</p>
<div class="sourceCode" id="cb2" data-chunk="mock-data-py" data-file="mock_data.py"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;In-memory data fixtures for orders, returns, and FAQ policies.</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="co">`ORDERS` and `RETURN_POLICY` are read by both the system prompt (so the prompt</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">quotes policy verbatim instead of paraphrasing) and the tool handlers (so the</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">two never drift apart). `RETURNS` is mutated by `initiate_return` at runtime.</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> datetime <span class="im">import</span> date, timedelta</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co"># A frozen &quot;today&quot; so the four-order fixture stays deterministic across runs.</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>TODAY <span class="op">=</span> date(<span class="dv">2026</span>, <span class="dv">4</span>, <span class="dv">14</span>)</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _days_ago(n: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (TODAY <span class="op">-</span> timedelta(days<span class="op">=</span>n)).isoformat()</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>RETURN_POLICY: <span class="bu">dict</span> <span class="op">=</span> {</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;window_days&quot;</span>: <span class="dv">30</span>,</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;condition_requirements&quot;</span>: <span class="st">&quot;Items must be unread, undamaged, and in their original packaging.&quot;</span>,</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;refund_method&quot;</span>: <span class="st">&quot;Refunds are issued to the original payment method.&quot;</span>,</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;refund_timeline_days&quot;</span>: <span class="dv">7</span>,</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;non_returnable_categories&quot;</span>: [<span class="st">&quot;ebooks&quot;</span>, <span class="st">&quot;audiobooks&quot;</span>, <span class="st">&quot;gift cards&quot;</span>, <span class="st">&quot;personalized items&quot;</span>],</span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a><span class="co"># Four orders covering the interesting scenarios. Sarah Chen has two orders so</span></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a><span class="co"># the agent must disambiguate when she says &quot;my order&quot;.</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>ORDERS: <span class="bu">dict</span> <span class="op">=</span> {</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;BK-10042&quot;</span>: {</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: <span class="st">&quot;BK-10042&quot;</span>,</span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_name&quot;</span>: <span class="st">&quot;Sarah Chen&quot;</span>,</span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;email&quot;</span>: <span class="st">&quot;sarah.chen@example.com&quot;</span>,</span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;status&quot;</span>: <span class="st">&quot;delivered&quot;</span>,</span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_date&quot;</span>: _days_ago(<span class="dv">20</span>),</span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;delivered_date&quot;</span>: _days_ago(<span class="dv">15</span>),</span>
<span id="cb2-37"><a href="#cb2-37" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tracking_number&quot;</span>: <span class="st">&quot;1Z999AA10123456784&quot;</span>,</span>
<span id="cb2-38"><a href="#cb2-38" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: [</span>
<span id="cb2-39"><a href="#cb2-39" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;title&quot;</span>: <span class="st">&quot;The Goldfinch&quot;</span>, <span class="st">&quot;author&quot;</span>: <span class="st">&quot;Donna Tartt&quot;</span>, <span class="st">&quot;price&quot;</span>: <span class="fl">16.99</span>, <span class="st">&quot;category&quot;</span>: <span class="st">&quot;fiction&quot;</span>},</span>
<span id="cb2-40"><a href="#cb2-40" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;title&quot;</span>: <span class="st">&quot;Sapiens&quot;</span>, <span class="st">&quot;author&quot;</span>: <span class="st">&quot;Yuval Noah Harari&quot;</span>, <span class="st">&quot;price&quot;</span>: <span class="fl">19.99</span>, <span class="st">&quot;category&quot;</span>: <span class="st">&quot;nonfiction&quot;</span>},</span>
<span id="cb2-41"><a href="#cb2-41" aria-hidden="true" tabindex="-1"></a> ],</span>
<span id="cb2-42"><a href="#cb2-42" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;total&quot;</span>: <span class="fl">36.98</span>,</span>
<span id="cb2-43"><a href="#cb2-43" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb2-44"><a href="#cb2-44" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;BK-10089&quot;</span>: {</span>
<span id="cb2-45"><a href="#cb2-45" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: <span class="st">&quot;BK-10089&quot;</span>,</span>
<span id="cb2-46"><a href="#cb2-46" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_name&quot;</span>: <span class="st">&quot;James Murphy&quot;</span>,</span>
<span id="cb2-47"><a href="#cb2-47" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;email&quot;</span>: <span class="st">&quot;james.murphy@example.com&quot;</span>,</span>
<span id="cb2-48"><a href="#cb2-48" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;status&quot;</span>: <span class="st">&quot;shipped&quot;</span>,</span>
<span id="cb2-49"><a href="#cb2-49" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_date&quot;</span>: _days_ago(<span class="dv">4</span>),</span>
<span id="cb2-50"><a href="#cb2-50" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;delivered_date&quot;</span>: <span class="va">None</span>,</span>
<span id="cb2-51"><a href="#cb2-51" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tracking_number&quot;</span>: <span class="st">&quot;1Z999AA10987654321&quot;</span>,</span>
<span id="cb2-52"><a href="#cb2-52" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: [</span>
<span id="cb2-53"><a href="#cb2-53" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;title&quot;</span>: <span class="st">&quot;Project Hail Mary&quot;</span>, <span class="st">&quot;author&quot;</span>: <span class="st">&quot;Andy Weir&quot;</span>, <span class="st">&quot;price&quot;</span>: <span class="fl">18.99</span>, <span class="st">&quot;category&quot;</span>: <span class="st">&quot;fiction&quot;</span>},</span>
<span id="cb2-54"><a href="#cb2-54" aria-hidden="true" tabindex="-1"></a> ],</span>
<span id="cb2-55"><a href="#cb2-55" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;total&quot;</span>: <span class="fl">18.99</span>,</span>
<span id="cb2-56"><a href="#cb2-56" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb2-57"><a href="#cb2-57" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;BK-10103&quot;</span>: {</span>
<span id="cb2-58"><a href="#cb2-58" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: <span class="st">&quot;BK-10103&quot;</span>,</span>
<span id="cb2-59"><a href="#cb2-59" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_name&quot;</span>: <span class="st">&quot;Sarah Chen&quot;</span>,</span>
<span id="cb2-60"><a href="#cb2-60" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;email&quot;</span>: <span class="st">&quot;sarah.chen@example.com&quot;</span>,</span>
<span id="cb2-61"><a href="#cb2-61" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;status&quot;</span>: <span class="st">&quot;processing&quot;</span>,</span>
<span id="cb2-62"><a href="#cb2-62" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_date&quot;</span>: _days_ago(<span class="dv">1</span>),</span>
<span id="cb2-63"><a href="#cb2-63" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;delivered_date&quot;</span>: <span class="va">None</span>,</span>
<span id="cb2-64"><a href="#cb2-64" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tracking_number&quot;</span>: <span class="va">None</span>,</span>
<span id="cb2-65"><a href="#cb2-65" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: [</span>
<span id="cb2-66"><a href="#cb2-66" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;title&quot;</span>: <span class="st">&quot;Tomorrow, and Tomorrow, and Tomorrow&quot;</span>, <span class="st">&quot;author&quot;</span>: <span class="st">&quot;Gabrielle Zevin&quot;</span>, <span class="st">&quot;price&quot;</span>: <span class="fl">17.99</span>, <span class="st">&quot;category&quot;</span>: <span class="st">&quot;fiction&quot;</span>},</span>
<span id="cb2-67"><a href="#cb2-67" aria-hidden="true" tabindex="-1"></a> ],</span>
<span id="cb2-68"><a href="#cb2-68" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;total&quot;</span>: <span class="fl">17.99</span>,</span>
<span id="cb2-69"><a href="#cb2-69" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb2-70"><a href="#cb2-70" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;BK-9871&quot;</span>: {</span>
<span id="cb2-71"><a href="#cb2-71" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: <span class="st">&quot;BK-9871&quot;</span>,</span>
<span id="cb2-72"><a href="#cb2-72" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_name&quot;</span>: <span class="st">&quot;Maria Gonzalez&quot;</span>,</span>
<span id="cb2-73"><a href="#cb2-73" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;email&quot;</span>: <span class="st">&quot;maria.gonzalez@example.com&quot;</span>,</span>
<span id="cb2-74"><a href="#cb2-74" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;status&quot;</span>: <span class="st">&quot;delivered&quot;</span>,</span>
<span id="cb2-75"><a href="#cb2-75" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_date&quot;</span>: _days_ago(<span class="dv">60</span>),</span>
<span id="cb2-76"><a href="#cb2-76" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;delivered_date&quot;</span>: _days_ago(<span class="dv">55</span>),</span>
<span id="cb2-77"><a href="#cb2-77" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tracking_number&quot;</span>: <span class="st">&quot;1Z999AA10555555555&quot;</span>,</span>
<span id="cb2-78"><a href="#cb2-78" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: [</span>
<span id="cb2-79"><a href="#cb2-79" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;title&quot;</span>: <span class="st">&quot;The Midnight Library&quot;</span>, <span class="st">&quot;author&quot;</span>: <span class="st">&quot;Matt Haig&quot;</span>, <span class="st">&quot;price&quot;</span>: <span class="fl">15.99</span>, <span class="st">&quot;category&quot;</span>: <span class="st">&quot;fiction&quot;</span>},</span>
<span id="cb2-80"><a href="#cb2-80" aria-hidden="true" tabindex="-1"></a> ],</span>
<span id="cb2-81"><a href="#cb2-81" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;total&quot;</span>: <span class="fl">15.99</span>,</span>
<span id="cb2-82"><a href="#cb2-82" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb2-83"><a href="#cb2-83" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb2-84"><a href="#cb2-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-85"><a href="#cb2-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-86"><a href="#cb2-86" aria-hidden="true" tabindex="-1"></a><span class="co"># Verbatim FAQ entries returned by `lookup_policy`. The agent quotes these</span></span>
<span id="cb2-87"><a href="#cb2-87" aria-hidden="true" tabindex="-1"></a><span class="co"># without paraphrasing.</span></span>
<span id="cb2-88"><a href="#cb2-88" aria-hidden="true" tabindex="-1"></a>POLICIES: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">str</span>] <span class="op">=</span> {</span>
<span id="cb2-89"><a href="#cb2-89" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;shipping&quot;</span>: (</span>
<span id="cb2-90"><a href="#cb2-90" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Standard shipping is free on orders over $25 and takes 3-5 business days. &quot;</span></span>
<span id="cb2-91"><a href="#cb2-91" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Expedited shipping (1-2 business days) is $9.99. We ship to all 50 US states. &quot;</span></span>
<span id="cb2-92"><a href="#cb2-92" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;International shipping is not currently available.&quot;</span></span>
<span id="cb2-93"><a href="#cb2-93" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb2-94"><a href="#cb2-94" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;password_reset&quot;</span>: (</span>
<span id="cb2-95"><a href="#cb2-95" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;To reset your password, go to bookly.com/account/login and click </span><span class="ch">\&quot;</span><span class="st">Forgot password.</span><span class="ch">\&quot;</span><span class="st"> &quot;</span></span>
<span id="cb2-96"><a href="#cb2-96" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Enter the email on your account and we will send you a reset link. &quot;</span></span>
<span id="cb2-97"><a href="#cb2-97" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;The link expires after 24 hours. If you do not receive the email, check your spam folder.&quot;</span></span>
<span id="cb2-98"><a href="#cb2-98" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb2-99"><a href="#cb2-99" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;returns_overview&quot;</span>: (</span>
<span id="cb2-100"><a href="#cb2-100" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;You can return most items within 30 days of delivery for a full refund to your original &quot;</span></span>
<span id="cb2-101"><a href="#cb2-101" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;payment method. Items must be unread, undamaged, and in their original packaging. &quot;</span></span>
<span id="cb2-102"><a href="#cb2-102" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Ebooks, audiobooks, gift cards, and personalized items are not returnable. &quot;</span></span>
<span id="cb2-103"><a href="#cb2-103" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Refunds typically post within 7 business days of us receiving the return.&quot;</span></span>
<span id="cb2-104"><a href="#cb2-104" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb2-105"><a href="#cb2-105" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb2-106"><a href="#cb2-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-107"><a href="#cb2-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-108"><a href="#cb2-108" aria-hidden="true" tabindex="-1"></a><span class="co"># Mutated at runtime by `initiate_return`. Keyed by return_id.</span></span>
<span id="cb2-109"><a href="#cb2-109" aria-hidden="true" tabindex="-1"></a>RETURNS: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">dict</span>] <span class="op">=</span> {}</span></code></pre></div>
<h1 id="tools-layer-3-enforcement">Tools: Layer 3 enforcement</h1>
<p>Four tools back the agent: <code>lookup_order</code>,
<code>check_return_eligibility</code>, <code>initiate_return</code>, and
<code>lookup_policy</code>. Each has an Anthropic-format schema (used in
the <code>tools</code> argument to <code>messages.create</code>) and a
handler function that takes a validated arg dict plus the per-session
guard state and returns a dict that becomes the <code>tool_result</code>
content sent back to the model.</p>
<p>The most important guardrail in the entire system lives in this file.
<code>handle_initiate_return</code> refuses unless
<code>check_return_eligibility</code> has already succeeded for the same
order in the same session. This is enforced in code, not in the prompt
if a model somehow decides to skip the eligibility check, the tool
itself refuses. This is “Layer 3” in the stack: the models last line of
defence against itself.</p>
<p>A second guardrail is the privacy boundary in
<code>handle_lookup_order</code>. When a caller supplies a
<code>customer_email</code> and it does not match the email on the
order, the handler returns the same <code>order_not_found</code> error
as a missing order. This mirror means an attacker cannot probe for which
order IDs exist by watching response differences. The check uses
<code>hmac.compare_digest</code> for constant-time comparison so
response-time side channels cannot leak the correct email prefix
either.</p>
<p>Input validation lives in <code>_require_*</code> helpers at the top
of the file. Every string is control-character-stripped before length
checks so a malicious <code>\x00</code> byte injected into a tool arg
cannot sneak into the tool result JSON and reappear in the next turns
prompt. Order IDs, emails, and policy topics are validated with tight
regexes; unexpected input becomes a structured
<code>invalid_arguments</code> error that the model can recover from on
its next turn.</p>
<p><code>TypedDict</code> argument shapes make the schema-to-handler
contract visible to the type checker without losing runtime validation
the model is an untrusted caller, so the runtime checks stay.</p>
<div class="sourceCode" id="cb3" data-chunk="tools-py" data-file="tools.py"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;Tool schemas, dispatch, and Layer 3 (tool-side) guardrail enforcement.</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co">Each tool has an Anthropic-format schema (used in the `tools` argument to</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">`messages.create`) and a handler. Handlers are typed with `TypedDict`s so the</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">contract between schema and handler is visible to the type checker; inputs</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">are still validated at runtime because the caller is ultimately the model.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="co">The most important guardrail in the whole system lives here:</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">`handle_initiate_return` refuses unless `check_return_eligibility` has already</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="co">succeeded for the same order in the same session. This protects against the</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="co">agent skipping the protocol even if the system prompt is ignored entirely.</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> hmac</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> re</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> uuid</span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass, field</span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> datetime <span class="im">import</span> date</span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Any, Callable, TypedDict</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a><span class="cf">try</span>:</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> typing <span class="im">import</span> NotRequired <span class="co"># Python 3.11+</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a><span class="cf">except</span> <span class="pp">ImportError</span>: <span class="co"># pragma: no cover -- Python 3.10 fallback</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> <span class="im">from</span> typing_extensions <span class="im">import</span> NotRequired <span class="co"># type: ignore[assignment]</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mock_data <span class="im">import</span> ORDERS, POLICIES, RETURN_POLICY, RETURNS, TODAY</span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a><span class="co"># Validation helpers</span></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a><span class="co"># Validator limits. These are deliberately tight: tool arguments come from</span></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a><span class="co"># model output, which in turn reflects user input, so anything that would not</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a><span class="co"># plausibly appear in a real support conversation is rejected.</span></span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a>ORDER_ID_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">^</span><span class="vs">BK-</span><span class="dv">\d</span><span class="op">{4,6}</span><span class="dv">$</span><span class="vs">&quot;</span>)</span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a>EMAIL_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">^</span><span class="pp">[^@</span><span class="dv">\s</span><span class="pp">]</span><span class="op">{1,64}</span><span class="vs">@</span><span class="pp">[^@</span><span class="dv">\s</span><span class="pp">]</span><span class="op">{1,255}</span><span class="ch">\.</span><span class="pp">[^@</span><span class="dv">\s</span><span class="pp">]</span><span class="op">{1,10}</span><span class="dv">$</span><span class="vs">&quot;</span>)</span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a>TOPIC_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">^</span><span class="pp">[a-z][a-z_]</span><span class="op">{0,39}</span><span class="dv">$</span><span class="vs">&quot;</span>)</span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a>ITEM_TITLE_MAX_LENGTH <span class="op">=</span> <span class="dv">200</span></span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a>REASON_MAX_LENGTH <span class="op">=</span> <span class="dv">500</span></span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a>ITEM_TITLES_MAX_COUNT <span class="op">=</span> <span class="dv">50</span></span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a><span class="co"># Control characters are stripped from any free-form input. Keeping them out</span></span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a><span class="co"># of tool payloads means they cannot end up in prompts on later turns, which</span></span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a><span class="co"># closes one prompt-injection surface.</span></span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a>_CONTROL_CHAR_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="pp">[</span><span class="ch">\x00</span><span class="pp">-</span><span class="ch">\x08\x0b\x0c\x0e</span><span class="pp">-</span><span class="ch">\x1f\x7f</span><span class="pp">]</span><span class="vs">&quot;</span>)</span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ToolValidationError(<span class="pp">ValueError</span>):</span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Raised when a tool argument fails validation.</span></span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a><span class="co"> The dispatcher catches this and converts it into a tool-result error so</span></span>
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a><span class="co"> the model can recover on its next turn instead of crashing the request.</span></span>
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _require_string(value: Any, field_name: <span class="bu">str</span>, <span class="op">*</span>, max_length: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(value, <span class="bu">str</span>):</span>
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="ss">f&quot;</span><span class="sc">{</span>field_name<span class="sc">}</span><span class="ss"> must be a string&quot;</span>)</span>
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a> cleaned <span class="op">=</span> _CONTROL_CHAR_RE.sub(<span class="st">&quot;&quot;</span>, value).strip()</span>
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> cleaned:</span>
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="ss">f&quot;</span><span class="sc">{</span>field_name<span class="sc">}</span><span class="ss"> is required&quot;</span>)</span>
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">len</span>(cleaned) <span class="op">&gt;</span> max_length:</span>
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="ss">f&quot;</span><span class="sc">{</span>field_name<span class="sc">}</span><span class="ss"> must be at most </span><span class="sc">{</span>max_length<span class="sc">}</span><span class="ss"> characters&quot;</span>)</span>
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> cleaned</span>
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _require_order_id(value: Any) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a> order_id <span class="op">=</span> _require_string(value, <span class="st">&quot;order_id&quot;</span>, max_length<span class="op">=</span><span class="dv">16</span>)</span>
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> ORDER_ID_RE.match(order_id):</span>
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="st">&quot;order_id must match the format BK-NNNN&quot;</span>)</span>
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> order_id</span>
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _require_email(value: Any, <span class="op">*</span>, field_name: <span class="bu">str</span> <span class="op">=</span> <span class="st">&quot;customer_email&quot;</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true" tabindex="-1"></a> email <span class="op">=</span> _require_string(value, field_name, max_length<span class="op">=</span><span class="dv">320</span>)</span>
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> EMAIL_RE.match(email):</span>
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="ss">f&quot;</span><span class="sc">{</span>field_name<span class="sc">}</span><span class="ss"> is not a valid email address&quot;</span>)</span>
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> email</span>
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _optional_email(value: Any, <span class="op">*</span>, field_name: <span class="bu">str</span> <span class="op">=</span> <span class="st">&quot;customer_email&quot;</span>) <span class="op">-&gt;</span> <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span>:</span>
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> value <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-86"><a href="#cb3-86" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb3-87"><a href="#cb3-87" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> _require_email(value, field_name<span class="op">=</span>field_name)</span>
<span id="cb3-88"><a href="#cb3-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-89"><a href="#cb3-89" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-90"><a href="#cb3-90" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _require_topic(value: Any) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb3-91"><a href="#cb3-91" aria-hidden="true" tabindex="-1"></a> topic <span class="op">=</span> _require_string(value, <span class="st">&quot;topic&quot;</span>, max_length<span class="op">=</span><span class="dv">40</span>)</span>
<span id="cb3-92"><a href="#cb3-92" aria-hidden="true" tabindex="-1"></a> topic <span class="op">=</span> topic.lower()</span>
<span id="cb3-93"><a href="#cb3-93" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> TOPIC_RE.match(topic):</span>
<span id="cb3-94"><a href="#cb3-94" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="st">&quot;topic must be lowercase letters and underscores only&quot;</span>)</span>
<span id="cb3-95"><a href="#cb3-95" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> topic</span>
<span id="cb3-96"><a href="#cb3-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-97"><a href="#cb3-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-98"><a href="#cb3-98" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _optional_item_titles(value: Any) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">str</span>] <span class="op">|</span> <span class="va">None</span>:</span>
<span id="cb3-99"><a href="#cb3-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> value <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-100"><a href="#cb3-100" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb3-101"><a href="#cb3-101" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(value, <span class="bu">list</span>):</span>
<span id="cb3-102"><a href="#cb3-102" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="st">&quot;item_titles must be a list of strings&quot;</span>)</span>
<span id="cb3-103"><a href="#cb3-103" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">len</span>(value) <span class="op">&gt;</span> ITEM_TITLES_MAX_COUNT:</span>
<span id="cb3-104"><a href="#cb3-104" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolValidationError(<span class="ss">f&quot;item_titles may contain at most </span><span class="sc">{</span>ITEM_TITLES_MAX_COUNT<span class="sc">}</span><span class="ss"> entries&quot;</span>)</span>
<span id="cb3-105"><a href="#cb3-105" aria-hidden="true" tabindex="-1"></a> cleaned: <span class="bu">list</span>[<span class="bu">str</span>] <span class="op">=</span> []</span>
<span id="cb3-106"><a href="#cb3-106" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> index, entry <span class="kw">in</span> <span class="bu">enumerate</span>(value):</span>
<span id="cb3-107"><a href="#cb3-107" aria-hidden="true" tabindex="-1"></a> cleaned.append(_require_string(entry, <span class="ss">f&quot;item_titles[</span><span class="sc">{</span>index<span class="sc">}</span><span class="ss">]&quot;</span>, max_length<span class="op">=</span>ITEM_TITLE_MAX_LENGTH))</span>
<span id="cb3-108"><a href="#cb3-108" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> cleaned</span>
<span id="cb3-109"><a href="#cb3-109" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-110"><a href="#cb3-110" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-111"><a href="#cb3-111" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _emails_match(supplied: <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span>, stored: <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb3-112"><a href="#cb3-112" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Constant-time email comparison with normalization.</span></span>
<span id="cb3-113"><a href="#cb3-113" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-114"><a href="#cb3-114" aria-hidden="true" tabindex="-1"></a><span class="co"> Returns False if either side is missing. Uses `hmac.compare_digest` to</span></span>
<span id="cb3-115"><a href="#cb3-115" aria-hidden="true" tabindex="-1"></a><span class="co"> close the timing side-channel that would otherwise leak the correct</span></span>
<span id="cb3-116"><a href="#cb3-116" aria-hidden="true" tabindex="-1"></a><span class="co"> prefix of a stored email.</span></span>
<span id="cb3-117"><a href="#cb3-117" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-118"><a href="#cb3-118" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> supplied <span class="kw">is</span> <span class="va">None</span> <span class="kw">or</span> stored <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-119"><a href="#cb3-119" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb3-120"><a href="#cb3-120" aria-hidden="true" tabindex="-1"></a> supplied_norm <span class="op">=</span> supplied.strip().lower().encode(<span class="st">&quot;utf-8&quot;</span>)</span>
<span id="cb3-121"><a href="#cb3-121" aria-hidden="true" tabindex="-1"></a> stored_norm <span class="op">=</span> stored.strip().lower().encode(<span class="st">&quot;utf-8&quot;</span>)</span>
<span id="cb3-122"><a href="#cb3-122" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> hmac.compare_digest(supplied_norm, stored_norm)</span>
<span id="cb3-123"><a href="#cb3-123" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-124"><a href="#cb3-124" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-125"><a href="#cb3-125" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _is_within_return_window(delivered_date: <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span>) <span class="op">-&gt;</span> <span class="bu">tuple</span>[<span class="bu">bool</span>, <span class="bu">int</span> <span class="op">|</span> <span class="va">None</span>]:</span>
<span id="cb3-126"><a href="#cb3-126" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Return (within_window, days_since_delivery).&quot;&quot;&quot;</span></span>
<span id="cb3-127"><a href="#cb3-127" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> delivered_date <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-128"><a href="#cb3-128" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (<span class="va">False</span>, <span class="va">None</span>)</span>
<span id="cb3-129"><a href="#cb3-129" aria-hidden="true" tabindex="-1"></a> delivered <span class="op">=</span> date.fromisoformat(delivered_date)</span>
<span id="cb3-130"><a href="#cb3-130" aria-hidden="true" tabindex="-1"></a> days_since <span class="op">=</span> (TODAY <span class="op">-</span> delivered).days</span>
<span id="cb3-131"><a href="#cb3-131" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (days_since <span class="op">&lt;=</span> RETURN_POLICY[<span class="st">&quot;window_days&quot;</span>], days_since)</span>
<span id="cb3-132"><a href="#cb3-132" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-133"><a href="#cb3-133" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-134"><a href="#cb3-134" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-135"><a href="#cb3-135" aria-hidden="true" tabindex="-1"></a><span class="co"># TypedDict argument shapes</span></span>
<span id="cb3-136"><a href="#cb3-136" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-137"><a href="#cb3-137" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-138"><a href="#cb3-138" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-139"><a href="#cb3-139" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> LookupOrderArgs(TypedDict, total<span class="op">=</span><span class="va">False</span>):</span>
<span id="cb3-140"><a href="#cb3-140" aria-hidden="true" tabindex="-1"></a> order_id: <span class="bu">str</span></span>
<span id="cb3-141"><a href="#cb3-141" aria-hidden="true" tabindex="-1"></a> customer_email: NotRequired[<span class="bu">str</span>]</span>
<span id="cb3-142"><a href="#cb3-142" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-143"><a href="#cb3-143" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-144"><a href="#cb3-144" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> CheckReturnEligibilityArgs(TypedDict):</span>
<span id="cb3-145"><a href="#cb3-145" aria-hidden="true" tabindex="-1"></a> order_id: <span class="bu">str</span></span>
<span id="cb3-146"><a href="#cb3-146" aria-hidden="true" tabindex="-1"></a> customer_email: <span class="bu">str</span></span>
<span id="cb3-147"><a href="#cb3-147" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-148"><a href="#cb3-148" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-149"><a href="#cb3-149" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> InitiateReturnArgs(TypedDict, total<span class="op">=</span><span class="va">False</span>):</span>
<span id="cb3-150"><a href="#cb3-150" aria-hidden="true" tabindex="-1"></a> order_id: <span class="bu">str</span></span>
<span id="cb3-151"><a href="#cb3-151" aria-hidden="true" tabindex="-1"></a> customer_email: <span class="bu">str</span></span>
<span id="cb3-152"><a href="#cb3-152" aria-hidden="true" tabindex="-1"></a> reason: <span class="bu">str</span></span>
<span id="cb3-153"><a href="#cb3-153" aria-hidden="true" tabindex="-1"></a> item_titles: NotRequired[<span class="bu">list</span>[<span class="bu">str</span>]]</span>
<span id="cb3-154"><a href="#cb3-154" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-155"><a href="#cb3-155" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-156"><a href="#cb3-156" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> LookupPolicyArgs(TypedDict):</span>
<span id="cb3-157"><a href="#cb3-157" aria-hidden="true" tabindex="-1"></a> topic: <span class="bu">str</span></span>
<span id="cb3-158"><a href="#cb3-158" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-159"><a href="#cb3-159" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-160"><a href="#cb3-160" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb3-161"><a href="#cb3-161" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> SessionGuardState:</span>
<span id="cb3-162"><a href="#cb3-162" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Per-session protocol state used to enforce tool ordering rules.</span></span>
<span id="cb3-163"><a href="#cb3-163" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-164"><a href="#cb3-164" aria-hidden="true" tabindex="-1"></a><span class="co"> Sessions are short-lived chats, so plain in-memory sets are fine. A</span></span>
<span id="cb3-165"><a href="#cb3-165" aria-hidden="true" tabindex="-1"></a><span class="co"> production deployment would back this with a session store.</span></span>
<span id="cb3-166"><a href="#cb3-166" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb3-167"><a href="#cb3-167" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-168"><a href="#cb3-168" aria-hidden="true" tabindex="-1"></a> eligibility_checks_passed: <span class="bu">set</span>[<span class="bu">str</span>] <span class="op">=</span> field(default_factory<span class="op">=</span><span class="bu">set</span>)</span>
<span id="cb3-169"><a href="#cb3-169" aria-hidden="true" tabindex="-1"></a> returns_initiated: <span class="bu">set</span>[<span class="bu">str</span>] <span class="op">=</span> field(default_factory<span class="op">=</span><span class="bu">set</span>)</span>
<span id="cb3-170"><a href="#cb3-170" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-171"><a href="#cb3-171" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-172"><a href="#cb3-172" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-173"><a href="#cb3-173" aria-hidden="true" tabindex="-1"></a><span class="co"># Tool schemas (Anthropic format)</span></span>
<span id="cb3-174"><a href="#cb3-174" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-175"><a href="#cb3-175" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-176"><a href="#cb3-176" aria-hidden="true" tabindex="-1"></a>LOOKUP_ORDER_SCHEMA: <span class="bu">dict</span>[<span class="bu">str</span>, Any] <span class="op">=</span> {</span>
<span id="cb3-177"><a href="#cb3-177" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: <span class="st">&quot;lookup_order&quot;</span>,</span>
<span id="cb3-178"><a href="#cb3-178" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: (</span>
<span id="cb3-179"><a href="#cb3-179" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Look up the status and details of a Bookly order by order ID. &quot;</span></span>
<span id="cb3-180"><a href="#cb3-180" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Optionally pass the customer email to verify ownership before returning details. &quot;</span></span>
<span id="cb3-181"><a href="#cb3-181" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Use this whenever the customer asks about an order.&quot;</span></span>
<span id="cb3-182"><a href="#cb3-182" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-183"><a href="#cb3-183" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;input_schema&quot;</span>: {</span>
<span id="cb3-184"><a href="#cb3-184" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;object&quot;</span>,</span>
<span id="cb3-185"><a href="#cb3-185" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;properties&quot;</span>: {</span>
<span id="cb3-186"><a href="#cb3-186" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: {</span>
<span id="cb3-187"><a href="#cb3-187" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>,</span>
<span id="cb3-188"><a href="#cb3-188" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: <span class="st">&quot;The order ID, formatted as &#39;BK-&#39; followed by digits.&quot;</span>,</span>
<span id="cb3-189"><a href="#cb3-189" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-190"><a href="#cb3-190" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_email&quot;</span>: {</span>
<span id="cb3-191"><a href="#cb3-191" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>,</span>
<span id="cb3-192"><a href="#cb3-192" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: <span class="st">&quot;Optional email used to verify the customer owns the order.&quot;</span>,</span>
<span id="cb3-193"><a href="#cb3-193" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-194"><a href="#cb3-194" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-195"><a href="#cb3-195" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;required&quot;</span>: [<span class="st">&quot;order_id&quot;</span>],</span>
<span id="cb3-196"><a href="#cb3-196" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-197"><a href="#cb3-197" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb3-198"><a href="#cb3-198" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-199"><a href="#cb3-199" aria-hidden="true" tabindex="-1"></a>CHECK_RETURN_ELIGIBILITY_SCHEMA: <span class="bu">dict</span>[<span class="bu">str</span>, Any] <span class="op">=</span> {</span>
<span id="cb3-200"><a href="#cb3-200" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: <span class="st">&quot;check_return_eligibility&quot;</span>,</span>
<span id="cb3-201"><a href="#cb3-201" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: (</span>
<span id="cb3-202"><a href="#cb3-202" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Check whether an order is eligible for return. Requires both order ID and the email &quot;</span></span>
<span id="cb3-203"><a href="#cb3-203" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;on the order. Must be called and succeed before initiate_return.&quot;</span></span>
<span id="cb3-204"><a href="#cb3-204" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-205"><a href="#cb3-205" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;input_schema&quot;</span>: {</span>
<span id="cb3-206"><a href="#cb3-206" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;object&quot;</span>,</span>
<span id="cb3-207"><a href="#cb3-207" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;properties&quot;</span>: {</span>
<span id="cb3-208"><a href="#cb3-208" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>},</span>
<span id="cb3-209"><a href="#cb3-209" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_email&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>},</span>
<span id="cb3-210"><a href="#cb3-210" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-211"><a href="#cb3-211" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;required&quot;</span>: [<span class="st">&quot;order_id&quot;</span>, <span class="st">&quot;customer_email&quot;</span>],</span>
<span id="cb3-212"><a href="#cb3-212" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-213"><a href="#cb3-213" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb3-214"><a href="#cb3-214" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-215"><a href="#cb3-215" aria-hidden="true" tabindex="-1"></a>INITIATE_RETURN_SCHEMA: <span class="bu">dict</span>[<span class="bu">str</span>, Any] <span class="op">=</span> {</span>
<span id="cb3-216"><a href="#cb3-216" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: <span class="st">&quot;initiate_return&quot;</span>,</span>
<span id="cb3-217"><a href="#cb3-217" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: (</span>
<span id="cb3-218"><a href="#cb3-218" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Start a return for an order. Only call this after check_return_eligibility has &quot;</span></span>
<span id="cb3-219"><a href="#cb3-219" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;succeeded for the same order in this conversation, and after the customer has &quot;</span></span>
<span id="cb3-220"><a href="#cb3-220" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;confirmed they want to proceed.&quot;</span></span>
<span id="cb3-221"><a href="#cb3-221" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-222"><a href="#cb3-222" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;input_schema&quot;</span>: {</span>
<span id="cb3-223"><a href="#cb3-223" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;object&quot;</span>,</span>
<span id="cb3-224"><a href="#cb3-224" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;properties&quot;</span>: {</span>
<span id="cb3-225"><a href="#cb3-225" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>},</span>
<span id="cb3-226"><a href="#cb3-226" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_email&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>},</span>
<span id="cb3-227"><a href="#cb3-227" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;reason&quot;</span>: {</span>
<span id="cb3-228"><a href="#cb3-228" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>,</span>
<span id="cb3-229"><a href="#cb3-229" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: <span class="st">&quot;The customer&#39;s stated reason for the return.&quot;</span>,</span>
<span id="cb3-230"><a href="#cb3-230" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-231"><a href="#cb3-231" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;item_titles&quot;</span>: {</span>
<span id="cb3-232"><a href="#cb3-232" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;array&quot;</span>,</span>
<span id="cb3-233"><a href="#cb3-233" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>},</span>
<span id="cb3-234"><a href="#cb3-234" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: <span class="st">&quot;Optional list of specific item titles to return. Defaults to all items.&quot;</span>,</span>
<span id="cb3-235"><a href="#cb3-235" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-236"><a href="#cb3-236" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-237"><a href="#cb3-237" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;required&quot;</span>: [<span class="st">&quot;order_id&quot;</span>, <span class="st">&quot;customer_email&quot;</span>, <span class="st">&quot;reason&quot;</span>],</span>
<span id="cb3-238"><a href="#cb3-238" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-239"><a href="#cb3-239" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb3-240"><a href="#cb3-240" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-241"><a href="#cb3-241" aria-hidden="true" tabindex="-1"></a>LOOKUP_POLICY_SCHEMA: <span class="bu">dict</span>[<span class="bu">str</span>, Any] <span class="op">=</span> {</span>
<span id="cb3-242"><a href="#cb3-242" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: <span class="st">&quot;lookup_policy&quot;</span>,</span>
<span id="cb3-243"><a href="#cb3-243" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: (</span>
<span id="cb3-244"><a href="#cb3-244" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Look up a Bookly customer policy by topic. Use this whenever the customer asks &quot;</span></span>
<span id="cb3-245"><a href="#cb3-245" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;about shipping, password reset, returns overview, or similar standard policies. &quot;</span></span>
<span id="cb3-246"><a href="#cb3-246" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Returns the verbatim policy text or topic_not_supported.&quot;</span></span>
<span id="cb3-247"><a href="#cb3-247" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-248"><a href="#cb3-248" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;input_schema&quot;</span>: {</span>
<span id="cb3-249"><a href="#cb3-249" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;object&quot;</span>,</span>
<span id="cb3-250"><a href="#cb3-250" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;properties&quot;</span>: {</span>
<span id="cb3-251"><a href="#cb3-251" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;topic&quot;</span>: {</span>
<span id="cb3-252"><a href="#cb3-252" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;string&quot;</span>,</span>
<span id="cb3-253"><a href="#cb3-253" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;description&quot;</span>: <span class="st">&quot;Policy topic, e.g. &#39;shipping&#39;, &#39;password_reset&#39;, &#39;returns_overview&#39;.&quot;</span>,</span>
<span id="cb3-254"><a href="#cb3-254" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-255"><a href="#cb3-255" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-256"><a href="#cb3-256" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;required&quot;</span>: [<span class="st">&quot;topic&quot;</span>],</span>
<span id="cb3-257"><a href="#cb3-257" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb3-258"><a href="#cb3-258" aria-hidden="true" tabindex="-1"></a> <span class="co"># Cache breakpoint: marking the last tool with `cache_control` extends the</span></span>
<span id="cb3-259"><a href="#cb3-259" aria-hidden="true" tabindex="-1"></a> <span class="co"># prompt cache over the whole tools block so schemas are not re-tokenized</span></span>
<span id="cb3-260"><a href="#cb3-260" aria-hidden="true" tabindex="-1"></a> <span class="co"># on every turn. The big system prompt already has its own breakpoint.</span></span>
<span id="cb3-261"><a href="#cb3-261" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;cache_control&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;ephemeral&quot;</span>},</span>
<span id="cb3-262"><a href="#cb3-262" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb3-263"><a href="#cb3-263" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-264"><a href="#cb3-264" aria-hidden="true" tabindex="-1"></a>TOOL_SCHEMAS: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> [</span>
<span id="cb3-265"><a href="#cb3-265" aria-hidden="true" tabindex="-1"></a> LOOKUP_ORDER_SCHEMA,</span>
<span id="cb3-266"><a href="#cb3-266" aria-hidden="true" tabindex="-1"></a> CHECK_RETURN_ELIGIBILITY_SCHEMA,</span>
<span id="cb3-267"><a href="#cb3-267" aria-hidden="true" tabindex="-1"></a> INITIATE_RETURN_SCHEMA,</span>
<span id="cb3-268"><a href="#cb3-268" aria-hidden="true" tabindex="-1"></a> LOOKUP_POLICY_SCHEMA,</span>
<span id="cb3-269"><a href="#cb3-269" aria-hidden="true" tabindex="-1"></a>]</span>
<span id="cb3-270"><a href="#cb3-270" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-271"><a href="#cb3-271" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-272"><a href="#cb3-272" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-273"><a href="#cb3-273" aria-hidden="true" tabindex="-1"></a><span class="co"># Handlers</span></span>
<span id="cb3-274"><a href="#cb3-274" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-275"><a href="#cb3-275" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-276"><a href="#cb3-276" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-277"><a href="#cb3-277" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_lookup_order(args: LookupOrderArgs, state: SessionGuardState) <span class="op">-&gt;</span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb3-278"><a href="#cb3-278" aria-hidden="true" tabindex="-1"></a> order_id <span class="op">=</span> _require_order_id(args.get(<span class="st">&quot;order_id&quot;</span>))</span>
<span id="cb3-279"><a href="#cb3-279" aria-hidden="true" tabindex="-1"></a> customer_email <span class="op">=</span> _optional_email(args.get(<span class="st">&quot;customer_email&quot;</span>))</span>
<span id="cb3-280"><a href="#cb3-280" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-281"><a href="#cb3-281" aria-hidden="true" tabindex="-1"></a> order <span class="op">=</span> ORDERS.get(order_id)</span>
<span id="cb3-282"><a href="#cb3-282" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-283"><a href="#cb3-283" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;order_not_found&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="ss">f&quot;No order found with ID </span><span class="sc">{</span>order_id<span class="sc">}</span><span class="ss">.&quot;</span>}</span>
<span id="cb3-284"><a href="#cb3-284" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-285"><a href="#cb3-285" aria-hidden="true" tabindex="-1"></a> <span class="co"># Privacy: when an email is supplied and does not match, return the same</span></span>
<span id="cb3-286"><a href="#cb3-286" aria-hidden="true" tabindex="-1"></a> <span class="co"># error as a missing order so callers cannot enumerate which IDs exist.</span></span>
<span id="cb3-287"><a href="#cb3-287" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> customer_email <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span> <span class="kw">and</span> <span class="kw">not</span> _emails_match(customer_email, order[<span class="st">&quot;email&quot;</span>]):</span>
<span id="cb3-288"><a href="#cb3-288" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;order_not_found&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="ss">f&quot;No order found with ID </span><span class="sc">{</span>order_id<span class="sc">}</span><span class="ss">.&quot;</span>}</span>
<span id="cb3-289"><a href="#cb3-289" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-290"><a href="#cb3-290" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;order&quot;</span>: order}</span>
<span id="cb3-291"><a href="#cb3-291" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-292"><a href="#cb3-292" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-293"><a href="#cb3-293" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_check_return_eligibility(</span>
<span id="cb3-294"><a href="#cb3-294" aria-hidden="true" tabindex="-1"></a> args: CheckReturnEligibilityArgs, state: SessionGuardState</span>
<span id="cb3-295"><a href="#cb3-295" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb3-296"><a href="#cb3-296" aria-hidden="true" tabindex="-1"></a> order_id <span class="op">=</span> _require_order_id(args.get(<span class="st">&quot;order_id&quot;</span>))</span>
<span id="cb3-297"><a href="#cb3-297" aria-hidden="true" tabindex="-1"></a> customer_email <span class="op">=</span> _require_email(args.get(<span class="st">&quot;customer_email&quot;</span>))</span>
<span id="cb3-298"><a href="#cb3-298" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-299"><a href="#cb3-299" aria-hidden="true" tabindex="-1"></a> order <span class="op">=</span> ORDERS.get(order_id)</span>
<span id="cb3-300"><a href="#cb3-300" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order <span class="kw">is</span> <span class="va">None</span> <span class="kw">or</span> <span class="kw">not</span> _emails_match(customer_email, order[<span class="st">&quot;email&quot;</span>]):</span>
<span id="cb3-301"><a href="#cb3-301" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-302"><a href="#cb3-302" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;error&quot;</span>: <span class="st">&quot;auth_failed&quot;</span>,</span>
<span id="cb3-303"><a href="#cb3-303" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;message&quot;</span>: <span class="st">&quot;Could not verify that order ID and email together. Please double-check both.&quot;</span>,</span>
<span id="cb3-304"><a href="#cb3-304" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-305"><a href="#cb3-305" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-306"><a href="#cb3-306" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order[<span class="st">&quot;status&quot;</span>] <span class="op">!=</span> <span class="st">&quot;delivered&quot;</span>:</span>
<span id="cb3-307"><a href="#cb3-307" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-308"><a href="#cb3-308" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;eligible&quot;</span>: <span class="va">False</span>,</span>
<span id="cb3-309"><a href="#cb3-309" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;reason&quot;</span>: (</span>
<span id="cb3-310"><a href="#cb3-310" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;This order has status &#39;</span><span class="sc">{</span>order[<span class="st">&#39;status&#39;</span>]<span class="sc">}</span><span class="ss">&#39;, not &#39;delivered&#39;. &quot;</span></span>
<span id="cb3-311"><a href="#cb3-311" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Returns can only be started after an order has been delivered.&quot;</span></span>
<span id="cb3-312"><a href="#cb3-312" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-313"><a href="#cb3-313" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;policy&quot;</span>: RETURN_POLICY,</span>
<span id="cb3-314"><a href="#cb3-314" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-315"><a href="#cb3-315" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-316"><a href="#cb3-316" aria-hidden="true" tabindex="-1"></a> within_window, days_since <span class="op">=</span> _is_within_return_window(order.get(<span class="st">&quot;delivered_date&quot;</span>))</span>
<span id="cb3-317"><a href="#cb3-317" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> within_window:</span>
<span id="cb3-318"><a href="#cb3-318" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-319"><a href="#cb3-319" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;eligible&quot;</span>: <span class="va">False</span>,</span>
<span id="cb3-320"><a href="#cb3-320" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;reason&quot;</span>: (</span>
<span id="cb3-321"><a href="#cb3-321" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;This order was delivered </span><span class="sc">{</span>days_since<span class="sc">}</span><span class="ss"> days ago, which is outside the &quot;</span></span>
<span id="cb3-322"><a href="#cb3-322" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;window_days&#39;</span>]<span class="sc">}</span><span class="ss">-day return window.&quot;</span></span>
<span id="cb3-323"><a href="#cb3-323" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-324"><a href="#cb3-324" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;policy&quot;</span>: RETURN_POLICY,</span>
<span id="cb3-325"><a href="#cb3-325" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-326"><a href="#cb3-326" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-327"><a href="#cb3-327" aria-hidden="true" tabindex="-1"></a> state.eligibility_checks_passed.add(order_id)</span>
<span id="cb3-328"><a href="#cb3-328" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-329"><a href="#cb3-329" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;eligible&quot;</span>: <span class="va">True</span>,</span>
<span id="cb3-330"><a href="#cb3-330" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;reason&quot;</span>: (</span>
<span id="cb3-331"><a href="#cb3-331" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Order delivered </span><span class="sc">{</span>days_since<span class="sc">}</span><span class="ss"> days ago, within the &quot;</span></span>
<span id="cb3-332"><a href="#cb3-332" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;window_days&#39;</span>]<span class="sc">}</span><span class="ss">-day window.&quot;</span></span>
<span id="cb3-333"><a href="#cb3-333" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-334"><a href="#cb3-334" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: order[<span class="st">&quot;items&quot;</span>],</span>
<span id="cb3-335"><a href="#cb3-335" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;policy&quot;</span>: RETURN_POLICY,</span>
<span id="cb3-336"><a href="#cb3-336" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-337"><a href="#cb3-337" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-338"><a href="#cb3-338" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-339"><a href="#cb3-339" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_initiate_return(args: InitiateReturnArgs, state: SessionGuardState) <span class="op">-&gt;</span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb3-340"><a href="#cb3-340" aria-hidden="true" tabindex="-1"></a> order_id <span class="op">=</span> _require_order_id(args.get(<span class="st">&quot;order_id&quot;</span>))</span>
<span id="cb3-341"><a href="#cb3-341" aria-hidden="true" tabindex="-1"></a> customer_email <span class="op">=</span> _require_email(args.get(<span class="st">&quot;customer_email&quot;</span>))</span>
<span id="cb3-342"><a href="#cb3-342" aria-hidden="true" tabindex="-1"></a> reason <span class="op">=</span> _require_string(args.get(<span class="st">&quot;reason&quot;</span>), <span class="st">&quot;reason&quot;</span>, max_length<span class="op">=</span>REASON_MAX_LENGTH)</span>
<span id="cb3-343"><a href="#cb3-343" aria-hidden="true" tabindex="-1"></a> item_titles <span class="op">=</span> _optional_item_titles(args.get(<span class="st">&quot;item_titles&quot;</span>))</span>
<span id="cb3-344"><a href="#cb3-344" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-345"><a href="#cb3-345" aria-hidden="true" tabindex="-1"></a> <span class="co"># Layer 3 protocol guard: the agent must have called check_return_eligibility</span></span>
<span id="cb3-346"><a href="#cb3-346" aria-hidden="true" tabindex="-1"></a> <span class="co"># for this exact order in this session, and it must have passed.</span></span>
<span id="cb3-347"><a href="#cb3-347" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order_id <span class="kw">not</span> <span class="kw">in</span> state.eligibility_checks_passed:</span>
<span id="cb3-348"><a href="#cb3-348" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-349"><a href="#cb3-349" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;error&quot;</span>: <span class="st">&quot;eligibility_not_verified&quot;</span>,</span>
<span id="cb3-350"><a href="#cb3-350" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;message&quot;</span>: (</span>
<span id="cb3-351"><a href="#cb3-351" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Cannot initiate a return without a successful eligibility check for this &quot;</span></span>
<span id="cb3-352"><a href="#cb3-352" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order in the current session. Call check_return_eligibility first.&quot;</span></span>
<span id="cb3-353"><a href="#cb3-353" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-354"><a href="#cb3-354" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-355"><a href="#cb3-355" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-356"><a href="#cb3-356" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order_id <span class="kw">in</span> state.returns_initiated:</span>
<span id="cb3-357"><a href="#cb3-357" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-358"><a href="#cb3-358" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;error&quot;</span>: <span class="st">&quot;already_initiated&quot;</span>,</span>
<span id="cb3-359"><a href="#cb3-359" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;message&quot;</span>: <span class="st">&quot;A return has already been initiated for this order in this session.&quot;</span>,</span>
<span id="cb3-360"><a href="#cb3-360" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-361"><a href="#cb3-361" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-362"><a href="#cb3-362" aria-hidden="true" tabindex="-1"></a> order <span class="op">=</span> ORDERS.get(order_id)</span>
<span id="cb3-363"><a href="#cb3-363" aria-hidden="true" tabindex="-1"></a> <span class="co"># Paired assertion: we already checked eligibility against the same order,</span></span>
<span id="cb3-364"><a href="#cb3-364" aria-hidden="true" tabindex="-1"></a> <span class="co"># but re-verify here so a future edit that makes ORDERS mutable cannot</span></span>
<span id="cb3-365"><a href="#cb3-365" aria-hidden="true" tabindex="-1"></a> <span class="co"># silently break the email-binding guarantee.</span></span>
<span id="cb3-366"><a href="#cb3-366" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> order <span class="kw">is</span> <span class="va">None</span> <span class="kw">or</span> <span class="kw">not</span> _emails_match(customer_email, order[<span class="st">&quot;email&quot;</span>]):</span>
<span id="cb3-367"><a href="#cb3-367" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;auth_failed&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="st">&quot;Order/email mismatch.&quot;</span>}</span>
<span id="cb3-368"><a href="#cb3-368" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-369"><a href="#cb3-369" aria-hidden="true" tabindex="-1"></a> <span class="co"># Explicit: an empty list means &quot;no items selected&quot; (a caller error we</span></span>
<span id="cb3-370"><a href="#cb3-370" aria-hidden="true" tabindex="-1"></a> <span class="co"># reject) while `None` means &quot;default to all items on the order&quot;.</span></span>
<span id="cb3-371"><a href="#cb3-371" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> item_titles <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span> <span class="kw">and</span> <span class="kw">not</span> item_titles:</span>
<span id="cb3-372"><a href="#cb3-372" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;no_items_selected&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="st">&quot;item_titles cannot be an empty list.&quot;</span>}</span>
<span id="cb3-373"><a href="#cb3-373" aria-hidden="true" tabindex="-1"></a> titles <span class="op">=</span> item_titles <span class="cf">if</span> item_titles <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span> <span class="cf">else</span> [item[<span class="st">&quot;title&quot;</span>] <span class="cf">for</span> item <span class="kw">in</span> order[<span class="st">&quot;items&quot;</span>]]</span>
<span id="cb3-374"><a href="#cb3-374" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-375"><a href="#cb3-375" aria-hidden="true" tabindex="-1"></a> return_id <span class="op">=</span> <span class="ss">f&quot;RMA-</span><span class="sc">{</span>uuid<span class="sc">.</span>uuid4()<span class="sc">.</span><span class="bu">hex</span>[:<span class="dv">8</span>]<span class="sc">.</span>upper()<span class="sc">}</span><span class="ss">&quot;</span></span>
<span id="cb3-376"><a href="#cb3-376" aria-hidden="true" tabindex="-1"></a> record <span class="op">=</span> {</span>
<span id="cb3-377"><a href="#cb3-377" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;return_id&quot;</span>: return_id,</span>
<span id="cb3-378"><a href="#cb3-378" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;order_id&quot;</span>: order_id,</span>
<span id="cb3-379"><a href="#cb3-379" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;customer_email&quot;</span>: order[<span class="st">&quot;email&quot;</span>],</span>
<span id="cb3-380"><a href="#cb3-380" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;items&quot;</span>: titles,</span>
<span id="cb3-381"><a href="#cb3-381" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;reason&quot;</span>: reason,</span>
<span id="cb3-382"><a href="#cb3-382" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;refund_method&quot;</span>: RETURN_POLICY[<span class="st">&quot;refund_method&quot;</span>],</span>
<span id="cb3-383"><a href="#cb3-383" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;refund_timeline_days&quot;</span>: RETURN_POLICY[<span class="st">&quot;refund_timeline_days&quot;</span>],</span>
<span id="cb3-384"><a href="#cb3-384" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;next_steps&quot;</span>: (</span>
<span id="cb3-385"><a href="#cb3-385" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;We&#39;ve emailed a prepaid shipping label to the address on file. Drop the package at &quot;</span></span>
<span id="cb3-386"><a href="#cb3-386" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;any carrier location within 14 days. Your refund will post within &quot;</span></span>
<span id="cb3-387"><a href="#cb3-387" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;refund_timeline_days&#39;</span>]<span class="sc">}</span><span class="ss"> business days of us receiving the return.&quot;</span></span>
<span id="cb3-388"><a href="#cb3-388" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb3-389"><a href="#cb3-389" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-390"><a href="#cb3-390" aria-hidden="true" tabindex="-1"></a> RETURNS[return_id] <span class="op">=</span> record</span>
<span id="cb3-391"><a href="#cb3-391" aria-hidden="true" tabindex="-1"></a> state.returns_initiated.add(order_id)</span>
<span id="cb3-392"><a href="#cb3-392" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> record</span>
<span id="cb3-393"><a href="#cb3-393" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-394"><a href="#cb3-394" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-395"><a href="#cb3-395" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> handle_lookup_policy(args: LookupPolicyArgs, state: SessionGuardState) <span class="op">-&gt;</span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb3-396"><a href="#cb3-396" aria-hidden="true" tabindex="-1"></a> topic <span class="op">=</span> _require_topic(args.get(<span class="st">&quot;topic&quot;</span>))</span>
<span id="cb3-397"><a href="#cb3-397" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-398"><a href="#cb3-398" aria-hidden="true" tabindex="-1"></a> text <span class="op">=</span> POLICIES.get(topic)</span>
<span id="cb3-399"><a href="#cb3-399" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> text <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-400"><a href="#cb3-400" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb3-401"><a href="#cb3-401" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;error&quot;</span>: <span class="st">&quot;topic_not_supported&quot;</span>,</span>
<span id="cb3-402"><a href="#cb3-402" aria-hidden="true" tabindex="-1"></a> <span class="co"># Echo the normalized topic, not the raw input, so nothing the</span></span>
<span id="cb3-403"><a href="#cb3-403" aria-hidden="true" tabindex="-1"></a> <span class="co"># caller injected is ever reflected back into model context.</span></span>
<span id="cb3-404"><a href="#cb3-404" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;message&quot;</span>: <span class="ss">f&quot;No policy entry for topic &#39;</span><span class="sc">{</span>topic<span class="sc">}</span><span class="ss">&#39;.&quot;</span>,</span>
<span id="cb3-405"><a href="#cb3-405" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;available_topics&quot;</span>: <span class="bu">sorted</span>(POLICIES.keys()),</span>
<span id="cb3-406"><a href="#cb3-406" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb3-407"><a href="#cb3-407" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;topic&quot;</span>: topic, <span class="st">&quot;text&quot;</span>: text}</span>
<span id="cb3-408"><a href="#cb3-408" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-409"><a href="#cb3-409" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-410"><a href="#cb3-410" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-411"><a href="#cb3-411" aria-hidden="true" tabindex="-1"></a><span class="co"># Dispatch</span></span>
<span id="cb3-412"><a href="#cb3-412" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb3-413"><a href="#cb3-413" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-414"><a href="#cb3-414" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-415"><a href="#cb3-415" aria-hidden="true" tabindex="-1"></a>_HANDLERS: <span class="bu">dict</span>[<span class="bu">str</span>, Callable[[Any, SessionGuardState], <span class="bu">dict</span>[<span class="bu">str</span>, Any]]] <span class="op">=</span> {</span>
<span id="cb3-416"><a href="#cb3-416" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;lookup_order&quot;</span>: handle_lookup_order,</span>
<span id="cb3-417"><a href="#cb3-417" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;check_return_eligibility&quot;</span>: handle_check_return_eligibility,</span>
<span id="cb3-418"><a href="#cb3-418" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;initiate_return&quot;</span>: handle_initiate_return,</span>
<span id="cb3-419"><a href="#cb3-419" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;lookup_policy&quot;</span>: handle_lookup_policy,</span>
<span id="cb3-420"><a href="#cb3-420" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb3-421"><a href="#cb3-421" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-422"><a href="#cb3-422" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-423"><a href="#cb3-423" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> dispatch_tool(name: <span class="bu">str</span>, args: <span class="bu">dict</span>[<span class="bu">str</span>, Any], state: SessionGuardState) <span class="op">-&gt;</span> <span class="bu">dict</span>[<span class="bu">str</span>, Any]:</span>
<span id="cb3-424"><a href="#cb3-424" aria-hidden="true" tabindex="-1"></a> handler <span class="op">=</span> _HANDLERS.get(name)</span>
<span id="cb3-425"><a href="#cb3-425" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> handler <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb3-426"><a href="#cb3-426" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;unknown_tool&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="ss">f&quot;No tool named </span><span class="sc">{</span>name<span class="sc">}</span><span class="ss">.&quot;</span>}</span>
<span id="cb3-427"><a href="#cb3-427" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(args, <span class="bu">dict</span>):</span>
<span id="cb3-428"><a href="#cb3-428" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;invalid_arguments&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="st">&quot;Tool arguments must be an object.&quot;</span>}</span>
<span id="cb3-429"><a href="#cb3-429" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
<span id="cb3-430"><a href="#cb3-430" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> handler(args, state)</span>
<span id="cb3-431"><a href="#cb3-431" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> ToolValidationError <span class="im">as</span> exc:</span>
<span id="cb3-432"><a href="#cb3-432" aria-hidden="true" tabindex="-1"></a> <span class="co"># Return validation errors as structured tool errors so the model can</span></span>
<span id="cb3-433"><a href="#cb3-433" aria-hidden="true" tabindex="-1"></a> <span class="co"># recover. Never surface the message verbatim from untrusted input --</span></span>
<span id="cb3-434"><a href="#cb3-434" aria-hidden="true" tabindex="-1"></a> <span class="co"># `_require_string` already stripped control characters, and the error</span></span>
<span id="cb3-435"><a href="#cb3-435" aria-hidden="true" tabindex="-1"></a> <span class="co"># messages themselves are constructed from field names, not user data.</span></span>
<span id="cb3-436"><a href="#cb3-436" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;error&quot;</span>: <span class="st">&quot;invalid_arguments&quot;</span>, <span class="st">&quot;message&quot;</span>: <span class="bu">str</span>(exc)}</span></code></pre></div>
<h1 id="agent-loop">Agent loop</h1>
<p>This is the biggest file. It wires everything together: the system
prompt, runtime reminders, output validation (Layer 4), the in-memory
session store with per-session locking, the cached Anthropic client, and
the actual tool-use loop that drives a turn end to end.</p>
<h2 id="system-prompt">System prompt</h2>
<p>The prompt is structured with XML-style tags
(<code>&lt;identity&gt;</code>, <code>&lt;critical_rules&gt;</code>,
<code>&lt;scope&gt;</code>, <code>&lt;return_policy&gt;</code>,
<code>&lt;tool_rules&gt;</code>, <code>&lt;tone&gt;</code>,
<code>&lt;examples&gt;</code>, <code>&lt;reminders&gt;</code>). The
critical rules are stated up front and repeated at the bottom (primacy
plus recency). The return policy section interpolates the
<code>RETURN_POLICY</code> dict verbatim via
<code>_format_return_policy_block</code>, so the prompt and the
enforcement in <code>tools.py</code> cannot disagree.</p>
<p>Four few-shot examples are embedded directly in the prompt. Each one
demonstrates a case that is easy to get wrong: missing order ID, quoting
a policy verbatim, refusing an off-topic request, disambiguating between
two orders.</p>
<h2 id="runtime-reminders">Runtime reminders</h2>
<p>On every turn, <code>build_system_content</code> appends a short
<code>CRITICAL_REMINDER</code> block to the system content. Once the
turn count crosses <code>LONG_CONVERSATION_TURN_THRESHOLD</code>, a
second <code>LONG_CONVERSATION_REMINDER</code> is added. The big
<code>SYSTEM_PROMPT</code> block is the only one marked
<code>cache_control: ephemeral</code> the reminders vary per turn and
we want them at the highest-attention position, not in the cached
prefix.</p>
<h2 id="layer-4-output-validation">Layer 4 output validation</h2>
<p>After the model produces its final reply, <code>validate_reply</code>
runs four cheap deterministic checks: every <code>BK-NNNN</code> string
in the reply must also appear in a tool result from this turn, every ISO
date in the reply must appear in a tool result, the reply must not
contain markdown, and if the reply contains off-topic engagement phrases
it must also contain the refusal template. Violations are collected and
returned as a frozen <code>ValidationResult</code>.</p>
<p>The off-topic patterns used to be loose substring matches on a
keyword set. That false-positived on plenty of legitimate support
replies (“Id recommend contacting…”). The current patterns use word
boundaries so only the intended phrases trip them.</p>
<h2 id="session-store">Session store</h2>
<p><code>SessionStore</code> is a bounded in-memory LRU with an idle
TTL. It stores <code>Session</code> objects (history, guard state, turn
count) keyed by opaque server-issued session IDs. It also owns the
per-session locks used to serialize concurrent turns for the same
session, since FastAPI runs the sync <code>chat</code> handler in a
threadpool and two simultaneous requests for the same session would
otherwise corrupt the conversation history.</p>
<p>The locks-dict is itself protected by a class-level lock so two
threads trying to create the first lock for a session cannot race into
two different lock instances.</p>
<p>Under the “single-process demo deployment” constraint this is enough.
For multi-worker, the whole class would get swapped for a Redis-backed
equivalent.</p>
<h2 id="the-tool-use-loop">The tool-use loop</h2>
<p><code>_run_tool_use_loop</code> drives the model until it stops
asking for tools. It is bounded by
<code>settings.max_tool_use_iterations</code> so a runaway model cannot
burn credit in an infinite loop. Each iteration serializes the
assistants content blocks into history, dispatches every requested
tool, packs the results into a single <code>tool_result</code> user-role
message, and calls Claude again. Before each call,
<code>_with_last_message_cache_breakpoint</code> stamps the last message
with <code>cache_control: ephemeral</code> so prior turns do not need to
be re-tokenized on every call. This turns the per-turn input-token cost
from <code>O(turns^2)</code> into <code>O(turns)</code> across a
session.</p>
<h2 id="run_turn">run_turn</h2>
<p><code>run_turn</code> is the top-level entry point the server calls.
It validates its inputs, acquires the per-session lock, appends the user
message, runs the loop, and then either persists the final reply to
history or on validation failure drops the bad reply and returns a
safe fallback. Dropping a bad reply from history is important: it
prevents a hallucinated claim from poisoning subsequent turns.</p>
<p>Warning logs never include the reply body. Session IDs and reply
contents are logged only as short SHA-256 hashes for correlation, which
keeps PII out of the log pipeline even under active incident
response.</p>
<div class="sourceCode" id="cb4" data-chunk="agent-py" data-file="agent.py"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;Bookly agent: system prompt, guardrails, session store, and the agentic loop.</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">This module wires four guardrail layers together:</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="co">1. The system prompt itself (XML-tagged, primacy+recency duplication, verbatim</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="co"> policy block, refusal template, few-shot examples for edge cases).</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="co">2. Runtime reminder injection: a short &quot;non-negotiable rules&quot; block appended</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="co"> to the system content on every turn, plus a stronger reminder once the</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="co"> conversation gets long enough that the original prompt has decayed in</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="co"> effective attention.</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="co">3. Tool-side enforcement (lives in `tools.py`): handlers refuse unsafe calls</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="co"> regardless of what the model decides.</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="co">4. Output validation: deterministic regex checks on the final reply for</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="co"> ungrounded order IDs/dates, markdown leakage, and off-topic engagement</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="co"> without the refusal template. On failure, the bad reply is dropped and the</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="co"> user gets a safe canned message -- and the bad reply is never appended to</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="co"> history, so it cannot poison subsequent turns.</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a><span class="co">Anthropic prompt caching is enabled on the large system-prompt block AND on</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a><span class="co">the last tool schema and the last message in history, so per-turn input cost</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a><span class="co">scales linearly in the number of turns instead of quadratically.</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> functools</span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> hashlib</span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> json</span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> logging</span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> re</span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> threading</span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> collections <span class="im">import</span> OrderedDict</span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> dataclasses <span class="im">import</span> dataclass, field</span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> typing <span class="im">import</span> Any</span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> anthropic <span class="im">import</span> Anthropic</span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> config <span class="im">import</span> settings</span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> tools <span class="im">import</span> SessionGuardState, TOOL_SCHEMAS, dispatch_tool</span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> mock_data <span class="im">import</span> POLICIES, RETURN_POLICY</span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a>logger <span class="op">=</span> logging.getLogger(<span class="st">&quot;bookly.agent&quot;</span>)</span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a><span class="co"># System prompt</span></span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _format_return_policy_block() <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Render `RETURN_POLICY` as a compact, quotable block for the prompt.</span></span>
<span id="cb4-53"><a href="#cb4-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-54"><a href="#cb4-54" aria-hidden="true" tabindex="-1"></a><span class="co"> Embedding the dict verbatim (instead of paraphrasing it in English) is a</span></span>
<span id="cb4-55"><a href="#cb4-55" aria-hidden="true" tabindex="-1"></a><span class="co"> deliberate anti-hallucination move: the model quotes the block instead of</span></span>
<span id="cb4-56"><a href="#cb4-56" aria-hidden="true" tabindex="-1"></a><span class="co"> inventing details.</span></span>
<span id="cb4-57"><a href="#cb4-57" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-58"><a href="#cb4-58" aria-hidden="true" tabindex="-1"></a> non_returnable <span class="op">=</span> <span class="st">&quot;, &quot;</span>.join(RETURN_POLICY[<span class="st">&quot;non_returnable_categories&quot;</span>])</span>
<span id="cb4-59"><a href="#cb4-59" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (</span>
<span id="cb4-60"><a href="#cb4-60" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Return window: </span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;window_days&#39;</span>]<span class="sc">}</span><span class="ss"> days from delivery.</span><span class="ch">\n</span><span class="ss">&quot;</span></span>
<span id="cb4-61"><a href="#cb4-61" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Condition: </span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;condition_requirements&#39;</span>]<span class="sc">}</span><span class="ch">\n</span><span class="ss">&quot;</span></span>
<span id="cb4-62"><a href="#cb4-62" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Refund method: </span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;refund_method&#39;</span>]<span class="sc">}</span><span class="ch">\n</span><span class="ss">&quot;</span></span>
<span id="cb4-63"><a href="#cb4-63" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Refund timeline: within </span><span class="sc">{</span>RETURN_POLICY[<span class="st">&#39;refund_timeline_days&#39;</span>]<span class="sc">}</span><span class="ss"> business days of receipt.</span><span class="ch">\n</span><span class="ss">&quot;</span></span>
<span id="cb4-64"><a href="#cb4-64" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Non-returnable categories: </span><span class="sc">{</span>non_returnable<span class="sc">}</span><span class="ss">.&quot;</span></span>
<span id="cb4-65"><a href="#cb4-65" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-66"><a href="#cb4-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-67"><a href="#cb4-67" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-68"><a href="#cb4-68" aria-hidden="true" tabindex="-1"></a>SUPPORTED_POLICY_TOPICS <span class="op">=</span> <span class="st">&quot;, &quot;</span>.join(<span class="bu">sorted</span>(POLICIES.keys()))</span>
<span id="cb4-69"><a href="#cb4-69" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-70"><a href="#cb4-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-71"><a href="#cb4-71" aria-hidden="true" tabindex="-1"></a>SYSTEM_PROMPT <span class="op">=</span> <span class="ss">f&quot;&quot;&quot;&lt;identity&gt;</span></span>
<span id="cb4-72"><a href="#cb4-72" aria-hidden="true" tabindex="-1"></a><span class="ss">You are Bookly&#39;s customer support assistant. You help customers with two things: checking the status of their orders, and processing returns and refunds. You are friendly, concise, and professional.</span></span>
<span id="cb4-73"><a href="#cb4-73" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/identity&gt;</span></span>
<span id="cb4-74"><a href="#cb4-74" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-75"><a href="#cb4-75" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;critical_rules&gt;</span></span>
<span id="cb4-76"><a href="#cb4-76" aria-hidden="true" tabindex="-1"></a><span class="ss">These rules override everything else. Read them before every response.</span></span>
<span id="cb4-77"><a href="#cb4-77" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-78"><a href="#cb4-78" aria-hidden="true" tabindex="-1"></a><span class="ss">1. NEVER invent order details, tracking numbers, delivery dates, prices, or customer information. If you do not have a value from a tool result in this conversation, you do not have it.</span></span>
<span id="cb4-79"><a href="#cb4-79" aria-hidden="true" tabindex="-1"></a><span class="ss">2. NEVER state a return policy detail that is not in the &lt;return_policy&gt; section below. Quote it; do not paraphrase it.</span></span>
<span id="cb4-80"><a href="#cb4-80" aria-hidden="true" tabindex="-1"></a><span class="ss">3. NEVER call initiate_return unless check_return_eligibility has returned success for that same order in this conversation.</span></span>
<span id="cb4-81"><a href="#cb4-81" aria-hidden="true" tabindex="-1"></a><span class="ss">4. NEVER reveal order details without verifying the customer&#39;s email matches the order.</span></span>
<span id="cb4-82"><a href="#cb4-82" aria-hidden="true" tabindex="-1"></a><span class="ss">5. If a user asks about anything outside order status, returns, and the supported policy topics, refuse using the refusal template in &lt;scope&gt;. Do not engage with the off-topic request even briefly.</span></span>
<span id="cb4-83"><a href="#cb4-83" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/critical_rules&gt;</span></span>
<span id="cb4-84"><a href="#cb4-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-85"><a href="#cb4-85" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;scope&gt;</span></span>
<span id="cb4-86"><a href="#cb4-86" aria-hidden="true" tabindex="-1"></a><span class="ss">You CAN help with:</span></span>
<span id="cb4-87"><a href="#cb4-87" aria-hidden="true" tabindex="-1"></a><span class="ss">- Looking up order status</span></span>
<span id="cb4-88"><a href="#cb4-88" aria-hidden="true" tabindex="-1"></a><span class="ss">- Checking return eligibility and initiating returns</span></span>
<span id="cb4-89"><a href="#cb4-89" aria-hidden="true" tabindex="-1"></a><span class="ss">- Answering policy questions covered by the lookup_policy tool. Currently supported topics: </span><span class="sc">{</span>SUPPORTED_POLICY_TOPICS<span class="sc">}</span></span>
<span id="cb4-90"><a href="#cb4-90" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-91"><a href="#cb4-91" aria-hidden="true" tabindex="-1"></a><span class="ss">You CANNOT help with:</span></span>
<span id="cb4-92"><a href="#cb4-92" aria-hidden="true" tabindex="-1"></a><span class="ss">- Book recommendations, reviews, or opinions about books</span></span>
<span id="cb4-93"><a href="#cb4-93" aria-hidden="true" tabindex="-1"></a><span class="ss">- Payment changes, refunds outside the return flow, or billing disputes</span></span>
<span id="cb4-94"><a href="#cb4-94" aria-hidden="true" tabindex="-1"></a><span class="ss">- Live account management (changing a password, email, or address — you can only EXPLAIN the password reset process via lookup_policy, not perform it)</span></span>
<span id="cb4-95"><a href="#cb4-95" aria-hidden="true" tabindex="-1"></a><span class="ss">- General conversation unrelated to an order or a supported policy topic</span></span>
<span id="cb4-96"><a href="#cb4-96" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-97"><a href="#cb4-97" aria-hidden="true" tabindex="-1"></a><span class="ss">For any policy question, call lookup_policy first. Only if the tool returns topic_not_supported should you use the refusal template below.</span></span>
<span id="cb4-98"><a href="#cb4-98" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-99"><a href="#cb4-99" aria-hidden="true" tabindex="-1"></a><span class="ss">Refusal template (use verbatim, filling in the topic):</span></span>
<span id="cb4-100"><a href="#cb4-100" aria-hidden="true" tabindex="-1"></a><span class="ss">&quot;I can help with order status, returns, and our standard policies, but I&#39;m not able to help with </span><span class="ch">{{</span><span class="ss">topic</span><span class="ch">}}</span><span class="ss">. Is there an order or a policy question I can help you with instead?&quot;</span></span>
<span id="cb4-101"><a href="#cb4-101" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/scope&gt;</span></span>
<span id="cb4-102"><a href="#cb4-102" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-103"><a href="#cb4-103" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;return_policy&gt;</span></span>
<span id="cb4-104"><a href="#cb4-104" aria-hidden="true" tabindex="-1"></a><span class="sc">{</span>_format_return_policy_block()<span class="sc">}</span></span>
<span id="cb4-105"><a href="#cb4-105" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-106"><a href="#cb4-106" aria-hidden="true" tabindex="-1"></a><span class="ss">This is the authoritative policy. Any claim you make about returns must be traceable to a line in this block. If a customer asks about a scenario this policy does not cover, say so honestly and offer to connect them with a human agent.</span></span>
<span id="cb4-107"><a href="#cb4-107" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/return_policy&gt;</span></span>
<span id="cb4-108"><a href="#cb4-108" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-109"><a href="#cb4-109" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;tool_rules&gt;</span></span>
<span id="cb4-110"><a href="#cb4-110" aria-hidden="true" tabindex="-1"></a><span class="ss">You have four tools: lookup_order, check_return_eligibility, initiate_return, and lookup_policy.</span></span>
<span id="cb4-111"><a href="#cb4-111" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-112"><a href="#cb4-112" aria-hidden="true" tabindex="-1"></a><span class="ss">Before calling a tool:</span></span>
<span id="cb4-113"><a href="#cb4-113" aria-hidden="true" tabindex="-1"></a><span class="ss">- You must have every required parameter. If you are missing one, ask the customer for it. Do not guess, do not use placeholder values, do not call the tool and hope.</span></span>
<span id="cb4-114"><a href="#cb4-114" aria-hidden="true" tabindex="-1"></a><span class="ss">- For initiate_return, you must have already called check_return_eligibility for that exact order_id in this conversation, and it must have returned success.</span></span>
<span id="cb4-115"><a href="#cb4-115" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-116"><a href="#cb4-116" aria-hidden="true" tabindex="-1"></a><span class="ss">After a tool call:</span></span>
<span id="cb4-117"><a href="#cb4-117" aria-hidden="true" tabindex="-1"></a><span class="ss">- Relay the result honestly. If the tool returns an error, tell the customer what went wrong using the tool&#39;s error message, not a paraphrase.</span></span>
<span id="cb4-118"><a href="#cb4-118" aria-hidden="true" tabindex="-1"></a><span class="ss">- Do not mix tool results from different orders in a single response unless the customer explicitly asked about multiple.</span></span>
<span id="cb4-119"><a href="#cb4-119" aria-hidden="true" tabindex="-1"></a><span class="ss">- For lookup_policy, quote the returned policy text; do not summarize or embellish. If lookup_policy returns topic_not_supported, fall through to the refusal template in &lt;scope&gt;.</span></span>
<span id="cb4-120"><a href="#cb4-120" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/tool_rules&gt;</span></span>
<span id="cb4-121"><a href="#cb4-121" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-122"><a href="#cb4-122" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;clarifying_rules&gt;</span></span>
<span id="cb4-123"><a href="#cb4-123" aria-hidden="true" tabindex="-1"></a><span class="ss">Ask one clarifying question at a time, not a list. Common cases:</span></span>
<span id="cb4-124"><a href="#cb4-124" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-125"><a href="#cb4-125" aria-hidden="true" tabindex="-1"></a><span class="ss">- Customer mentions &quot;my order&quot; without an order ID: ask for the order ID. Tell them it starts with &quot;BK-&quot; and is in their confirmation email.</span></span>
<span id="cb4-126"><a href="#cb4-126" aria-hidden="true" tabindex="-1"></a><span class="ss">- Customer gives an order ID but no email, and wants a return: ask for the email on the order.</span></span>
<span id="cb4-127"><a href="#cb4-127" aria-hidden="true" tabindex="-1"></a><span class="ss">- A customer has multiple orders and was ambiguous: ask which order they mean, listing them by ID and status only.</span></span>
<span id="cb4-128"><a href="#cb4-128" aria-hidden="true" tabindex="-1"></a><span class="ss">- Customer wants to initiate a return: after eligibility is confirmed, summarize what will happen (which items, refund method, timeline) and ask for explicit confirmation before calling initiate_return.</span></span>
<span id="cb4-129"><a href="#cb4-129" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/clarifying_rules&gt;</span></span>
<span id="cb4-130"><a href="#cb4-130" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-131"><a href="#cb4-131" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;tone&gt;</span></span>
<span id="cb4-132"><a href="#cb4-132" aria-hidden="true" tabindex="-1"></a><span class="ss">- Friendly and warm, but not chatty. One or two sentences per turn is usually right.</span></span>
<span id="cb4-133"><a href="#cb4-133" aria-hidden="true" tabindex="-1"></a><span class="ss">- Use the customer&#39;s first name once you know it, but not in every message.</span></span>
<span id="cb4-134"><a href="#cb4-134" aria-hidden="true" tabindex="-1"></a><span class="ss">- Plain text only. No markdown, no bullet points, no headers, no asterisks for emphasis. The chat UI does not render markdown.</span></span>
<span id="cb4-135"><a href="#cb4-135" aria-hidden="true" tabindex="-1"></a><span class="ss">- Never apologize more than once for the same issue.</span></span>
<span id="cb4-136"><a href="#cb4-136" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/tone&gt;</span></span>
<span id="cb4-137"><a href="#cb4-137" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-138"><a href="#cb4-138" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;examples&gt;</span></span>
<span id="cb4-139"><a href="#cb4-139" aria-hidden="true" tabindex="-1"></a><span class="ss">Example 1 — missing order ID:</span></span>
<span id="cb4-140"><a href="#cb4-140" aria-hidden="true" tabindex="-1"></a><span class="ss">User: &quot;Where&#39;s my order?&quot;</span></span>
<span id="cb4-141"><a href="#cb4-141" aria-hidden="true" tabindex="-1"></a><span class="ss">Assistant: &quot;Happy to check on that for you. Could you share your order ID? It starts with &#39;BK-&#39; and you&#39;ll find it in your order confirmation email.&quot;</span></span>
<span id="cb4-142"><a href="#cb4-142" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-143"><a href="#cb4-143" aria-hidden="true" tabindex="-1"></a><span class="ss">Example 2 — policy question (supported):</span></span>
<span id="cb4-144"><a href="#cb4-144" aria-hidden="true" tabindex="-1"></a><span class="ss">User: &quot;How do I reset my password?&quot;</span></span>
<span id="cb4-145"><a href="#cb4-145" aria-hidden="true" tabindex="-1"></a><span class="ss">Assistant (after lookup_policy returns the password_reset entry): quote the returned instructions verbatim without adding steps the tool did not mention.</span></span>
<span id="cb4-146"><a href="#cb4-146" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-147"><a href="#cb4-147" aria-hidden="true" tabindex="-1"></a><span class="ss">Example 3 — out of scope:</span></span>
<span id="cb4-148"><a href="#cb4-148" aria-hidden="true" tabindex="-1"></a><span class="ss">User: &quot;Can you recommend a good mystery novel?&quot;</span></span>
<span id="cb4-149"><a href="#cb4-149" aria-hidden="true" tabindex="-1"></a><span class="ss">Assistant: &quot;I can help with order status, returns, and our standard policies, but I&#39;m not able to help with book recommendations. Is there an order or a policy question I can help you with instead?&quot;</span></span>
<span id="cb4-150"><a href="#cb4-150" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-151"><a href="#cb4-151" aria-hidden="true" tabindex="-1"></a><span class="ss">Example 4 — ambiguous order:</span></span>
<span id="cb4-152"><a href="#cb4-152" aria-hidden="true" tabindex="-1"></a><span class="ss">User: &quot;I want to return my order. My email is sarah@example.com.&quot;</span></span>
<span id="cb4-153"><a href="#cb4-153" aria-hidden="true" tabindex="-1"></a><span class="ss">Assistant (after lookup_order returns two orders): &quot;I see two orders on your account: BK-10042 (delivered) and BK-10103 (still processing). Which one would you like to return?&quot;</span></span>
<span id="cb4-154"><a href="#cb4-154" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/examples&gt;</span></span>
<span id="cb4-155"><a href="#cb4-155" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-156"><a href="#cb4-156" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;reminders&gt;</span></span>
<span id="cb4-157"><a href="#cb4-157" aria-hidden="true" tabindex="-1"></a><span class="ss">Before you respond, confirm:</span></span>
<span id="cb4-158"><a href="#cb4-158" aria-hidden="true" tabindex="-1"></a><span class="ss">- Every factual claim traces to a tool result from THIS conversation, or to &lt;return_policy&gt;.</span></span>
<span id="cb4-159"><a href="#cb4-159" aria-hidden="true" tabindex="-1"></a><span class="ss">- If this response would call initiate_return, you have already seen a successful check_return_eligibility for the same order in this conversation.</span></span>
<span id="cb4-160"><a href="#cb4-160" aria-hidden="true" tabindex="-1"></a><span class="ss">- If the request is off-topic, you are using the refusal template from &lt;scope&gt; verbatim.</span></span>
<span id="cb4-161"><a href="#cb4-161" aria-hidden="true" tabindex="-1"></a><span class="ss">- No markdown. Plain text only.</span></span>
<span id="cb4-162"><a href="#cb4-162" aria-hidden="true" tabindex="-1"></a><span class="ss">&lt;/reminders&gt;</span></span>
<span id="cb4-163"><a href="#cb4-163" aria-hidden="true" tabindex="-1"></a><span class="ss">&quot;&quot;&quot;</span></span>
<span id="cb4-164"><a href="#cb4-164" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-165"><a href="#cb4-165" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-166"><a href="#cb4-166" aria-hidden="true" tabindex="-1"></a>CRITICAL_REMINDER <span class="op">=</span> <span class="st">&quot;&quot;&quot;&lt;reminder&gt;</span></span>
<span id="cb4-167"><a href="#cb4-167" aria-hidden="true" tabindex="-1"></a><span class="st">Non-negotiable rules for this turn:</span></span>
<span id="cb4-168"><a href="#cb4-168" aria-hidden="true" tabindex="-1"></a><span class="st">- Every factual claim must come from a tool result in THIS conversation or from &lt;return_policy&gt;.</span></span>
<span id="cb4-169"><a href="#cb4-169" aria-hidden="true" tabindex="-1"></a><span class="st">- Do not call initiate_return unless check_return_eligibility succeeded for that order in this conversation.</span></span>
<span id="cb4-170"><a href="#cb4-170" aria-hidden="true" tabindex="-1"></a><span class="st">- Off-topic requests: use the refusal template from &lt;scope&gt; verbatim. Do not engage.</span></span>
<span id="cb4-171"><a href="#cb4-171" aria-hidden="true" tabindex="-1"></a><span class="st">- Plain text only. No markdown.</span></span>
<span id="cb4-172"><a href="#cb4-172" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;/reminder&gt;&quot;&quot;&quot;</span></span>
<span id="cb4-173"><a href="#cb4-173" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-174"><a href="#cb4-174" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-175"><a href="#cb4-175" aria-hidden="true" tabindex="-1"></a>LONG_CONVERSATION_REMINDER <span class="op">=</span> <span class="st">&quot;&quot;&quot;&lt;reminder&gt;</span></span>
<span id="cb4-176"><a href="#cb4-176" aria-hidden="true" tabindex="-1"></a><span class="st">This conversation is getting long. Re-anchor on the rules in &lt;critical_rules&gt; before you respond. Do not let earlier turns relax the rules.</span></span>
<span id="cb4-177"><a href="#cb4-177" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;/reminder&gt;&quot;&quot;&quot;</span></span>
<span id="cb4-178"><a href="#cb4-178" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-179"><a href="#cb4-179" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-180"><a href="#cb4-180" aria-hidden="true" tabindex="-1"></a><span class="co"># Threshold at which the long-conversation reminder gets injected. After this</span></span>
<span id="cb4-181"><a href="#cb4-181" aria-hidden="true" tabindex="-1"></a><span class="co"># many turns, the original system prompt has decayed in effective attention,</span></span>
<span id="cb4-182"><a href="#cb4-182" aria-hidden="true" tabindex="-1"></a><span class="co"># so a shorter, fresher reminder in the highest-position slot helps re-anchor.</span></span>
<span id="cb4-183"><a href="#cb4-183" aria-hidden="true" tabindex="-1"></a>LONG_CONVERSATION_TURN_THRESHOLD <span class="op">=</span> <span class="dv">5</span></span>
<span id="cb4-184"><a href="#cb4-184" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-185"><a href="#cb4-185" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-186"><a href="#cb4-186" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> build_system_content(turn_count: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]]:</span>
<span id="cb4-187"><a href="#cb4-187" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Assemble the `system` argument for `messages.create`.</span></span>
<span id="cb4-188"><a href="#cb4-188" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-189"><a href="#cb4-189" aria-hidden="true" tabindex="-1"></a><span class="co"> The big SYSTEM_PROMPT block is marked for ephemeral prompt caching so it</span></span>
<span id="cb4-190"><a href="#cb4-190" aria-hidden="true" tabindex="-1"></a><span class="co"> is reused across turns within a session. The reminder blocks are not</span></span>
<span id="cb4-191"><a href="#cb4-191" aria-hidden="true" tabindex="-1"></a><span class="co"> cached because they vary based on turn count and we want them in the</span></span>
<span id="cb4-192"><a href="#cb4-192" aria-hidden="true" tabindex="-1"></a><span class="co"> highest-attention position right before the latest user turn.</span></span>
<span id="cb4-193"><a href="#cb4-193" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-194"><a href="#cb4-194" aria-hidden="true" tabindex="-1"></a> blocks: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> [</span>
<span id="cb4-195"><a href="#cb4-195" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb4-196"><a href="#cb4-196" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;text&quot;</span>,</span>
<span id="cb4-197"><a href="#cb4-197" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;text&quot;</span>: SYSTEM_PROMPT,</span>
<span id="cb4-198"><a href="#cb4-198" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;cache_control&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;ephemeral&quot;</span>},</span>
<span id="cb4-199"><a href="#cb4-199" aria-hidden="true" tabindex="-1"></a> },</span>
<span id="cb4-200"><a href="#cb4-200" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;text&quot;</span>, <span class="st">&quot;text&quot;</span>: CRITICAL_REMINDER},</span>
<span id="cb4-201"><a href="#cb4-201" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-202"><a href="#cb4-202" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> turn_count <span class="op">&gt;=</span> LONG_CONVERSATION_TURN_THRESHOLD:</span>
<span id="cb4-203"><a href="#cb4-203" aria-hidden="true" tabindex="-1"></a> blocks.append({<span class="st">&quot;type&quot;</span>: <span class="st">&quot;text&quot;</span>, <span class="st">&quot;text&quot;</span>: LONG_CONVERSATION_REMINDER})</span>
<span id="cb4-204"><a href="#cb4-204" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> blocks</span>
<span id="cb4-205"><a href="#cb4-205" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-206"><a href="#cb4-206" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-207"><a href="#cb4-207" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-208"><a href="#cb4-208" aria-hidden="true" tabindex="-1"></a><span class="co"># Layer 4 -- output validation</span></span>
<span id="cb4-209"><a href="#cb4-209" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-210"><a href="#cb4-210" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-211"><a href="#cb4-211" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-212"><a href="#cb4-212" aria-hidden="true" tabindex="-1"></a>ORDER_ID_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">BK-</span><span class="dv">\d</span><span class="op">{4,6}</span><span class="dv">\b</span><span class="vs">&quot;</span>)</span>
<span id="cb4-213"><a href="#cb4-213" aria-hidden="true" tabindex="-1"></a>DATE_ISO_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b\d</span><span class="op">{4}</span><span class="vs">-</span><span class="dv">\d</span><span class="op">{2}</span><span class="vs">-</span><span class="dv">\d</span><span class="op">{2}</span><span class="dv">\b</span><span class="vs">&quot;</span>)</span>
<span id="cb4-214"><a href="#cb4-214" aria-hidden="true" tabindex="-1"></a>MARKDOWN_RE <span class="op">=</span> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="kw">(</span><span class="ch">\*\*</span><span class="cf">|</span><span class="vs">__</span><span class="cf">|</span><span class="dv">^</span><span class="vs">#</span><span class="op">{1,6}</span><span class="dv">\s</span><span class="cf">|</span><span class="dv">^\s</span><span class="op">*</span><span class="pp">[-*+]</span><span class="dv">\s</span><span class="kw">)</span><span class="vs">&quot;</span>, re.MULTILINE)</span>
<span id="cb4-215"><a href="#cb4-215" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-216"><a href="#cb4-216" aria-hidden="true" tabindex="-1"></a><span class="co"># Anchored word-boundary patterns for off-topic engagement. These used to be</span></span>
<span id="cb4-217"><a href="#cb4-217" aria-hidden="true" tabindex="-1"></a><span class="co"># substring matches on a small keyword set, which false-positived on plenty</span></span>
<span id="cb4-218"><a href="#cb4-218" aria-hidden="true" tabindex="-1"></a><span class="co"># of legitimate support replies (&quot;I&#39;d recommend contacting...&quot;). The word</span></span>
<span id="cb4-219"><a href="#cb4-219" aria-hidden="true" tabindex="-1"></a><span class="co"># boundaries make matches explicit -- only the intended phrases trip them.</span></span>
<span id="cb4-220"><a href="#cb4-220" aria-hidden="true" tabindex="-1"></a>OUT_OF_SCOPE_PATTERNS: <span class="bu">tuple</span>[re.Pattern[<span class="bu">str</span>], ...] <span class="op">=</span> (</span>
<span id="cb4-221"><a href="#cb4-221" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">i</span><span class="dv">\s</span><span class="op">+</span><span class="vs">recommend</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-222"><a href="#cb4-222" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">i</span><span class="dv">\s</span><span class="op">+</span><span class="vs">suggest</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-223"><a href="#cb4-223" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">you</span><span class="dv">\s</span><span class="op">+</span><span class="vs">should</span><span class="dv">\s</span><span class="op">+</span><span class="vs">read</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-224"><a href="#cb4-224" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">great</span><span class="dv">\s</span><span class="op">+</span><span class="vs">book</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-225"><a href="#cb4-225" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">favorite</span><span class="dv">\s</span><span class="op">+</span><span class="vs">book</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-226"><a href="#cb4-226" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">what</span><span class="dv">\s</span><span class="op">+</span><span class="vs">should</span><span class="dv">\s</span><span class="op">+</span><span class="vs">i</span><span class="dv">\s</span><span class="op">+</span><span class="vs">read</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-227"><a href="#cb4-227" aria-hidden="true" tabindex="-1"></a> re.<span class="bu">compile</span>(<span class="vs">r&quot;</span><span class="dv">\b</span><span class="vs">review</span><span class="dv">\s</span><span class="op">+</span><span class="vs">of</span><span class="dv">\b</span><span class="vs">&quot;</span>),</span>
<span id="cb4-228"><a href="#cb4-228" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb4-229"><a href="#cb4-229" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-230"><a href="#cb4-230" aria-hidden="true" tabindex="-1"></a>REFUSAL_PHRASE <span class="op">=</span> <span class="st">&quot;i&#39;m not able to help with&quot;</span></span>
<span id="cb4-231"><a href="#cb4-231" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-232"><a href="#cb4-232" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-233"><a href="#cb4-233" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span>(frozen<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb4-234"><a href="#cb4-234" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ValidationResult:</span>
<span id="cb4-235"><a href="#cb4-235" aria-hidden="true" tabindex="-1"></a> ok: <span class="bu">bool</span></span>
<span id="cb4-236"><a href="#cb4-236" aria-hidden="true" tabindex="-1"></a> violations: <span class="bu">tuple</span>[<span class="bu">str</span>, ...] <span class="op">=</span> ()</span>
<span id="cb4-237"><a href="#cb4-237" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-238"><a href="#cb4-238" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-239"><a href="#cb4-239" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _collect_grounded_values(</span>
<span id="cb4-240"><a href="#cb4-240" aria-hidden="true" tabindex="-1"></a> tool_results: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]], pattern: re.Pattern[<span class="bu">str</span>]</span>
<span id="cb4-241"><a href="#cb4-241" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">set</span>[<span class="bu">str</span>]:</span>
<span id="cb4-242"><a href="#cb4-242" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Pull every substring matching `pattern` out of the tool result JSON.&quot;&quot;&quot;</span></span>
<span id="cb4-243"><a href="#cb4-243" aria-hidden="true" tabindex="-1"></a> grounded: <span class="bu">set</span>[<span class="bu">str</span>] <span class="op">=</span> <span class="bu">set</span>()</span>
<span id="cb4-244"><a href="#cb4-244" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> entry <span class="kw">in</span> tool_results:</span>
<span id="cb4-245"><a href="#cb4-245" aria-hidden="true" tabindex="-1"></a> text <span class="op">=</span> json.dumps(entry.get(<span class="st">&quot;result&quot;</span>, {}))</span>
<span id="cb4-246"><a href="#cb4-246" aria-hidden="true" tabindex="-1"></a> grounded.update(pattern.findall(text))</span>
<span id="cb4-247"><a href="#cb4-247" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> grounded</span>
<span id="cb4-248"><a href="#cb4-248" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-249"><a href="#cb4-249" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-250"><a href="#cb4-250" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> validate_reply(reply: <span class="bu">str</span>, tool_results_this_turn: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]]) <span class="op">-&gt;</span> ValidationResult:</span>
<span id="cb4-251"><a href="#cb4-251" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Run deterministic checks on the final assistant reply.</span></span>
<span id="cb4-252"><a href="#cb4-252" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-253"><a href="#cb4-253" aria-hidden="true" tabindex="-1"></a><span class="co"> Heuristic, not exhaustive. Catches the cheap wins -- fabricated order IDs,</span></span>
<span id="cb4-254"><a href="#cb4-254" aria-hidden="true" tabindex="-1"></a><span class="co"> made-up dates, markdown leakage, and obvious off-topic engagement. For</span></span>
<span id="cb4-255"><a href="#cb4-255" aria-hidden="true" tabindex="-1"></a><span class="co"> anything subtler we rely on layers 1-3.</span></span>
<span id="cb4-256"><a href="#cb4-256" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-257"><a href="#cb4-257" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(reply, <span class="bu">str</span>):</span>
<span id="cb4-258"><a href="#cb4-258" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">TypeError</span>(<span class="st">&quot;reply must be a string&quot;</span>)</span>
<span id="cb4-259"><a href="#cb4-259" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(tool_results_this_turn, <span class="bu">list</span>):</span>
<span id="cb4-260"><a href="#cb4-260" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">TypeError</span>(<span class="st">&quot;tool_results_this_turn must be a list&quot;</span>)</span>
<span id="cb4-261"><a href="#cb4-261" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-262"><a href="#cb4-262" aria-hidden="true" tabindex="-1"></a> violations: <span class="bu">list</span>[<span class="bu">str</span>] <span class="op">=</span> []</span>
<span id="cb4-263"><a href="#cb4-263" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-264"><a href="#cb4-264" aria-hidden="true" tabindex="-1"></a> grounded_ids <span class="op">=</span> _collect_grounded_values(tool_results_this_turn, ORDER_ID_RE)</span>
<span id="cb4-265"><a href="#cb4-265" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> match <span class="kw">in</span> ORDER_ID_RE.findall(reply):</span>
<span id="cb4-266"><a href="#cb4-266" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> match <span class="kw">not</span> <span class="kw">in</span> grounded_ids:</span>
<span id="cb4-267"><a href="#cb4-267" aria-hidden="true" tabindex="-1"></a> violations.append(<span class="ss">f&quot;ungrounded_order_id:</span><span class="sc">{</span>match<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-268"><a href="#cb4-268" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-269"><a href="#cb4-269" aria-hidden="true" tabindex="-1"></a> grounded_dates <span class="op">=</span> _collect_grounded_values(tool_results_this_turn, DATE_ISO_RE)</span>
<span id="cb4-270"><a href="#cb4-270" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> match <span class="kw">in</span> DATE_ISO_RE.findall(reply):</span>
<span id="cb4-271"><a href="#cb4-271" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> match <span class="kw">not</span> <span class="kw">in</span> grounded_dates:</span>
<span id="cb4-272"><a href="#cb4-272" aria-hidden="true" tabindex="-1"></a> violations.append(<span class="ss">f&quot;ungrounded_date:</span><span class="sc">{</span>match<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb4-273"><a href="#cb4-273" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-274"><a href="#cb4-274" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> MARKDOWN_RE.search(reply):</span>
<span id="cb4-275"><a href="#cb4-275" aria-hidden="true" tabindex="-1"></a> violations.append(<span class="st">&quot;markdown_leaked&quot;</span>)</span>
<span id="cb4-276"><a href="#cb4-276" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-277"><a href="#cb4-277" aria-hidden="true" tabindex="-1"></a> lowered <span class="op">=</span> reply.lower()</span>
<span id="cb4-278"><a href="#cb4-278" aria-hidden="true" tabindex="-1"></a> engaged_off_topic <span class="op">=</span> <span class="bu">any</span>(pattern.search(lowered) <span class="cf">for</span> pattern <span class="kw">in</span> OUT_OF_SCOPE_PATTERNS)</span>
<span id="cb4-279"><a href="#cb4-279" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> engaged_off_topic <span class="kw">and</span> REFUSAL_PHRASE <span class="kw">not</span> <span class="kw">in</span> lowered:</span>
<span id="cb4-280"><a href="#cb4-280" aria-hidden="true" tabindex="-1"></a> violations.append(<span class="st">&quot;off_topic_engagement&quot;</span>)</span>
<span id="cb4-281"><a href="#cb4-281" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-282"><a href="#cb4-282" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ValidationResult(ok<span class="op">=</span><span class="kw">not</span> violations, violations<span class="op">=</span><span class="bu">tuple</span>(violations))</span>
<span id="cb4-283"><a href="#cb4-283" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-284"><a href="#cb4-284" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-285"><a href="#cb4-285" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-286"><a href="#cb4-286" aria-hidden="true" tabindex="-1"></a><span class="co"># Session store</span></span>
<span id="cb4-287"><a href="#cb4-287" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-288"><a href="#cb4-288" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-289"><a href="#cb4-289" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-290"><a href="#cb4-290" aria-hidden="true" tabindex="-1"></a>SAFE_FALLBACK <span class="op">=</span> (</span>
<span id="cb4-291"><a href="#cb4-291" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;I hit a problem generating a response. Could you rephrase your question, &quot;</span></span>
<span id="cb4-292"><a href="#cb4-292" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;or share an order ID so I can try again?&quot;</span></span>
<span id="cb4-293"><a href="#cb4-293" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb4-294"><a href="#cb4-294" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-295"><a href="#cb4-295" aria-hidden="true" tabindex="-1"></a>CONVERSATION_TOO_LONG_MESSAGE <span class="op">=</span> (</span>
<span id="cb4-296"><a href="#cb4-296" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;This conversation has gone long enough that I need to reset before I keep &quot;</span></span>
<span id="cb4-297"><a href="#cb4-297" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;making mistakes. Please start a new chat and I&#39;ll be happy to help from there.&quot;</span></span>
<span id="cb4-298"><a href="#cb4-298" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb4-299"><a href="#cb4-299" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-300"><a href="#cb4-300" aria-hidden="true" tabindex="-1"></a>TOOL_LOOP_EXCEEDED_MESSAGE <span class="op">=</span> (</span>
<span id="cb4-301"><a href="#cb4-301" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;I got stuck working on that request. Could you try rephrasing it, or share &quot;</span></span>
<span id="cb4-302"><a href="#cb4-302" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;an order ID so I can try a fresh approach?&quot;</span></span>
<span id="cb4-303"><a href="#cb4-303" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb4-304"><a href="#cb4-304" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-305"><a href="#cb4-305" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-306"><a href="#cb4-306" aria-hidden="true" tabindex="-1"></a><span class="at">@dataclass</span></span>
<span id="cb4-307"><a href="#cb4-307" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Session:</span>
<span id="cb4-308"><a href="#cb4-308" aria-hidden="true" tabindex="-1"></a> history: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> field(default_factory<span class="op">=</span><span class="bu">list</span>)</span>
<span id="cb4-309"><a href="#cb4-309" aria-hidden="true" tabindex="-1"></a> guard_state: SessionGuardState <span class="op">=</span> field(default_factory<span class="op">=</span>SessionGuardState)</span>
<span id="cb4-310"><a href="#cb4-310" aria-hidden="true" tabindex="-1"></a> turn_count: <span class="bu">int</span> <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb4-311"><a href="#cb4-311" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-312"><a href="#cb4-312" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-313"><a href="#cb4-313" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> SessionStore:</span>
<span id="cb4-314"><a href="#cb4-314" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Bounded in-memory session store with LRU eviction and idle TTL.</span></span>
<span id="cb4-315"><a href="#cb4-315" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-316"><a href="#cb4-316" aria-hidden="true" tabindex="-1"></a><span class="co"> Also owns the per-session locks used to serialize turns for the same</span></span>
<span id="cb4-317"><a href="#cb4-317" aria-hidden="true" tabindex="-1"></a><span class="co"> session_id when FastAPI runs the sync handler in its threadpool. The</span></span>
<span id="cb4-318"><a href="#cb4-318" aria-hidden="true" tabindex="-1"></a><span class="co"> creation of a per-session lock is itself guarded by a class-level lock to</span></span>
<span id="cb4-319"><a href="#cb4-319" aria-hidden="true" tabindex="-1"></a><span class="co"> avoid a double-create race.</span></span>
<span id="cb4-320"><a href="#cb4-320" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-321"><a href="#cb4-321" aria-hidden="true" tabindex="-1"></a><span class="co"> Designed for a single-process demo deployment. For multi-worker, swap</span></span>
<span id="cb4-322"><a href="#cb4-322" aria-hidden="true" tabindex="-1"></a><span class="co"> this class out for a Redis-backed equivalent.</span></span>
<span id="cb4-323"><a href="#cb4-323" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-324"><a href="#cb4-324" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-325"><a href="#cb4-325" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, <span class="op">*</span>, max_entries: <span class="bu">int</span>, idle_ttl_seconds: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-326"><a href="#cb4-326" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> max_entries <span class="op">&lt;=</span> <span class="dv">0</span>:</span>
<span id="cb4-327"><a href="#cb4-327" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;max_entries must be positive&quot;</span>)</span>
<span id="cb4-328"><a href="#cb4-328" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> idle_ttl_seconds <span class="op">&lt;=</span> <span class="dv">0</span>:</span>
<span id="cb4-329"><a href="#cb4-329" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;idle_ttl_seconds must be positive&quot;</span>)</span>
<span id="cb4-330"><a href="#cb4-330" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._max_entries <span class="op">=</span> max_entries</span>
<span id="cb4-331"><a href="#cb4-331" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._idle_ttl_seconds <span class="op">=</span> idle_ttl_seconds</span>
<span id="cb4-332"><a href="#cb4-332" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._entries: OrderedDict[<span class="bu">str</span>, <span class="bu">tuple</span>[Session, <span class="bu">float</span>]] <span class="op">=</span> OrderedDict()</span>
<span id="cb4-333"><a href="#cb4-333" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._store_lock <span class="op">=</span> threading.Lock()</span>
<span id="cb4-334"><a href="#cb4-334" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._session_locks: <span class="bu">dict</span>[<span class="bu">str</span>, threading.Lock] <span class="op">=</span> {}</span>
<span id="cb4-335"><a href="#cb4-335" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._locks_lock <span class="op">=</span> threading.Lock()</span>
<span id="cb4-336"><a href="#cb4-336" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-337"><a href="#cb4-337" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> get_or_create(<span class="va">self</span>, session_id: <span class="bu">str</span>) <span class="op">-&gt;</span> Session:</span>
<span id="cb4-338"><a href="#cb4-338" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(session_id, <span class="bu">str</span>) <span class="kw">or</span> <span class="kw">not</span> session_id:</span>
<span id="cb4-339"><a href="#cb4-339" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;session_id is required&quot;</span>)</span>
<span id="cb4-340"><a href="#cb4-340" aria-hidden="true" tabindex="-1"></a> now <span class="op">=</span> time.monotonic()</span>
<span id="cb4-341"><a href="#cb4-341" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._store_lock:</span>
<span id="cb4-342"><a href="#cb4-342" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._evict_expired_locked(now)</span>
<span id="cb4-343"><a href="#cb4-343" aria-hidden="true" tabindex="-1"></a> entry <span class="op">=</span> <span class="va">self</span>._entries.get(session_id)</span>
<span id="cb4-344"><a href="#cb4-344" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> entry <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb4-345"><a href="#cb4-345" aria-hidden="true" tabindex="-1"></a> session <span class="op">=</span> Session()</span>
<span id="cb4-346"><a href="#cb4-346" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._entries[session_id] <span class="op">=</span> (session, now)</span>
<span id="cb4-347"><a href="#cb4-347" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._enforce_size_cap_locked()</span>
<span id="cb4-348"><a href="#cb4-348" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session</span>
<span id="cb4-349"><a href="#cb4-349" aria-hidden="true" tabindex="-1"></a> session, _ <span class="op">=</span> entry</span>
<span id="cb4-350"><a href="#cb4-350" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._entries[session_id] <span class="op">=</span> (session, now)</span>
<span id="cb4-351"><a href="#cb4-351" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._entries.move_to_end(session_id)</span>
<span id="cb4-352"><a href="#cb4-352" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session</span>
<span id="cb4-353"><a href="#cb4-353" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-354"><a href="#cb4-354" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> lock_for(<span class="va">self</span>, session_id: <span class="bu">str</span>) <span class="op">-&gt;</span> threading.Lock:</span>
<span id="cb4-355"><a href="#cb4-355" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Return the lock guarding turns for `session_id`, creating if new.&quot;&quot;&quot;</span></span>
<span id="cb4-356"><a href="#cb4-356" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(session_id, <span class="bu">str</span>) <span class="kw">or</span> <span class="kw">not</span> session_id:</span>
<span id="cb4-357"><a href="#cb4-357" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;session_id is required&quot;</span>)</span>
<span id="cb4-358"><a href="#cb4-358" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._locks_lock:</span>
<span id="cb4-359"><a href="#cb4-359" aria-hidden="true" tabindex="-1"></a> lock <span class="op">=</span> <span class="va">self</span>._session_locks.get(session_id)</span>
<span id="cb4-360"><a href="#cb4-360" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> lock <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb4-361"><a href="#cb4-361" aria-hidden="true" tabindex="-1"></a> lock <span class="op">=</span> threading.Lock()</span>
<span id="cb4-362"><a href="#cb4-362" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._session_locks[session_id] <span class="op">=</span> lock</span>
<span id="cb4-363"><a href="#cb4-363" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> lock</span>
<span id="cb4-364"><a href="#cb4-364" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-365"><a href="#cb4-365" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> clear(<span class="va">self</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-366"><a href="#cb4-366" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Drop all sessions. Intended for tests and admin operations only.&quot;&quot;&quot;</span></span>
<span id="cb4-367"><a href="#cb4-367" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._store_lock:</span>
<span id="cb4-368"><a href="#cb4-368" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._entries.clear()</span>
<span id="cb4-369"><a href="#cb4-369" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._locks_lock:</span>
<span id="cb4-370"><a href="#cb4-370" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._session_locks.clear()</span>
<span id="cb4-371"><a href="#cb4-371" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-372"><a href="#cb4-372" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__len__</span>(<span class="va">self</span>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
<span id="cb4-373"><a href="#cb4-373" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._store_lock:</span>
<span id="cb4-374"><a href="#cb4-374" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">len</span>(<span class="va">self</span>._entries)</span>
<span id="cb4-375"><a href="#cb4-375" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-376"><a href="#cb4-376" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__contains__</span>(<span class="va">self</span>, session_id: <span class="bu">object</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb4-377"><a href="#cb4-377" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(session_id, <span class="bu">str</span>):</span>
<span id="cb4-378"><a href="#cb4-378" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb4-379"><a href="#cb4-379" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._store_lock:</span>
<span id="cb4-380"><a href="#cb4-380" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session_id <span class="kw">in</span> <span class="va">self</span>._entries</span>
<span id="cb4-381"><a href="#cb4-381" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-382"><a href="#cb4-382" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__getitem__</span>(<span class="va">self</span>, session_id: <span class="bu">str</span>) <span class="op">-&gt;</span> Session:</span>
<span id="cb4-383"><a href="#cb4-383" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._store_lock:</span>
<span id="cb4-384"><a href="#cb4-384" aria-hidden="true" tabindex="-1"></a> entry <span class="op">=</span> <span class="va">self</span>._entries.get(session_id)</span>
<span id="cb4-385"><a href="#cb4-385" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> entry <span class="kw">is</span> <span class="va">None</span>:</span>
<span id="cb4-386"><a href="#cb4-386" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">KeyError</span>(session_id)</span>
<span id="cb4-387"><a href="#cb4-387" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> entry[<span class="dv">0</span>]</span>
<span id="cb4-388"><a href="#cb4-388" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-389"><a href="#cb4-389" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> _evict_expired_locked(<span class="va">self</span>, now: <span class="bu">float</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-390"><a href="#cb4-390" aria-hidden="true" tabindex="-1"></a> expired <span class="op">=</span> [</span>
<span id="cb4-391"><a href="#cb4-391" aria-hidden="true" tabindex="-1"></a> sid <span class="cf">for</span> sid, (_, last) <span class="kw">in</span> <span class="va">self</span>._entries.items() <span class="cf">if</span> now <span class="op">-</span> last <span class="op">&gt;</span> <span class="va">self</span>._idle_ttl_seconds</span>
<span id="cb4-392"><a href="#cb4-392" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-393"><a href="#cb4-393" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> sid <span class="kw">in</span> expired:</span>
<span id="cb4-394"><a href="#cb4-394" aria-hidden="true" tabindex="-1"></a> <span class="kw">del</span> <span class="va">self</span>._entries[sid]</span>
<span id="cb4-395"><a href="#cb4-395" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._locks_lock:</span>
<span id="cb4-396"><a href="#cb4-396" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._session_locks.pop(sid, <span class="va">None</span>)</span>
<span id="cb4-397"><a href="#cb4-397" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-398"><a href="#cb4-398" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> _enforce_size_cap_locked(<span class="va">self</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb4-399"><a href="#cb4-399" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> <span class="bu">len</span>(<span class="va">self</span>._entries) <span class="op">&gt;</span> <span class="va">self</span>._max_entries:</span>
<span id="cb4-400"><a href="#cb4-400" aria-hidden="true" tabindex="-1"></a> sid, _ <span class="op">=</span> <span class="va">self</span>._entries.popitem(last<span class="op">=</span><span class="va">False</span>)</span>
<span id="cb4-401"><a href="#cb4-401" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._locks_lock:</span>
<span id="cb4-402"><a href="#cb4-402" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._session_locks.pop(sid, <span class="va">None</span>)</span>
<span id="cb4-403"><a href="#cb4-403" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-404"><a href="#cb4-404" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-405"><a href="#cb4-405" aria-hidden="true" tabindex="-1"></a>SESSIONS <span class="op">=</span> SessionStore(</span>
<span id="cb4-406"><a href="#cb4-406" aria-hidden="true" tabindex="-1"></a> max_entries<span class="op">=</span>settings.session_store_max_entries,</span>
<span id="cb4-407"><a href="#cb4-407" aria-hidden="true" tabindex="-1"></a> idle_ttl_seconds<span class="op">=</span>settings.session_idle_ttl_seconds,</span>
<span id="cb4-408"><a href="#cb4-408" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb4-409"><a href="#cb4-409" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-410"><a href="#cb4-410" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-411"><a href="#cb4-411" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-412"><a href="#cb4-412" aria-hidden="true" tabindex="-1"></a><span class="co"># Anthropic client</span></span>
<span id="cb4-413"><a href="#cb4-413" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-414"><a href="#cb4-414" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-415"><a href="#cb4-415" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-416"><a href="#cb4-416" aria-hidden="true" tabindex="-1"></a><span class="at">@functools.lru_cache</span>(maxsize<span class="op">=</span><span class="dv">1</span>)</span>
<span id="cb4-417"><a href="#cb4-417" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _get_client() <span class="op">-&gt;</span> Anthropic:</span>
<span id="cb4-418"><a href="#cb4-418" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Return the shared Anthropic client.</span></span>
<span id="cb4-419"><a href="#cb4-419" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-420"><a href="#cb4-420" aria-hidden="true" tabindex="-1"></a><span class="co"> Cached so every turn reuses the same HTTP connection pool. Tests swap</span></span>
<span id="cb4-421"><a href="#cb4-421" aria-hidden="true" tabindex="-1"></a><span class="co"> this out with `monkeypatch.setattr(agent, &quot;_get_client&quot;, ...)`.</span></span>
<span id="cb4-422"><a href="#cb4-422" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-423"><a href="#cb4-423" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> Anthropic(</span>
<span id="cb4-424"><a href="#cb4-424" aria-hidden="true" tabindex="-1"></a> api_key<span class="op">=</span>settings.anthropic_api_key.get_secret_value(),</span>
<span id="cb4-425"><a href="#cb4-425" aria-hidden="true" tabindex="-1"></a> timeout<span class="op">=</span>settings.anthropic_timeout_seconds,</span>
<span id="cb4-426"><a href="#cb4-426" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-427"><a href="#cb4-427" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-428"><a href="#cb4-428" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-429"><a href="#cb4-429" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-430"><a href="#cb4-430" aria-hidden="true" tabindex="-1"></a><span class="co"># Content serialization helpers</span></span>
<span id="cb4-431"><a href="#cb4-431" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-432"><a href="#cb4-432" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-433"><a href="#cb4-433" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-434"><a href="#cb4-434" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _extract_text(content_blocks: <span class="bu">list</span>[Any]) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb4-435"><a href="#cb4-435" aria-hidden="true" tabindex="-1"></a> parts: <span class="bu">list</span>[<span class="bu">str</span>] <span class="op">=</span> []</span>
<span id="cb4-436"><a href="#cb4-436" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> block <span class="kw">in</span> content_blocks:</span>
<span id="cb4-437"><a href="#cb4-437" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">getattr</span>(block, <span class="st">&quot;type&quot;</span>, <span class="va">None</span>) <span class="op">==</span> <span class="st">&quot;text&quot;</span>:</span>
<span id="cb4-438"><a href="#cb4-438" aria-hidden="true" tabindex="-1"></a> parts.append(<span class="bu">getattr</span>(block, <span class="st">&quot;text&quot;</span>, <span class="st">&quot;&quot;</span>))</span>
<span id="cb4-439"><a href="#cb4-439" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;&quot;</span>.join(parts).strip()</span>
<span id="cb4-440"><a href="#cb4-440" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-441"><a href="#cb4-441" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-442"><a href="#cb4-442" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _serialize_assistant_content(content_blocks: <span class="bu">list</span>[Any]) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]]:</span>
<span id="cb4-443"><a href="#cb4-443" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Convert SDK content blocks back into JSON-serializable dicts for history.&quot;&quot;&quot;</span></span>
<span id="cb4-444"><a href="#cb4-444" aria-hidden="true" tabindex="-1"></a> serialized: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> []</span>
<span id="cb4-445"><a href="#cb4-445" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> block <span class="kw">in</span> content_blocks:</span>
<span id="cb4-446"><a href="#cb4-446" aria-hidden="true" tabindex="-1"></a> block_type <span class="op">=</span> <span class="bu">getattr</span>(block, <span class="st">&quot;type&quot;</span>, <span class="va">None</span>)</span>
<span id="cb4-447"><a href="#cb4-447" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> block_type <span class="op">==</span> <span class="st">&quot;text&quot;</span>:</span>
<span id="cb4-448"><a href="#cb4-448" aria-hidden="true" tabindex="-1"></a> serialized.append({<span class="st">&quot;type&quot;</span>: <span class="st">&quot;text&quot;</span>, <span class="st">&quot;text&quot;</span>: <span class="bu">getattr</span>(block, <span class="st">&quot;text&quot;</span>, <span class="st">&quot;&quot;</span>)})</span>
<span id="cb4-449"><a href="#cb4-449" aria-hidden="true" tabindex="-1"></a> <span class="cf">elif</span> block_type <span class="op">==</span> <span class="st">&quot;tool_use&quot;</span>:</span>
<span id="cb4-450"><a href="#cb4-450" aria-hidden="true" tabindex="-1"></a> serialized.append(</span>
<span id="cb4-451"><a href="#cb4-451" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb4-452"><a href="#cb4-452" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;tool_use&quot;</span>,</span>
<span id="cb4-453"><a href="#cb4-453" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;id&quot;</span>: block.<span class="bu">id</span>,</span>
<span id="cb4-454"><a href="#cb4-454" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: block.name,</span>
<span id="cb4-455"><a href="#cb4-455" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;input&quot;</span>: <span class="bu">getattr</span>(block, <span class="st">&quot;input&quot;</span>, <span class="va">None</span>) <span class="kw">or</span> {},</span>
<span id="cb4-456"><a href="#cb4-456" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb4-457"><a href="#cb4-457" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-458"><a href="#cb4-458" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> serialized</span>
<span id="cb4-459"><a href="#cb4-459" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-460"><a href="#cb4-460" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-461"><a href="#cb4-461" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _with_last_message_cache_breakpoint(</span>
<span id="cb4-462"><a href="#cb4-462" aria-hidden="true" tabindex="-1"></a> messages: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]],</span>
<span id="cb4-463"><a href="#cb4-463" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]]:</span>
<span id="cb4-464"><a href="#cb4-464" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Return a shallow-copied message list with a cache breakpoint on the last block.</span></span>
<span id="cb4-465"><a href="#cb4-465" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-466"><a href="#cb4-466" aria-hidden="true" tabindex="-1"></a><span class="co"> Marking the last content block with `cache_control: ephemeral` extends the</span></span>
<span id="cb4-467"><a href="#cb4-467" aria-hidden="true" tabindex="-1"></a><span class="co"> prompt cache through the full conversation history so prior turns do not</span></span>
<span id="cb4-468"><a href="#cb4-468" aria-hidden="true" tabindex="-1"></a><span class="co"> need to be re-tokenized on every call. We avoid mutating the stored history</span></span>
<span id="cb4-469"><a href="#cb4-469" aria-hidden="true" tabindex="-1"></a><span class="co"> because the stored form should stay canonical.</span></span>
<span id="cb4-470"><a href="#cb4-470" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-471"><a href="#cb4-471" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> messages:</span>
<span id="cb4-472"><a href="#cb4-472" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> messages</span>
<span id="cb4-473"><a href="#cb4-473" aria-hidden="true" tabindex="-1"></a> head <span class="op">=</span> messages[:<span class="op">-</span><span class="dv">1</span>]</span>
<span id="cb4-474"><a href="#cb4-474" aria-hidden="true" tabindex="-1"></a> last <span class="op">=</span> <span class="bu">dict</span>(messages[<span class="op">-</span><span class="dv">1</span>])</span>
<span id="cb4-475"><a href="#cb4-475" aria-hidden="true" tabindex="-1"></a> content <span class="op">=</span> last.get(<span class="st">&quot;content&quot;</span>)</span>
<span id="cb4-476"><a href="#cb4-476" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">isinstance</span>(content, <span class="bu">str</span>):</span>
<span id="cb4-477"><a href="#cb4-477" aria-hidden="true" tabindex="-1"></a> last[<span class="st">&quot;content&quot;</span>] <span class="op">=</span> [</span>
<span id="cb4-478"><a href="#cb4-478" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;text&quot;</span>, <span class="st">&quot;text&quot;</span>: content, <span class="st">&quot;cache_control&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;ephemeral&quot;</span>}}</span>
<span id="cb4-479"><a href="#cb4-479" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-480"><a href="#cb4-480" aria-hidden="true" tabindex="-1"></a> <span class="cf">elif</span> <span class="bu">isinstance</span>(content, <span class="bu">list</span>) <span class="kw">and</span> content:</span>
<span id="cb4-481"><a href="#cb4-481" aria-hidden="true" tabindex="-1"></a> new_content <span class="op">=</span> [<span class="bu">dict</span>(block) <span class="cf">for</span> block <span class="kw">in</span> content]</span>
<span id="cb4-482"><a href="#cb4-482" aria-hidden="true" tabindex="-1"></a> new_content[<span class="op">-</span><span class="dv">1</span>] <span class="op">=</span> {<span class="op">**</span>new_content[<span class="op">-</span><span class="dv">1</span>], <span class="st">&quot;cache_control&quot;</span>: {<span class="st">&quot;type&quot;</span>: <span class="st">&quot;ephemeral&quot;</span>}}</span>
<span id="cb4-483"><a href="#cb4-483" aria-hidden="true" tabindex="-1"></a> last[<span class="st">&quot;content&quot;</span>] <span class="op">=</span> new_content</span>
<span id="cb4-484"><a href="#cb4-484" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> head <span class="op">+</span> [last]</span>
<span id="cb4-485"><a href="#cb4-485" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-486"><a href="#cb4-486" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-487"><a href="#cb4-487" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _hash_for_logging(value: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb4-488"><a href="#cb4-488" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Short stable hash for log correlation without leaking content.&quot;&quot;&quot;</span></span>
<span id="cb4-489"><a href="#cb4-489" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> hashlib.sha256(value.encode(<span class="st">&quot;utf-8&quot;</span>)).hexdigest()[:<span class="dv">16</span>]</span>
<span id="cb4-490"><a href="#cb4-490" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-491"><a href="#cb4-491" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-492"><a href="#cb4-492" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-493"><a href="#cb4-493" aria-hidden="true" tabindex="-1"></a><span class="co"># Agent loop</span></span>
<span id="cb4-494"><a href="#cb4-494" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb4-495"><a href="#cb4-495" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-496"><a href="#cb4-496" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-497"><a href="#cb4-497" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ToolLoopLimitExceeded(<span class="pp">RuntimeError</span>):</span>
<span id="cb4-498"><a href="#cb4-498" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Raised when the tool-use loop hits `settings.max_tool_use_iterations`.&quot;&quot;&quot;</span></span>
<span id="cb4-499"><a href="#cb4-499" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-500"><a href="#cb4-500" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-501"><a href="#cb4-501" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _run_tool_use_loop(</span>
<span id="cb4-502"><a href="#cb4-502" aria-hidden="true" tabindex="-1"></a> session: Session,</span>
<span id="cb4-503"><a href="#cb4-503" aria-hidden="true" tabindex="-1"></a> system_content: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]],</span>
<span id="cb4-504"><a href="#cb4-504" aria-hidden="true" tabindex="-1"></a> client: Anthropic,</span>
<span id="cb4-505"><a href="#cb4-505" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">tuple</span>[Any, <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]]]:</span>
<span id="cb4-506"><a href="#cb4-506" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Drive the model until it stops asking for tools.</span></span>
<span id="cb4-507"><a href="#cb4-507" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-508"><a href="#cb4-508" aria-hidden="true" tabindex="-1"></a><span class="co"> Returns the final Anthropic response object plus the cumulative list of</span></span>
<span id="cb4-509"><a href="#cb4-509" aria-hidden="true" tabindex="-1"></a><span class="co"> tool results produced during the turn (used by Layer 4 validation to</span></span>
<span id="cb4-510"><a href="#cb4-510" aria-hidden="true" tabindex="-1"></a><span class="co"> check for ungrounded claims in the final reply).</span></span>
<span id="cb4-511"><a href="#cb4-511" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-512"><a href="#cb4-512" aria-hidden="true" tabindex="-1"></a><span class="co"> Raises `ToolLoopLimitExceeded` if the model keeps asking for tools past</span></span>
<span id="cb4-513"><a href="#cb4-513" aria-hidden="true" tabindex="-1"></a><span class="co"> `settings.max_tool_use_iterations`. This is the structural guard that</span></span>
<span id="cb4-514"><a href="#cb4-514" aria-hidden="true" tabindex="-1"></a><span class="co"> prevents one bad request from burning API credit in an infinite loop.</span></span>
<span id="cb4-515"><a href="#cb4-515" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-516"><a href="#cb4-516" aria-hidden="true" tabindex="-1"></a> tool_results_this_turn: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> []</span>
<span id="cb4-517"><a href="#cb4-517" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-518"><a href="#cb4-518" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb4-519"><a href="#cb4-519" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span>settings.anthropic_model,</span>
<span id="cb4-520"><a href="#cb4-520" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span>settings.max_tokens,</span>
<span id="cb4-521"><a href="#cb4-521" aria-hidden="true" tabindex="-1"></a> system<span class="op">=</span>system_content,</span>
<span id="cb4-522"><a href="#cb4-522" aria-hidden="true" tabindex="-1"></a> tools<span class="op">=</span>TOOL_SCHEMAS,</span>
<span id="cb4-523"><a href="#cb4-523" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>_with_last_message_cache_breakpoint(session.history),</span>
<span id="cb4-524"><a href="#cb4-524" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-525"><a href="#cb4-525" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-526"><a href="#cb4-526" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> _ <span class="kw">in</span> <span class="bu">range</span>(settings.max_tool_use_iterations):</span>
<span id="cb4-527"><a href="#cb4-527" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">getattr</span>(response, <span class="st">&quot;stop_reason&quot;</span>, <span class="va">None</span>) <span class="op">!=</span> <span class="st">&quot;tool_use&quot;</span>:</span>
<span id="cb4-528"><a href="#cb4-528" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response, tool_results_this_turn</span>
<span id="cb4-529"><a href="#cb4-529" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-530"><a href="#cb4-530" aria-hidden="true" tabindex="-1"></a> assistant_blocks <span class="op">=</span> _serialize_assistant_content(response.content)</span>
<span id="cb4-531"><a href="#cb4-531" aria-hidden="true" tabindex="-1"></a> session.history.append({<span class="st">&quot;role&quot;</span>: <span class="st">&quot;assistant&quot;</span>, <span class="st">&quot;content&quot;</span>: assistant_blocks})</span>
<span id="cb4-532"><a href="#cb4-532" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-533"><a href="#cb4-533" aria-hidden="true" tabindex="-1"></a> tool_result_blocks: <span class="bu">list</span>[<span class="bu">dict</span>[<span class="bu">str</span>, Any]] <span class="op">=</span> []</span>
<span id="cb4-534"><a href="#cb4-534" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> block <span class="kw">in</span> response.content:</span>
<span id="cb4-535"><a href="#cb4-535" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">getattr</span>(block, <span class="st">&quot;type&quot;</span>, <span class="va">None</span>) <span class="op">!=</span> <span class="st">&quot;tool_use&quot;</span>:</span>
<span id="cb4-536"><a href="#cb4-536" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
<span id="cb4-537"><a href="#cb4-537" aria-hidden="true" tabindex="-1"></a> name <span class="op">=</span> block.name</span>
<span id="cb4-538"><a href="#cb4-538" aria-hidden="true" tabindex="-1"></a> args <span class="op">=</span> <span class="bu">getattr</span>(block, <span class="st">&quot;input&quot;</span>, <span class="va">None</span>) <span class="kw">or</span> {}</span>
<span id="cb4-539"><a href="#cb4-539" aria-hidden="true" tabindex="-1"></a> tool_id <span class="op">=</span> block.<span class="bu">id</span></span>
<span id="cb4-540"><a href="#cb4-540" aria-hidden="true" tabindex="-1"></a> result <span class="op">=</span> dispatch_tool(name, args, session.guard_state)</span>
<span id="cb4-541"><a href="#cb4-541" aria-hidden="true" tabindex="-1"></a> tool_results_this_turn.append({<span class="st">&quot;name&quot;</span>: name, <span class="st">&quot;result&quot;</span>: result})</span>
<span id="cb4-542"><a href="#cb4-542" aria-hidden="true" tabindex="-1"></a> tool_result_blocks.append(</span>
<span id="cb4-543"><a href="#cb4-543" aria-hidden="true" tabindex="-1"></a> {</span>
<span id="cb4-544"><a href="#cb4-544" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;type&quot;</span>: <span class="st">&quot;tool_result&quot;</span>,</span>
<span id="cb4-545"><a href="#cb4-545" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tool_use_id&quot;</span>: tool_id,</span>
<span id="cb4-546"><a href="#cb4-546" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;content&quot;</span>: json.dumps(result, ensure_ascii<span class="op">=</span><span class="va">False</span>),</span>
<span id="cb4-547"><a href="#cb4-547" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb4-548"><a href="#cb4-548" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-549"><a href="#cb4-549" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-550"><a href="#cb4-550" aria-hidden="true" tabindex="-1"></a> session.history.append({<span class="st">&quot;role&quot;</span>: <span class="st">&quot;user&quot;</span>, <span class="st">&quot;content&quot;</span>: tool_result_blocks})</span>
<span id="cb4-551"><a href="#cb4-551" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-552"><a href="#cb4-552" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> client.messages.create(</span>
<span id="cb4-553"><a href="#cb4-553" aria-hidden="true" tabindex="-1"></a> model<span class="op">=</span>settings.anthropic_model,</span>
<span id="cb4-554"><a href="#cb4-554" aria-hidden="true" tabindex="-1"></a> max_tokens<span class="op">=</span>settings.max_tokens,</span>
<span id="cb4-555"><a href="#cb4-555" aria-hidden="true" tabindex="-1"></a> system<span class="op">=</span>system_content,</span>
<span id="cb4-556"><a href="#cb4-556" aria-hidden="true" tabindex="-1"></a> tools<span class="op">=</span>TOOL_SCHEMAS,</span>
<span id="cb4-557"><a href="#cb4-557" aria-hidden="true" tabindex="-1"></a> messages<span class="op">=</span>_with_last_message_cache_breakpoint(session.history),</span>
<span id="cb4-558"><a href="#cb4-558" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-559"><a href="#cb4-559" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-560"><a href="#cb4-560" aria-hidden="true" tabindex="-1"></a> <span class="co"># Fell out of the for loop without hitting `end_turn` -- the model is</span></span>
<span id="cb4-561"><a href="#cb4-561" aria-hidden="true" tabindex="-1"></a> <span class="co"># still asking for tools. Refuse the request rather than loop forever.</span></span>
<span id="cb4-562"><a href="#cb4-562" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> ToolLoopLimitExceeded(</span>
<span id="cb4-563"><a href="#cb4-563" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;Tool-use loop did not terminate within </span><span class="sc">{</span>settings<span class="sc">.</span>max_tool_use_iterations<span class="sc">}</span><span class="ss"> iterations&quot;</span></span>
<span id="cb4-564"><a href="#cb4-564" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-565"><a href="#cb4-565" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-566"><a href="#cb4-566" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-567"><a href="#cb4-567" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> run_turn(session_id: <span class="bu">str</span>, user_message: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb4-568"><a href="#cb4-568" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Run one user turn end-to-end and return the assistant&#39;s reply text.</span></span>
<span id="cb4-569"><a href="#cb4-569" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-570"><a href="#cb4-570" aria-hidden="true" tabindex="-1"></a><span class="co"> Wires together: session lookup with locking, history append, system</span></span>
<span id="cb4-571"><a href="#cb4-571" aria-hidden="true" tabindex="-1"></a><span class="co"> content with reminders, the tool-use loop, output validation, and the</span></span>
<span id="cb4-572"><a href="#cb4-572" aria-hidden="true" tabindex="-1"></a><span class="co"> safe-fallback path on validation failure.</span></span>
<span id="cb4-573"><a href="#cb4-573" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb4-574"><a href="#cb4-574" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(session_id, <span class="bu">str</span>) <span class="kw">or</span> <span class="kw">not</span> session_id:</span>
<span id="cb4-575"><a href="#cb4-575" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;session_id is required&quot;</span>)</span>
<span id="cb4-576"><a href="#cb4-576" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> <span class="bu">isinstance</span>(user_message, <span class="bu">str</span>) <span class="kw">or</span> <span class="kw">not</span> user_message.strip():</span>
<span id="cb4-577"><a href="#cb4-577" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;user_message is required&quot;</span>)</span>
<span id="cb4-578"><a href="#cb4-578" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-579"><a href="#cb4-579" aria-hidden="true" tabindex="-1"></a> session_lock <span class="op">=</span> SESSIONS.lock_for(session_id)</span>
<span id="cb4-580"><a href="#cb4-580" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> session_lock:</span>
<span id="cb4-581"><a href="#cb4-581" aria-hidden="true" tabindex="-1"></a> session <span class="op">=</span> SESSIONS.get_or_create(session_id)</span>
<span id="cb4-582"><a href="#cb4-582" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-583"><a href="#cb4-583" aria-hidden="true" tabindex="-1"></a> <span class="co"># Bounded conversations. Past the limit we refuse rather than let</span></span>
<span id="cb4-584"><a href="#cb4-584" aria-hidden="true" tabindex="-1"></a> <span class="co"># history grow unbounded, which keeps memory usage predictable and</span></span>
<span id="cb4-585"><a href="#cb4-585" aria-hidden="true" tabindex="-1"></a> <span class="co"># avoids the model losing coherence late in a chat.</span></span>
<span id="cb4-586"><a href="#cb4-586" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> session.turn_count <span class="op">&gt;=</span> settings.max_turns_per_session:</span>
<span id="cb4-587"><a href="#cb4-587" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> CONVERSATION_TOO_LONG_MESSAGE</span>
<span id="cb4-588"><a href="#cb4-588" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-589"><a href="#cb4-589" aria-hidden="true" tabindex="-1"></a> session.history.append({<span class="st">&quot;role&quot;</span>: <span class="st">&quot;user&quot;</span>, <span class="st">&quot;content&quot;</span>: user_message})</span>
<span id="cb4-590"><a href="#cb4-590" aria-hidden="true" tabindex="-1"></a> system_content <span class="op">=</span> build_system_content(session.turn_count)</span>
<span id="cb4-591"><a href="#cb4-591" aria-hidden="true" tabindex="-1"></a> client <span class="op">=</span> _get_client()</span>
<span id="cb4-592"><a href="#cb4-592" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-593"><a href="#cb4-593" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
<span id="cb4-594"><a href="#cb4-594" aria-hidden="true" tabindex="-1"></a> response, tool_results_this_turn <span class="op">=</span> _run_tool_use_loop(session, system_content, client)</span>
<span id="cb4-595"><a href="#cb4-595" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> ToolLoopLimitExceeded:</span>
<span id="cb4-596"><a href="#cb4-596" aria-hidden="true" tabindex="-1"></a> logger.warning(</span>
<span id="cb4-597"><a href="#cb4-597" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;tool_loop_exceeded session=</span><span class="sc">%s</span><span class="st"> turn=</span><span class="sc">%s</span><span class="st">&quot;</span>,</span>
<span id="cb4-598"><a href="#cb4-598" aria-hidden="true" tabindex="-1"></a> _hash_for_logging(session_id),</span>
<span id="cb4-599"><a href="#cb4-599" aria-hidden="true" tabindex="-1"></a> session.turn_count,</span>
<span id="cb4-600"><a href="#cb4-600" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-601"><a href="#cb4-601" aria-hidden="true" tabindex="-1"></a> session.turn_count <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-602"><a href="#cb4-602" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> TOOL_LOOP_EXCEEDED_MESSAGE</span>
<span id="cb4-603"><a href="#cb4-603" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-604"><a href="#cb4-604" aria-hidden="true" tabindex="-1"></a> reply_text <span class="op">=</span> _extract_text(response.content)</span>
<span id="cb4-605"><a href="#cb4-605" aria-hidden="true" tabindex="-1"></a> validation <span class="op">=</span> validate_reply(reply_text, tool_results_this_turn)</span>
<span id="cb4-606"><a href="#cb4-606" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> validation.ok:</span>
<span id="cb4-607"><a href="#cb4-607" aria-hidden="true" tabindex="-1"></a> <span class="co"># Redact PII: log only violation codes plus hashes of session and</span></span>
<span id="cb4-608"><a href="#cb4-608" aria-hidden="true" tabindex="-1"></a> <span class="co"># reply. Never log the reply body -- it may contain customer name,</span></span>
<span id="cb4-609"><a href="#cb4-609" aria-hidden="true" tabindex="-1"></a> <span class="co"># email, order ID, or tracking number.</span></span>
<span id="cb4-610"><a href="#cb4-610" aria-hidden="true" tabindex="-1"></a> logger.warning(</span>
<span id="cb4-611"><a href="#cb4-611" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;validation_failed session=</span><span class="sc">%s</span><span class="st"> turn=</span><span class="sc">%s</span><span class="st"> violations=</span><span class="sc">%s</span><span class="st"> reply_sha=</span><span class="sc">%s</span><span class="st">&quot;</span>,</span>
<span id="cb4-612"><a href="#cb4-612" aria-hidden="true" tabindex="-1"></a> _hash_for_logging(session_id),</span>
<span id="cb4-613"><a href="#cb4-613" aria-hidden="true" tabindex="-1"></a> session.turn_count,</span>
<span id="cb4-614"><a href="#cb4-614" aria-hidden="true" tabindex="-1"></a> <span class="bu">list</span>(validation.violations),</span>
<span id="cb4-615"><a href="#cb4-615" aria-hidden="true" tabindex="-1"></a> _hash_for_logging(reply_text),</span>
<span id="cb4-616"><a href="#cb4-616" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-617"><a href="#cb4-617" aria-hidden="true" tabindex="-1"></a> <span class="co"># Do NOT append the bad reply to history -- that would poison</span></span>
<span id="cb4-618"><a href="#cb4-618" aria-hidden="true" tabindex="-1"></a> <span class="co"># future turns.</span></span>
<span id="cb4-619"><a href="#cb4-619" aria-hidden="true" tabindex="-1"></a> session.turn_count <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-620"><a href="#cb4-620" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> SAFE_FALLBACK</span>
<span id="cb4-621"><a href="#cb4-621" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-622"><a href="#cb4-622" aria-hidden="true" tabindex="-1"></a> session.history.append(</span>
<span id="cb4-623"><a href="#cb4-623" aria-hidden="true" tabindex="-1"></a> {<span class="st">&quot;role&quot;</span>: <span class="st">&quot;assistant&quot;</span>, <span class="st">&quot;content&quot;</span>: _serialize_assistant_content(response.content)}</span>
<span id="cb4-624"><a href="#cb4-624" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-625"><a href="#cb4-625" aria-hidden="true" tabindex="-1"></a> session.turn_count <span class="op">+=</span> <span class="dv">1</span></span>
<span id="cb4-626"><a href="#cb4-626" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> reply_text</span></code></pre></div>
<h1 id="http-surface">HTTP surface</h1>
<p>The FastAPI app exposes four routes: <code>GET /health</code>,
<code>GET /</code> (redirects to <code>/static/index.html</code>),
<code>POST /api/chat</code>, and <code>GET /architecture</code> (this
very document). Everything else is deliberately missing the OpenAPI
docs pages and the redoc pages are disabled so the public surface is as
small as possible.</p>
<h2 id="security-headers">Security headers</h2>
<p>A middleware injects a strict Content-Security-Policy and friends on
every response. CSP is defense in depth: the chat UI in
<code>static/chat.js</code> already renders model replies with
<code>textContent</code> rather than <code>innerHTML</code>, so XSS is
structurally impossible today. The CSP exists to catch any future
regression that accidentally switches to <code>innerHTML</code>.</p>
<p>The <code>/architecture</code> route overrides the middleware CSP
with a more permissive one because pandocs standalone HTML has inline
styles.</p>
<h2 id="sliding-window-rate-limiter">Sliding-window rate limiter</h2>
<p><code>SlidingWindowRateLimiter</code> keeps a deque of timestamps per
key and evicts anything older than the window. The
<code>/api/chat</code> handler checks twice per call once with an
<code>ip:</code> prefix, once with a <code>session:</code> prefix so a
single attacker cannot exhaust the per-session budget by rotating
cookies, and a legitimate user does not get locked out by a noisy
neighbour on the same IP.</p>
<p>Suitable for a single-process demo deployment. A multi-worker
deployment would externalize this to Redis.</p>
<h2 id="session-cookies">Session cookies</h2>
<p>The client never chooses its own session ID. On the first request a
new random ID is minted, HMAC-signed with
<code>settings.session_secret</code>, and set in an HttpOnly,
SameSite=Lax cookie. Subsequent requests carry the cookie; the server
verifies the signature in constant time
(<code>hmac.compare_digest</code>) and trusts nothing else. A leaked or
guessed request body cannot hijack another users conversation because
the session ID is not in the body at all.</p>
<h2 id="apichat">/api/chat</h2>
<p>The handler resolves the session, checks both rate limits, then calls
into <code>agent.run_turn</code>. The Anthropic exception hierarchy is
caught explicitly so a rate-limit incident and a code bug cannot look
identical to operators: <code>anthropic.RateLimitError</code> becomes
503, <code>APIConnectionError</code> becomes 503,
<code>APIStatusError</code> becomes 502, <code>ValueError</code> from
the agent becomes 400, anything else becomes 500.</p>
<h2 id="architecture">/architecture</h2>
<p>This is where the woven literate program is served. The handler reads
<code>static/architecture.html</code> (produced by pandoc from this
file) and returns it with a relaxed CSP. If the file does not exist yet,
the route 404s with a clear message rather than raising a 500.</p>
<div class="sourceCode" id="cb5" data-chunk="server-py" data-file="server.py"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;FastAPI app for Bookly. Hosts /api/chat, /health, and the static chat UI.</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co">Security posture notes:</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co">- Sessions are server-issued and HMAC-signed. The client never chooses its</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co"> own session ID, so a leaked or guessed body cannot hijack someone else&#39;s</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co"> chat history. See `_resolve_session`.</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co">- Every response carries a strict Content-Security-Policy and related</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co"> headers (see `security_headers`). The chat UI already uses `textContent`</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co"> for model replies, so XSS is structurally impossible; CSP is defense in</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="co"> depth for future edits.</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="co">- In-memory sliding-window rate limiting is applied per IP and per session.</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="co"> Suitable for a single-process demo deployment; swap to a shared store for</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="co"> multi-worker.</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> hashlib</span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> hmac</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> logging</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> secrets</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> threading</span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> collections <span class="im">import</span> defaultdict, deque</span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pathlib <span class="im">import</span> Path</span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> anthropic</span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> fastapi <span class="im">import</span> FastAPI, HTTPException, Request, Response</span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> fastapi.responses <span class="im">import</span> HTMLResponse, RedirectResponse</span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> fastapi.staticfiles <span class="im">import</span> StaticFiles</span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> pydantic <span class="im">import</span> BaseModel, Field</span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> agent</span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> config <span class="im">import</span> settings</span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a>logging.basicConfig(level<span class="op">=</span>logging.INFO, <span class="bu">format</span><span class="op">=</span><span class="st">&quot;</span><span class="sc">%(asctime)s</span><span class="st"> </span><span class="sc">%(levelname)s</span><span class="st"> </span><span class="sc">%(name)s</span><span class="st"> </span><span class="sc">%(message)s</span><span class="st">&quot;</span>)</span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a>logger <span class="op">=</span> logging.getLogger(<span class="st">&quot;bookly.server&quot;</span>)</span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a>app <span class="op">=</span> FastAPI(title<span class="op">=</span><span class="st">&quot;Bookly&quot;</span>, docs_url<span class="op">=</span><span class="va">None</span>, redoc_url<span class="op">=</span><span class="va">None</span>)</span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Security headers</span></span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a>_SECURITY_HEADERS: <span class="bu">dict</span>[<span class="bu">str</span>, <span class="bu">str</span>] <span class="op">=</span> {</span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a> <span class="co"># Tight CSP: only same-origin assets, no inline scripts, no embedding.</span></span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a> <span class="co"># The UI is plain HTML+JS under /static, all same-origin.</span></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Content-Security-Policy&quot;</span>: (</span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;default-src &#39;self&#39;; &quot;</span></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;script-src &#39;self&#39;; &quot;</span></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;style-src &#39;self&#39;; &quot;</span></span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;img-src &#39;self&#39; data:; &quot;</span></span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;connect-src &#39;self&#39;; &quot;</span></span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;object-src &#39;none&#39;; &quot;</span></span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;base-uri &#39;none&#39;; &quot;</span></span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;frame-ancestors &#39;none&#39;; &quot;</span></span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;form-action &#39;self&#39;&quot;</span></span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb5-62"><a href="#cb5-62" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;X-Content-Type-Options&quot;</span>: <span class="st">&quot;nosniff&quot;</span>,</span>
<span id="cb5-63"><a href="#cb5-63" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;X-Frame-Options&quot;</span>: <span class="st">&quot;DENY&quot;</span>,</span>
<span id="cb5-64"><a href="#cb5-64" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Referrer-Policy&quot;</span>: <span class="st">&quot;no-referrer&quot;</span>,</span>
<span id="cb5-65"><a href="#cb5-65" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Permissions-Policy&quot;</span>: <span class="st">&quot;geolocation=(), microphone=(), camera=()&quot;</span>,</span>
<span id="cb5-66"><a href="#cb5-66" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb5-67"><a href="#cb5-67" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a><span class="at">@app.middleware</span>(<span class="st">&quot;http&quot;</span>)</span>
<span id="cb5-70"><a href="#cb5-70" aria-hidden="true" tabindex="-1"></a><span class="cf">async</span> <span class="kw">def</span> security_headers(request: Request, call_next):</span>
<span id="cb5-71"><a href="#cb5-71" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> <span class="cf">await</span> call_next(request)</span>
<span id="cb5-72"><a href="#cb5-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> header_name, header_value <span class="kw">in</span> _SECURITY_HEADERS.items():</span>
<span id="cb5-73"><a href="#cb5-73" aria-hidden="true" tabindex="-1"></a> response.headers.setdefault(header_name, header_value)</span>
<span id="cb5-74"><a href="#cb5-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response</span>
<span id="cb5-75"><a href="#cb5-75" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-76"><a href="#cb5-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-77"><a href="#cb5-77" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-78"><a href="#cb5-78" aria-hidden="true" tabindex="-1"></a><span class="co"># Sliding-window rate limiter (in-memory)</span></span>
<span id="cb5-79"><a href="#cb5-79" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-80"><a href="#cb5-80" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-81"><a href="#cb5-81" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-82"><a href="#cb5-82" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> SlidingWindowRateLimiter:</span>
<span id="cb5-83"><a href="#cb5-83" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Per-key request counter over a fixed trailing window.</span></span>
<span id="cb5-84"><a href="#cb5-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-85"><a href="#cb5-85" aria-hidden="true" tabindex="-1"></a><span class="co"> Not meant to be bulletproof -- this is a small demo deployment, not an</span></span>
<span id="cb5-86"><a href="#cb5-86" aria-hidden="true" tabindex="-1"></a><span class="co"> edge-network WAF. Enforces a ceiling per IP and per session so a single</span></span>
<span id="cb5-87"><a href="#cb5-87" aria-hidden="true" tabindex="-1"></a><span class="co"> caller cannot burn the Anthropic budget or exhaust memory by spamming</span></span>
<span id="cb5-88"><a href="#cb5-88" aria-hidden="true" tabindex="-1"></a><span class="co"> `/api/chat`.</span></span>
<span id="cb5-89"><a href="#cb5-89" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-90"><a href="#cb5-90" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-91"><a href="#cb5-91" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, <span class="op">*</span>, window_seconds: <span class="bu">int</span> <span class="op">=</span> <span class="dv">60</span>) <span class="op">-&gt;</span> <span class="va">None</span>:</span>
<span id="cb5-92"><a href="#cb5-92" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> window_seconds <span class="op">&lt;=</span> <span class="dv">0</span>:</span>
<span id="cb5-93"><a href="#cb5-93" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> <span class="pp">ValueError</span>(<span class="st">&quot;window_seconds must be positive&quot;</span>)</span>
<span id="cb5-94"><a href="#cb5-94" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._window <span class="op">=</span> window_seconds</span>
<span id="cb5-95"><a href="#cb5-95" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._hits: defaultdict[<span class="bu">str</span>, deque[<span class="bu">float</span>]] <span class="op">=</span> defaultdict(deque)</span>
<span id="cb5-96"><a href="#cb5-96" aria-hidden="true" tabindex="-1"></a> <span class="va">self</span>._lock <span class="op">=</span> threading.Lock()</span>
<span id="cb5-97"><a href="#cb5-97" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-98"><a href="#cb5-98" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> check(<span class="va">self</span>, key: <span class="bu">str</span>, max_hits: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">bool</span>:</span>
<span id="cb5-99"><a href="#cb5-99" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Record a hit on `key`. Returns True if under the limit, False otherwise.&quot;&quot;&quot;</span></span>
<span id="cb5-100"><a href="#cb5-100" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> max_hits <span class="op">&lt;=</span> <span class="dv">0</span>:</span>
<span id="cb5-101"><a href="#cb5-101" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb5-102"><a href="#cb5-102" aria-hidden="true" tabindex="-1"></a> now <span class="op">=</span> time.monotonic()</span>
<span id="cb5-103"><a href="#cb5-103" aria-hidden="true" tabindex="-1"></a> cutoff <span class="op">=</span> now <span class="op">-</span> <span class="va">self</span>._window</span>
<span id="cb5-104"><a href="#cb5-104" aria-hidden="true" tabindex="-1"></a> <span class="cf">with</span> <span class="va">self</span>._lock:</span>
<span id="cb5-105"><a href="#cb5-105" aria-hidden="true" tabindex="-1"></a> bucket <span class="op">=</span> <span class="va">self</span>._hits[key]</span>
<span id="cb5-106"><a href="#cb5-106" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> bucket <span class="kw">and</span> bucket[<span class="dv">0</span>] <span class="op">&lt;</span> cutoff:</span>
<span id="cb5-107"><a href="#cb5-107" aria-hidden="true" tabindex="-1"></a> bucket.popleft()</span>
<span id="cb5-108"><a href="#cb5-108" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">len</span>(bucket) <span class="op">&gt;=</span> max_hits:</span>
<span id="cb5-109"><a href="#cb5-109" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">False</span></span>
<span id="cb5-110"><a href="#cb5-110" aria-hidden="true" tabindex="-1"></a> bucket.append(now)</span>
<span id="cb5-111"><a href="#cb5-111" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">True</span></span>
<span id="cb5-112"><a href="#cb5-112" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-113"><a href="#cb5-113" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-114"><a href="#cb5-114" aria-hidden="true" tabindex="-1"></a>_rate_limiter <span class="op">=</span> SlidingWindowRateLimiter(window_seconds<span class="op">=</span><span class="dv">60</span>)</span>
<span id="cb5-115"><a href="#cb5-115" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-116"><a href="#cb5-116" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-117"><a href="#cb5-117" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _client_ip(request: Request) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb5-118"><a href="#cb5-118" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Best-effort client IP for rate limiting.</span></span>
<span id="cb5-119"><a href="#cb5-119" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-120"><a href="#cb5-120" aria-hidden="true" tabindex="-1"></a><span class="co"> If the app is deployed behind a reverse proxy, set the proxy to add</span></span>
<span id="cb5-121"><a href="#cb5-121" aria-hidden="true" tabindex="-1"></a><span class="co"> `X-Forwarded-For` and trust the first hop. Otherwise fall back to the</span></span>
<span id="cb5-122"><a href="#cb5-122" aria-hidden="true" tabindex="-1"></a><span class="co"> direct client address.</span></span>
<span id="cb5-123"><a href="#cb5-123" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-124"><a href="#cb5-124" aria-hidden="true" tabindex="-1"></a> forwarded <span class="op">=</span> request.headers.get(<span class="st">&quot;x-forwarded-for&quot;</span>, <span class="st">&quot;&quot;</span>)</span>
<span id="cb5-125"><a href="#cb5-125" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> forwarded:</span>
<span id="cb5-126"><a href="#cb5-126" aria-hidden="true" tabindex="-1"></a> first <span class="op">=</span> forwarded.split(<span class="st">&quot;,&quot;</span>, <span class="dv">1</span>)[<span class="dv">0</span>].strip()</span>
<span id="cb5-127"><a href="#cb5-127" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> first:</span>
<span id="cb5-128"><a href="#cb5-128" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> first</span>
<span id="cb5-129"><a href="#cb5-129" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> request.client <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb5-130"><a href="#cb5-130" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request.client.host</span>
<span id="cb5-131"><a href="#cb5-131" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;unknown&quot;</span></span>
<span id="cb5-132"><a href="#cb5-132" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-133"><a href="#cb5-133" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-134"><a href="#cb5-134" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-135"><a href="#cb5-135" aria-hidden="true" tabindex="-1"></a><span class="co"># Session cookies (server-issued, HMAC-signed)</span></span>
<span id="cb5-136"><a href="#cb5-136" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-137"><a href="#cb5-137" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-138"><a href="#cb5-138" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-139"><a href="#cb5-139" aria-hidden="true" tabindex="-1"></a>_SESSION_COOKIE_SEPARATOR <span class="op">=</span> <span class="st">&quot;.&quot;</span></span>
<span id="cb5-140"><a href="#cb5-140" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-141"><a href="#cb5-141" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-142"><a href="#cb5-142" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _sign_session_id(session_id: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb5-143"><a href="#cb5-143" aria-hidden="true" tabindex="-1"></a> secret <span class="op">=</span> settings.session_secret.get_secret_value().encode(<span class="st">&quot;utf-8&quot;</span>)</span>
<span id="cb5-144"><a href="#cb5-144" aria-hidden="true" tabindex="-1"></a> signature <span class="op">=</span> hmac.new(secret, session_id.encode(<span class="st">&quot;utf-8&quot;</span>), hashlib.sha256).hexdigest()</span>
<span id="cb5-145"><a href="#cb5-145" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="ss">f&quot;</span><span class="sc">{</span>session_id<span class="sc">}{</span>_SESSION_COOKIE_SEPARATOR<span class="sc">}{</span>signature<span class="sc">}</span><span class="ss">&quot;</span></span>
<span id="cb5-146"><a href="#cb5-146" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-147"><a href="#cb5-147" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-148"><a href="#cb5-148" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _verify_signed_session(signed_value: <span class="bu">str</span>) <span class="op">-&gt;</span> <span class="bu">str</span> <span class="op">|</span> <span class="va">None</span>:</span>
<span id="cb5-149"><a href="#cb5-149" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> signed_value <span class="kw">or</span> _SESSION_COOKIE_SEPARATOR <span class="kw">not</span> <span class="kw">in</span> signed_value:</span>
<span id="cb5-150"><a href="#cb5-150" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb5-151"><a href="#cb5-151" aria-hidden="true" tabindex="-1"></a> session_id, _, signature <span class="op">=</span> signed_value.partition(_SESSION_COOKIE_SEPARATOR)</span>
<span id="cb5-152"><a href="#cb5-152" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> session_id <span class="kw">or</span> <span class="kw">not</span> signature:</span>
<span id="cb5-153"><a href="#cb5-153" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb5-154"><a href="#cb5-154" aria-hidden="true" tabindex="-1"></a> expected <span class="op">=</span> _sign_session_id(session_id)</span>
<span id="cb5-155"><a href="#cb5-155" aria-hidden="true" tabindex="-1"></a> <span class="co"># Compare the full signed form in constant time to avoid timing leaks on</span></span>
<span id="cb5-156"><a href="#cb5-156" aria-hidden="true" tabindex="-1"></a> <span class="co"># the signature bytes.</span></span>
<span id="cb5-157"><a href="#cb5-157" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> hmac.compare_digest(expected, signed_value):</span>
<span id="cb5-158"><a href="#cb5-158" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span>
<span id="cb5-159"><a href="#cb5-159" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session_id</span>
<span id="cb5-160"><a href="#cb5-160" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-161"><a href="#cb5-161" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-162"><a href="#cb5-162" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _issue_new_session_id() <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb5-163"><a href="#cb5-163" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> secrets.token_urlsafe(<span class="dv">24</span>)</span>
<span id="cb5-164"><a href="#cb5-164" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-165"><a href="#cb5-165" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-166"><a href="#cb5-166" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> _resolve_session(request: Request, response: Response) <span class="op">-&gt;</span> <span class="bu">str</span>:</span>
<span id="cb5-167"><a href="#cb5-167" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Return the session_id for this request, issuing a fresh cookie if needed.</span></span>
<span id="cb5-168"><a href="#cb5-168" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-169"><a href="#cb5-169" aria-hidden="true" tabindex="-1"></a><span class="co"> The client never chooses the session_id. Anything in the request body</span></span>
<span id="cb5-170"><a href="#cb5-170" aria-hidden="true" tabindex="-1"></a><span class="co"> that claims to be one is ignored. If the cookie is missing or tampered</span></span>
<span id="cb5-171"><a href="#cb5-171" aria-hidden="true" tabindex="-1"></a><span class="co"> with, we mint a new session_id and set the cookie on the response.</span></span>
<span id="cb5-172"><a href="#cb5-172" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-173"><a href="#cb5-173" aria-hidden="true" tabindex="-1"></a> signed_cookie <span class="op">=</span> request.cookies.get(settings.session_cookie_name, <span class="st">&quot;&quot;</span>)</span>
<span id="cb5-174"><a href="#cb5-174" aria-hidden="true" tabindex="-1"></a> session_id <span class="op">=</span> _verify_signed_session(signed_cookie)</span>
<span id="cb5-175"><a href="#cb5-175" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> session_id <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
<span id="cb5-176"><a href="#cb5-176" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session_id</span>
<span id="cb5-177"><a href="#cb5-177" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-178"><a href="#cb5-178" aria-hidden="true" tabindex="-1"></a> session_id <span class="op">=</span> _issue_new_session_id()</span>
<span id="cb5-179"><a href="#cb5-179" aria-hidden="true" tabindex="-1"></a> response.set_cookie(</span>
<span id="cb5-180"><a href="#cb5-180" aria-hidden="true" tabindex="-1"></a> key<span class="op">=</span>settings.session_cookie_name,</span>
<span id="cb5-181"><a href="#cb5-181" aria-hidden="true" tabindex="-1"></a> value<span class="op">=</span>_sign_session_id(session_id),</span>
<span id="cb5-182"><a href="#cb5-182" aria-hidden="true" tabindex="-1"></a> max_age<span class="op">=</span>settings.session_cookie_max_age_seconds,</span>
<span id="cb5-183"><a href="#cb5-183" aria-hidden="true" tabindex="-1"></a> httponly<span class="op">=</span><span class="va">True</span>,</span>
<span id="cb5-184"><a href="#cb5-184" aria-hidden="true" tabindex="-1"></a> secure<span class="op">=</span>settings.session_cookie_secure,</span>
<span id="cb5-185"><a href="#cb5-185" aria-hidden="true" tabindex="-1"></a> samesite<span class="op">=</span><span class="st">&quot;lax&quot;</span>,</span>
<span id="cb5-186"><a href="#cb5-186" aria-hidden="true" tabindex="-1"></a> path<span class="op">=</span><span class="st">&quot;/&quot;</span>,</span>
<span id="cb5-187"><a href="#cb5-187" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb5-188"><a href="#cb5-188" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> session_id</span>
<span id="cb5-189"><a href="#cb5-189" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-190"><a href="#cb5-190" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-191"><a href="#cb5-191" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-192"><a href="#cb5-192" aria-hidden="true" tabindex="-1"></a><span class="co"># Request/response models</span></span>
<span id="cb5-193"><a href="#cb5-193" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-194"><a href="#cb5-194" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-195"><a href="#cb5-195" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-196"><a href="#cb5-196" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ChatRequest(BaseModel):</span>
<span id="cb5-197"><a href="#cb5-197" aria-hidden="true" tabindex="-1"></a> <span class="co"># `session_id` is intentionally NOT accepted from clients. Sessions are</span></span>
<span id="cb5-198"><a href="#cb5-198" aria-hidden="true" tabindex="-1"></a> <span class="co"># tracked server-side via the signed cookie.</span></span>
<span id="cb5-199"><a href="#cb5-199" aria-hidden="true" tabindex="-1"></a> message: <span class="bu">str</span> <span class="op">=</span> Field(..., min_length<span class="op">=</span><span class="dv">1</span>, max_length<span class="op">=</span><span class="dv">4000</span>)</span>
<span id="cb5-200"><a href="#cb5-200" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-201"><a href="#cb5-201" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-202"><a href="#cb5-202" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> ChatResponse(BaseModel):</span>
<span id="cb5-203"><a href="#cb5-203" aria-hidden="true" tabindex="-1"></a> reply: <span class="bu">str</span></span>
<span id="cb5-204"><a href="#cb5-204" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-205"><a href="#cb5-205" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-206"><a href="#cb5-206" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-207"><a href="#cb5-207" aria-hidden="true" tabindex="-1"></a><span class="co"># Routes</span></span>
<span id="cb5-208"><a href="#cb5-208" aria-hidden="true" tabindex="-1"></a><span class="co"># ---------------------------------------------------------------------------</span></span>
<span id="cb5-209"><a href="#cb5-209" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-210"><a href="#cb5-210" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-211"><a href="#cb5-211" aria-hidden="true" tabindex="-1"></a><span class="at">@app.get</span>(<span class="st">&quot;/health&quot;</span>)</span>
<span id="cb5-212"><a href="#cb5-212" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> health() <span class="op">-&gt;</span> <span class="bu">dict</span>:</span>
<span id="cb5-213"><a href="#cb5-213" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {<span class="st">&quot;status&quot;</span>: <span class="st">&quot;ok&quot;</span>}</span>
<span id="cb5-214"><a href="#cb5-214" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-215"><a href="#cb5-215" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-216"><a href="#cb5-216" aria-hidden="true" tabindex="-1"></a><span class="at">@app.get</span>(<span class="st">&quot;/&quot;</span>)</span>
<span id="cb5-217"><a href="#cb5-217" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> root() <span class="op">-&gt;</span> RedirectResponse:</span>
<span id="cb5-218"><a href="#cb5-218" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> RedirectResponse(url<span class="op">=</span><span class="st">&quot;/static/index.html&quot;</span>)</span>
<span id="cb5-219"><a href="#cb5-219" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-220"><a href="#cb5-220" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-221"><a href="#cb5-221" aria-hidden="true" tabindex="-1"></a><span class="at">@app.post</span>(<span class="st">&quot;/api/chat&quot;</span>, response_model<span class="op">=</span>ChatResponse)</span>
<span id="cb5-222"><a href="#cb5-222" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> chat(body: ChatRequest, http_request: Request, http_response: Response) <span class="op">-&gt;</span> ChatResponse:</span>
<span id="cb5-223"><a href="#cb5-223" aria-hidden="true" tabindex="-1"></a> session_id <span class="op">=</span> _resolve_session(http_request, http_response)</span>
<span id="cb5-224"><a href="#cb5-224" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-225"><a href="#cb5-225" aria-hidden="true" tabindex="-1"></a> ip <span class="op">=</span> _client_ip(http_request)</span>
<span id="cb5-226"><a href="#cb5-226" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> _rate_limiter.check(<span class="ss">f&quot;ip:</span><span class="sc">{</span>ip<span class="sc">}</span><span class="ss">&quot;</span>, settings.rate_limit_per_ip_per_minute):</span>
<span id="cb5-227"><a href="#cb5-227" aria-hidden="true" tabindex="-1"></a> logger.info(<span class="st">&quot;rate_limited scope=ip&quot;</span>)</span>
<span id="cb5-228"><a href="#cb5-228" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(status_code<span class="op">=</span><span class="dv">429</span>, detail<span class="op">=</span><span class="st">&quot;Too many requests. Please slow down.&quot;</span>)</span>
<span id="cb5-229"><a href="#cb5-229" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="kw">not</span> _rate_limiter.check(<span class="ss">f&quot;session:</span><span class="sc">{</span>session_id<span class="sc">}</span><span class="ss">&quot;</span>, settings.rate_limit_per_session_per_minute):</span>
<span id="cb5-230"><a href="#cb5-230" aria-hidden="true" tabindex="-1"></a> logger.info(<span class="st">&quot;rate_limited scope=session&quot;</span>)</span>
<span id="cb5-231"><a href="#cb5-231" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(status_code<span class="op">=</span><span class="dv">429</span>, detail<span class="op">=</span><span class="st">&quot;Too many requests. Please slow down.&quot;</span>)</span>
<span id="cb5-232"><a href="#cb5-232" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-233"><a href="#cb5-233" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
<span id="cb5-234"><a href="#cb5-234" aria-hidden="true" tabindex="-1"></a> reply <span class="op">=</span> agent.run_turn(session_id, body.message)</span>
<span id="cb5-235"><a href="#cb5-235" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> anthropic.RateLimitError:</span>
<span id="cb5-236"><a href="#cb5-236" aria-hidden="true" tabindex="-1"></a> logger.warning(<span class="st">&quot;anthropic_rate_limited&quot;</span>)</span>
<span id="cb5-237"><a href="#cb5-237" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(</span>
<span id="cb5-238"><a href="#cb5-238" aria-hidden="true" tabindex="-1"></a> status_code<span class="op">=</span><span class="dv">503</span>,</span>
<span id="cb5-239"><a href="#cb5-239" aria-hidden="true" tabindex="-1"></a> detail<span class="op">=</span><span class="st">&quot;Our AI provider is busy right now. Please try again in a moment.&quot;</span>,</span>
<span id="cb5-240"><a href="#cb5-240" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb5-241"><a href="#cb5-241" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> anthropic.APIConnectionError:</span>
<span id="cb5-242"><a href="#cb5-242" aria-hidden="true" tabindex="-1"></a> logger.warning(<span class="st">&quot;anthropic_connection_error&quot;</span>)</span>
<span id="cb5-243"><a href="#cb5-243" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(</span>
<span id="cb5-244"><a href="#cb5-244" aria-hidden="true" tabindex="-1"></a> status_code<span class="op">=</span><span class="dv">503</span>,</span>
<span id="cb5-245"><a href="#cb5-245" aria-hidden="true" tabindex="-1"></a> detail<span class="op">=</span><span class="st">&quot;We couldn&#39;t reach our AI provider. Please try again in a moment.&quot;</span>,</span>
<span id="cb5-246"><a href="#cb5-246" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb5-247"><a href="#cb5-247" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> anthropic.APIStatusError <span class="im">as</span> exc:</span>
<span id="cb5-248"><a href="#cb5-248" aria-hidden="true" tabindex="-1"></a> logger.error(<span class="st">&quot;anthropic_api_error status=</span><span class="sc">%s</span><span class="st">&quot;</span>, exc.status_code)</span>
<span id="cb5-249"><a href="#cb5-249" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(</span>
<span id="cb5-250"><a href="#cb5-250" aria-hidden="true" tabindex="-1"></a> status_code<span class="op">=</span><span class="dv">502</span>,</span>
<span id="cb5-251"><a href="#cb5-251" aria-hidden="true" tabindex="-1"></a> detail<span class="op">=</span><span class="st">&quot;Our AI provider returned an error. Please try again.&quot;</span>,</span>
<span id="cb5-252"><a href="#cb5-252" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb5-253"><a href="#cb5-253" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">ValueError</span>:</span>
<span id="cb5-254"><a href="#cb5-254" aria-hidden="true" tabindex="-1"></a> <span class="co"># Programmer-visible input errors (e.g., blank message). Surface a</span></span>
<span id="cb5-255"><a href="#cb5-255" aria-hidden="true" tabindex="-1"></a> <span class="co"># 400 rather than a 500 so clients can distinguish.</span></span>
<span id="cb5-256"><a href="#cb5-256" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(status_code<span class="op">=</span><span class="dv">400</span>, detail<span class="op">=</span><span class="st">&quot;Invalid request.&quot;</span>)</span>
<span id="cb5-257"><a href="#cb5-257" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">Exception</span>:</span>
<span id="cb5-258"><a href="#cb5-258" aria-hidden="true" tabindex="-1"></a> logger.exception(<span class="st">&quot;chat_failed&quot;</span>)</span>
<span id="cb5-259"><a href="#cb5-259" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(status_code<span class="op">=</span><span class="dv">500</span>, detail<span class="op">=</span><span class="st">&quot;Something went wrong handling that message.&quot;</span>)</span>
<span id="cb5-260"><a href="#cb5-260" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-261"><a href="#cb5-261" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> ChatResponse(reply<span class="op">=</span>reply)</span>
<span id="cb5-262"><a href="#cb5-262" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-263"><a href="#cb5-263" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-264"><a href="#cb5-264" aria-hidden="true" tabindex="-1"></a><span class="co"># Absolute path so the mount works regardless of the process working directory.</span></span>
<span id="cb5-265"><a href="#cb5-265" aria-hidden="true" tabindex="-1"></a>_STATIC_DIR <span class="op">=</span> Path(<span class="va">__file__</span>).resolve().parent <span class="op">/</span> <span class="st">&quot;static&quot;</span></span>
<span id="cb5-266"><a href="#cb5-266" aria-hidden="true" tabindex="-1"></a>_ARCHITECTURE_HTML_PATH <span class="op">=</span> _STATIC_DIR <span class="op">/</span> <span class="st">&quot;architecture.html&quot;</span></span>
<span id="cb5-267"><a href="#cb5-267" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-268"><a href="#cb5-268" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-269"><a href="#cb5-269" aria-hidden="true" tabindex="-1"></a><span class="co"># Pandoc-generated literate program. The HTML comes from weaving Bookly.lit.md</span></span>
<span id="cb5-270"><a href="#cb5-270" aria-hidden="true" tabindex="-1"></a><span class="co"># and contains inline styles (and inline SVG from mermaid-filter), so the</span></span>
<span id="cb5-271"><a href="#cb5-271" aria-hidden="true" tabindex="-1"></a><span class="co"># default strict CSP must be relaxed for this one route.</span></span>
<span id="cb5-272"><a href="#cb5-272" aria-hidden="true" tabindex="-1"></a>_ARCHITECTURE_CSP <span class="op">=</span> (</span>
<span id="cb5-273"><a href="#cb5-273" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;default-src &#39;self&#39;; &quot;</span></span>
<span id="cb5-274"><a href="#cb5-274" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;style-src &#39;self&#39; &#39;unsafe-inline&#39;; &quot;</span></span>
<span id="cb5-275"><a href="#cb5-275" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;script-src &#39;none&#39;; &quot;</span></span>
<span id="cb5-276"><a href="#cb5-276" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;img-src &#39;self&#39; data:; &quot;</span></span>
<span id="cb5-277"><a href="#cb5-277" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;object-src &#39;none&#39;; &quot;</span></span>
<span id="cb5-278"><a href="#cb5-278" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;base-uri &#39;none&#39;; &quot;</span></span>
<span id="cb5-279"><a href="#cb5-279" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;frame-ancestors &#39;none&#39;&quot;</span></span>
<span id="cb5-280"><a href="#cb5-280" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb5-281"><a href="#cb5-281" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-282"><a href="#cb5-282" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-283"><a href="#cb5-283" aria-hidden="true" tabindex="-1"></a><span class="at">@app.get</span>(<span class="st">&quot;/architecture&quot;</span>, response_class<span class="op">=</span>HTMLResponse)</span>
<span id="cb5-284"><a href="#cb5-284" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> architecture() <span class="op">-&gt;</span> HTMLResponse:</span>
<span id="cb5-285"><a href="#cb5-285" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Serve the woven literate program for the Bookly codebase.&quot;&quot;&quot;</span></span>
<span id="cb5-286"><a href="#cb5-286" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span>:</span>
<span id="cb5-287"><a href="#cb5-287" aria-hidden="true" tabindex="-1"></a> html <span class="op">=</span> _ARCHITECTURE_HTML_PATH.read_text(encoding<span class="op">=</span><span class="st">&quot;utf-8&quot;</span>)</span>
<span id="cb5-288"><a href="#cb5-288" aria-hidden="true" tabindex="-1"></a> <span class="cf">except</span> <span class="pp">FileNotFoundError</span>:</span>
<span id="cb5-289"><a href="#cb5-289" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> HTTPException(</span>
<span id="cb5-290"><a href="#cb5-290" aria-hidden="true" tabindex="-1"></a> status_code<span class="op">=</span><span class="dv">404</span>,</span>
<span id="cb5-291"><a href="#cb5-291" aria-hidden="true" tabindex="-1"></a> detail<span class="op">=</span><span class="st">&quot;Architecture document has not been built yet.&quot;</span>,</span>
<span id="cb5-292"><a href="#cb5-292" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb5-293"><a href="#cb5-293" aria-hidden="true" tabindex="-1"></a> response <span class="op">=</span> HTMLResponse(content<span class="op">=</span>html)</span>
<span id="cb5-294"><a href="#cb5-294" aria-hidden="true" tabindex="-1"></a> response.headers[<span class="st">&quot;Content-Security-Policy&quot;</span>] <span class="op">=</span> _ARCHITECTURE_CSP</span>
<span id="cb5-295"><a href="#cb5-295" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> response</span>
<span id="cb5-296"><a href="#cb5-296" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-297"><a href="#cb5-297" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-298"><a href="#cb5-298" aria-hidden="true" tabindex="-1"></a>app.mount(<span class="st">&quot;/static&quot;</span>, StaticFiles(directory<span class="op">=</span><span class="bu">str</span>(_STATIC_DIR)), name<span class="op">=</span><span class="st">&quot;static&quot;</span>)</span></code></pre></div>
</body>
</html>