<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Code on Amit Bahree&#39;s (useless?) insight!</title>
    <link>/tags/code/</link>
    <description>Recent content in Code on Amit Bahree&#39;s (useless?) insight!</description>
    <generator>Hugo -- 0.151.0</generator>
    <language>en-us</language>
    <lastBuildDate>Mon, 27 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="/tags/code/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>🎉Announcing My New Book: Generative AI in Action📚</title>
      <link>/post/2024/10/book-release-genai-in-action/</link>
      <pubDate>Mon, 16 Sep 2024 00:00:00 +0000</pubDate>
      <guid>/post/2024/10/book-release-genai-in-action/</guid>
      <description>A practical guide to unlocking the power of Generative AI</description>
      <content:encoded><![CDATA[<p>In today&rsquo;s rapidly evolving tech world, mastering <strong>Generative AI</strong> isn&rsquo;t just an advantage—it&rsquo;s a necessity. Are you ready to harness its power to transform your business and solve real-world challenges? I&rsquo;m excited to announce that my new book, <em><strong>Generative AI in Action</strong></em>, is now available in print and ebook formats from <a
	
		href = "https://www.manning.com/books/generative-ai-in-action"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Manning Publications
	</span>
</a>. 📖</p>
<h3 id="special-launch-offer-">Special Launch Offer 🌟</h3>
<p>As a thank-you to my early supporters, I&rsquo;m offering an exclusive discount. Use the code <strong>pbbahree</strong> at checkout to receive <strong>45% off</strong> your purchase of <em>Generative AI in Action</em> in all formats (valid through Sept. 30, 2024)!</p>
<p>Get your discounted copy &#x1f449; <a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		» here «
	</span>
</a> .</p>
<p><p>

    <figure>
        <img src="images/Bahree-HI.png" alt="Cover of Generative AI in Action"/>
        <figcaption>Cover of Generative AI in Action</figcaption>
    </figure>

</p></p>
<h3 id="why-you-should-read-generative-ai-in-action-">Why You Should Read <em>Generative AI in Action</em> ✨</h3>
<h4 id="comprehensive-coverage">Comprehensive Coverage</h4>
<p>This book offers an in-depth introduction to Generative AI, covering foundation models, large language models (#LLMs), small language models (#SLMs), and practical applications. From the basics to advanced topics like prompt engineering, <em>Generative AI in Action</em> provides everything you need to start building and scaling AI solutions. Whether you&rsquo;re a beginner or a seasoned professional, you&rsquo;ll find valuable insights to accelerate your AI journey.</p>
<h4 id="real-world-examples">Real-World Examples</h4>
<p>Discover how enterprises across industries are leveraging Generative AI to innovate and solve complex problems. Whether it&rsquo;s improving customer engagement or optimizing operations, the practical examples provided can be directly applied to your projects for immediate impact.</p>
<h4 id="hands-on-techniques">Hands-On Techniques</h4>
<p>Dive into step-by-step guides and hands-on examples for integrating AI models into your workflows. Learn techniques such as:</p>
<ul>
<li><strong>Prompt Engineering</strong>: Craft effective prompts to unlock the full potential of AI models like GPT-4.</li>
<li><strong>Retrieval-Augmented Generation (RAG)</strong>: Enhance your AI models with real-time data for improved accuracy.</li>
<li><strong>Model Adaptation</strong>: Fine-tune AI models to meet your organization&rsquo;s specific needs.</li>
</ul>
<h4 id="ethical-ai-and-best-practices">Ethical AI and Best Practices</h4>
<p>As AI becomes more critical in decision-making, understanding its ethical implications is crucial. <em>Generative AI in Action</em> covers topics like privacy, security, and bias mitigation—ensuring your AI deployments are fair, transparent, and aligned with your organizational values.</p>
<h4 id="expert-insights">Expert Insights</h4>
<p>Drawing from my experience helping build the Azure AI platform, I share insider knowledge on leveraging the latest advancements in AI for your projects. This book provides you with the tools to make the most of cutting-edge technologies like large language models (LLMs) and small language models (SLMs).</p>
<h3 id="advanced-techniques-covered-in-the-book-">Advanced Techniques Covered in the Book 🔥</h3>
<p>Beyond the basics, <em>Generative AI in Action</em> delves into advanced techniques essential for mastering Generative AI in modern enterprise environments:</p>
<ul>
<li><strong>Prompt Engineering</strong>: Strategies like zero-shot, few-shot, and many-shot learning, along with chain-of-thought reasoning, to optimize AI outputs.</li>
<li><strong>Retrieval-Augmented Generation (RAG)</strong>: Combine retrieval-based methods with generative models for real-time, relevant data integration.</li>
<li><strong>Model Adaptation and Fine-Tuning</strong>: Customize generative models to specific tasks using techniques such as low-rank adaptation and reinforcement learning from human feedback (RLHF).</li>
<li><strong>Chatting with Your Data</strong>: Build AI-powered chat systems that interact with enterprise data using vector databases and retrieval techniques.</li>
<li><strong>Scaling and Production Deployment</strong>: Strategies for scaling AI solutions while ensuring performance, reliability, and compliance with enterprise standards.</li>
<li><strong>Evaluations and Benchmarks</strong>: Learn to evaluate and benchmark AI models using traditional metrics and cutting-edge frameworks.</li>
</ul>
<!-- ### A Special Thanks 🙌

A huge thank you to [Eric Boyd](https://www.linkedin.com/in/emboyd/), CVP Engineering, AI Platform at Microsoft, for writing the foreword for *Generative AI in Action*. His insights into AI's transformative power help set the stage for how this technology will shape the future.

I'd also like to express my gratitude to [Wee Hyong Tok](https://www.linkedin.com/in/weehyongtok/), my technical editor. His expertise in AI and data has been invaluable in ensuring that this book is technically robust and accessible for developers, data scientists, and enterprise leaders. -->
<h3 id="explore-the-github-repository-">Explore the GitHub Repository 💾</h3>
<p>For those eager to dive into the code, the book has a companion GitHub repository filled with examples and projects to get you started. Check it out at <a
	
		href = "https://bit.ly/GenAIBook"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bit.ly/GenAIBook
	</span>
</a>. Explore the code, experiment, and start building your AI-powered solutions today.</p>
<h3 id="get-your-copy-today-">Get Your Copy Today! 🛒</h3>
<p>Don&rsquo;t miss this opportunity to lead the AI revolution within your organization. Order your copy of <em><a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a></em> and use the code <strong>pbbahree</strong> to receive <strong>45% off</strong> (valid through Sept. 30, 2024). Transform your organization&rsquo;s AI capabilities today!</p>
<p><em>With gratitude</em> 💚</p>
<p>Amit Bahree.</p>
<hr>
<p><em>In the world of AI, there&rsquo;s a thrill,</em><br>
<em>With &ldquo;Generative AI in Action,&rdquo; you&rsquo;ll skill.</em><br>
<em>From prompts to fine-tuning,</em><br>
<em>Your projects are blooming,</em><br>
<em>Grab your copy, and master the drill!</em></p>
<hr>
]]></content:encoded>
    </item>
    <item>
      <title>Benchmarking big OSS LLMs on a 16x H200 cluster</title>
      <link>/post/2026/04/benchmarking-oss-llms/</link>
      <pubDate>Mon, 27 Apr 2026 00:00:00 +0000</pubDate>
      <guid>/post/2026/04/benchmarking-oss-llms/</guid>
      <description>A technical deep dive into benchmarking Qwen, Kimi, DeepSeek, GLM, MiniMax, and Llama-family open models on a 16x H200 cluster.</description>
      <content:encoded><![CDATA[<p>Over the last several benchmark cycles, I kept coming back to the same practical question: once you hold the hardware and methodology constant, which large open models are actually pleasant to serve, which ones merely load, and which ones become operationally awkward the moment you move beyond a demo?</p>
<p>This post is a technical deep dive into that question. Instead of presenting a generic leaderboard, I focus on the details that usually matter in real deployments: throughput under fixed traffic shapes, latency behavior, scaling across <code>8x</code> and <code>16x H200</code> shapes, and the caveats that only show up when you try to run these models end to end.</p>
<p>It also grew out of a public <code>r/LocalLLaMA</code> thread asking what people most wanted to see tested on this hardware. The model set in this post is therefore not just a random grab bag. It reflects the cluster bring-up requests that came up most often, plus the operational work required to turn those requests into comparable benchmark results.</p>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><code>Llama 4 Scout</code> and <code>MiniMax M2.1</code> were the strongest overall performers in this benchmark set.</li>
<li>In several cases, <code>8x H200</code> was a better serving shape than <code>16x H200</code> for the same workload mix.</li>
<li><code>DeepSeek V4 Flash</code> was healthy and interesting, especially on long-context runs, while <code>DeepSeek V4 Pro</code> only produced fallback-shape numbers.</li>
<li>A fixed benchmark matrix plus Langfuse validation mattered almost as much as the raw throughput numbers.</li>
</ul>
<h2 id="1-hardware-and-profiles">1. Hardware and Profiles</h2>
<p>The benchmark environment is a <code>16 x H200</code> cluster across two nodes, but the machine shape is important because this is not a generic collection of GPUs. The cluster is built on <strong>Dell PowerEdge XE9680L</strong> servers, each with <strong>8 x NVIDIA H200</strong>, dual <strong>Intel Xeon Platinum 8570</strong> CPUs, and <strong>2.0 TiB</strong> of system RAM. Each node also carries <strong>8 x 3.84 TB Dell U.2 NVMe</strong> drives plus a <strong>Dell BOSS-N1</strong> boot device.</p>
<p>On the data-plane side, each node exposes <strong>8 active 400G ConnectX-7 InfiniBand links</strong>, which is <code>3.2 Tb/s</code> of raw InfiniBand link rate per node, along with <code>2</code> active <code>200G</code> BlueField-3 / ConnectX-7 Ethernet links. So at the cluster level, this is roughly <strong>2.30 TB of aggregate HBM</strong>, <strong>4.0 TiB of host RAM</strong>, and a genuinely fast multi-rail fabric rather than a generic dual-server setup.</p>
<p><p>

    <figure>
        <img src="images/test-cluster-overview.png" alt="Test Cluster"/>
        <figcaption>Figure 1: Test cluster overview</figcaption>
    </figure>

</p></p>
<p>For this study, the primary serving runtime is <strong>vLLM</strong>, and the benchmark profiles are intentionally fixed so the comparisons do not drift from one model to the next. Every model was measured against the same three workload profiles:</p>
<ul>
<li><code>1024 in / 256 out / concurrency 1</code></li>
<li><code>1024 in / 256 out / concurrency 16</code></li>
<li><code>8192 in / 256 out / concurrency 4</code></li>
</ul>
<p>For anyone new to this notation:</p>
<ul>
<li><strong><code>1024 in</code></strong> means each request starts with a prompt of about 1024 input tokens.</li>
<li><strong><code>256 out</code></strong> means each request is allowed to generate up to about 256 output tokens.</li>
<li><strong><code>concurrency N</code></strong> means how many requests are in flight at the same time.</li>
</ul>
<p>What each target represents in practice:</p>
<table>
  <thead>
      <tr>
          <th>Target</th>
          <th>What it represents</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>1024 in / 256 out / concurrency 1</code></td>
          <td>Single-user responsiveness (best for reading latency and per-token decode behavior).</td>
      </tr>
      <tr>
          <td><code>1024 in / 256 out / concurrency 16</code></td>
          <td>Loaded serving throughput (best for seeing how well the model holds up under parallel demand).</td>
      </tr>
      <tr>
          <td><code>8192 in / 256 out / concurrency 4</code></td>
          <td>Long-context behavior (best for testing heavier prompt processing with moderate parallelism).</td>
      </tr>
  </tbody>
</table>
<p>Throughout the post, results are labeled with shorthand profile names that encode the same information:</p>
<table>
  <thead>
      <tr>
          <th>Profile shorthand</th>
          <th>Meaning</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td>Single-user latency profile (<code>1024</code> input tokens, <code>256</code> output tokens, concurrency <code>1</code>).</td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td>Loaded serving throughput profile (<code>1024</code> input tokens, <code>256</code> output tokens, concurrency <code>16</code>).</td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td>Long-context profile (<code>8192</code> input tokens, <code>256</code> output tokens, concurrency <code>4</code>).</td>
      </tr>
  </tbody>
</table>
<p>Where a model was tested on multiple hardware shapes, the suffix <code>-16x</code> (or <code>-8x</code>) is appended to the profile name to indicate which shape that row covers, e.g. <code>latency-1024x256-c1-16x</code> means the latency profile run on the <code>16x H200</code> shape.</p>
<p>Later tables also use runtime topology labels such as <code>TP</code>, <code>PP</code>, <code>DP</code>, and <code>EP</code>. These describe how the model was distributed across GPUs during serving:</p>
<ul>
<li><code>TP</code> = tensor parallelism, meaning tensor operations are split across multiple GPUs</li>
<li><code>PP</code> = pipeline parallelism, meaning different layers or blocks are split into sequential pipeline stages</li>
<li><code>DP</code> = data parallelism, meaning multiple replicas process different requests in parallel</li>
<li><code>EP</code> = expert parallelism, meaning MoE experts are distributed across GPUs</li>
</ul>
<p>So <code>TP=8, PP=2</code> means the model was served with <code>8</code>-way tensor parallelism and <code>2</code> pipeline stages, which typically implies a <code>16</code>-GPU deployment shape for that run.</p>
<p>The results tables in each model section report three metrics alongside the profile label:</p>
<ul>
<li><code>Output tok/s</code> — aggregate output throughput across all concurrent requests. This is the headline number: how many tokens per second the serving stack is generating in total under that workload shape.</li>
<li><code>TTFT (ms)</code> — time to first token. How long from when the request was sent until the first output token arrived back. Lower is better. TTFT reflects prefill time, KV cache allocation, and scheduling overhead combined, and it is the number that determines how responsive the model feels to a user waiting for a reply.</li>
<li><code>TPOT (ms)</code> — time per output token. The average time between consecutive output tokens after the first one. Lower is better. TPOT reflects decode speed and is what determines whether a streaming response feels smooth or choppy once it starts.</li>
</ul>
<p>The reason all three matter together: a model can post high aggregate tok/s at <code>c16</code> but still have a painful user experience if TTFT is high, because every user waits that long before seeing any output. Conversely, a model with modest tok/s but low TPOT can feel snappier than the numbers suggest.</p>
<p>The model list below is presented in no particular order. These are the large open models that were both available and runnable during the benchmark window, spanning a mix of architectures, training approaches, and quantization formats. It is not an exhaustive survey of the open-model ecosystem, but it is a practical cross-section of the models people are actively evaluating and that I could run with enough stability to generate comparable results.</p>
<ul>
<li><code>Qwen/Qwen3-235B-A22B-Instruct-2507</code></li>
<li><code>moonshotai/Kimi-K2.6</code></li>
<li><code>deepseek-ai/DeepSeek-V4-Flash</code></li>
<li><code>deepseek-ai/DeepSeek-V4-Pro</code></li>
<li><code>meta-llama/Llama-4-Scout-17B-16E-Instruct</code></li>
<li><code>zai-org/GLM-5.1-FP8</code></li>
<li><code>MiniMaxAI/MiniMax-M2.1</code></li>
<li><code>mistralai/Mistral-Large-3-675B-Instruct-2512</code></li>
</ul>
<p>The goal was straightforward: one fixed benchmark suite, one cluster, several large open models, and enough implementation detail that the results are useful beyond this specific environment.</p>
<h2 id="2-method">2. Method</h2>
<p>All of the vLLM runs use the same serving profiles and the same result format. Each saved result includes the exact model name, prompt length, output length, concurrency, request rate, hardware label, engine, and timestamp. That consistency matters because a standalone &ldquo;tokens per second&rdquo; number stops being useful the moment people start changing prompt length, output length, or concurrency and still try to compare the outcome directly.</p>
<p>Not every model ran on the same vLLM build, and that was deliberate rather than inconsistent. The default lane was the stable <code>v0.19.1</code> release. The exceptions were models where the generic stable image was either not the supported path, not feature-complete enough for that model&rsquo;s runtime requirements, or not what the model vendor&rsquo;s integration expected:</p>
<ul>
<li><strong>GLM-5.1-FP8</strong> used <code>v0.19.1.dev1</code> — the FP8 and multi-node bring-up path for GLM required a newer-than-stable build to work cleanly.</li>
<li><strong>MiniMax M2.1</strong> used <code>v0.19.1rc1.dev203</code> via the <code>vllm/vllm-openai:minimax27</code> image — MiniMax ships its own official runtime lane with MiniMax-specific parser and expert-parallel behavior; the stock image is not the right lane for this model.</li>
<li><strong>DeepSeek V4 Flash and V4 Pro</strong> both used <code>vllm/vllm-openai:deepseekv4-cu130</code> — DeepSeek V4 requires the dedicated CUDA image; the generic image is not the supported serving path.</li>
</ul>
<p>The short version: deviations happened only when the model or runtime integration required a special lane. Where the engine lane affected the result, it is noted in the model-specific metadata block.</p>
<h3 id="21-langfuse">2.1 Langfuse</h3>
<p><a
	
		href = "https://langfuse.com"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Langfuse
	</span>
</a> is the observability layer I used to verify that a run was genuinely healthy from end to end. In other words, the bar was not merely &ldquo;the server came up&rdquo; but &ldquo;the model served requests correctly, produced output, and emitted traces that matched the saved benchmark artifacts.&rdquo;</p>
<p>Each run emitted traces tagged with model identity, profile (<code>c1</code>, <code>c16</code>, <code>c4</code>), and hardware shape. Those traces served as a cross-check against the saved result files. Only runs with both complete result files and successful traces were counted as comparable in the summary tables.</p>
<p><p>

    <figure>
        <img src="images/test-cluster-langfuse.png" alt="Langfuse trace view showing cluster benchmark runs across all models"/>
        <figcaption>Figure 2: Langfuse tracing view for the GPU-Cluster-Benchmark project, showing completed traces for every model in this post. Each trace row contains the benchmark profile, model identity, and output throughput in the input/output fields.</figcaption>
    </figure>

</p></p>
<h2 id="3-quick-comparison-throughput-first">3. Quick Comparison (Throughput-First)</h2>
<p>If you want the numbers before the per-model context, this is the place to start. The tables and charts below cover all comparable runs side by side.</p>
<h3 id="31-top-3-by-output-throughput-toks-per-profile">3.1 Top 3 by Output Throughput (tok/s) per Profile</h3>
<p>The table below answers a simple question: which model led each workload profile? Each profile represents a different traffic shape, so the rankings should be interpreted within a profile rather than across profiles. Higher output tok/s means more generated tokens per second across all in-flight requests.</p>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th style="text-align: right">#1 (highest tok/s)</th>
          <th style="text-align: right">#2</th>
          <th style="text-align: right">#3</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td style="text-align: right">MiniMax M2.1 (<code>145.94</code> tok/s)</td>
          <td style="text-align: right">Llama 4 Scout (<code>126.70</code> tok/s)</td>
          <td style="text-align: right">Mistral Large 3 (<code>93.07</code> tok/s)</td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td style="text-align: right">Llama 4 Scout (<code>1378.30</code> tok/s)</td>
          <td style="text-align: right">MiniMax M2.1 (<code>1358.19</code> tok/s)</td>
          <td style="text-align: right">Qwen 235B (<code>643.56</code> tok/s)</td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td style="text-align: right">Llama 4 Scout (<code>404.41</code> tok/s)</td>
          <td style="text-align: right">MiniMax M2.1 (<code>379.29</code> tok/s)</td>
          <td style="text-align: right">DeepSeek V4 Flash (<code>220.59</code> tok/s)</td>
      </tr>
  </tbody>
</table>
<h3 id="32-completed-runs-side-by-side-output-throughput">3.2 Completed Runs Side-by-Side (Output Throughput)</h3>
<p>Where the Top 3 table highlights the leaders, this table shows the full completed field so every model can be compared across the same three benchmark profiles at once. The values here are output throughput (tok/s), and the Notes column captures important caveats such as fallback topologies.</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Hardware shape used</th>
          <th style="text-align: right">c1 tok/s</th>
          <th style="text-align: right">c16 tok/s</th>
          <th style="text-align: right">c4 tok/s</th>
          <th>Notes</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Llama 4 Scout</td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>126.70</code></td>
          <td style="text-align: right"><code>1378.30</code></td>
          <td style="text-align: right"><code>404.41</code></td>
          <td>Best overall in this fixed profile mix</td>
      </tr>
      <tr>
          <td>MiniMax M2.1</td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>145.94</code></td>
          <td style="text-align: right"><code>1358.19</code></td>
          <td style="text-align: right"><code>379.29</code></td>
          <td>Fastest c1 profile</td>
      </tr>
      <tr>
          <td>Mistral Large 3</td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>93.07</code></td>
          <td style="text-align: right"><code>554.50</code></td>
          <td style="text-align: right"><code>199.59</code></td>
          <td>Stable baseline, mid-pack throughput</td>
      </tr>
      <tr>
          <td>GLM-5.1-FP8</td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>88.66</code></td>
          <td style="text-align: right"><code>509.93</code></td>
          <td style="text-align: right"><code>163.37</code></td>
          <td><code>16x</code> scaling regressed</td>
      </tr>
      <tr>
          <td>DeepSeek V4 Flash</td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>69.96</code></td>
          <td style="text-align: right"><code>543.13</code></td>
          <td style="text-align: right"><code>220.59</code></td>
          <td><code>8x</code> helps mostly on long-context</td>
      </tr>
      <tr>
          <td>Kimi K2.6</td>
          <td><code>16x H200</code></td>
          <td style="text-align: right"><code>64.38</code></td>
          <td style="text-align: right"><code>470.52</code></td>
          <td style="text-align: right"><code>179.45</code></td>
          <td>Stable completed run</td>
      </tr>
      <tr>
          <td>Qwen 235B</td>
          <td><code>16x H200</code></td>
          <td style="text-align: right"><code>56.46</code></td>
          <td style="text-align: right"><code>643.56</code></td>
          <td style="text-align: right"><code>170.47</code></td>
          <td>Stable completed run</td>
      </tr>
      <tr>
          <td>DeepSeek V4 Pro</td>
          <td><code>8x H200</code> fallback (<code>TP=8</code>, eager)</td>
          <td style="text-align: right"><code>6.43</code></td>
          <td style="text-align: right"><code>90.10</code></td>
          <td style="text-align: right"><code>23.27</code></td>
          <td>Intended <code>DP+EP</code> lane still blocked</td>
      </tr>
  </tbody>
</table>
<p>The three charts below show output throughput (tok/s) for all models, split by workload profile so each chart can use an appropriate scale.</p>
<p>What each chart presents:</p>
<ul>
<li><code>c1</code> (<code>latency-1024x256-c1</code>): single-user responsiveness at concurrency <code>1</code></li>
<li><code>c16</code> (<code>serve-1024x256-c16</code>): loaded serving throughput at concurrency <code>16</code></li>
<li><code>c4</code> (<code>longctx-8192x256-c4</code>): long-context behavior with <code>8192</code>-token prompts at concurrency <code>4</code></li>
</ul>
<p><strong>Single-user latency (<code>c1</code>) - output tok/s, all models</strong></p>
<figure class="align-center " id="fig3-latency">
    <pre class="mermaid">xychart-beta
  title &#34;latency-1024x256-c1: output tok/s (higher = better)&#34;
  x-axis [&#34;MiniMax M2.1&#34;, &#34;Llama 4 Scout&#34;, &#34;Mistral L3&#34;, &#34;GLM-5.1-FP8&#34;, &#34;DS Flash&#34;, &#34;Kimi K2.6&#34;, &#34;Qwen 235B&#34;, &#34;DS Pro*&#34;]
  y-axis &#34;output tok/s&#34; 0 --&gt; 160
  bar [145.94, 126.70, 93.07, 88.66, 69.96, 64.38, 56.46, 6.43]</pre>
    <figcaption>Figure 3: Single-user latency output throughput across all models</figcaption>
</figure>
<p><strong>Loaded serving throughput (<code>c16</code>) - output tok/s, all models</strong></p>
<figure class="align-center " id="fig4-serve">
    <pre class="mermaid">xychart-beta
  title &#34;serve-1024x256-c16: output tok/s (higher = better)&#34;
  x-axis [&#34;Llama 4 Scout&#34;, &#34;MiniMax M2.1&#34;, &#34;Qwen 235B&#34;, &#34;Mistral L3&#34;, &#34;DS Flash&#34;, &#34;GLM-5.1-FP8&#34;, &#34;Kimi K2.6&#34;, &#34;DS Pro*&#34;]
  y-axis &#34;output tok/s&#34; 0 --&gt; 1500
  bar [1378.30, 1358.19, 643.56, 554.50, 543.13, 509.93, 470.52, 90.10]</pre>
    <figcaption>Figure 4: Loaded serving throughput across all models</figcaption>
</figure>
<p><strong>Long-context (<code>c4</code>) - output tok/s, all models</strong></p>
<figure class="align-center " id="fig5-longctx">
    <pre class="mermaid">xychart-beta
  title &#34;longctx-8192x256-c4: output tok/s (higher = better)&#34;
  x-axis [&#34;Llama 4 Scout&#34;, &#34;MiniMax M2.1&#34;, &#34;DS Flash&#34;, &#34;Mistral L3&#34;, &#34;Kimi K2.6&#34;, &#34;Qwen 235B&#34;, &#34;GLM-5.1-FP8&#34;, &#34;DS Pro*&#34;]
  y-axis &#34;output tok/s&#34; 0 --&gt; 450
  bar [404.41, 379.29, 220.59, 199.59, 179.45, 170.47, 163.37, 23.27]</pre>
    <figcaption>Figure 5: Long-context output throughput across all models</figcaption>
</figure>
<p>* DS Pro ran on a fallback topology (<code>TP=8</code>, <code>--enforce-eager</code>) - see the DeepSeek V4 Pro section for context.</p>
<h2 id="4-model-status-snapshot">4. Model Status Snapshot</h2>
<p>Most models completed cleanly, but a few required fallback topologies or other caveats that materially change how their results should be interpreted. <code>DeepSeek V4 Pro</code> is the main exception: its published numbers come from a fallback deployment shape rather than the intended lane.</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Final status</th>
          <th>See section</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Llama 4 Scout (official)</td>
          <td>Completed (<code>8x</code> + <code>16x</code> tested)</td>
          <td><code>Section 5</code></td>
      </tr>
      <tr>
          <td>Llama 4 Scout (unsloth)</td>
          <td>Blocked / unreliable</td>
          <td><code>Section 5 note</code></td>
      </tr>
      <tr>
          <td>MiniMax M2.1</td>
          <td>Completed (<code>8x</code> + <code>16x</code> tested)</td>
          <td><code>Section 11</code></td>
      </tr>
      <tr>
          <td>Mistral Large 3</td>
          <td>Completed (<code>8x</code>)</td>
          <td><code>Section 12</code></td>
      </tr>
      <tr>
          <td>GLM-5.1-FP8</td>
          <td>Completed (<code>8x</code> + <code>16x</code> tested)</td>
          <td><code>Section 10</code></td>
      </tr>
      <tr>
          <td>DeepSeek V4 Flash</td>
          <td>Completed (<code>deepseekv4-cu130</code>, <code>4x</code> + <code>8x</code> tested)</td>
          <td><code>Section 8</code></td>
      </tr>
      <tr>
          <td>DeepSeek V4 Pro</td>
          <td>Completed on fallback shape (<code>TP=8</code>, eager)</td>
          <td><code>Section 9</code></td>
      </tr>
      <tr>
          <td>Kimi K2.6</td>
          <td>Completed</td>
          <td><code>Section 6</code></td>
      </tr>
      <tr>
          <td>Qwen 235B</td>
          <td>Completed</td>
          <td><code>Section 7</code></td>
      </tr>
  </tbody>
</table>
<h2 id="5-llama-4-official-scout">5. Llama 4 Official Scout</h2>
<p>Llama 4 Scout ended up being one of the clearest scaling tests in the whole post because it ran cleanly on both shapes and produced directly comparable results. The question here is simple: once the benchmark matrix is held constant, does moving from a single-node <code>8x H200</code> lane to a two-node <code>16x H200</code> deployment actually improve the serving outcome?</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>meta-llama/Llama-4-Scout-17B-16E-Instruct</code></li>
<li>engine: <code>vLLM v0.19.1</code></li>
<li>status: completed (<code>8x</code> and <code>16x</code> lanes)</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th>Shape</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>126.70</code></td>
          <td style="text-align: right"><code>103.83</code></td>
          <td style="text-align: right"><code>7.51</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>1378.30</code></td>
          <td style="text-align: right"><code>396.57</code></td>
          <td style="text-align: right"><code>9.73</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>404.41</code></td>
          <td style="text-align: right"><code>368.10</code></td>
          <td style="text-align: right"><code>8.14</code></td>
      </tr>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>PP=2</code>)</td>
          <td style="text-align: right"><code>71.19</code></td>
          <td style="text-align: right"><code>230.68</code></td>
          <td style="text-align: right"><code>13.20</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>PP=2</code>)</td>
          <td style="text-align: right"><code>834.80</code></td>
          <td style="text-align: right"><code>520.57</code></td>
          <td style="text-align: right"><code>16.62</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>PP=2</code>)</td>
          <td style="text-align: right"><code>228.72</code></td>
          <td style="text-align: right"><code>344.43</code></td>
          <td style="text-align: right"><code>15.57</code></td>
      </tr>
  </tbody>
</table>
<p>As a reminder, <code>TP=8, PP=2</code> means the model was served with <code>8</code>-way tensor parallelism and <code>2</code> pipeline stages, which typically implies a <code>16</code>-GPU deployment shape for that run. That makes this a clean apples-to-apples scaling comparison: the <code>16x H200</code> rows show the intended two-node path for this model, the <code>8x H200</code> rows show the single-node alternative, and both lanes produced complete benchmark artifacts with matching Langfuse traces.</p>
<p>The result is clear — scaling from <code>8</code> to <code>16</code> GPUs was not beneficial for <code>Llama 4 Scout</code> on this workload mix:</p>
<ul>
<li>the single-request <code>c1</code> profile got worse</li>
<li>the <code>c16</code> throughput profile also got worse</li>
<li>the long-context profile only improved slightly on TTFT, while overall throughput and per-token decode still got worse</li>
</ul>
<p>In practical terms, official Scout is a strong and fully benchmarkable <code>8x H200</code> result, but not a good candidate for this <code>16x H200</code> serving shape. The separate <code>unsloth</code> Scout lane never produced stable comparable runs, so it is excluded from the cross-model comparison.</p>
<h2 id="6-kimi-k26">6. Kimi K2.6</h2>
<p>Kimi K2.6 completed on <code>16x H200</code> using the standard <code>vLLM</code> path. Unlike some of the other models in this post, there is no alternate shape comparison here, so the table below should be read as the canonical Kimi reference point.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>moonshotai/Kimi-K2.6</code></li>
<li>engine: <code>vLLM v0.19.1</code></li>
<li>shape: <code>16x H200</code></li>
<li>status: completed</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td style="text-align: right"><code>64.38</code></td>
          <td style="text-align: right"><code>229.46</code></td>
          <td style="text-align: right"><code>14.69</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td style="text-align: right"><code>470.52</code></td>
          <td style="text-align: right"><code>421.06</code></td>
          <td style="text-align: right"><code>31.72</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td style="text-align: right"><code>179.45</code></td>
          <td style="text-align: right"><code>888.37</code></td>
          <td style="text-align: right"><code>18.16</code></td>
      </tr>
  </tbody>
</table>
<p>The main takeaway is that the final Kimi run was fully benchmarkable end to end — not just running without crashing, but producing clean, complete artifacts at every profile. These are the numbers used in the cross-model comparison.</p>
<h2 id="7-qwen-235b">7. Qwen 235B</h2>
<p>Qwen 235B completed on <code>16x H200</code> using the standard <code>vLLM</code> path. The values below are the Qwen reference point used throughout the comparison tables and charts.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>Qwen/Qwen3-235B-A22B-Instruct-2507</code></li>
<li>engine: <code>vLLM v0.19.1</code></li>
<li>shape: <code>16x H200</code></li>
<li>status: completed</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td style="text-align: right"><code>56.46</code></td>
          <td style="text-align: right"><code>191.56</code></td>
          <td style="text-align: right"><code>17.03</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td style="text-align: right"><code>643.56</code></td>
          <td style="text-align: right"><code>395.10</code></td>
          <td style="text-align: right"><code>22.56</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td style="text-align: right"><code>170.47</code></td>
          <td style="text-align: right"><code>549.22</code></td>
          <td style="text-align: right"><code>20.62</code></td>
      </tr>
  </tbody>
</table>
<p>Qwen produced stable, usable benchmark numbers across all three profiles, but did not challenge the top performers in this fixed workload mix.</p>
<h2 id="8-deepseek-v4-flash">8. DeepSeek V4 Flash</h2>
<p>DeepSeek V4 Flash is interesting here not just for the final numbers, but for how the <code>4x</code> and <code>8x</code> shapes compare. Flash required the dedicated <code>vllm/vllm-openai:deepseekv4-cu130</code> image plus one configuration fix: removing <code>--attention_config.use_fp4_indexer_cache=True</code>, which is Blackwell-only and fails on H200 after weight load.</p>
<p>I benchmarked Flash on both <code>4x H200</code> and <code>8x H200</code> to test local scaling behavior. All runs produced Langfuse traces confirming end-to-end health, and the cross-model comparison table uses the <code>8x</code> row as the canonical Flash entry because that is the shape that best represents the larger benchmark set.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>deepseek-ai/DeepSeek-V4-Flash</code></li>
<li>engine: <code>vLLM deepseekv4-cu130</code></li>
<li>status: completed (<code>4x</code> and <code>8x</code> lanes)</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th>Shape</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>4x H200</code></td>
          <td style="text-align: right"><code>77.46</code></td>
          <td style="text-align: right"><code>306.80</code></td>
          <td style="text-align: right"><code>11.76</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>4x H200</code></td>
          <td style="text-align: right"><code>539.06</code></td>
          <td style="text-align: right"><code>2228.17</code></td>
          <td style="text-align: right"><code>20.34</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>4x H200</code></td>
          <td style="text-align: right"><code>198.86</code></td>
          <td style="text-align: right"><code>1186.05</code></td>
          <td style="text-align: right"><code>15.53</code></td>
      </tr>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>69.96</code></td>
          <td style="text-align: right"><code>508.84</code></td>
          <td style="text-align: right"><code>12.35</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>543.13</code></td>
          <td style="text-align: right"><code>1844.81</code></td>
          <td style="text-align: right"><code>21.66</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>220.59</code></td>
          <td style="text-align: right"><code>974.53</code></td>
          <td style="text-align: right"><code>14.38</code></td>
      </tr>
  </tbody>
</table>
<p>The scaling result is worth noting: <code>8x H200</code> did not uniformly help.</p>
<ul>
<li>The <code>c1</code> single-request profile got slightly worse - the overhead of spreading across more GPUs outweighed any memory bandwidth benefit at that concurrency level.</li>
<li>The <code>c16</code> throughput profile was roughly flat.</li>
<li>The <code>c4</code> long-context profile improved meaningfully - the larger working set genuinely benefits from the extra capacity.</li>
</ul>
<p>The practical conclusion is that <code>4x H200</code> is the better efficiency shape for ordinary Flash serving, while <code>8x</code> starts to make sense primarily when prompt lengths get heavier.</p>
<h2 id="9-deepseek-v4-pro">9. DeepSeek V4 Pro</h2>
<p>DeepSeek V4 Pro comes with an important caveat: the intended <code>DP+EP</code> MoE lane did not stabilize, so the published numbers come from a fallback shape. The intended deployment (<code>DP + EP</code> on <code>16x H200</code> using <code>deepseekv4-cu130</code>) repeatedly failed in the fused MoE router with <code>expected scalar type Long but found Int</code>, reproduced on both <code>16x</code> multi-node and <code>8x</code> single-node attempts, including runs with <code>--enforce-eager</code>. I filed the upstream issue at <a
	
		href = "https://github.com/vllm-project/vllm/issues/40862"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		vllm-project/vllm#40862
	</span>
</a>.</p>
<p>The lane that did work was single-node <code>TP=8</code> with <code>--enforce-eager</code>. The table below should therefore be read as a valid fallback benchmark, not as representative of the intended <code>DP+EP</code> deployment target.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>deepseek-ai/DeepSeek-V4-Pro</code></li>
<li>engine: <code>vLLM deepseekv4-cu130</code></li>
<li>shape: <code>8x H200</code> (fallback: <code>TP=8</code>, <code>--enforce-eager</code>)</li>
<li>status: completed on fallback shape - intended <code>DP+EP</code> lane blocked</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td style="text-align: right"><code>6.43</code></td>
          <td style="text-align: right"><code>222.70</code></td>
          <td style="text-align: right"><code>155.30</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td style="text-align: right"><code>90.10</code></td>
          <td style="text-align: right"><code>3493.92</code></td>
          <td style="text-align: right"><code>158.24</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td style="text-align: right"><code>23.27</code></td>
          <td style="text-align: right"><code>1864.31</code></td>
          <td style="text-align: right"><code>158.86</code></td>
      </tr>
  </tbody>
</table>
<p>The numbers reflect the fallback shape constraints directly: per-token decode is slow across all profiles, TTFT is extremely high under load, and aggregate throughput is far below what the intended <code>DP+EP</code> lane should have produced. These are still real and reproducible results, but they answer a different question than the one the original benchmark plan set out to answer.</p>
<h2 id="10-glm-51-fp8">10. GLM 5.1 FP8</h2>
<p>GLM-5.1-FP8 was tested on both <code>8x</code> and <code>16x H200</code> under the same benchmark profiles. Because GLM-5.1 does not support pipeline parallelism, the two-node lane used <code>TP=8 + DP=2</code>. After a long initial model weight loading stage, both shapes ran cleanly and produced comparable results.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>zai-org/GLM-5.1-FP8</code></li>
<li>engine: <code>vLLM v0.19.1.dev1</code></li>
<li>status: completed (<code>8x</code> and <code>16x</code> lanes)</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th>Shape</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>88.66</code></td>
          <td style="text-align: right"><code>385.24</code></td>
          <td style="text-align: right"><code>9.81</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>509.93</code></td>
          <td style="text-align: right"><code>763.64</code></td>
          <td style="text-align: right"><code>27.79</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>163.37</code></td>
          <td style="text-align: right"><code>1317.81</code></td>
          <td style="text-align: right"><code>19.30</code></td>
      </tr>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>)</td>
          <td style="text-align: right"><code>63.96</code></td>
          <td style="text-align: right"><code>658.52</code></td>
          <td style="text-align: right"><code>13.11</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>)</td>
          <td style="text-align: right"><code>432.03</code></td>
          <td style="text-align: right"><code>944.60</code></td>
          <td style="text-align: right"><code>32.63</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>)</td>
          <td style="text-align: right"><code>132.46</code></td>
          <td style="text-align: right"><code>1309.63</code></td>
          <td style="text-align: right"><code>24.75</code></td>
      </tr>
  </tbody>
</table>
<p>The result is unambiguous: scaling to <code>16x H200</code> made every profile worse. Throughput dropped on all three profiles, TTFT got substantially worse, and TPOT degraded as well. For this benchmark mix, <code>8x H200</code> is the right serving shape for this model.</p>
<h2 id="11-minimax-m21">11. MiniMax M2.1</h2>
<p>MiniMax M2.1 was tested on both <code>8x</code> and <code>16x H200</code>. As with the other dual-shape models, the key question is whether scaling improved the operational result enough to justify the additional complexity. Bring-up was straightforward on the official <code>minimax27</code> runtime (<code>TP=8</code> with expert parallelism), and the <code>8x</code> lane was strong enough to justify a full <code>16x</code> comparison.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>MiniMaxAI/MiniMax-M2.1</code></li>
<li>engine: <code>vLLM v0.19.1rc1.dev203</code> (MiniMax <code>minimax27</code> runtime image)</li>
<li>status: completed (<code>8x</code> and <code>16x</code> lanes)</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th>Shape</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>145.94</code></td>
          <td style="text-align: right"><code>102.29</code></td>
          <td style="text-align: right"><code>6.48</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>1358.19</code></td>
          <td style="text-align: right"><code>235.56</code></td>
          <td style="text-align: right"><code>10.51</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>8x H200</code></td>
          <td style="text-align: right"><code>379.29</code></td>
          <td style="text-align: right"><code>390.94</code></td>
          <td style="text-align: right"><code>8.71</code></td>
      </tr>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>, <code>EP</code>)</td>
          <td style="text-align: right"><code>103.98</code></td>
          <td style="text-align: right"><code>178.73</code></td>
          <td style="text-align: right"><code>8.95</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>, <code>EP</code>)</td>
          <td style="text-align: right"><code>1034.06</code></td>
          <td style="text-align: right"><code>283.14</code></td>
          <td style="text-align: right"><code>13.90</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td><code>16x H200</code> (<code>TP=8</code>, <code>DP=2</code>, <code>EP</code>)</td>
          <td style="text-align: right"><code>292.28</code></td>
          <td style="text-align: right"><code>630.96</code></td>
          <td style="text-align: right"><code>10.81</code></td>
      </tr>
  </tbody>
</table>
<p>The <code>16x H200</code> pass was worse across all three profiles. Like <code>Llama 4 Scout</code> and <code>GLM-5.1-FP8</code>, <code>MiniMax M2.1</code> does not benefit from the two-node shape on this workload mix. The important nuance is that this is not a weak model result; it is a strong model result that simply performs better on the smaller shape.</p>
<h2 id="12-mistral-large-3">12. Mistral Large 3</h2>
<p>Mistral Large 3 was benchmarked on the single-node <code>8x H200</code> shape. It should be read as a stable single-shape reference lane rather than a scaling study: by this point in the benchmark cycle, enough models had already shown weak <code>16x</code> returns for this workload mix that a two-node pass was intentionally skipped. The <code>8x</code> run came up cleanly and produced complete Langfuse traces.</p>
<p><strong>Metadata</strong></p>
<ul>
<li>model: <code>mistralai/Mistral-Large-3-675B-Instruct-2512</code></li>
<li>engine: <code>vLLM v0.19.1</code></li>
<li>shape: <code>8x H200</code></li>
<li>status: completed</li>
</ul>
<table>
  <thead>
      <tr>
          <th>Profile</th>
          <th style="text-align: right">Output tok/s</th>
          <th style="text-align: right">TTFT (ms)</th>
          <th style="text-align: right">TPOT (ms)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>latency-1024x256-c1</code></td>
          <td style="text-align: right"><code>93.07</code></td>
          <td style="text-align: right"><code>308.06</code></td>
          <td style="text-align: right"><code>9.58</code></td>
      </tr>
      <tr>
          <td><code>serve-1024x256-c16</code></td>
          <td style="text-align: right"><code>554.50</code></td>
          <td style="text-align: right"><code>1192.90</code></td>
          <td style="text-align: right"><code>23.73</code></td>
      </tr>
      <tr>
          <td><code>longctx-8192x256-c4</code></td>
          <td style="text-align: right"><code>199.59</code></td>
          <td style="text-align: right"><code>1226.20</code></td>
          <td style="text-align: right"><code>14.79</code></td>
      </tr>
  </tbody>
</table>
<p>This is a solid and reproducible result, but it is not a standout against the top performers in this set. <code>c16</code> TTFT of <code>1193 ms</code> and long-context TTFT of <code>1226 ms</code> are notably higher than <code>MiniMax M2.1</code> and <code>Llama 4 Scout</code>, and throughput stays in the middle of the field across all three profiles.</p>
<h2 id="13-benchmark-pipeline">13. Benchmark Pipeline</h2>
<p>The benchmark pipeline underneath these results is an important part of why the comparison holds together. In this setup, models are served in Docker and benchmarked through the <code>vLLM</code> API, with each run saving both performance metrics and metadata so results stay comparable across models, versions, and hardware shapes.</p>
<p>At a high level, the flow is:</p>
<ol>
<li>Choose model and served model name</li>
<li>Run the standard 3-profile matrix</li>
<li>Monitor live engine throughput while runs execute</li>
<li>Save JSON results with run metadata</li>
<li>Cross-check with Langfuse traces</li>
<li>Aggregate into comparison tables and charts</li>
</ol>
<h3 id="131-core-pipeline-components">13.1 Core Pipeline Components</h3>
<p>Rather than running ad-hoc <code>vllm bench serve</code> commands by hand each time, the pipeline is split into four distinct roles. Each one is lightweight on its own, but together they are what makes the results repeatable and comparable across model runs, hardware shapes, and benchmark sessions.</p>
<ul>
<li><strong>Benchmark runner</strong> - the <code>vllm bench serve</code> command pointed at a live vLLM endpoint. It handles prompt dispatch, concurrency, output token collection, and raw result saving. Everything else depends on this working cleanly.</li>
<li><strong>Matrix runner</strong> - a thin shell wrapper that calls the benchmark runner three times in sequence with fixed parameters for each profile (<code>c1</code>, <code>c16</code>, <code>c4</code>). The only thing that changes between model runs is the model identifier and served model name; input length, output length, prompt count, and concurrency are locked.</li>
<li><strong>Metadata enricher</strong> - a short Python script that reads the saved JSON from each run and adds structured run metadata before archiving. Without this, a result file from three weeks ago is just a pile of numbers with no clear link back to the hardware shape, engine version, or traffic profile that produced it.</li>
<li><strong>Live monitor</strong> - a <code>docker logs</code> tail filtered to the vLLM engine stats lines. It does not produce any saved artifact, but it is useful for spotting problems while a run is still in flight: flat generation throughput, GPU KV cache filling up earlier than expected, or a long tail of slow requests that suggests the serving shape is under-provisioned for the workload.</li>
</ul>
<h3 id="132-single-profile-benchmark-snippet">13.2 Single-Profile Benchmark Snippet</h3>
<p>Before running the full three-profile matrix against a new model, it is worth running a single profile first to confirm the serving lane is actually healthy. A new model or container image can load without error and still fail silently on the first real request, produce garbled output, or hit a memory issue that only shows up under inference load. A single <code>c1</code> run is cheap and fast, and it catches the obvious problems before committing to a longer matrix run that might need to be discarded anyway.</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>vllm bench serve <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --host &lt;api-host&gt; <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --port <span style="color:#f5a97f">8000</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --endpoint /v1/completions <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --model &lt;model-id&gt; <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --served-model-name &lt;served-model-id&gt; <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --dataset-name random <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --random-input-len <span style="color:#f5a97f">1024</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --random-output-len <span style="color:#f5a97f">256</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --num-prompts <span style="color:#f5a97f">20</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --max-concurrency <span style="color:#f5a97f">1</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --request-rate inf <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --temperature <span style="color:#f5a97f">0</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --save-result <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --result-filename ./benchmarks/latency-1024x256-c1.json</span></span></code></pre></div><figcaption>
        <strong>Listing 1: Single-Profile vLLM Benchmark</strong>
    </figcaption>
</figure>
<h3 id="133-standard-matrix-snippet">13.3 Standard Matrix Snippet</h3>
<p>Once the single-profile sanity check passes, the matrix runner runs all three profiles back to back. The key discipline here is that nothing changes between profiles except the input length and concurrency level - the model, endpoint, serving stack, and result format all stay identical. That consistency is what makes the results comparable later, both within a single model and across all models in the post. Without it, you end up with numbers that look like a comparison but are actually measuring different things.</p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/usr/bin/env bash
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#91d7e3">set</span> -euo pipefail
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">MODEL</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;model-id&gt;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">SERVED</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;served-model-id&gt;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>run_profile <span style="color:#91d7e3;font-weight:bold">()</span> <span style="color:#91d7e3;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3">local</span> <span style="color:#f4dbd6">tag</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">&#34;</span> <span style="color:#f4dbd6">in_len</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$2</span><span style="color:#a6da95">&#34;</span> <span style="color:#f4dbd6">out_len</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$3</span><span style="color:#a6da95">&#34;</span> <span style="color:#f4dbd6">prompts</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$4</span><span style="color:#a6da95">&#34;</span> <span style="color:#f4dbd6">conc</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$5</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>  vllm bench serve <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --host &lt;api-host&gt; <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --port <span style="color:#f5a97f">8000</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --endpoint /v1/completions <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --model <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$MODEL</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --served-model-name <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$SERVED</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --dataset-name random <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --random-input-len <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$in_len</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --random-output-len <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$out_len</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --num-prompts <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$prompts</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --max-concurrency <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$conc</span><span style="color:#a6da95">&#34;</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --request-rate inf <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --temperature <span style="color:#f5a97f">0</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --save-result <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    --result-filename <span style="color:#a6da95">&#34;./benchmarks/</span><span style="color:#a6da95">${</span><span style="color:#f4dbd6">tag</span><span style="color:#a6da95">}</span><span style="color:#a6da95">.json&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>run_profile latency-1024x256-c1   <span style="color:#f5a97f">1024</span> <span style="color:#f5a97f">256</span> <span style="color:#f5a97f">20</span>  <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>run_profile serve-1024x256-c16    <span style="color:#f5a97f">1024</span> <span style="color:#f5a97f">256</span> <span style="color:#f5a97f">200</span> <span style="color:#f5a97f">16</span>
</span></span><span style="display:flex;"><span>run_profile longctx-8192x256-c4   <span style="color:#f5a97f">8192</span> <span style="color:#f5a97f">256</span> <span style="color:#f5a97f">50</span>  <span style="color:#f5a97f">4</span></span></span></code></pre></div><figcaption>
        <strong>Listing 2: Standard Benchmark Matrix Runner</strong>
    </figcaption>
</figure>
<h3 id="134-metadata-enrichment-snippet">13.4 Metadata Enrichment Snippet</h3>
<p>The raw JSON that <code>vllm bench serve</code> saves contains throughput, latency, and token count data, but nothing about the context that produced it. No hardware shape, no engine version, no timestamp, no indication of which traffic profile was running. That is fine for a one-off experiment, but it becomes a serious problem the moment you want to compare results across models or revisit a run a few weeks later.</p>
<p>The enrichment step adds a structured <code>run_metadata</code> block to each result file immediately after the run completes. With this in place, every result file is self-describing: it contains both the benchmark output and the exact context needed to understand and reproduce it. It also makes aggregating results into summary tables and charts much more reliable, since the grouping and filtering logic can operate on explicit metadata fields rather than trying to infer context from filenames.</p>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">json</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">datetime</span> <span style="color:#8bd5ca">import</span> datetime, timezone
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>path <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./benchmarks/serve-1024x256-c16.json&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(path, <span style="color:#a6da95">&#34;r&#34;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;utf-8&#34;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>    data <span style="color:#91d7e3;font-weight:bold">=</span> json<span style="color:#91d7e3;font-weight:bold">.</span>load(f)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#91d7e3">isinstance</span>(data, <span style="color:#91d7e3">dict</span>):
</span></span><span style="display:flex;"><span>    data <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#34;benchmark_result&#34;</span>: data}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>data[<span style="color:#a6da95">&#34;run_metadata&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;model&#34;</span>: <span style="color:#a6da95">&#34;&lt;model-id&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;served_model_name&#34;</span>: <span style="color:#a6da95">&#34;&lt;served-model-id&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;benchmark_type&#34;</span>: <span style="color:#a6da95">&#34;serve&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;dataset_name&#34;</span>: <span style="color:#a6da95">&#34;random&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;input_len&#34;</span>: <span style="color:#f5a97f">1024</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;output_len&#34;</span>: <span style="color:#f5a97f">256</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;num_prompts&#34;</span>: <span style="color:#f5a97f">200</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;max_concurrency&#34;</span>: <span style="color:#f5a97f">16</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;request_rate&#34;</span>: <span style="color:#a6da95">&#34;inf&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;endpoint&#34;</span>: <span style="color:#a6da95">&#34;/v1/completions&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;hardware&#34;</span>: <span style="color:#a6da95">&#34;16x H200&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;engine&#34;</span>: <span style="color:#a6da95">&#34;vLLM&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;cluster_label&#34;</span>: <span style="color:#a6da95">&#34;&lt;cluster-label&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;result_tag&#34;</span>: <span style="color:#a6da95">&#34;serve-1024x256-c16&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;saved_at_utc&#34;</span>: datetime<span style="color:#91d7e3;font-weight:bold">.</span>now(timezone<span style="color:#91d7e3;font-weight:bold">.</span>utc)<span style="color:#91d7e3;font-weight:bold">.</span>isoformat(),
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(path, <span style="color:#a6da95">&#34;w&#34;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;utf-8&#34;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>    json<span style="color:#91d7e3;font-weight:bold">.</span>dump(data, f, indent<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>, sort_keys<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    f<span style="color:#91d7e3;font-weight:bold">.</span>write(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>)</span></span></code></pre></div><figcaption>
        <strong>Listing 3: Benchmark Metadata Enrichment</strong>
    </figcaption>
</figure>
<h3 id="135-live-throughput-monitor-snippet">13.5 Live Throughput Monitor Snippet</h3>
<p>The benchmark runner saves results at the end of a run, which means you only find out something went wrong after it is already over. For longer matrix runs - especially on large models with slower TTFT - that is a lot of time to spend waiting on a result that turns out to be invalid. The live monitor is a simple <code>docker logs</code> tail filtered to the vLLM engine stat lines, so you can watch generation throughput, KV cache utilization, and request latency in real time while the run is executing. It does not produce any saved artifact, but it lets you catch a stuck or unhealthy run early and abort rather than waiting for it to finish.</p>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker logs -f &lt;vllm-container&gt; 2&gt;&amp;<span style="color:#f5a97f">1</span> | <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  grep --line-buffered <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  <span style="color:#a6da95">&#39;Avg prompt throughput\|Avg generation throughput\|GPU KV cache usage\|Prefix cache hit rate\|POST /v1/&#39;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 4: Live Throughput Monitor</strong>
    </figcaption>
</figure>
<h3 id="136-validation-and-extension-rules">13.6 Validation and Extension Rules</h3>
<p>The three-profile matrix only stays useful as a comparison baseline if it is actually treated as a baseline. The temptation when benchmarking multiple models is to make small adjustments for each one - a different prompt count here, a slightly different concurrency there, a custom profile added for one model but not others. Each individual change seems reasonable in isolation, but the cumulative effect is a result set where the numbers cannot be compared cleanly because they were not produced under the same conditions.</p>
<p>The rules below are the discipline that keeps that from happening. The standard matrix is the contract. Custom profiles, extra concurrency levels, or hardware variants are extensions that go alongside it, not replacements for it.</p>
<ol>
<li>Always run the standard matrix (<code>c1</code>, <code>c16</code>, <code>c4</code>) first</li>
<li>Only swap model identifiers when comparing model families</li>
<li>Keep hardware/engine labels explicit in metadata</li>
<li>Treat runs as comparable only when both JSON artifacts and Langfuse traces are complete</li>
<li>Add custom profiles as extra runs, not replacements</li>
</ol>
<h2 id="14-summary">14. Summary</h2>
<p>The short version is that benchmarkability mattered almost as much as raw speed. Several models loaded and ran cleanly enough to produce useful, repeatable numbers, but the most important result was how often the simpler <code>8x H200</code> shape outperformed the larger <code>16x H200</code> deployment on this workload mix.</p>
<p>For practical serving, <code>Llama 4 Scout</code> and <code>MiniMax M2.1</code> were the strongest overall performers in this set, especially once latency, loaded throughput, and long-context behavior were considered together. <code>Mistral Large 3</code>, <code>GLM-5.1-FP8</code>, <code>Qwen 235B</code>, and <code>Kimi K2.6</code> all produced usable reference numbers, but they did not challenge the top tier in this fixed profile mix.</p>
<p>The other major takeaway is that topology caveats matter. <code>DeepSeek V4 Flash</code> was healthy and interesting, especially on long-context workloads, while <code>DeepSeek V4 Pro</code> only produced fallback-shape results because the intended <code>DP+EP</code> path did not stabilize. That is exactly why a fixed methodology, saved artifacts, and trace validation are worth the extra effort: they let you separate actual model behavior from deployment-path noise.</p>
<p>If there is one conclusion from the full run, it is this: large-model benchmarking is only useful when the numbers stay attached to the exact serving shape, traffic profile, and validation path that produced them. Once that discipline is in place, even a messy benchmark diary becomes a practical decision document.</p>
<p>The original community request thread also surfaced a separate follow-up idea that deserves its own methodology rather than being mixed into this write-up: controlled comparison work around quantization, KV-cache variants, and task-level coding benchmarks where the model, runtime, and evaluation target are all held fixed.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Making a headless AI assistant observable - without SSH</title>
      <link>/post/2026/04/openclaw-nanoclaw-observability/</link>
      <pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate>
      <guid>/post/2026/04/openclaw-nanoclaw-observability/</guid>
      <description>Real-time status, task management, and event tracing for NanoClaw (OpenClaw fork) - nine features, zero SSH.</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/nanoclaw"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/nanoclaw
	</span>
</a> - full source code</p></blockquote>
<hr>
<p>NanoClaw is a headless AI assistant running on my personal server. It processes messages from WhatsApp, Telegram, and Slack, runs scheduled tasks, and manages conversations with Claude agents in isolated containers. It&rsquo;s been incredibly useful, but it had one major pain point: no visibility into what it was doing or why. If something went wrong - a message didn&rsquo;t get a reply, a task didn&rsquo;t run - the only way to debug was to SSH into the server, tail logs, and piece together what happened. Not ideal when you&rsquo;re on the go and your assistant just&hellip; stops responding.</p>
<p>I use NanoClaw as my personal AI assistant - it handles everything from answering questions, to tracking flights, to giving me a daily morning briefing with F1 standings and weather. It is my agent and allows for me to check on the kind of thing I want to check on from my phone, not have to SSH into a server for.</p>
<p>The problem with a headless service that runs 24/7 is that you can&rsquo;t see what it&rsquo;s doing. When a message doesn&rsquo;t get a reply, or a scheduled task doesn&rsquo;t fire, the debugging workflow is: SSH into the server, tail the pino logs, grep for timestamps, piece together what happened. Not great when you&rsquo;re out and about and your assistant just&hellip; stops responding.</p>
<p>I wanted three things:</p>
<ul>
<li>A way to ask the system &ldquo;what are you doing right now?&rdquo; from the same WhatsApp chat I use to talk to it.</li>
<li>A way to manage scheduled tasks without SSH.</li>
<li>And a way to ask &ldquo;why did you do that?&rdquo; after the fact, with full traceability from triggering event to outbound action.</li>
</ul>
<p>This post covers all three and how we built them in NanoClaw - nine features (~1100 lines of new TypeScript, two new modules, and three new SQLite tables).</p>
<h2 id="tldr">TL;DR</h2>
<p>Given the existing architecture of NanoClaw, we added a suite of observability features that are all accessible from the main WhatsApp group. No SSH, no separate dashboards, no external logging services - just commands you can type in the chat to see what&rsquo;s going on and manage the system.</p>
<p>I added three groups of features to NanoClaw:</p>
<ol>
<li><strong>Real-time visibility</strong> - <code>/status</code> shows uptime, memory, active containers, channels, groups, and task summaries. <code>/status tasks</code> shows the full task list with schedules, next run times, and IDs.</li>
<li><strong>Operational control</strong> - <code>/task pause|resume|delete &lt;id&gt;</code> manages scheduled tasks directly from the chat. No SSH, no restarts.</li>
<li><strong>Event tracing and debugging</strong> - three SQLite tables that trace every action back to its triggering event. Pipeline is instrumented at message ingress, agent output, scheduled tasks, and IPC. Query with <code>/debug last 10</code>, <code>/debug why</code>, <code>/debug event &lt;id&gt;</code>, and <code>/debug report</code>. Auto-prunes after 3 days.</li>
</ol>
<p>All operated entirely from the messaging channel.</p>
<h2 id="1-the-architecture-quick-context">1. The architecture (quick context)</h2>
<p>Before diving in, here&rsquo;s how NanoClaw works at a high level. Understanding this makes the instrumentation decisions clearer. NanoClaw is a OSS fork of OpenClaw, so it shares the same core architecture:</p>
<figure class="align-center " id="fig1">
    <pre class="mermaid">flowchart LR
    WA[WhatsApp] --&gt; ORC[Orchestrator]
    TG[Telegram] --&gt; ORC
    SL[Slack] --&gt; ORC
    ORC --&gt; DB[(SQLite)]
    ORC --&gt; Q[Group Queue]
    Q --&gt; C1[Container 1]
    Q --&gt; C2[Container 2]
    C1 --&gt; IPC[IPC Watcher]
    IPC --&gt; ORC
    SCH[Task Scheduler] --&gt; Q</pre>
    <figcaption>Figure 1: NanoClaw message flow</figcaption>
</figure>
<p>Messages arrive from channels, get stored in SQLite, and the orchestrator polls for new messages every 2 seconds. When a registered group has unprocessed messages, the group queue spawns a container running Claude&rsquo;s Agent SDK. The agent&rsquo;s output streams back through the orchestrator to the originating channel. Scheduled tasks follow the same path but are triggered by a cron/interval scheduler rather than by incoming messages.</p>
<p>The key insight: everything flows through the orchestrator. That&rsquo;s where we intercept commands, instrument actions, and expose state. The group queue manages the container lifecycle. The database is already there for message storage. All the pieces are in place - we just need to wire them up.</p>
<h2 id="2-what-was-built">2. What was built</h2>
<p>Here&rsquo;s the full inventory. Two new modules, six modified files, nine distinct features:</p>
<table>
  <thead>
      <tr>
          <th>#</th>
          <th>Feature</th>
          <th>Type</th>
          <th>Files</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td><code>/status</code> - system dashboard</td>
          <td>Command</td>
          <td><code>status.ts</code>, <code>index.ts</code></td>
      </tr>
      <tr>
          <td>2</td>
          <td><code>/status tasks</code> - task detail view</td>
          <td>Command</td>
          <td><code>status.ts</code>, <code>index.ts</code></td>
      </tr>
      <tr>
          <td>3</td>
          <td><code>/task pause|resume|delete</code> - task management</td>
          <td>Command</td>
          <td><code>status.ts</code>, <code>index.ts</code></td>
      </tr>
      <tr>
          <td>4</td>
          <td><code>GroupQueue.getStatus()</code> - queue introspection</td>
          <td>API</td>
          <td><code>group-queue.ts</code></td>
      </tr>
      <tr>
          <td>5</td>
          <td>Three-table event log schema</td>
          <td>Schema</td>
          <td><code>db.ts</code></td>
      </tr>
      <tr>
          <td>6</td>
          <td><code>logEvent</code> / <code>logAction</code> / <code>logToolCall</code></td>
          <td>Module</td>
          <td><code>event-log.ts</code> (new)</td>
      </tr>
      <tr>
          <td>7</td>
          <td>Pipeline instrumentation</td>
          <td>Instrumentation</td>
          <td><code>index.ts</code>, <code>task-scheduler.ts</code>, <code>ipc.ts</code></td>
      </tr>
      <tr>
          <td>8</td>
          <td><code>/debug last|why|event|report</code></td>
          <td>Command</td>
          <td><code>status.ts</code>, <code>event-log.ts</code></td>
      </tr>
      <tr>
          <td>9</td>
          <td>Auto-pruning with configurable retention</td>
          <td>Config</td>
          <td><code>event-log.ts</code>, <code>config.ts</code></td>
      </tr>
  </tbody>
</table>
<p>All commands are restricted to the <strong>main group</strong> only. Non-main groups are silently ignored, preventing random group members from querying system status or managing tasks.</p>
<h2 id="3-status---real-time-system-dashboard">3. /status - real-time system dashboard</h2>
<p>The <code>/status</code> command assembles information from several subsystems into a single message. It queries the group queue for container states, the database for registered groups and tasks, and formats it all as a WhatsApp-friendly message.</p>
<p>The output is designed to give a quick overview of the system&rsquo;s health and activity at a glance:</p>
<figure id="fig2">
<img src="images/status-output.png" alt="/status output showing uptime, an active container processing an F1 query, WhatsApp channel, and 2 active cron tasks" title="/status command output in WhatsApp">
<figcaption><strong>Figure 2:</strong> /status while the agent is answering an F1 question - containers, channels, groups, and tasks at a glance.</figcaption>
</figure>
<p>What it shows:</p>
<ul>
<li><strong>Uptime and memory</strong> - how long the process has been running, RSS in MB</li>
<li><strong>Timezone</strong> - the configured timezone (important for cron schedules)</li>
<li><strong>Containers</strong> - active/max concurrent, with per-group detail (idle, processing, running task, queued)</li>
<li><strong>Channels</strong> - which messaging channels are connected (WhatsApp, Telegram, etc.)</li>
<li><strong>Groups</strong> - all registered groups with the main group indicator</li>
<li><strong>Tasks</strong> - count of active/paused tasks, next upcoming task with time-until</li>
<li><strong>Remote control</strong> - whether a remote Claude Code session is active</li>
</ul>
<p>The implementation pulls from existing subsystems - no new state tracking was needed:</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> buildStatus(queue: <span style="color:#ed8796">GroupQueue</span>, channels: <span style="color:#ed8796">Channel</span>[])<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#ed8796">string</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> uptime <span style="color:#91d7e3;font-weight:bold">=</span> formatDuration(<span style="color:#91d7e3">Date</span>.now() <span style="color:#91d7e3;font-weight:bold">-</span> startTime);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> mem <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Math</span>.round(process.memoryUsage.rss() <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">1024</span> <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">1024</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> qs <span style="color:#91d7e3;font-weight:bold">=</span> queue.getStatus();       <span style="color:#6e738d;font-style:italic">// new method on GroupQueue
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#c6a0f6">const</span> channelNames <span style="color:#91d7e3;font-weight:bold">=</span> channels.map((ch) <span style="color:#91d7e3;font-weight:bold">=&gt;</span> ch.name).join(<span style="color:#a6da95">&#39;, &#39;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> groups <span style="color:#91d7e3;font-weight:bold">=</span> getAllRegisteredGroups();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> tasks <span style="color:#91d7e3;font-weight:bold">=</span> getAllTasks();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> activeTasks <span style="color:#91d7e3;font-weight:bold">=</span> tasks.filter((t) <span style="color:#91d7e3;font-weight:bold">=&gt;</span> t.status <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;active&#39;</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> rc <span style="color:#91d7e3;font-weight:bold">=</span> getActiveSession();       <span style="color:#6e738d;font-style:italic">// remote control state
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> lines: <span style="color:#ed8796">string</span>[] <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`*</span><span style="color:#a6da95">${</span>ASSISTANT_NAME<span style="color:#a6da95">}</span><span style="color:#a6da95"> Status*`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`----------------`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`Uptime: </span><span style="color:#a6da95">${</span>uptime<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`Memory: </span><span style="color:#a6da95">${</span>mem<span style="color:#a6da95">}</span><span style="color:#a6da95"> MB`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`Timezone: </span><span style="color:#a6da95">${</span>TIMEZONE<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">``</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`*Containers:* </span><span style="color:#a6da95">${</span>qs.activeCount<span style="color:#a6da95">}</span><span style="color:#a6da95">/</span><span style="color:#a6da95">${</span>qs.maxConcurrent<span style="color:#a6da95">}</span><span style="color:#a6da95"> active`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`*Channels:* </span><span style="color:#a6da95">${</span>channelNames <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#a6da95">&#39;none&#39;</span><span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>  ];
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// ... active container details, groups, tasks, remote control
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#c6a0f6">return</span> lines.join(<span style="color:#a6da95">&#39;\n&#39;</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 1: Building the status response</strong>
    </figcaption>
</figure>
<h3 id="31-exposing-queue-internals">3.1 Exposing queue internals</h3>
<p>The <code>GroupQueue</code> class already tracked everything we needed internally: active containers, pending messages, pending tasks, and idle state. It just wasn&rsquo;t exposed. A new <code>getStatus()</code> method surfaces this without leaking internal implementation:</p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>getStatus()<span style="color:#91d7e3;font-weight:bold">:</span> {
</span></span><span style="display:flex;"><span>  activeCount: <span style="color:#ed8796">number</span>;
</span></span><span style="display:flex;"><span>  maxConcurrent: <span style="color:#ed8796">number</span>;
</span></span><span style="display:flex;"><span>  waitingCount: <span style="color:#ed8796">number</span>;
</span></span><span style="display:flex;"><span>  groups: <span style="color:#ed8796">Array</span><span style="color:#91d7e3;font-weight:bold">&lt;</span>{
</span></span><span style="display:flex;"><span>    jid: <span style="color:#ed8796">string</span>;
</span></span><span style="display:flex;"><span>    active: <span style="color:#ed8796">boolean</span>;
</span></span><span style="display:flex;"><span>    idleWaiting: <span style="color:#ed8796">boolean</span>;
</span></span><span style="display:flex;"><span>    isTaskContainer: <span style="color:#ed8796">boolean</span>;
</span></span><span style="display:flex;"><span>    pendingMessages: <span style="color:#ed8796">boolean</span>;
</span></span><span style="display:flex;"><span>    pendingTaskCount: <span style="color:#ed8796">number</span>;
</span></span><span style="display:flex;"><span>  }<span style="color:#91d7e3;font-weight:bold">&gt;</span>;
</span></span><span style="display:flex;"><span>} {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> groups <span style="color:#91d7e3;font-weight:bold">=</span> [];
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (<span style="color:#c6a0f6">const</span> [jid, state] <span style="color:#c6a0f6">of</span> <span style="color:#c6a0f6">this</span>.groups) {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (state.active <span style="color:#91d7e3;font-weight:bold">||</span> state.pendingMessages <span style="color:#91d7e3;font-weight:bold">||</span> state.pendingTasks.length <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span>) {
</span></span><span style="display:flex;"><span>      groups.push({
</span></span><span style="display:flex;"><span>        jid,
</span></span><span style="display:flex;"><span>        active: <span style="color:#ed8796">state.active</span>,
</span></span><span style="display:flex;"><span>        idleWaiting: <span style="color:#ed8796">state.idleWaiting</span>,
</span></span><span style="display:flex;"><span>        isTaskContainer: <span style="color:#ed8796">state.isTaskContainer</span>,
</span></span><span style="display:flex;"><span>        pendingMessages: <span style="color:#ed8796">state.pendingMessages</span>,
</span></span><span style="display:flex;"><span>        pendingTaskCount: <span style="color:#ed8796">state.pendingTasks.length</span>,
</span></span><span style="display:flex;"><span>      });
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> {
</span></span><span style="display:flex;"><span>    activeCount: <span style="color:#ed8796">this.activeCount</span>,
</span></span><span style="display:flex;"><span>    maxConcurrent: <span style="color:#ed8796">MAX_CONCURRENT_CONTAINERS</span>,
</span></span><span style="display:flex;"><span>    waitingCount: <span style="color:#ed8796">this.waitingGroups.length</span>,
</span></span><span style="display:flex;"><span>    groups,
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 2: GroupQueue.getStatus() - queue introspection</strong>
    </figcaption>
</figure>
<p>Only groups with activity (active, pending messages, or pending tasks) are included - no noise from idle groups. The status output translates these states into human-readable labels: &ldquo;idle&rdquo;, &ldquo;processing&rdquo;, &ldquo;running task&rdquo;, or &ldquo;queued&rdquo;.</p>
<h2 id="4-status-tasks---task-detail-view">4. /status tasks - task detail view</h2>
<p>While <code>/status</code> includes a task summary (count + next upcoming), <code>/status tasks</code> gives the full picture. Each task shows its prompt (truncated to 50 chars), schedule type, next run time, last run time, and the task ID you need for management commands.</p>
<figure id="fig3">
<img src="images/status-tasks-output.png" alt="/status tasks showing daily morning briefing with F1 standings, AI news digest, and completed flight tracking tasks" title="/status tasks command output in WhatsApp">
<figcaption><strong>Figure 3:</strong> /status tasks - two active daily crons (morning briefing with F1 + weather, AI news digest) and completed one-offs (flight tracking, reminders).</figcaption>
</figure>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#ed8796">function</span> formatTaskLine(task: <span style="color:#ed8796">ScheduledTask</span>, index: <span style="color:#ed8796">number</span>)<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#ed8796">string</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> status <span style="color:#91d7e3;font-weight:bold">=</span> task.status <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;active&#39;</span> <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">:</span> task.status <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;paused&#39;</span> <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39; [paused]&#39;</span> <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39; [done]&#39;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> schedule <span style="color:#91d7e3;font-weight:bold">=</span> task.schedule_type <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;cron&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">`cron: </span><span style="color:#a6da95">${</span>task.schedule_value<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">:</span> task.schedule_type <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;interval&#39;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">`every </span><span style="color:#a6da95">${</span>task.schedule_value<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>
</span></span><span style="display:flex;"><span>      <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`once`</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> prompt <span style="color:#91d7e3;font-weight:bold">=</span> task.prompt.length <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">50</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">?</span> task.prompt.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">50</span>) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#39;...&#39;</span> <span style="color:#91d7e3;font-weight:bold">:</span> task.prompt;
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> next <span style="color:#91d7e3;font-weight:bold">=</span> task.next_run <span style="color:#91d7e3;font-weight:bold">?</span> formatTimeUntil(task.next_run) <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;n/a&#39;</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> lastRun <span style="color:#91d7e3;font-weight:bold">=</span> task.last_run <span style="color:#91d7e3;font-weight:bold">?</span> formatTimeAgo(task.last_run) <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;never&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> [
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`*</span><span style="color:#a6da95">${</span>index <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span><span style="color:#a6da95">}</span><span style="color:#a6da95">.* </span><span style="color:#a6da95">${</span>prompt<span style="color:#a6da95">}${</span>status<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`   Schedule: </span><span style="color:#a6da95">${</span>schedule<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`   Next: </span><span style="color:#a6da95">${</span>next<span style="color:#a6da95">}</span><span style="color:#a6da95"> | Last: </span><span style="color:#a6da95">${</span>lastRun<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">`   ID: </span><span style="color:#a6da95">${</span>task.id<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>  ].join(<span style="color:#a6da95">&#39;\n&#39;</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 3: Formatting a task line</strong>
    </figcaption>
</figure>
<p>Tasks are grouped by status - active first, then paused, then completed. The relative time formatting (<code>in 2h 15m</code>, <code>3m 42s ago</code>) makes it easy to see at a glance what&rsquo;s coming up and what ran recently.</p>
<h2 id="5-task---operational-control">5. /task - operational control</h2>
<p>The <code>/task</code> command turns the chat into a control plane. No more SSH-ing in to pause a runaway task or clean up a completed one.</p>
<table>
  <thead>
      <tr>
          <th>Command</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>/task pause &lt;id&gt;</code></td>
          <td>Pause an active task (stops scheduling, preserves config)</td>
      </tr>
      <tr>
          <td><code>/task resume &lt;id&gt;</code></td>
          <td>Resume a paused task</td>
      </tr>
      <tr>
          <td><code>/task delete &lt;id&gt;</code></td>
          <td>Delete a task and its run history</td>
      </tr>
  </tbody>
</table>
<figure id="fig4">
<img src="images/task-command-output.png" alt="/task pause and /task resume on the AI news digest task" title="/task command output in WhatsApp">
<figcaption><strong>Figure 4:</strong> Pausing and resuming the AI news digest task - no SSH, no restart.</figcaption>
</figure>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> handleTaskCommand(
</span></span><span style="display:flex;"><span>  args: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>)<span style="color:#91d7e3;font-weight:bold">:</span> { ok: <span style="color:#ed8796">true</span>; message: <span style="color:#ed8796">string</span> } <span style="color:#91d7e3;font-weight:bold">|</span> { ok: <span style="color:#ed8796">false</span>; error: <span style="color:#ed8796">string</span> } {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> parts <span style="color:#91d7e3;font-weight:bold">=</span> args.trim().split(<span style="color:#8bd5ca">/\s+/</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> action <span style="color:#91d7e3;font-weight:bold">=</span> parts[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">?</span>.toLowerCase();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> taskId <span style="color:#91d7e3;font-weight:bold">=</span> parts.slice(<span style="color:#f5a97f">1</span>).join(<span style="color:#a6da95">&#39; &#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> task <span style="color:#91d7e3;font-weight:bold">=</span> getTaskById(taskId);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">!</span>task) <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">false</span>, error<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Task not found: </span><span style="color:#a6da95">${</span>taskId<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span> };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">switch</span> (action) {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">case</span> <span style="color:#a6da95">&#39;pause&#39;</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">if</span> (task.status <span style="color:#91d7e3;font-weight:bold">!==</span> <span style="color:#a6da95">&#39;active&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">false</span>, error<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Task is already </span><span style="color:#a6da95">${</span>task.status<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span> };
</span></span><span style="display:flex;"><span>      updateTask(taskId, { status<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;paused&#39;</span> });
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">true</span>, message<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Paused: &#34;</span><span style="color:#a6da95">${</span>task.prompt.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">50</span>)<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;`</span> };
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">case</span> <span style="color:#a6da95">&#39;resume&#39;</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">if</span> (task.status <span style="color:#91d7e3;font-weight:bold">!==</span> <span style="color:#a6da95">&#39;paused&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">false</span>, error<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Task is </span><span style="color:#a6da95">${</span>task.status<span style="color:#a6da95">}</span><span style="color:#a6da95">, not paused`</span> };
</span></span><span style="display:flex;"><span>      updateTask(taskId, { status<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;active&#39;</span> });
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">true</span>, message<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Resumed: &#34;</span><span style="color:#a6da95">${</span>task.prompt.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">50</span>)<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;`</span> };
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">case</span> <span style="color:#a6da95">&#39;delete&#39;</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>      deleteTask(taskId);
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">true</span>, message<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Deleted: &#34;</span><span style="color:#a6da95">${</span>task.prompt.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">50</span>)<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;`</span> };
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">default</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span> { ok: <span style="color:#ed8796">false</span>, error<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">`Unknown action: </span><span style="color:#a6da95">${</span>action<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span> };
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 4: Task command handler</strong>
    </figcaption>
</figure>
<p>The result type (<code>{ ok: true; message } | { ok: false; error }</code>) is a pattern used throughout NanoClaw for commands; the caller doesn&rsquo;t need to know the implementation details, just whether it succeeded and what to tell the user. State validation is done upfront (can&rsquo;t pause an already-paused task, can&rsquo;t resume an active one).</p>
<h2 id="6-command-interception">6. Command interception</h2>
<p>All built-in commands (<code>/status</code>, <code>/status tasks</code>, <code>/task</code>, <code>/debug</code>) share the same interception pattern: they&rsquo;re caught at the top of the <code>onMessage</code> callback, <strong>before</strong> the message is stored or processed.</p>
<figure id="listing5"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> channelOpts <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>  onMessage<span style="color:#91d7e3;font-weight:bold">:</span> (chatJid: <span style="color:#ed8796">string</span>, msg: <span style="color:#ed8796">NewMessage</span>) <span style="color:#91d7e3;font-weight:bold">=&gt;</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">const</span> trimmed <span style="color:#91d7e3;font-weight:bold">=</span> msg.content.trim();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Built-in commands - intercept before storage
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span> (trimmed <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;/status&#39;</span> <span style="color:#91d7e3;font-weight:bold">||</span> trimmed <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;/status tasks&#39;</span>) {
</span></span><span style="display:flex;"><span>      handleStatus(trimmed, chatJid, msg).<span style="color:#c6a0f6">catch</span>(<span style="color:#6e738d;font-style:italic">/* ... */</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span>;  <span style="color:#6e738d;font-style:italic">// don&#39;t store, don&#39;t process
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (trimmed.startsWith(<span style="color:#a6da95">&#39;/task &#39;</span>)) {
</span></span><span style="display:flex;"><span>      handleTaskCmd(chatJid, msg).<span style="color:#c6a0f6">catch</span>(<span style="color:#6e738d;font-style:italic">/* ... */</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (trimmed.startsWith(<span style="color:#a6da95">&#39;/debug&#39;</span>)) {
</span></span><span style="display:flex;"><span>      handleDebugCmd(chatJid, msg).<span style="color:#c6a0f6">catch</span>(<span style="color:#6e738d;font-style:italic">/* ... */</span>);
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">return</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// ... sender allowlist filtering, event logging, message storage
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    storeMessage(msg);
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div><figcaption>
        <strong>Listing 5: Intercepting built-in commands before storage</strong>
    </figcaption>
</figure>
<p>The <code>return</code> before <code>storeMessage()</code> is the key design decision. These commands are ephemeral &ndash; they shouldn&rsquo;t appear in the conversation history, shouldn&rsquo;t trigger the agent, and shouldn&rsquo;t affect message cursors. They&rsquo;re handled entirely by the host process and respond instantly (no container spawn needed).</p>
<p>Each handler checks <code>group?.isMain</code> before proceeding. Non-main-group commands are silently dropped, with a warning in the server logs.</p>
<h2 id="7-eventactiontool-logging">7. Event/action/tool logging</h2>
<p>The commands above tell you what&rsquo;s happening <em>now</em>. But when something went wrong an hour ago, you need a trail. That&rsquo;s where event logging comes in.</p>
<h3 id="71-the-three-table-schema">7.1 The three-table schema</h3>
<p>NanoClaw&rsquo;s existing SQLite schema was focused on message storage and container state. We needed a new schema to capture the full traceability from inbound triggers to outbound actions to tool calls. The design is a simple three-table structure. Every inbound trigger is an <strong>event</strong>, every outbound action is an <strong>action</strong> linked to its triggering event, and every tool invocation is a <strong>tool call</strong> linked to its parent action.</p>
<figure id="listing6"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">TABLE</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> event_log (
</span></span><span style="display:flex;"><span>  id           <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">PRIMARY</span> <span style="color:#c6a0f6">KEY</span>,    <span style="color:#6e738d;font-style:italic">-- UUID
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#c6a0f6">timestamp</span>    DATETIME <span style="color:#c6a0f6">DEFAULT</span> <span style="color:#c6a0f6">CURRENT_TIMESTAMP</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">source</span>       <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">NULL</span>,       <span style="color:#6e738d;font-style:italic">-- &#39;whatsapp&#39;, &#39;telegram&#39;, &#39;scheduled_task&#39;, &#39;ipc&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  source_id    <span style="color:#91d7e3">TEXT</span>,                <span style="color:#6e738d;font-style:italic">-- message ID, task ID, etc.
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  raw_content  <span style="color:#91d7e3">TEXT</span>,                <span style="color:#6e738d;font-style:italic">-- full payload (JSON, truncated to 10KB)
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  summary      <span style="color:#91d7e3">TEXT</span>                 <span style="color:#6e738d;font-style:italic">-- human-readable one-liner
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">TABLE</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> action_log (
</span></span><span style="display:flex;"><span>  id            <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">PRIMARY</span> <span style="color:#c6a0f6">KEY</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">timestamp</span>     DATETIME <span style="color:#c6a0f6">DEFAULT</span> <span style="color:#c6a0f6">CURRENT_TIMESTAMP</span>,
</span></span><span style="display:flex;"><span>  triggered_by  <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">REFERENCES</span> event_log(id),
</span></span><span style="display:flex;"><span>  action_type   <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">NULL</span>,      <span style="color:#6e738d;font-style:italic">-- &#39;message_sent&#39;, &#39;task_scheduled&#39;, &#39;tool_call&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  target        <span style="color:#91d7e3">TEXT</span>,               <span style="color:#6e738d;font-style:italic">-- group JID, email address, task ID
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  content       <span style="color:#91d7e3">TEXT</span>,               <span style="color:#6e738d;font-style:italic">-- what was sent or done
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  tool_calls    <span style="color:#91d7e3">TEXT</span>                <span style="color:#6e738d;font-style:italic">-- JSON array of tool names
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">TABLE</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> tool_call_log (
</span></span><span style="display:flex;"><span>  id           <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">PRIMARY</span> <span style="color:#c6a0f6">KEY</span>,
</span></span><span style="display:flex;"><span>  action_id    <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">REFERENCES</span> action_log(id),
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">timestamp</span>    DATETIME <span style="color:#c6a0f6">DEFAULT</span> <span style="color:#c6a0f6">CURRENT_TIMESTAMP</span>,
</span></span><span style="display:flex;"><span>  tool_name    <span style="color:#91d7e3">TEXT</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">NULL</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">input</span>        <span style="color:#91d7e3">TEXT</span>,                <span style="color:#6e738d;font-style:italic">-- JSON (truncated to 10KB)
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#c6a0f6">output</span>       <span style="color:#91d7e3">TEXT</span>,                <span style="color:#6e738d;font-style:italic">-- JSON (truncated to 10KB)
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  duration_ms  <span style="color:#91d7e3">INTEGER</span>,
</span></span><span style="display:flex;"><span>  success      <span style="color:#91d7e3">INTEGER</span> <span style="color:#c6a0f6">DEFAULT</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">INDEX</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> idx_event_log_timestamp <span style="color:#c6a0f6">ON</span> event_log(<span style="color:#c6a0f6">timestamp</span>);
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">INDEX</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> idx_action_log_triggered_by <span style="color:#c6a0f6">ON</span> action_log(triggered_by);
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span> <span style="color:#c6a0f6">INDEX</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">NOT</span> <span style="color:#c6a0f6">EXISTS</span> idx_tool_call_log_action_id <span style="color:#c6a0f6">ON</span> tool_call_log(action_id);</span></span></code></pre></div><figcaption>
        <strong>Listing 6: Event logging schema</strong>
    </figcaption>
</figure>
<p>The foreign key chain is <code>event_log</code> &lt;- <code>action_log</code> &lt;- <code>tool_call_log</code>. Given any action, you can trace back to <em>why</em> it happened. Given any event, you can see <em>everything</em> it caused. The indexes support the <code>/debug</code> query patterns: filtering by timestamp (pruning), joining by <code>triggered_by</code> (event lookups), and grouping by <code>action_id</code> (tool call chains).</p>
<figure class="align-center " id="fig5">
    <pre class="mermaid">flowchart TD
    E[Event: WhatsApp message received] --&gt; A1[Action: message_sent to group]
    E --&gt; A2[Action: task_scheduled]
    A1 --&gt; T1[Tool Call: runContainerAgent]
    A1 --&gt; T2[Tool Call: channel.sendMessage]</pre>
    <figcaption>Figure 5: Event -&gt; Action -&gt; Tool Call tracing chain</figcaption>
</figure>
<p>These tables live in the same <code>messages.db</code> file as everything else. No additional file handles, no additional backup concerns, no additional connection management. They&rsquo;re created in the existing <code>createSchema()</code> function using <code>CREATE TABLE IF NOT EXISTS</code>, so they&rsquo;re added transparently on first startup after the upgrade.</p>
<h3 id="72-the-logger-module">7.2 The logger module</h3>
<p>Three functions, matching the three tables. All IDs are UUIDs via <code>crypto.randomUUID()</code>. All writes are fire-and-forget - wrapped in <code>try/catch</code>, errors logged at <code>debug</code> level, never blocking the pipeline.</p>
<figure id="listing7"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> logEvent(
</span></span><span style="display:flex;"><span>  source: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>  sourceId: <span style="color:#ed8796">string</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span>,
</span></span><span style="display:flex;"><span>  rawContent: <span style="color:#ed8796">unknown</span>,
</span></span><span style="display:flex;"><span>  summary: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>)<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#ed8796">string</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> id <span style="color:#91d7e3;font-weight:bold">=</span> crypto.randomUUID();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>    insertEventStmt().run(
</span></span><span style="display:flex;"><span>      id, <span style="color:#c6a0f6">new</span> <span style="color:#91d7e3">Date</span>().toISOString(), source, sourceId,
</span></span><span style="display:flex;"><span>      truncate(rawContent), summary,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  } <span style="color:#c6a0f6">catch</span> (err) {
</span></span><span style="display:flex;"><span>    logger.debug({ err, source, sourceId }, <span style="color:#a6da95">&#39;Failed to log event&#39;</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> id;  <span style="color:#6e738d;font-style:italic">// always returns an ID, even if the write failed
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> logAction(
</span></span><span style="display:flex;"><span>  triggeredBy: <span style="color:#ed8796">string</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span>,
</span></span><span style="display:flex;"><span>  actionType: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>  target: <span style="color:#ed8796">string</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span>,
</span></span><span style="display:flex;"><span>  content: <span style="color:#ed8796">unknown</span>,
</span></span><span style="display:flex;"><span>  toolCalls?: <span style="color:#ed8796">string</span>[],
</span></span><span style="display:flex;"><span>)<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#ed8796">string</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> id <span style="color:#91d7e3;font-weight:bold">=</span> crypto.randomUUID();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>    insertActionStmt().run(
</span></span><span style="display:flex;"><span>      id, <span style="color:#c6a0f6">new</span> <span style="color:#91d7e3">Date</span>().toISOString(), triggeredBy, actionType,
</span></span><span style="display:flex;"><span>      target, truncate(content),
</span></span><span style="display:flex;"><span>      toolCalls <span style="color:#91d7e3;font-weight:bold">?</span> JSON.stringify(toolCalls) <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#f5a97f">null</span>,
</span></span><span style="display:flex;"><span>    );
</span></span><span style="display:flex;"><span>  } <span style="color:#c6a0f6">catch</span> (err) {
</span></span><span style="display:flex;"><span>    logger.debug({ err, actionType, target }, <span style="color:#a6da95">&#39;Failed to log action&#39;</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> id;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 7: Core logging functions</strong>
    </figcaption>
</figure>
<p>Every call returns an ID, so callers can chain events -&gt; actions -&gt; tool calls, even if the write fails silently. This is intentional - the pipeline code doesn&rsquo;t check whether logging succeeded, it just carries the ID forward.</p>
<p>The <code>truncate()</code> helper caps content at 10KB to prevent DB bloat:</p>
<figure id="listing8"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> MAX_CONTENT_SIZE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">10</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">1024</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">function</span> truncate(value: <span style="color:#ed8796">unknown</span>)<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#ed8796">string</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> (value <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#f5a97f">undefined</span> <span style="color:#91d7e3;font-weight:bold">||</span> value <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#f5a97f">null</span>) <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">null</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> str <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">typeof</span> value <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#a6da95">&#39;string&#39;</span> <span style="color:#91d7e3;font-weight:bold">?</span> value : <span style="color:#ed8796">JSON.stringify</span>(value);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> (str.length <span style="color:#91d7e3;font-weight:bold">&gt;</span> MAX_CONTENT_SIZE) <span style="color:#c6a0f6">return</span> str.slice(<span style="color:#f5a97f">0</span>, MAX_CONTENT_SIZE);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> str;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 8: Content truncation</strong>
    </figcaption>
</figure>
<h3 id="73-the-tool-call-wrapper">7.3 The tool call wrapper</h3>
<p><code>logToolCall</code> is different from the other two - it wraps an async operation and automatically records input, output, duration, and success/failure:</p>
<figure id="listing9"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#c6a0f6">async</span> <span style="color:#ed8796">function</span> logToolCall&lt;<span style="color:#c6a0f6">T</span>&gt;(
</span></span><span style="display:flex;"><span>  actionId: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>  toolName: <span style="color:#ed8796">string</span>,
</span></span><span style="display:flex;"><span>  input: <span style="color:#ed8796">unknown</span>,
</span></span><span style="display:flex;"><span>  fn<span style="color:#91d7e3;font-weight:bold">:</span> () <span style="color:#91d7e3;font-weight:bold">=&gt;</span> Promise&lt;<span style="color:#c6a0f6">T</span>&gt;,
</span></span><span style="display:flex;"><span>)<span style="color:#91d7e3;font-weight:bold">:</span> Promise&lt;<span style="color:#c6a0f6">T</span>&gt; {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> id <span style="color:#91d7e3;font-weight:bold">=</span> crypto.randomUUID();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> start <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Date</span>.now();
</span></span><span style="display:flex;"><span>  <span style="color:#ed8796">let</span> success <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">true</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#ed8796">let</span> output: <span style="color:#ed8796">unknown</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">null</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">const</span> result <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">await</span> fn();
</span></span><span style="display:flex;"><span>    output <span style="color:#91d7e3;font-weight:bold">=</span> result;
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> result;
</span></span><span style="display:flex;"><span>  } <span style="color:#c6a0f6">catch</span> (err) {
</span></span><span style="display:flex;"><span>    success <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">false</span>;
</span></span><span style="display:flex;"><span>    output <span style="color:#91d7e3;font-weight:bold">=</span> err <span style="color:#c6a0f6">instanceof</span> <span style="color:#91d7e3">Error</span> <span style="color:#91d7e3;font-weight:bold">?</span> err.message : <span style="color:#ed8796">String</span>(err);
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">throw</span> err;  <span style="color:#6e738d;font-style:italic">// re-throw - logging doesn&#39;t swallow errors
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  } <span style="color:#c6a0f6">finally</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">const</span> durationMs <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Date</span>.now() <span style="color:#91d7e3;font-weight:bold">-</span> start;
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>      insertToolCallStmt().run(
</span></span><span style="display:flex;"><span>        id, actionId, <span style="color:#c6a0f6">new</span> <span style="color:#91d7e3">Date</span>().toISOString(), toolName,
</span></span><span style="display:flex;"><span>        truncate(input), truncate(output), durationMs, success <span style="color:#91d7e3;font-weight:bold">?</span> 1 : <span style="color:#ed8796">0</span>,
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>    } <span style="color:#c6a0f6">catch</span> (logErr) {
</span></span><span style="display:flex;"><span>      logger.debug({ err: <span style="color:#ed8796">logErr</span>, toolName }, <span style="color:#a6da95">&#39;Failed to log tool call&#39;</span>);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 9: Tool call wrapper with automatic instrumentation</strong>
    </figcaption>
</figure>
<p>The <code>finally</code> block ensures the log entry is written regardless of whether the wrapped operation succeeded or failed. The error is always re-thrown - <code>logToolCall</code> is transparent to the caller. It&rsquo;s a decorator pattern: wrap any async operation and get free instrumentation.</p>
<p>Insert statements are lazily prepared and reused across calls, avoiding the overhead of re-preparing the same SQL on every log write.</p>
<h3 id="74-instrumenting-the-pipeline">7.4 Instrumenting the pipeline</h3>
<p>With the logger module in place, instrumentation is surgical - a few lines at each key point in the message flow:</p>
<p><strong>Inbound messages</strong> (in the <code>onMessage</code> callback):</p>
<figure id="listing10"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Detect channel from JID format
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#c6a0f6">const</span> evtChannel <span style="color:#91d7e3;font-weight:bold">=</span> chatJid.includes(<span style="color:#a6da95">&#39;@g.us&#39;</span>) <span style="color:#91d7e3;font-weight:bold">||</span> chatJid.includes(<span style="color:#a6da95">&#39;@s.whatsapp.net&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39;whatsapp&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">:</span> chatJid.startsWith(<span style="color:#a6da95">&#39;tg:&#39;</span>) <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39;telegram&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">:</span> chatJid.startsWith(<span style="color:#a6da95">&#39;dc:&#39;</span>) <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39;discord&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">:</span> chatJid.startsWith(<span style="color:#a6da95">&#39;sl:&#39;</span>) <span style="color:#91d7e3;font-weight:bold">?</span> <span style="color:#a6da95">&#39;slack&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;channel&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>logEvent(
</span></span><span style="display:flex;"><span>  evtChannel, msg.id,
</span></span><span style="display:flex;"><span>  { sender: <span style="color:#ed8796">msg.sender_name</span>, content: <span style="color:#ed8796">msg.content?.slice</span>(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">200</span>) },
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">`Message from </span><span style="color:#a6da95">${</span>msg.sender_name<span style="color:#a6da95">}</span><span style="color:#a6da95">: </span><span style="color:#a6da95">${</span>(msg.content <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#a6da95">&#39;&#39;</span>).slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">80</span>)<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>);</span></span></code></pre></div><figcaption>
        <strong>Listing 10: Logging inbound messages with channel detection</strong>
    </figcaption>
</figure>
<p><strong>Agent processing</strong> (when the orchestrator starts handling a group&rsquo;s messages):</p>
<figure id="listing11"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Log the processing event - this ID links to all downstream actions
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#c6a0f6">const</span> eventId <span style="color:#91d7e3;font-weight:bold">=</span> logEvent(
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">&#39;message_batch&#39;</span>, chatJid,
</span></span><span style="display:flex;"><span>  { messageCount: <span style="color:#ed8796">missedMessages.length</span>, group: <span style="color:#ed8796">group.name</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">`Processing </span><span style="color:#a6da95">${</span>missedMessages.length<span style="color:#a6da95">}</span><span style="color:#a6da95"> message(s) for </span><span style="color:#a6da95">${</span>group.name<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>);</span></span></code></pre></div><figcaption>
        <strong>Listing 11: Logging the processing event and carrying the eventId</strong>
    </figcaption>
</figure>
<p><strong>Outbound messages</strong> (in the streaming output callback):</p>
<figure id="listing12"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> (text) {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">await</span> channel.sendMessage(chatJid, text);
</span></span><span style="display:flex;"><span>  logAction(eventId, <span style="color:#a6da95">&#39;message_sent&#39;</span>, chatJid, text.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">500</span>));
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 12: Logging outbound actions linked to the triggering event</strong>
    </figcaption>
</figure>
<p>The <code>eventId</code> from the processing event links the outbound action back to the batch that triggered it. This is the chain that <code>/debug why</code> follows.</p>
<p><strong>Scheduled tasks</strong> - logged at the point the scheduler picks up a due task, and again when the result is sent to the user:</p>
<figure id="listing13"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> eventId <span style="color:#91d7e3;font-weight:bold">=</span> logEvent(
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">&#39;scheduled_task&#39;</span>, task.id,
</span></span><span style="display:flex;"><span>  { prompt: <span style="color:#ed8796">task.prompt.slice</span>(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">200</span>), schedule: <span style="color:#ed8796">task.schedule_type</span> },
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">`Scheduled task: </span><span style="color:#a6da95">${</span>task.prompt.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">80</span>)<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// ... later, when the container produces output:
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#c6a0f6">await</span> deps.sendMessage(task.chat_jid, streamedOutput.result);
</span></span><span style="display:flex;"><span>logAction(eventId, <span style="color:#a6da95">&#39;message_sent&#39;</span>, task.chat_jid, result<span style="color:#91d7e3;font-weight:bold">?</span>.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">500</span>) <span style="color:#91d7e3;font-weight:bold">??</span> <span style="color:#f5a97f">null</span>);</span></span></code></pre></div><figcaption>
        <strong>Listing 13: Scheduled task instrumentation</strong>
    </figcaption>
</figure>
<p><strong>IPC</strong> - logged when the IPC watcher processes messages and task operations from containers:</p>
<figure id="listing14"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> ipcEventId <span style="color:#91d7e3;font-weight:bold">=</span> logEvent(
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">&#39;ipc&#39;</span>, <span style="color:#f5a97f">null</span>,
</span></span><span style="display:flex;"><span>  { chatJid: <span style="color:#ed8796">data.chatJid</span>, sourceGroup, text: <span style="color:#ed8796">data.text?.slice</span>(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">200</span>) },
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">`IPC message from </span><span style="color:#a6da95">${</span>sourceGroup<span style="color:#a6da95">}</span><span style="color:#a6da95">`</span>,
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">await</span> deps.sendMessage(data.chatJid, data.text);
</span></span><span style="display:flex;"><span>logAction(ipcEventId, <span style="color:#a6da95">&#39;message_sent&#39;</span>, data.chatJid, data.text<span style="color:#91d7e3;font-weight:bold">?</span>.slice(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">500</span>) <span style="color:#91d7e3;font-weight:bold">??</span> <span style="color:#f5a97f">null</span>);</span></span></code></pre></div><figcaption>
        <strong>Listing 14: IPC message instrumentation</strong>
    </figcaption>
</figure>
<h2 id="8-the-debug-commands">8. The /debug commands</h2>
<p>Four subcommands for querying the event log, all main-group only:</p>
<table>
  <thead>
      <tr>
          <th>Command</th>
          <th>What it shows</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>/debug last &lt;n&gt;</code></td>
          <td>Last <em>n</em> actions with their triggering events</td>
      </tr>
      <tr>
          <td><code>/debug why</code></td>
          <td>Most recent action with full event context and tool call chain</td>
      </tr>
      <tr>
          <td><code>/debug event &lt;id&gt;</code></td>
          <td>Everything triggered by a specific event</td>
      </tr>
      <tr>
          <td><code>/debug report</code></td>
          <td>Summary dashboard: table sizes, events by source, actions by type, busiest hours, recent errors</td>
      </tr>
  </tbody>
</table>
<h3 id="81-debug-last---quick-scan">8.1 /debug last - quick scan</h3>
<p>The &ldquo;what happened recently?&rdquo; view. Joins <code>action_log</code> with <code>event_log</code> to show each action alongside what caused it:</p>
<figure id="fig6">
<img src="images/debug-last-output.png" alt="/debug last 5 showing F1 query response, flight tracking updates, and Gmail cleanup actions" title="/debug last output in WhatsApp">
<figcaption><strong>Figure 6:</strong> /debug last 5 - the F1 response, flight landing alert, and Gmail cleanup, each traced back to its trigger.</figcaption>
</figure>
<figure id="listing15"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> getLastActions(n: <span style="color:#ed8796">number</span>)<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#91d7e3">Array</span><span style="color:#91d7e3;font-weight:bold">&lt;</span>{
</span></span><span style="display:flex;"><span>  action: <span style="color:#ed8796">ActionLogRow</span>;
</span></span><span style="display:flex;"><span>  event: <span style="color:#ed8796">EventLogRow</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span>;
</span></span><span style="display:flex;"><span>}<span style="color:#91d7e3;font-weight:bold">&gt;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> rows <span style="color:#91d7e3;font-weight:bold">=</span> getDb()
</span></span><span style="display:flex;"><span>    .prepare(
</span></span><span style="display:flex;"><span>      <span style="color:#a6da95">`SELECT a.*, e.id as e_id, e.timestamp as e_timestamp,
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">              e.source as e_source, e.source_id as e_source_id,
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">              e.summary as e_summary
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">       FROM action_log a
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">       LEFT JOIN event_log e ON a.triggered_by = e.id
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">       ORDER BY a.timestamp DESC
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">       LIMIT ?`</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    .all(n);
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// ... map to structured result
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 15: Querying last N actions with LEFT JOIN</strong>
    </figcaption>
</figure>
<p>The <code>LEFT JOIN</code> is important - some actions might not have a triggering event (e.g., system-initiated actions), and we still want to see them.</p>
<h3 id="82-debug-why---full-trace">8.2 /debug why - full trace</h3>
<p>Answers &ldquo;why did the last thing happen?&rdquo; by pulling the most recent action, its triggering event, and all associated tool calls:</p>
<figure id="fig7">
<img src="images/debug-why-output.png" alt="/debug why showing the most recent action with its triggering WhatsApp message and tool call chain" title="/debug why output in WhatsApp">
<figcaption><strong>Figure 7:</strong> /debug why - tracing the most recent action back to the WhatsApp message that caused it.</figcaption>
</figure>
<figure id="listing16"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> getLastActionWithToolCalls()<span style="color:#91d7e3;font-weight:bold">:</span> {
</span></span><span style="display:flex;"><span>  action: <span style="color:#ed8796">ActionLogRow</span>;
</span></span><span style="display:flex;"><span>  event: <span style="color:#ed8796">EventLogRow</span> <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span>;
</span></span><span style="display:flex;"><span>  toolCalls: <span style="color:#ed8796">ToolCallLogRow</span>[];
</span></span><span style="display:flex;"><span>} <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#f5a97f">null</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> results <span style="color:#91d7e3;font-weight:bold">=</span> getLastActions(<span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> (results.length <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#f5a97f">0</span>) <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">null</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> { action, event } <span style="color:#91d7e3;font-weight:bold">=</span> results[<span style="color:#f5a97f">0</span>];
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> toolCalls <span style="color:#91d7e3;font-weight:bold">=</span> getDb()
</span></span><span style="display:flex;"><span>    .prepare(
</span></span><span style="display:flex;"><span>      <span style="color:#a6da95">`SELECT * FROM tool_call_log WHERE action_id = ? ORDER BY timestamp`</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    .all(action.id) <span style="color:#c6a0f6">as</span> ToolCallLogRow[];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> { action, event, toolCalls };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 16: Full trace for the most recent action</strong>
    </figcaption>
</figure>
<p>The output shows the full chain: triggering event (source, summary, timestamp, ID), the action taken (type, target, content), and each tool call with its duration and success/failure status. Copy the event ID from here and use <code>/debug event &lt;id&gt;</code> to see everything else that event triggered.</p>
<h3 id="83-debug-report---summary-dashboard">8.3 /debug report - summary dashboard</h3>
<p>The &ldquo;is everything healthy?&rdquo; view. Aggregates across all three tables into a single report:</p>
<figure id="fig8">
<img src="images/debug-report-output.png" alt="/debug report showing event counts by source, action breakdown, and busiest hours from a day of flight tracking and F1 queries" title="/debug report output in WhatsApp">
<figcaption><strong>Figure 8:</strong> /debug report - a day's worth of WhatsApp messages, flight tracking, Gmail cleanup, and F1 queries summarized.</figcaption>
</figure>
<p>What it includes:</p>
<ul>
<li><strong>Retention period</strong> - configured days and current time window</li>
<li><strong>Table sizes</strong> - row counts for events, actions, and tool calls</li>
<li><strong>Events by source</strong> - breakdown by channel (whatsapp, telegram, scheduled_task, ipc)</li>
<li><strong>Actions by type</strong> - breakdown by what was done (message_sent, task_scheduled, etc.)</li>
<li><strong>Busiest hours</strong> - top 5 hours by event count, in local timezone</li>
<li><strong>Recent failed tool calls</strong> - last 10 with tool name, duration, and error output</li>
<li><strong>Recent errors</strong> - last 10 error-like actions with their triggering event</li>
</ul>
<p>One thing worth noting: the busiest hours are computed in JavaScript using <code>toLocaleString</code> with the configured time zone, not in SQL. SQLite stores timestamps as UTC (as ISO strings), and performing timezone conversions in SQL would require loading an extension. Instead, we fetch the raw timestamps and bucket them in JS:</p>
<figure id="listing17"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> allTimestamps <span style="color:#91d7e3;font-weight:bold">=</span> db.prepare(<span style="color:#a6da95">`SELECT timestamp FROM event_log`</span>).all();
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">const</span> hourCounts <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">new</span> Map&lt;<span style="color:#c6a0f6">string</span>, <span style="color:#8aadf4">number</span>&gt;();
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">for</span> (<span style="color:#c6a0f6">const</span> { timestamp } <span style="color:#c6a0f6">of</span> allTimestamps) {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> localHour <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">new</span> <span style="color:#91d7e3">Date</span>(timestamp).toLocaleString(<span style="color:#a6da95">&#39;en-US&#39;</span>, {
</span></span><span style="display:flex;"><span>    timeZone: <span style="color:#ed8796">TIMEZONE</span>,
</span></span><span style="display:flex;"><span>    hour<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#a6da95">&#39;numeric&#39;</span>, hour12: <span style="color:#ed8796">true</span>,
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span>  hourCounts.<span style="color:#c6a0f6">set</span>(localHour, (hourCounts.<span style="color:#c6a0f6">get</span>(localHour) <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#f5a97f">0</span>) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 17: Bucketing event timestamps by local hour</strong>
    </figcaption>
</figure>
<p>Learned this the hard way when the report initially showed UTC hours, and I couldn&rsquo;t figure out why 5 PM was my busiest time. 😄</p>
<h2 id="9-auto-pruning-and-retention">9. Auto-pruning and retention</h2>
<p>An unbounded observability system is a liability. Logs older than 3 days are automatically deleted. The retention period is configurable via the <code>EVENT_LOG_RETENTION_DAYS</code> environment variable (set to 0 to disable pruning).</p>
<figure id="listing18"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// config.ts
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#c6a0f6">export</span> <span style="color:#c6a0f6">const</span> EVENT_LOG_RETENTION_DAYS <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Math</span>.max(
</span></span><span style="display:flex;"><span>  <span style="color:#f5a97f">0</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3">parseInt</span>(process.env.EVENT_LOG_RETENTION_DAYS <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#a6da95">&#39;3&#39;</span>, <span style="color:#f5a97f">10</span>) <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#f5a97f">3</span>,
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#c6a0f6">const</span> EVENT_LOG_PRUNE_INTERVAL <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">60</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">60</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">1000</span>; <span style="color:#6e738d;font-style:italic">// hourly
</span></span></span></code></pre></div><figcaption>
        <strong>Listing 18: Log retention configuration</strong>
    </figcaption>
</figure>
<p>Pruning runs at startup (clean up anything that expired while the service was down) and then every 60 minutes:</p>
<figure id="listing19"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> pruneOldLogs() {
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> (EVENT_LOG_RETENTION_DAYS <span style="color:#91d7e3;font-weight:bold">===</span> <span style="color:#f5a97f">0</span>) <span style="color:#c6a0f6">return</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> cutoff <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">new</span> <span style="color:#91d7e3">Date</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Date</span>.now() <span style="color:#91d7e3;font-weight:bold">-</span> EVENT_LOG_RETENTION_DAYS <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">24</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">60</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">60</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">1000</span>,
</span></span><span style="display:flex;"><span>  ).toISOString();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> db <span style="color:#91d7e3;font-weight:bold">=</span> getDb();
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Delete in FK-safe order: children first
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  db.prepare(<span style="color:#a6da95">`DELETE FROM tool_call_log WHERE action_id IN (
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    SELECT id FROM action_log WHERE timestamp &lt; ?
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">  )`</span>).run(cutoff);
</span></span><span style="display:flex;"><span>  db.prepare(<span style="color:#a6da95">`DELETE FROM action_log WHERE timestamp &lt; ?`</span>).run(cutoff);
</span></span><span style="display:flex;"><span>  db.prepare(<span style="color:#a6da95">`DELETE FROM event_log WHERE timestamp &lt; ?`</span>).run(cutoff);
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">export</span> <span style="color:#ed8796">function</span> startLogPruning()<span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#c6a0f6">void</span> {
</span></span><span style="display:flex;"><span>  pruneOldLogs();
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">const</span> timer <span style="color:#91d7e3;font-weight:bold">=</span> setInterval(pruneOldLogs, EVENT_LOG_PRUNE_INTERVAL);
</span></span><span style="display:flex;"><span>  timer.unref();   <span style="color:#6e738d;font-style:italic">// don&#39;t keep the process alive for pruning
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 19: Pruning with FK-safe deletion order</strong>
    </figcaption>
</figure>
<p>The deletion order matters: <code>tool_call_log</code> rows reference <code>action_log</code>, which in turn references <code>event_log</code>. Deleting parents first would violate foreign key constraints. The <code>timer.unref()</code> call ensures the pruning interval doesn&rsquo;t prevent graceful shutdown.</p>
<h2 id="10-design-decisions">10. Design decisions</h2>
<p>A few choices that are worth calling out:</p>
<p><strong>Fire-and-forget, not await.</strong> Every logging call is synchronous (better-sqlite3) and wrapped in <code>try/catch</code>. If the write fails - disk full, DB locked, schema mismatch - the error is logged at <code>debug</code> level and the pipeline continues. The logging system is never on the critical path. An observability system that can take down the thing it&rsquo;s observing is worse than useless.</p>
<p><strong>Same database, no new dependencies.</strong> The logging tables live in <code>messages.db</code> alongside messages, tasks, sessions, and router state. No new files to back up, no new connections to manage, no new packages to install. <code>CREATE TABLE IF NOT EXISTS</code> means existing installations pick up the schema on restart.</p>
<p><strong>Commands intercepted before storage.</strong> <code>/status</code>, <code>/task</code>, and <code>/debug</code> messages never reach the agent container. They don&rsquo;t appear in conversation history, don&rsquo;t trigger container spawns, and don&rsquo;t affect message cursors. This is important - a <code>/status</code> check shouldn&rsquo;t cost you a container slot or show up as context in the agent&rsquo;s next conversation.</p>
<p><strong>Prepared statements, lazily created.</strong> The insert statements are created on first use and reused across calls. For a system logging every message and action, re-preparing SQL on every call would add up.</p>
<p><strong>UUIDs for everything.</strong> <code>crypto.randomUUID()</code> for all IDs. No auto-increment, no collision risk across restarts, and IDs are meaningful in isolation (you can paste one into <code>/debug event &lt;id&gt;</code> without context).</p>
<h2 id="11-try-it-yourself">11. Try it yourself</h2>
<p>If you&rsquo;re running NanoClaw (or OpenClaw), these features are available out of the box. Here&rsquo;s how to get started:</p>
<p><strong>If you already have NanoClaw running:</strong></p>
<ol>
<li>
<p>Pull the latest code and rebuild:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git pull
</span></span><span style="display:flex;"><span>npm run build</span></span></code></pre></td></tr></table>
</div>
</div>
</li>
<li>
<p>Restart the service:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Linux (systemd)</span>
</span></span><span style="display:flex;"><span>systemctl --user restart nanoclaw
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># macOS (launchd)</span>
</span></span><span style="display:flex;"><span>launchctl kickstart -k gui/<span style="color:#c6a0f6">$(</span>id -u<span style="color:#c6a0f6">)</span>/com.nanoclaw</span></span></code></pre></td></tr></table>
</div>
</div>
</li>
<li>
<p>The new tables are created automatically on startup. No migration step needed.</p>
</li>
<li>
<p>Send <code>/status</code> in your main group to verify it&rsquo;s working.</p>
</li>
</ol>
<p><strong>If you&rsquo;re starting fresh:</strong></p>
<ol>
<li>Fork or clone <a
	
		href = "https://github.com/bahree/nanoclaw"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/nanoclaw
	</span>
</a> (or the upstream <a
	
		href = "https://github.com/qwibitai/nanoclaw"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		OpenClaw
	</span>
</a>)</li>
<li>Follow the setup instructions in the README</li>
<li>Once connected to a channel, all commands are available immediately</li>
</ol>
<p><strong>Command reference:</strong></p>
<table>
  <thead>
      <tr>
          <th>Command</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>/status</code></td>
          <td>System overview: uptime, memory, containers, channels, groups, tasks</td>
      </tr>
      <tr>
          <td><code>/status tasks</code></td>
          <td>Full task list with schedules, next run, last run, IDs</td>
      </tr>
      <tr>
          <td><code>/task pause &lt;id&gt;</code></td>
          <td>Pause a scheduled task</td>
      </tr>
      <tr>
          <td><code>/task resume &lt;id&gt;</code></td>
          <td>Resume a paused task</td>
      </tr>
      <tr>
          <td><code>/task delete &lt;id&gt;</code></td>
          <td>Delete a task and its run history</td>
      </tr>
      <tr>
          <td><code>/debug last &lt;n&gt;</code></td>
          <td>Last n actions with their triggering events</td>
      </tr>
      <tr>
          <td><code>/debug why</code></td>
          <td>Most recent action with full trace</td>
      </tr>
      <tr>
          <td><code>/debug event &lt;id&gt;</code></td>
          <td>All actions triggered by a specific event</td>
      </tr>
      <tr>
          <td><code>/debug report</code></td>
          <td>Summary dashboard with stats and errors</td>
      </tr>
  </tbody>
</table>
<p><strong>Configuration:</strong></p>
<table>
  <thead>
      <tr>
          <th>Env variable</th>
          <th>Default</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>EVENT_LOG_RETENTION_DAYS</code></td>
          <td><code>3</code></td>
          <td>Days to keep event logs (0 = keep forever)</td>
      </tr>
  </tbody>
</table>
<p>All commands are main-group only. They respond instantly (no container needed) and don&rsquo;t appear in the conversation history.</p>
<h2 id="12-summary">12. Summary</h2>
<p>Three problems, one philosophy: make the system controllable and observable from the same interface you use to interact with it.</p>
<p><code>/status</code> gives you real-time visibility - what&rsquo;s running, what&rsquo;s queued, what&rsquo;s scheduled, which channels are connected. <code>/task</code> gives you operational control: pause a runaway task, resume one you paused, and clean up completed ones. Event logging gives you after-the-fact traceability - every action links back to its triggering event, every tool call links back to its parent action. <code>/debug</code> commands let you query the trail. Auto-pruning keeps it from growing unbounded.</p>
<p>About ~1100 lines of new TypeScript across 8 files. Two new modules (<code>status.ts</code> and <code>event-log.ts</code>), three new SQLite tables, a handful of indexes, and one new config variable. No new dependencies, no separate services. It just works on the next restart.</p>
<hr>
<p><em>The source code for NanoClaw is available at <a
	
		href = "https://github.com/bahree/nanoclaw"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/nanoclaw
	</span>
</a>.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>Building LLMs from Scratch - Part 4: Evaluation &amp; Deployment</title>
      <link>/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/</guid>
      <description>Complete evaluation, testing, and deployment pipeline for historical language models. From model validation to Hugging Face publishing. Final part of 4-part series.</description>
      <content:encoded><![CDATA[<p><strong>TL;DR</strong></p>
<p>In this final part of our 4-part series on building language models from scratch, we explore the evaluation, testing, and deployment pipeline that transforms our trained historical language models into working systems. <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1
	</span>
</a> showed you how to use the published models, <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a> covered data collection and custom tokenization, and <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a> detailed the model architecture and training infrastructure. Here, we complete the journey with evaluation frameworks, testing infrastructure, and deployment to Hugging Face Hub.</p>
<blockquote>
<p><strong>⚠️ Educational Purpose</strong>: This is a learning project designed to teach LLM development concepts. For production-scale LLMs, you&rsquo;ll need much larger datasets, more sophisticated infrastructure, and additional considerations not covered here.</p></blockquote>
<p>As outlined in <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1
	</span>
</a>, both the SLM (117M parameters) and the Regular Model (354M parameters) use the same training code and infrastructure with different configurations defined in <strong><code>config.py</code></strong>. The evaluation and deployment infrastructure is also identical - only the model architecture parameters differ.</p>
<p>Both PyTorch checkpoint inference and Hugging Face model inference are fully working and available. Both the SLM and the Regular model are published on <a
	
		href = "https://huggingface.co/bahree"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Hugging Face Hub
	</span>
</a>. Local PyTorch checkpoints can be used directly for inference with the script <strong><code>inference_pytorch.py</code></strong>.</p>
<blockquote>
<p><strong>🔗 GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete evaluation and deployment infrastructure (<strong><code>05_evaluation/</code></strong>, <strong><code>06_inference/</code></strong>, <strong><code>10_scripts/</code></strong>) plus guides (<strong><code>08_documentation/EVALUATION_GUIDE.md</code></strong>, <strong><code>08_documentation/HUGGINGFACE_PUBLISHING.md</code></strong>, <strong><code>08_documentation/DEPLOYMENT_GUIDE.md</code></strong>)</p>
<p><strong>🟥 Series Posts</strong>: <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1 - Using the Published Historical Models
	</span>
</a> | <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2 - Data Collection &amp; Custom Tokenizer
	</span>
</a> | <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3 - Training Architecture &amp; GPU Optimization
	</span>
</a> | Part 4 (this post)</p>
<p><strong>🟧 Published Models</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SLM Model
	</span>
</a> | <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Regular Model
	</span>
</a> - Ready-to-use historical language models on Hugging Face</p>
<p><strong>📗 Book Reference</strong>: <a
	
		href = "https://a.co/d/gr87rem"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts</p></blockquote>
<h2 id="1-the-evaluation-challenge-measuring-what-matters-for-historical-language-models">1. The Evaluation Challenge: Measuring What Matters for Historical Language Models</h2>
<p>Now that we have trained models from <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a>, we face a critical question: <em>How do we know if our models actually work?</em> This isn&rsquo;t just about checking if the code runs - it&rsquo;s about validating that the models can generate historically accurate, linguistically appropriate text that captures the essence of 1500-1850 London English.</p>
<p>The challenge with evaluating historical language models goes far beyond standard LLM metrics. Standard evaluation approaches like Perplexity and BLEU scores (we explain these and other metrics in <a
	
		href = "#industry-standard-metrics"
	

	

	>
	
	<span>
		Section 2.1
	</span>
</a>) tell us whether the model generates fluent text. Still, they don&rsquo;t answer the questions that matter for historical applications: <em>Does the model avoid anachronisms? Can it distinguish between Tudor and Victorian language patterns? Does it understand London geography and historical context?</em></p>
<p>Consider a simple example: if we prompt the model with <em>&ldquo;In the year 1600, I traveled to London by railway&rdquo;</em>, a standard language model might generate this without flagging the obvious problem - railways didn&rsquo;t exist in 1600. The evaluation framework needs to catch these <strong>temporal inconsistencies</strong>, <strong>period-inappropriate language</strong>, and <strong>historical inaccuracies</strong> that standard metrics miss.</p>
<p>This evaluation challenge requires building a specialized assessment pipeline that understands historical context, temporal boundaries, and period-specific linguistic patterns. We need metrics that can distinguish between a model that generates fluent modern English and one that produces authentic historical text - two very different capabilities.</p>
<h3 id="11-high-level-evaluation-strategy">1.1 High-Level Evaluation Strategy</h3>
<p>Our evaluation framework provides two complementary approaches that work with both PyTorch checkpoints and Hugging Face models, as illustrated in <a href="#fig1" class="figure-ref">Figure 1</a> below.</p>
<figure class="align-center " id="fig1">
    <pre class="mermaid">graph TD
    A[🤖 Trained Models&lt;br/&gt;SLM 117M / Regular 354M] --&gt; B{Evaluation Type}
    
    B --&gt;|Quick| C[⚡ Quick Evaluation&lt;br/&gt;Historical accuracy, language quality, coherence]
    B --&gt;|Comprehensive| D[🔬 Comprehensive Evaluation&lt;br/&gt;Benchmarks, G-Eval, groundedness]
    
    C --&gt; E[📊 Evaluation Results&lt;br/&gt;Historical accuracy scores, metrics]
    D --&gt; E
    
    E --&gt; F{Quality OK?}
    F --&gt;|Yes| G[🚀 Deployment Options]
    F --&gt;|No| H[🔄 Retrain/Adjust]
    H --&gt; A
    
    G --&gt; I[📦 PyTorch Checkpoints&lt;br/&gt;Direct inference]
    G --&gt; J[🤗 Hugging Face Hub&lt;br/&gt;Published models]
    G --&gt; K[💻 Local Deployment&lt;br/&gt;API, CLI, notebooks]
    
    I --&gt; L[✅ Working Models&lt;br/&gt;Ready for use]
    J --&gt; L
    K --&gt; L
    
    style A fill:#e1f5fe
    style E fill:#f3e5f5
    style L fill:#e8f5e8
    style H fill:#fff3e0</pre>
    <figcaption>Figure 1: Complete Evaluation and Deployment Pipeline</figcaption>
</figure>
<p><strong>Quick Evaluation</strong> (<strong><code>quick_eval.py</code></strong>): Rapid validation testing historical accuracy on key events (e.g., 1665 plague, 1666 fire, etc.), language quality metrics (vocabulary diversity, historical pattern detection, readability), and coherence (ROUGE scores). Runs in minutes without external APIs.</p>
<p><strong>Comprehensive Evaluation</strong> (<strong><code>comprehensive_evaluator.py</code></strong>): Extends quick evaluation with benchmark datasets (small <strong>MMLU</strong> and <strong>HellaSWAG</strong> subsets), groundedness/fluency metrics, and optional LLM-as-a-judge scoring via <strong>G-Eval</strong> (using an external GPT model). Produces detailed reports with generation samples.</p>
<p>Both evaluators test across historical periods (such as Tudor, Stuart, and Georgian), language patterns (archaic pronouns and verb forms), and London-specific knowledge (geography and landmarks). The framework goes beyond standard LM metrics to assess period-appropriate language, temporal consistency, and historical accuracy.</p>
<h2 id="2-model-evaluation-framework">2. Model Evaluation Framework</h2>
<p>Now that we&rsquo;ve outlined the evaluation challenge, let&rsquo;s dive into the implementation. Our evaluation framework provides two complementary approaches that work with both PyTorch checkpoints and Hugging Face models. The framework is designed to be practical for a learning project while still providing meaningful insights into model performance.</p>
<h3 id="21-historical-linguistic-and-category-specific-evaluation">2.1 Historical, Linguistic, and Category-Specific Evaluation</h3>
<p>To make the evaluation concrete, we look at the model from three complementary aspects that together capture how well it understands the period, writes fluent text, and handles the different slices of the corpus. This multi-dimensional approach ensures we catch various types of failures - a model might generate grammatically perfect text but fail historically, or vice versa.</p>
<ul>
<li><strong>Historical assessments</strong>: Quick evaluation uses targeted prompts around key events (e.g., 1665 plague, 1666 fire, Old Bailey trials) and checks for expected keywords and phrases. Comprehensive evaluation adds temporal consistency checks (forbidden/required terms per period), date-range sanity checks, and historical benchmarks (custom historical questions and the MMLU subset).</li>
<li><strong>Linguistic assessments</strong>: We measure surface quality (chars/words/sentences per sample, words per sentence), vocabulary diversity (unique/total tokens), readability (Flesch-style scores), and presence of historical patterns (archaic verb forms like <em>hath, doth</em>, pronouns like <em>thou, thee</em>, conjunctions and interjections). This shows whether the model writes in a historically flavored yet readable style.</li>
<li><strong>Category-specific benchmarks</strong>: Evaluations are grouped by period (Tudor, Stuart, Georgian), by linguistic phenomena (archaic forms, dialogue patterns), and by London knowledge (Thames, Westminster, Old Bailey, etc.). The comprehensive evaluator further probes general reasoning using HellaSWAG and MMLU subsets to assess the model&rsquo;s performance across broader benchmarks.</li>
</ul>
<blockquote>
<p><a id="industry-standard-metrics"></a><strong>Industry-Standard Evaluation Metrics and Benchmarks</strong></p>
<p>Our evaluation framework uses several standard metrics and benchmarks from LLM research. Here&rsquo;s what each one measures and why we include it:</p>
<ul>
<li><strong>Perplexity</strong>: How surprised the model is by the reference text; lower is better because it means the model assigns higher probability to what actually happened in the corpus.</li>
<li><strong>BLEU / ROUGE</strong>: N-gram overlap between generated and reference text, giving a rough sense of literal similarity and how closely the model &ldquo;sticks&rdquo; to the reference phrasing. We use <strong>ROUGE-L</strong> (longest common subsequence) to evaluate coherence and narrative flow.</li>
<li><strong>MMLU</strong> (<em>Massive Multitask Language Understanding</em>): A large multiple-choice exam covering many academic subjects. Here, we use a tiny subset as a sanity check for general knowledge and reasoning, not as a primary goal.</li>
<li><strong>HellaSWAG</strong>: A commonsense inference benchmark where the model must pick a plausible continuation for a short story-like context. We use it to see whether the model&rsquo;s basic reasoning looks sensible.</li>
<li><strong>G-Eval</strong>: An <em>LLM-as-a-judge</em> pattern where a stronger reference model (for example, GPT) scores generated text along dimensions like coherence or groundedness. In this project, it is optional and requires an external API key.</li>
<li><strong>Groundedness</strong>: Asks: <em>does the model stick to the provided context / known facts, or hallucinate?</em> Our implementation approximates this by comparing generations against reference answers and historical constraints.</li>
</ul>
<p>For a deeper treatment of evaluation benchmarks (including MMLU, HellaSWAG, and LLM-as-a-judge methods like G-Eval), see <strong>Chapter 12 - Evaluating and Monitoring Generative Systems</strong> in the book 📘 <em><a
	
		href = "https://a.co/d/gr87rem"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a></em>.</p></blockquote>
<h3 id="22-automated-evaluation-pipeline">2.2 Automated Evaluation Pipeline</h3>
<p>The <code>run_comprehensive_evaluation</code> function in <strong><code>05_evaluation/comprehensive_evaluator.py</code></strong> orchestrates the entire evaluation process. <a href="#listing1" class="listing-ref">Listing 1</a> shows how it works: We iterate over test sets, generate text with the model, compute all the metrics defined above, and aggregate the results into a results dictionary for analysis.</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">run_comprehensive_evaluation</span>(model, tokenizer, test_data, device<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Run comprehensive evaluation on historical language model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Initialize evaluation metrics</span>
</span></span><span style="display:flex;"><span>    metrics <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;perplexity&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;bleu_scores&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;rouge_scores&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;historical_accuracy&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;linguistic_quality&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;coherence_scores&#39;</span>: [],
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;temporal_consistency&#39;</span>: []
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Evaluate on different text types</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> text_type, samples <span style="color:#91d7e3;font-weight:bold">in</span> test_data<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Evaluating on </span><span style="color:#a6da95">{</span>text_type<span style="color:#a6da95">}</span><span style="color:#a6da95"> samples...&#34;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> sample <span style="color:#91d7e3;font-weight:bold">in</span> samples:
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Generate text</span>
</span></span><span style="display:flex;"><span>            generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_text(model, tokenizer, sample[<span style="color:#a6da95">&#39;prompt&#39;</span>], device)
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Calculate metrics</span>
</span></span><span style="display:flex;"><span>            perplexity <span style="color:#91d7e3;font-weight:bold">=</span> calculate_perplexity(model, tokenizer, sample[<span style="color:#a6da95">&#39;text&#39;</span>], device)
</span></span><span style="display:flex;"><span>            bleu <span style="color:#91d7e3;font-weight:bold">=</span> calculate_bleu(generated, sample[<span style="color:#a6da95">&#39;reference&#39;</span>])
</span></span><span style="display:flex;"><span>            rouge <span style="color:#91d7e3;font-weight:bold">=</span> calculate_rouge(generated, sample[<span style="color:#a6da95">&#39;reference&#39;</span>])
</span></span><span style="display:flex;"><span>            hist_acc <span style="color:#91d7e3;font-weight:bold">=</span> assess_historical_accuracy(generated, sample[<span style="color:#a6da95">&#39;context&#39;</span>])
</span></span><span style="display:flex;"><span>            ling_qual <span style="color:#91d7e3;font-weight:bold">=</span> assess_linguistic_quality(generated)
</span></span><span style="display:flex;"><span>            coherence <span style="color:#91d7e3;font-weight:bold">=</span> assess_coherence(generated)
</span></span><span style="display:flex;"><span>            temp_cons <span style="color:#91d7e3;font-weight:bold">=</span> assess_temporal_consistency(generated, sample[<span style="color:#a6da95">&#39;time_period&#39;</span>])
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Store metrics</span>
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;perplexity&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(perplexity)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;bleu_scores&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(bleu)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;rouge_scores&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(rouge)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;historical_accuracy&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(hist_acc)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;linguistic_quality&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(ling_qual)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;coherence_scores&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(coherence)
</span></span><span style="display:flex;"><span>            metrics[<span style="color:#a6da95">&#39;temporal_consistency&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append(temp_cons)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Calculate aggregate metrics</span>
</span></span><span style="display:flex;"><span>    results <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> metric_name, values <span style="color:#91d7e3;font-weight:bold">in</span> metrics<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        results[metric_name] <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;mean&#39;</span>: np<span style="color:#91d7e3;font-weight:bold">.</span>mean(values),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;std&#39;</span>: np<span style="color:#91d7e3;font-weight:bold">.</span>std(values),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;min&#39;</span>: np<span style="color:#91d7e3;font-weight:bold">.</span>min(values),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;max&#39;</span>: np<span style="color:#91d7e3;font-weight:bold">.</span>max(values),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;median&#39;</span>: np<span style="color:#91d7e3;font-weight:bold">.</span>median(values)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> results</span></span></code></pre></div><figcaption>
        <strong>Listing 1: Comprehensive Evaluation Pipeline</strong>
    </figcaption>
</figure>
<p>The pipeline computes all the metrics we outlined above (standard LM metrics such as perplexity and BLEU/ROUGE, plus our historically specific assessments of accuracy, linguistic quality, and coherence). Each metric provides a different lens through which to view model performance: perplexity measures how well the model predicts the training distribution, BLEU/ROUGE measures literal similarity to the reference text, and the custom metrics assess historical authenticity and linguistic appropriateness.</p>
<p><strong>Why This Multi-Metric Approach Matters?</strong></p>
<p>Standard language model evaluation often focuses on perplexity and n-gram overlap metrics, which measure general language quality but miss domain-specific requirements. For historical language models, we need to know not just whether the text is fluent, but whether it&rsquo;s historically accurate, temporally consistent, and linguistically appropriate for the target period. This multi-metric approach ensures we catch different types of failures - a model might generate grammatically perfect text but fail historically, or produce historically accurate content with poor linguistic quality.</p>
<p>The aggregation step (<code>computing mean</code>, <code>std</code>, <code>min</code>, <code>max</code>, <code>median</code>) provides a comprehensive view of model performance across different test cases. This statistical summary helps identify whether the model performs consistently or has high variance, whether certain types of prompts cause failures, and how the model compares across different historical periods and linguistic phenomena.</p>
<h3 id="23-historical-accuracy-assessment">2.3 Historical Accuracy Assessment</h3>
<p>Standard LLM evaluation metrics (perplexity, BLEU, ROUGE) measure general language quality, but they don&rsquo;t tell us whether the model generates historically accurate text for London between 1500-1850. To address this, we built customized evaluation tools that check period-appropriate language, temporal consistency, London-specific geography and landmarks, and historical fact accuracy. These tools are implemented in <strong><code>05_evaluation/comprehensive_evaluator.py</code></strong> as shown in <a href="#listing2" class="listing-ref">Listing 2</a>:</p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">assess_historical_accuracy</span>(generated_text, historical_context):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Assess the historical accuracy of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0.0</span>
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check temporal consistency</span>
</span></span><span style="display:flex;"><span>    temporal_score <span style="color:#91d7e3;font-weight:bold">=</span> check_temporal_consistency(generated_text, historical_context[<span style="color:#a6da95">&#39;time_period&#39;</span>])
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">+=</span> temporal_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check historical facts</span>
</span></span><span style="display:flex;"><span>    fact_score <span style="color:#91d7e3;font-weight:bold">=</span> check_historical_facts(generated_text, historical_context[<span style="color:#a6da95">&#39;facts&#39;</span>])
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">+=</span> fact_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check period-appropriate language</span>
</span></span><span style="display:flex;"><span>    language_score <span style="color:#91d7e3;font-weight:bold">=</span> check_period_language(generated_text, historical_context[<span style="color:#a6da95">&#39;time_period&#39;</span>])
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">+=</span> language_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check geographical accuracy</span>
</span></span><span style="display:flex;"><span>    geo_score <span style="color:#91d7e3;font-weight:bold">=</span> check_geographical_accuracy(generated_text, historical_context[<span style="color:#a6da95">&#39;location&#39;</span>])
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">+=</span> geo_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check social context accuracy</span>
</span></span><span style="display:flex;"><span>    social_score <span style="color:#91d7e3;font-weight:bold">=</span> check_social_context(generated_text, historical_context[<span style="color:#a6da95">&#39;social_class&#39;</span>])
</span></span><span style="display:flex;"><span>    accuracy_score <span style="color:#91d7e3;font-weight:bold">+=</span> social_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> accuracy_score <span style="color:#91d7e3;font-weight:bold">/</span> total_checks
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">check_temporal_consistency</span>(text, time_period):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Check if text maintains temporal consistency with the specified period&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Define period-specific constraints</span>
</span></span><span style="display:flex;"><span>    period_constraints <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;1500-1600&#39;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;forbidden_terms&#39;</span>: [<span style="color:#a6da95">&#39;electricity&#39;</span>, <span style="color:#a6da95">&#39;steam engine&#39;</span>, <span style="color:#a6da95">&#39;railway&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;required_terms&#39;</span>: [<span style="color:#a6da95">&#39;ye&#39;</span>, <span style="color:#a6da95">&#39;hath&#39;</span>, <span style="color:#a6da95">&#39;doth&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;date_range&#39;</span>: (<span style="color:#f5a97f">1500</span>, <span style="color:#f5a97f">1600</span>)
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;1600-1700&#39;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;forbidden_terms&#39;</span>: [<span style="color:#a6da95">&#39;railway&#39;</span>, <span style="color:#a6da95">&#39;telegraph&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;required_terms&#39;</span>: [<span style="color:#a6da95">&#39;hath&#39;</span>, <span style="color:#a6da95">&#39;doth&#39;</span>, <span style="color:#a6da95">&#39;verily&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;date_range&#39;</span>: (<span style="color:#f5a97f">1600</span>, <span style="color:#f5a97f">1700</span>)
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;1700-1800&#39;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;forbidden_terms&#39;</span>: [<span style="color:#a6da95">&#39;telegraph&#39;</span>, <span style="color:#a6da95">&#39;telephone&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;required_terms&#39;</span>: [<span style="color:#a6da95">&#39;hath&#39;</span>, <span style="color:#a6da95">&#39;doth&#39;</span>, <span style="color:#a6da95">&#39;indeed&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;date_range&#39;</span>: (<span style="color:#f5a97f">1700</span>, <span style="color:#f5a97f">1800</span>)
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;1800-1850&#39;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;forbidden_terms&#39;</span>: [<span style="color:#a6da95">&#39;telephone&#39;</span>, <span style="color:#a6da95">&#39;automobile&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;required_terms&#39;</span>: [<span style="color:#a6da95">&#39;indeed&#39;</span>, <span style="color:#a6da95">&#39;verily&#39;</span>, <span style="color:#a6da95">&#39;pray&#39;</span>],
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;date_range&#39;</span>: (<span style="color:#f5a97f">1800</span>, <span style="color:#f5a97f">1850</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> time_period <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#91d7e3;font-weight:bold">in</span> period_constraints:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">0.5</span>  <span style="color:#6e738d;font-style:italic"># Neutral score for unknown periods</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    constraints <span style="color:#91d7e3;font-weight:bold">=</span> period_constraints[time_period]
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1.0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for forbidden terms (anachronisms)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> term <span style="color:#91d7e3;font-weight:bold">in</span> constraints[<span style="color:#a6da95">&#39;forbidden_terms&#39;</span>]:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> term<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            score <span style="color:#91d7e3;font-weight:bold">-=</span> <span style="color:#f5a97f">0.2</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for required period-appropriate terms</span>
</span></span><span style="display:flex;"><span>    period_terms_found <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> term <span style="color:#91d7e3;font-weight:bold">in</span> constraints[<span style="color:#a6da95">&#39;required_terms&#39;</span>]:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> term<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            period_terms_found <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> constraints[<span style="color:#a6da95">&#39;required_terms&#39;</span>]:
</span></span><span style="display:flex;"><span>        score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.3</span> <span style="color:#91d7e3;font-weight:bold">*</span> (period_terms_found <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(constraints[<span style="color:#a6da95">&#39;required_terms&#39;</span>]))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check date references</span>
</span></span><span style="display:flex;"><span>    date_score <span style="color:#91d7e3;font-weight:bold">=</span> check_date_references(text, constraints[<span style="color:#a6da95">&#39;date_range&#39;</span>])
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.2</span> <span style="color:#91d7e3;font-weight:bold">*</span> date_score
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">max</span>(<span style="color:#f5a97f">0.0</span>, <span style="color:#91d7e3">min</span>(<span style="color:#f5a97f">1.0</span>, score))</span></span></code></pre></div><figcaption>
        <strong>Listing 2: Historical Accuracy Assessment</strong>
    </figcaption>
</figure>
<p>The forbidden terms (like &ldquo;electricity&rdquo; for 1500-1600, &ldquo;railway&rdquo; for 1600-1700) are anachronisms - technologies or concepts that didn&rsquo;t exist in those periods. We selected them based on historical timelines: electricity wasn&rsquo;t harnessed until the late 1700s, railways didn&rsquo;t appear until the early 1800s, and telegraphs came later. Similarly, the required terms (such as &ldquo;hath&rdquo;, &ldquo;doth&rdquo;, and &ldquo;verily&rdquo;) are archaic language patterns we observed frequently in the training corpus for each period.</p>
<p>We analyzed the corpus to identify which linguistic markers were most characteristic of each era, then selected a small set that would catch obvious anachronisms without being overly restrictive. This is a practical heuristic rather than an exhaustive historical grammar - we focus on high-impact anachronisms and common period markers that are easy to detect automatically.</p>
<p><strong>How the scoring works</strong></p>
<p>The <strong><code>check_temporal_consistency()</code></strong> function starts with a score of <code>1.0</code> and applies penalties and bonuses: each forbidden term found subtracts 0.2 (so finding &ldquo;railway&rdquo; in 1600-1700 text drops the score), while finding required period-appropriate terms adds up to <code>0.3</code> based on how many are present. Date references within the period add up to 0.2. The final score ranges from <code>0.0</code> to <code>1.0</code>.</p>
<p>The overall <strong><code>assess_historical_accuracy()</code></strong> function then averages the five component scores (temporal consistency, historical facts, period-appropriate language, geographical accuracy, and social context) to produce a single score between 0 and 1, with higher values indicating better historical accuracy. In practice (and yes, we are generalizing), scores above <code>0.7</code> indicate good historical consistency, while scores below <code>0.5</code> suggest significant anachronisms or factual errors.</p>
<h3 id="24-linguistic-quality-evaluation">2.4 Linguistic Quality Evaluation</h3>
<p>While historical accuracy checks whether the model gets facts and period-appropriate terms right, linguistic quality measures how well the model writes - grammar, coherence, vocabulary diversity, sentence structure, and the presence of historical language patterns.</p>
<p>Standard metrics like BLEU and ROUGE don&rsquo;t capture whether the text reads naturally or uses appropriate archaic forms. We built customized tools that assess these dimensions, implemented in <strong><code>05_evaluation/comprehensive_evaluator.py</code></strong> as shown in <a href="#listing3" class="listing-ref">Listing 3</a>:</p>
<p>To make this easier to read, it helps to view the code as a scoring <em>scaffold</em> rather than a complete NLP system. Each <strong><code>check_*</code></strong> function is expected to return a normalized score in the range [0, 1] (higher is better), and <strong><code>assess_linguistic_quality()</code></strong> simply averages those components so you can track one headline number over time.</p>
<p>This mirrors patterns from earlier in the series: in <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a> we used lightweight, automatable checks to validate data quality, and in <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a> we relied on simple, repeatable metrics to judge training health. Here, we do the same for generation quality: start with cheap checks that run everywhere, then iterate toward richer evaluators as needed.</p>
<p>Also note that the exact weights (0.3/0.2, etc.) are tunable. The main benefit is splitting &ldquo;linguistic quality&rdquo; into components you can inspect individually, so when output is bad, you can tell <em>why</em> (grammar-ish structure vs coherence vs vocabulary vs historically flavored patterns).</p>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">assess_linguistic_quality</span>(generated_text):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Assess the linguistic quality of generated historical text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0.0</span>
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check grammatical correctness</span>
</span></span><span style="display:flex;"><span>    grammar_score <span style="color:#91d7e3;font-weight:bold">=</span> check_grammatical_correctness(generated_text)
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">+=</span> grammar_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check coherence and flow</span>
</span></span><span style="display:flex;"><span>    coherence_score <span style="color:#91d7e3;font-weight:bold">=</span> check_text_coherence(generated_text)
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">+=</span> coherence_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check vocabulary appropriateness</span>
</span></span><span style="display:flex;"><span>    vocab_score <span style="color:#91d7e3;font-weight:bold">=</span> check_vocabulary_appropriateness(generated_text)
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">+=</span> vocab_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check sentence structure variety</span>
</span></span><span style="display:flex;"><span>    structure_score <span style="color:#91d7e3;font-weight:bold">=</span> check_sentence_structure_variety(generated_text)
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">+=</span> structure_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check historical language patterns</span>
</span></span><span style="display:flex;"><span>    pattern_score <span style="color:#91d7e3;font-weight:bold">=</span> check_historical_language_patterns(generated_text)
</span></span><span style="display:flex;"><span>    quality_score <span style="color:#91d7e3;font-weight:bold">+=</span> pattern_score
</span></span><span style="display:flex;"><span>    total_checks <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> quality_score <span style="color:#91d7e3;font-weight:bold">/</span> total_checks
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">check_grammatical_correctness</span>(text):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Check grammatical correctness of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Parse text into sentences</span>
</span></span><span style="display:flex;"><span>    sentences <span style="color:#91d7e3;font-weight:bold">=</span> nltk<span style="color:#91d7e3;font-weight:bold">.</span>sent_tokenize(text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> sentences:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">0.0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    correct_sentences <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> sentence <span style="color:#91d7e3;font-weight:bold">in</span> sentences:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Check for basic grammatical patterns</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> check_sentence_grammar(sentence):
</span></span><span style="display:flex;"><span>            correct_sentences <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> correct_sentences <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(sentences)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">check_historical_language_patterns</span>(text):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Check if text follows appropriate historical language patterns&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0.0</span>
</span></span><span style="display:flex;"><span>    total_patterns <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for appropriate use of historical verb forms</span>
</span></span><span style="display:flex;"><span>    historical_verbs <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;hath&#39;</span>, <span style="color:#a6da95">&#39;doth&#39;</span>, <span style="color:#a6da95">&#39;dost&#39;</span>, <span style="color:#a6da95">&#39;art&#39;</span>, <span style="color:#a6da95">&#39;wilt&#39;</span>, <span style="color:#a6da95">&#39;shalt&#39;</span>]
</span></span><span style="display:flex;"><span>    verb_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> verb <span style="color:#91d7e3;font-weight:bold">in</span> historical_verbs:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> verb <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            verb_score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> historical_verbs:
</span></span><span style="display:flex;"><span>        score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.3</span> <span style="color:#91d7e3;font-weight:bold">*</span> (verb_score <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(historical_verbs))
</span></span><span style="display:flex;"><span>    total_patterns <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for appropriate use of historical pronouns</span>
</span></span><span style="display:flex;"><span>    historical_pronouns <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;thou&#39;</span>, <span style="color:#a6da95">&#39;thee&#39;</span>, <span style="color:#a6da95">&#39;thy&#39;</span>, <span style="color:#a6da95">&#39;thine&#39;</span>, <span style="color:#a6da95">&#39;ye&#39;</span>]
</span></span><span style="display:flex;"><span>    pronoun_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> pronoun <span style="color:#91d7e3;font-weight:bold">in</span> historical_pronouns:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> pronoun <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            pronoun_score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> historical_pronouns:
</span></span><span style="display:flex;"><span>        score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.3</span> <span style="color:#91d7e3;font-weight:bold">*</span> (pronoun_score <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(historical_pronouns))
</span></span><span style="display:flex;"><span>    total_patterns <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for appropriate use of historical conjunctions</span>
</span></span><span style="display:flex;"><span>    historical_conjunctions <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;whilst&#39;</span>, <span style="color:#a6da95">&#39;betwixt&#39;</span>, <span style="color:#a6da95">&#39;amongst&#39;</span>, <span style="color:#a6da95">&#39;ere&#39;</span>, <span style="color:#a6da95">&#39;anon&#39;</span>]
</span></span><span style="display:flex;"><span>    conj_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> conj <span style="color:#91d7e3;font-weight:bold">in</span> historical_conjunctions:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> conj <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            conj_score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> historical_conjunctions:
</span></span><span style="display:flex;"><span>        score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.2</span> <span style="color:#91d7e3;font-weight:bold">*</span> (conj_score <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(historical_conjunctions))
</span></span><span style="display:flex;"><span>    total_patterns <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for appropriate use of historical interjections</span>
</span></span><span style="display:flex;"><span>    historical_interjections <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;verily&#39;</span>, <span style="color:#a6da95">&#39;indeed&#39;</span>, <span style="color:#a6da95">&#39;forsooth&#39;</span>, <span style="color:#a6da95">&#39;prithee&#39;</span>, <span style="color:#a6da95">&#39;marry&#39;</span>]
</span></span><span style="display:flex;"><span>    interj_score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> interj <span style="color:#91d7e3;font-weight:bold">in</span> historical_interjections:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> interj <span style="color:#91d7e3;font-weight:bold">in</span> text<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            interj_score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> historical_interjections:
</span></span><span style="display:flex;"><span>        score <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">0.2</span> <span style="color:#91d7e3;font-weight:bold">*</span> (interj_score <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(historical_interjections))
</span></span><span style="display:flex;"><span>    total_patterns <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> score <span style="color:#91d7e3;font-weight:bold">/</span> total_patterns <span style="color:#c6a0f6">if</span> total_patterns <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span> <span style="color:#c6a0f6">else</span> <span style="color:#f5a97f">0.0</span></span></span></code></pre></div><figcaption>
        <strong>Listing 3: Linguistic Quality Evaluation</strong>
    </figcaption>
</figure>
<p><strong>About NLTK:</strong> We use <strong>NLTK</strong> (Natural Language Toolkit), a standard Python library for natural language processing, to handle text tokenization. If you followed <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1
	</span>
</a>&rsquo;s setup instructions, NLTK was already installed as part of the data processing dependencies. In <code>check_grammatical_correctness()</code>, we use <code>nltk.sent_tokenize()</code> to split text into sentences so we can evaluate grammar sentence-by-sentence. NLTK also provides word tokenization (<code>word_tokenize</code>) and BLEU score calculation (<code>sentence_bleu</code>), which are used elsewhere in the evaluation pipeline.</p>
<p>We chose NLTK because it&rsquo;s well-established, handles edge cases (like abbreviations and historical punctuation), and provides reliable sentence boundaries even with archaic English patterns. The same qualities made it useful during data collection and cleaning (covered in <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a>).</p>
<p>The historical language patterns we check (verbs like <em><strong>hath, doth</strong></em>, pronouns like <em><strong>thou, thee</strong></em>, conjunctions like <em><strong>whilst, betwixt</strong></em>, and interjections like <em><strong>verily, forsooth</strong></em>) are the same archaic forms we identified during corpus analysis for temporal consistency. The difference here is that we&rsquo;re measuring their presence as a positive signal of historical authenticity, rather than using them as required/forbidden constraints. Each pattern category (verbs, pronouns, conjunctions, interjections) contributes proportionally to the score based on how many patterns from that category appear in the text.</p>
<p><strong>How the scoring works</strong></p>
<p>The <strong><code>assess_linguistic_quality()</code></strong> function averages five component scores (<code>grammar</code>, <code>coherence</code>, <code>vocabulary appropriateness</code>, <code>sentence structure variety</code>, and <code>historical language patterns</code>) to produce a single score between <code>0</code> and <code>1</code>. Each component is evaluated independently and returns a score in the range <code>[0, 1]</code>.</p>
<p>For example, <strong><code>check_grammatical_correctness()</code></strong> counts the proportion of grammatically correct sentences, while <strong><code>check_historical_language_patterns()</code></strong> weights the presence of archaic verb forms (30%), pronouns (30%), conjunctions (20%), and interjections (20%) to produce a pattern score. The final linguistic quality score is the simple average of all five components. In practice, scores above <code>0.75</code> indicate strong linguistic quality with good grammar and historical flavor, while scores below 0.6 suggest the model struggles with either basic grammar or historical language patterns.</p>
<h3 id="25-running-evaluations">2.5 Running Evaluations</h3>
<p>You can run the evaluators directly from the command line. The framework defaults to CPU for safety (so you can evaluate during training without GPU conflicts), but you can use <code>--device gpu</code> when the GPU is free for faster evaluation.</p>
<p><strong>Quick example:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Quick evaluation (runs in minutes, no external APIs)</span>
</span></span><span style="display:flex;"><span>python 05_evaluation/run_evaluation.py --mode quick --device cpu
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Comprehensive evaluation (includes benchmarks, optional G-Eval)</span>
</span></span><span style="display:flex;"><span>python 05_evaluation/run_evaluation.py --mode comprehensive --device cpu</span></span></code></pre></div>
<p>The unified launcher (<strong><code>run_evaluation.py</code></strong>) supports multiple modes: <code>setup</code> (install dependencies), <code>quick</code> (fast validation), <code>comprehensive</code> (full suite with benchmarks), <code>dataset</code> (generate test cases), and <code>all</code> (complete evaluation). You can also call <strong><code>quick_eval.py</code></strong> or <strong><code>comprehensive_evaluator.py</code></strong> directly if you need more control.</p>
<p><strong>Practical Evaluation Workflow:</strong></p>
<p>Our typical evaluation workflow follows this pattern:</p>
<ol>
<li><strong>After Training</strong>: Run a quick evaluation to get immediate feedback on model performance</li>
<li><strong>Before Publishing</strong>: Run a comprehensive evaluation to ensure the model meets quality standards</li>
<li><strong>During Development</strong>: Use interactive testing to explore model behavior on specific prompts</li>
<li><strong>For Research</strong>: Generate custom test datasets and run targeted evaluations</li>
</ol>
<p>The framework defaults to CPU for safety (so you can evaluate during training without GPU conflicts), but you can use <code>--device gpu</code> when the GPU is free for faster evaluation. This design allows continuous assessment throughout the training process without interfering with GPU resources needed for training.</p>
<p>For complete usage examples, command-line options, and troubleshooting, see the <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/EVALUATION_GUIDE.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Evaluation Guide
	</span>
</a> in the repository.</p>
<h2 id="3-comprehensive-testing-pipeline">3. Comprehensive Testing Pipeline</h2>
<h3 id="31-automated-testing-framework">3.1 Automated Testing Framework</h3>
<p>The <strong><code>06_testing</code></strong> package contains a parallel set of tests that double-check the full system. <a href="#listing4" class="listing-ref">Listing 4</a> captures the idea behind <strong><code>run_comprehensive_tests</code></strong>.</p>
<p>We group tests into basic functionality, historical accuracy, linguistic quality, performance, edge cases, and integration, then run them as a batch and emit a structured report. This mirrors how you would build a real CI test suite, but at a scale appropriate for this learning project.</p>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">run_comprehensive_tests</span>(model, tokenizer, device<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Run comprehensive tests on historical language model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    test_results <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;basic_functionality&#39;</span>: test_basic_functionality(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;historical_accuracy&#39;</span>: test_historical_accuracy(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;linguistic_quality&#39;</span>: test_linguistic_quality(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;performance_metrics&#39;</span>: test_performance_metrics(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;edge_cases&#39;</span>: test_edge_cases(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;integration_tests&#39;</span>: test_integration(model, tokenizer, device)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Generate test report</span>
</span></span><span style="display:flex;"><span>    generate_test_report(test_results)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> test_results
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_basic_functionality</span>(model, tokenizer, device):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test basic model functionality&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;text_generation&#39;</span>: test_text_generation(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;tokenization&#39;</span>: test_tokenization(tokenizer),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;model_loading&#39;</span>: test_model_loading(model, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;memory_usage&#39;</span>: test_memory_usage(model, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;inference_speed&#39;</span>: test_inference_speed(model, tokenizer, device)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_historical_accuracy</span>(model, tokenizer, device):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test historical accuracy of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;temporal_consistency&#39;</span>: test_temporal_consistency(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;factual_accuracy&#39;</span>: test_factual_accuracy(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;period_appropriate_language&#39;</span>: test_period_language(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;geographical_accuracy&#39;</span>: test_geographical_accuracy(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;social_context_accuracy&#39;</span>: test_social_context(model, tokenizer, device)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests</span></span></code></pre></div><figcaption>
        <strong>Listing 4: Comprehensive Testing Framework</strong>
    </figcaption>
</figure>
<p>Automated tests cover basics, historical accuracy, linguistic quality, performance, edge cases, and integration.</p>
<h3 id="32-interactive-testing-and-validation">3.2 Interactive Testing and Validation</h3>
<p>For manual exploration, the interactive testing interface (conceptually similar to the CLI flows in <strong><code>06_inference/inference_unified.py</code></strong>) lets you type prompts, trigger specific test groups, and immediately inspect analysis for each generation. <a href="#listing5" class="listing-ref">Listing 5</a> shows a simple REPL loop that dispatches to the same evaluation helpers used in the automated tests.</p>
<figure id="listing5"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">interactive_testing</span>(model, tokenizer, device<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Interactive testing interface for historical language model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Interactive Testing Mode&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;=&#34;</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">50</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Enter prompts to test the model. Type &#39;quit&#39; to exit.&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Available commands:&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;  - Enter any text prompt to generate continuation&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;  - &#39;test_historical&#39; - Run historical accuracy tests&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;  - &#39;test_linguistic&#39; - Run linguistic quality tests&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;  - &#39;test_performance&#39; - Run performance tests&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;  - &#39;quit&#39; - Exit testing mode&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>            prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Enter prompt: &#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> prompt<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;quit&#39;</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">elif</span> prompt<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;test_historical&#39;</span>:
</span></span><span style="display:flex;"><span>                run_historical_tests(model, tokenizer, device)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">elif</span> prompt<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;test_linguistic&#39;</span>:
</span></span><span style="display:flex;"><span>                run_linguistic_tests(model, tokenizer, device)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">elif</span> prompt<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;test_performance&#39;</span>:
</span></span><span style="display:flex;"><span>                run_performance_tests(model, tokenizer, device)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">elif</span> prompt:
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Generate text</span>
</span></span><span style="display:flex;"><span>                generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_text(model, tokenizer, prompt, device)
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Generated: </span><span style="color:#a6da95">{</span>generated<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>()
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Analyze generated text</span>
</span></span><span style="display:flex;"><span>                analysis <span style="color:#91d7e3;font-weight:bold">=</span> analyze_generated_text(generated, prompt)
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Analysis: </span><span style="color:#a6da95">{</span>analysis<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Please enter a valid prompt or command.&#34;</span>)
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">KeyboardInterrupt</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Exiting interactive testing mode...&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Error: </span><span style="color:#a6da95">{</span>e<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Please try again.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">analyze_generated_text</span>(text, prompt):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Analyze generated text for quality and accuracy&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    analysis <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;length&#39;</span>: <span style="color:#91d7e3">len</span>(text),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;sentences&#39;</span>: <span style="color:#91d7e3">len</span>(nltk<span style="color:#91d7e3;font-weight:bold">.</span>sent_tokenize(text)),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;historical_accuracy&#39;</span>: assess_historical_accuracy(text, {}),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;linguistic_quality&#39;</span>: assess_linguistic_quality(text),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;coherence&#39;</span>: assess_coherence(text),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;relevance&#39;</span>: assess_relevance(text, prompt)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> analysis</span></span></code></pre></div><figcaption>
        <strong>Listing 5: Interactive Testing Interface</strong>
    </figcaption>
</figure>
<p>Interactive mode lets you try prompts, run quick tests, and see immediate analysis.</p>
<h3 id="33-performance-benchmarking">3.3 Performance Benchmarking</h3>
<p>Performance benchmarking follows the same pattern: generate controlled workloads and measure speed and resource usage. <a href="#listing6" class="listing-ref">Listing 6</a> illustrates how we vary sequence length, measure average latency, and compute tokens-per-second, alongside separate helpers for memory, batch throughput, long-sequence handling, and basic concurrency.</p>
<figure id="listing6"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">benchmark_model_performance</span>(model, tokenizer, device<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Benchmark model performance across different scenarios&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    benchmarks <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;inference_speed&#39;</span>: benchmark_inference_speed(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;memory_usage&#39;</span>: benchmark_memory_usage(model, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;batch_processing&#39;</span>: benchmark_batch_processing(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;long_sequence_handling&#39;</span>: benchmark_long_sequences(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;concurrent_requests&#39;</span>: benchmark_concurrent_requests(model, tokenizer, device)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> benchmarks
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">benchmark_inference_speed</span>(model, tokenizer, device):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Benchmark inference speed for different sequence lengths&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    sequence_lengths <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#f5a97f">50</span>, <span style="color:#f5a97f">100</span>, <span style="color:#f5a97f">200</span>, <span style="color:#f5a97f">500</span>, <span style="color:#f5a97f">1000</span>]
</span></span><span style="display:flex;"><span>    results <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> length <span style="color:#91d7e3;font-weight:bold">in</span> sequence_lengths:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Generate test prompts of different lengths</span>
</span></span><span style="display:flex;"><span>        prompts <span style="color:#91d7e3;font-weight:bold">=</span> generate_test_prompts(length, num_prompts<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">100</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Measure inference time</span>
</span></span><span style="display:flex;"><span>        start_time <span style="color:#91d7e3;font-weight:bold">=</span> time<span style="color:#91d7e3;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> prompt <span style="color:#91d7e3;font-weight:bold">in</span> prompts:
</span></span><span style="display:flex;"><span>            generate_text(model, tokenizer, prompt, device)
</span></span><span style="display:flex;"><span>        end_time <span style="color:#91d7e3;font-weight:bold">=</span> time<span style="color:#91d7e3;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        total_time <span style="color:#91d7e3;font-weight:bold">=</span> end_time <span style="color:#91d7e3;font-weight:bold">-</span> start_time
</span></span><span style="display:flex;"><span>        avg_time_per_prompt <span style="color:#91d7e3;font-weight:bold">=</span> total_time <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">len</span>(prompts)
</span></span><span style="display:flex;"><span>        tokens_per_second <span style="color:#91d7e3;font-weight:bold">=</span> length <span style="color:#91d7e3;font-weight:bold">/</span> avg_time_per_prompt
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        results[length] <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;avg_time_per_prompt&#39;</span>: avg_time_per_prompt,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;tokens_per_second&#39;</span>: tokens_per_second,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;total_time&#39;</span>: total_time
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> results</span></span></code></pre></div><figcaption>
        <strong>Listing 6: Performance Benchmarking</strong>
    </figcaption>
</figure>
<p>Benchmarks capture inference speed, memory, batch throughput, long-sequence handling, and simple concurrency.</p>
<h2 id="4-model-deployment-and-publishing">4. Model Deployment and Publishing</h2>
<p>With evaluation and testing complete, we&rsquo;re ready to make our models available for use. This section covers the two deployment paths we support: direct inference from PyTorch checkpoints (useful during development and for maximum control) and publishing to Hugging Face Hub (for easy sharing and community access).</p>
<p>As called out in <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1
	</span>
</a>, both the SLM (117M parameters) and the Regular Model (354M parameters) are fully trained and available. The SLM has already been published on <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Hugging Face Hub
	</span>
</a>, while the Regular Model is ready for publication. Both can also be run directly from local PyTorch checkpoints.</p>
<h3 id="41-two-paths-to-inference">4.1 Two Paths to Inference</h3>
<p>We provide two complementary ways to run inference, each suited to different use cases.</p>
<p><strong>PyTorch Checkpoint Inference</strong> gives you direct access to the trained model weights without any conversion overhead. This is ideal during development, when you want to test a freshly trained checkpoint, or when you need maximum control over the inference process. The checkpoints live in <strong><code>09_models/checkpoints/</code></strong> - the SLM at <strong><code>slm/checkpoint-4000.pt</code></strong> (117M parameters) and the Regular Model at <strong><code>checkpoint-60001.pt</code></strong> (354M parameters). The <strong><code>inference_pytorch.py</code></strong> script handles loading these directly: <a href="#listing7" class="listing-ref">Listing 7</a></p>
<figure id="listing7"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># SLM inference from checkpoint</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_pytorch.py <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --checkpoint 09_models/checkpoints/slm/checkpoint-4000.pt <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Regular model inference from checkpoint</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_pytorch.py <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --checkpoint 09_models/checkpoints/checkpoint-60001.pt <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 7: Running Inference from PyTorch Checkpoints</strong>
    </figcaption>
</figure>
<p><strong>Hugging Face Model Inference</strong> uses the published models on Hugging Face Hub, which means anyone can load and use them with just a few lines of code - no need to download checkpoints or set up the full training environment. The <strong><code>inference_unified.py</code></strong> script provides a consistent interface for both published models and local checkpoints: <a href="#listing8" class="listing-ref">Listing 8</a></p>
<figure id="listing8"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Published model inference (downloads from Hugging Face Hub)</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --published <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --model_name bahree/london-historical-slm <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Interactive mode for exploration</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type slm --interactive
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Demo mode with curated historical prompts</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type slm --demo</span></span></code></pre></div><figcaption>
        <strong>Listing 8: Hugging Face Model Inference</strong>
    </figcaption>
</figure>
<p>We&rsquo;ve tested both paths extensively. The published SLM loads in about 9 seconds on a GPU, generates text in under 6 seconds, and passes all 10 automated validation tests. The unified inference script provides clean logging, proper model detection, and accurate parameter counts - small details that make a big difference when debugging or demonstrating the models.</p>
<h3 id="42-publishing-to-hugging-face-hub">4.2 Publishing to Hugging Face Hub</h3>
<p>Publishing to Hugging Face Hub makes our models accessible to the broader community without requiring anyone to clone our repository or set up a training environment. The process involves converting our PyTorch checkpoints to the Hugging Face format, creating a model card with documentation, and uploading everything to the Hub.</p>
<p>The publishing workflow is handled by scripts in <strong><code>10_scripts/</code></strong> - specifically <strong><code>publish_slm_to_huggingface.py</code></strong> for the SLM and <strong><code>publish_to_huggingface.py</code></strong> for the Regular Model. <a href="#listing9" class="listing-ref">Listing 9</a> shows the core publishing flow: authenticate with the Hub, create (or reuse) a repository, save the model and tokenizer locally in Hugging Face format, upload the folder, and generate a model card.</p>
<figure id="listing9"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">publish_to_huggingface</span>(model, tokenizer, model_name, description, tags):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Publish model to Hugging Face Hub&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">huggingface_hub</span> <span style="color:#8bd5ca">import</span> HfApi
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    api <span style="color:#91d7e3;font-weight:bold">=</span> HfApi()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create model repository</span>
</span></span><span style="display:flex;"><span>    repo_id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;bahree/</span><span style="color:#a6da95">{</span>model_name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    api<span style="color:#91d7e3;font-weight:bold">.</span>create_repo(repo_id<span style="color:#91d7e3;font-weight:bold">=</span>repo_id, exist_ok<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Save model and tokenizer locally</span>
</span></span><span style="display:flex;"><span>    model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;./models/</span><span style="color:#a6da95">{</span>model_name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;./models/</span><span style="color:#a6da95">{</span>model_name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Upload to Hub</span>
</span></span><span style="display:flex;"><span>    api<span style="color:#91d7e3;font-weight:bold">.</span>upload_folder(
</span></span><span style="display:flex;"><span>        folder_path<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;./models/</span><span style="color:#a6da95">{</span>model_name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>,
</span></span><span style="display:flex;"><span>        repo_id<span style="color:#91d7e3;font-weight:bold">=</span>repo_id,
</span></span><span style="display:flex;"><span>        commit_message<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Initial model upload&#34;</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Generate and upload model card (README.md)</span>
</span></span><span style="display:flex;"><span>    model_card <span style="color:#91d7e3;font-weight:bold">=</span> generate_model_card(model_name, description, tags)
</span></span><span style="display:flex;"><span>    api<span style="color:#91d7e3;font-weight:bold">.</span>upload_file(
</span></span><span style="display:flex;"><span>        path_or_fileobj<span style="color:#91d7e3;font-weight:bold">=</span>model_card,
</span></span><span style="display:flex;"><span>        path_in_repo<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;README.md&#34;</span>,
</span></span><span style="display:flex;"><span>        repo_id<span style="color:#91d7e3;font-weight:bold">=</span>repo_id
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> repo_id</span></span></code></pre></div><figcaption>
        <strong>Listing 9: Hugging Face Publishing</strong>
    </figcaption>
</figure>
<p>The <code>generate_model_card()</code> function creates the <strong><code>README.md</code></strong> that appears on the Hugging Face model page. This includes model description, architecture details, training data sources, usage examples, and limitations. You can see the live model cards at <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-slm
	</span>
</a> and <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-llm
	</span>
</a>.</p>
<p><a href="#listing10" class="listing-ref">Listing 10</a> shows how to load and use the published models:</p>
<figure id="listing10"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoTokenizer, AutoModelForCausalLM
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Load the published model</span>
</span></span><span style="display:flex;"><span>model_name <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>  <span style="color:#6e738d;font-style:italic"># or &#34;bahree/london-historical-llm&#34;</span>
</span></span><span style="display:flex;"><span>tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Move to GPU if available</span>
</span></span><span style="display:flex;"><span>device <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;cuda&#34;</span> <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;cpu&#34;</span>
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Generate historical text</span>
</span></span><span style="display:flex;"><span>prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(
</span></span><span style="display:flex;"><span>    inputs[<span style="color:#a6da95">&#34;input_ids&#34;</span>],
</span></span><span style="display:flex;"><span>    max_new_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">50</span>,
</span></span><span style="display:flex;"><span>    do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span>    temperature<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.8</span>,
</span></span><span style="display:flex;"><span>    top_p<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.95</span>,
</span></span><span style="display:flex;"><span>    repetition_penalty<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1.2</span>,
</span></span><span style="display:flex;"><span>    pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">print</span>(tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>))</span></span></code></pre></div><figcaption>
        <strong>Listing 10: Loading Models from Hugging Face Hub</strong>
    </figcaption>
</figure>
<h3 id="43-publishing-workflow">4.3 Publishing Workflow</h3>
<p>If you want to publish your own trained model to Hugging Face Hub, here&rsquo;s the workflow we followed:</p>
<ol>
<li>
<p><strong>Set up authentication</strong>: Install <code>huggingface_hub</code> and authenticate with a token that has Write permissions. You can generate tokens at <a
	
		href = "https://huggingface.co/settings/tokens"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		huggingface.co/settings/tokens
	</span>
</a>.</p>
</li>
<li>
<p><strong>Convert the checkpoint</strong>: PyTorch training checkpoints include optimizer states and training metadata that aren&rsquo;t needed for inference. The conversion scripts extract just the model weights and translate them to Hugging Face&rsquo;s naming conventions (covered in detail in Section 5).</p>
</li>
<li>
<p><strong>Prepare the tokenizer</strong>: Save the tokenizer files alongside the model. Our custom tokenizer with 30,000 tokens and 150+ historical special tokens needs to be converted to the <code>transformers</code> library format.</p>
</li>
<li>
<p><strong>Generate a model card</strong>: The <strong><code>README.md</code></strong> on your Hugging Face model page serves as documentation. Include model architecture details, training data sources, usage examples, evaluation results, and limitations. The scripts generate this automatically, but you should review and customize it.</p>
</li>
<li>
<p><strong>Upload and validate</strong>: Push everything to the Hub, then immediately test with <code>from_pretrained()</code> to ensure the published model loads and generates correctly.</p>
</li>
</ol>
<blockquote>
<p><strong>📝 Full documentation</strong>: See <strong><code>08_documentation/HUGGINGFACE_PUBLISHING.md</code></strong> and <strong><code>08_documentation/DEPLOYMENT_GUIDE.md</code></strong> in the repository for the complete step-by-step workflow with troubleshooting guidance.</p></blockquote>
<h2 id="5-pytorch-to-hugging-face-format-conversion">5. PyTorch to Hugging Face Format Conversion</h2>
<h3 id="51-why-format-conversion-is-necessary">5.1 Why Format Conversion is Necessary</h3>
<p>During training, our models are saved in PyTorch&rsquo;s native <code>.pt</code> format. These checkpoints include everything needed to resume training: model weights, optimizer states, learning rate schedules, and training metadata. However, for deployment and sharing, we need a leaner, inference-optimized format compatible with the broader machine learning ecosystem.</p>
<p>Think of it like the difference between a development environment and a production deployment: training checkpoints are like a developer&rsquo;s workspace with all the tools and intermediate files, while Hugging Face format is like a clean, standardized package that anyone can use without understanding the internal training details.</p>
<p>The Hugging Face Hub expects models to follow specific file structures, naming conventions, and metadata requirements. The conversion process extracts just the model weights (discarding optimizer states and training metadata), translates weight names to match Hugging Face conventions, creates proper configuration files, and ensures the tokenizer is compatible with the <code>transformers</code> library.</p>
<h3 id="52-the-conversion-process">5.2 The Conversion Process</h3>
<p>The conversion handles several transformations to bridge PyTorch and Hugging Face formats:</p>
<ul>
<li><strong>Weight name mapping</strong>: PyTorch layer names like <code>transformer.h.0.attn.c_attn.weight</code> become Hugging Face names like <code>transformer.h.0.attn.c_attn.weight</code> (mostly the same for GPT-2, but with careful handling of edge cases)</li>
<li><strong>Automatic torch.compile handling</strong>: If you used <code>torch.compile()</code> during training, weights get prefixed with <code>_orig_mod.</code> - the conversion strips these prefixes</li>
<li><strong>Configuration translation</strong>: Model hyperparameters (n_layer, n_head, n_embd, etc.) are mapped to Hugging Face&rsquo;s <code>config.json</code> format</li>
<li><strong>Tokenizer conversion</strong>: Our custom 30,000-token vocabulary with 150+ historical special tokens is converted to <code>transformers</code> library format</li>
<li><strong>Validation</strong>: After conversion, we verify that the model loads correctly and produces expected outputs</li>
</ul>
<blockquote>
<p><strong>💻 Full Implementation</strong>: See <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/10_scripts/publish_slm_to_huggingface.py"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong><code>10_scripts/publish_slm_to_huggingface.py</code></strong>
	</span>
</a> for the complete conversion pipeline with error handling, validation, and model card generation.</p></blockquote>
<h3 id="53-dependencies-for-hugging-face-integration">5.3 Dependencies for Hugging Face Integration</h3>
<p>The Hugging Face integration requires specific dependencies and follows established patterns for model publishing and usage: <a href="#listing11" class="listing-ref">Listing 11</a></p>
<figure id="listing11"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Required dependencies for Hugging Face integration</span>
</span></span><span style="display:flex;"><span>huggingface_dependencies <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;transformers&#34;</span>: <span style="color:#a6da95">&#34;&gt;=4.21.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;torch&#34;</span>: <span style="color:#a6da95">&#34;&gt;=1.12.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;tokenizers&#34;</span>: <span style="color:#a6da95">&#34;&gt;=0.12.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;safetensors&#34;</span>: <span style="color:#a6da95">&#34;&gt;=0.3.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;accelerate&#34;</span>: <span style="color:#a6da95">&#34;&gt;=0.20.0&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;huggingface_hub&#34;</span>: <span style="color:#a6da95">&#34;&gt;=0.10.0&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Model loading and usage example</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">load_published_model</span>(model_name<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Load published model from Hugging Face Hub&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Suppress warnings for cleaner output</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">warnings</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">logging</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;TRANSFORMERS_VERBOSITY&#39;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;error&#39;</span>
</span></span><span style="display:flex;"><span>    warnings<span style="color:#91d7e3;font-weight:bold">.</span>filterwarnings(<span style="color:#a6da95">&#39;ignore&#39;</span>)
</span></span><span style="display:flex;"><span>    logging<span style="color:#91d7e3;font-weight:bold">.</span>getLogger(<span style="color:#a6da95">&#34;transformers&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>setLevel(logging<span style="color:#91d7e3;font-weight:bold">.</span>ERROR)
</span></span><span style="display:flex;"><span>    os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;TOKENIZERS_PARALLELISM&#39;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;false&#39;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load model and tokenizer</span>
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>    model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set pad token if not set</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>pad_token <span style="color:#91d7e3;font-weight:bold">is</span> <span style="color:#f5a97f">None</span>:
</span></span><span style="display:flex;"><span>        tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>pad_token <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token
</span></span><span style="display:flex;"><span>        tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>pad_token_id <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> model, tokenizer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_historical_text</span>(model, tokenizer, prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">50</span>, temperature<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.3</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Generate historical text using the published model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Tokenize input</span>
</span></span><span style="display:flex;"><span>    inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Generate text</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(
</span></span><span style="display:flex;"><span>            inputs,
</span></span><span style="display:flex;"><span>            max_new_tokens<span style="color:#91d7e3;font-weight:bold">=</span>max_length,
</span></span><span style="display:flex;"><span>            do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span>            temperature<span style="color:#91d7e3;font-weight:bold">=</span>temperature,
</span></span><span style="display:flex;"><span>            top_p<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.9</span>,
</span></span><span style="display:flex;"><span>            top_k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">20</span>,
</span></span><span style="display:flex;"><span>            repetition_penalty<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1.2</span>,
</span></span><span style="display:flex;"><span>            no_repeat_ngram_size<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">3</span>,
</span></span><span style="display:flex;"><span>            pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>pad_token_id,
</span></span><span style="display:flex;"><span>            eos_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id,
</span></span><span style="display:flex;"><span>            early_stopping<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Decode output</span>
</span></span><span style="display:flex;"><span>    generated_text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> generated_text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Example usage</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#f4dbd6">__name__</span> <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the published model</span>
</span></span><span style="display:flex;"><span>    model, tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> load_published_model()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test prompts</span>
</span></span><span style="display:flex;"><span>    test_prompts <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The gentleman from the country said, &#39;we have never seen such a sight&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Thames flowed dark and mysterious through the heart&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;Parliament sat in Westminster Hall&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Great Fire of 1666 had destroyed&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Generate text for each prompt</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> prompt <span style="color:#91d7e3;font-weight:bold">in</span> test_prompts:
</span></span><span style="display:flex;"><span>        generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, prompt)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Prompt: </span><span style="color:#a6da95">{</span>prompt<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Generated: </span><span style="color:#a6da95">{</span>generated<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;-&#34;</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">80</span>)</span></span></code></pre></div><figcaption>
        <strong>Listing 11: Hugging Face Dependencies</strong>
    </figcaption>
</figure>
<p>Hugging Face integration provides standard <code>from_pretrained()</code> loading and generation with minimal setup, making the models easy to share and reuse.</p>
<h3 id="54-comprehensive-testing-and-validation-framework">5.4 Comprehensive Testing and Validation Framework</h3>
<p>Once a model is on the Hub, <strong><code>06_inference/test_published_models.py</code></strong> provides a concrete implementation of the testing pattern in <a href="#listing12" class="listing-ref">Listing 12</a>. It loads the model via <code>from_pretrained</code>, runs functional, historical, linguistic, and performance checks, and prints a human-readable summary so you can verify the published artefact behaves like your local checkpoints.</p>
<figure id="listing12"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_published_model</span>(model_name<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Comprehensive testing of published model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Testing published model: </span><span style="color:#a6da95">{</span>model_name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load model</span>
</span></span><span style="display:flex;"><span>    model, tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> load_published_model(model_name)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test basic functionality</span>
</span></span><span style="display:flex;"><span>    basic_tests <span style="color:#91d7e3;font-weight:bold">=</span> test_basic_functionality(model, tokenizer)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test historical accuracy</span>
</span></span><span style="display:flex;"><span>    historical_tests <span style="color:#91d7e3;font-weight:bold">=</span> test_historical_accuracy(model, tokenizer)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test linguistic quality</span>
</span></span><span style="display:flex;"><span>    linguistic_tests <span style="color:#91d7e3;font-weight:bold">=</span> test_linguistic_quality(model, tokenizer)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test performance metrics</span>
</span></span><span style="display:flex;"><span>    performance_tests <span style="color:#91d7e3;font-weight:bold">=</span> test_performance_metrics(model, tokenizer)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Compile results</span>
</span></span><span style="display:flex;"><span>    results <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;basic_functionality&#34;</span>: basic_tests,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;historical_accuracy&#34;</span>: historical_tests,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;linguistic_quality&#34;</span>: linguistic_tests,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;performance_metrics&#34;</span>: performance_tests
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Print summary</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Test Results Summary:&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;=&#34;</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">50</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> category, tests <span style="color:#91d7e3;font-weight:bold">in</span> results<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">{</span>category<span style="color:#91d7e3;font-weight:bold">.</span>replace(<span style="color:#a6da95">&#39;_&#39;</span>, <span style="color:#a6da95">&#39; &#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>title()<span style="color:#a6da95">}</span><span style="color:#a6da95">:&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> test_name, result <span style="color:#91d7e3;font-weight:bold">in</span> tests<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>            status <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;PASS&#34;</span> <span style="color:#c6a0f6">if</span> result <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;FAIL&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;  </span><span style="color:#a6da95">{</span>test_name<span style="color:#a6da95">}</span><span style="color:#a6da95">: </span><span style="color:#a6da95">{</span>status<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> results
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_basic_functionality</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test basic model functionality&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test model loading</span>
</span></span><span style="display:flex;"><span>    tests[<span style="color:#a6da95">&#34;model_loading&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> model <span style="color:#91d7e3;font-weight:bold">is</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#f5a97f">None</span> <span style="color:#91d7e3;font-weight:bold">and</span> tokenizer <span style="color:#91d7e3;font-weight:bold">is</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test tokenizer functionality</span>
</span></span><span style="display:flex;"><span>    test_text <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;In the year 1834, London was&#34;</span>
</span></span><span style="display:flex;"><span>    tokens <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(test_text)
</span></span><span style="display:flex;"><span>    decoded <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(tokens)
</span></span><span style="display:flex;"><span>    tests[<span style="color:#a6da95">&#34;tokenizer_encode_decode&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> test_text <span style="color:#91d7e3;font-weight:bold">in</span> decoded
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test model generation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(test_text, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>            outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(inputs, max_new_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">10</span>, do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        generated <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;model_generation&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">len</span>(generated) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#91d7e3">len</span>(test_text)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;model_generation&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test special tokens</span>
</span></span><span style="display:flex;"><span>    special_tokens <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#34;&lt;|london|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thou|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|hath|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|doth|&gt;&#34;</span>]
</span></span><span style="display:flex;"><span>    special_token_tests <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> token <span style="color:#91d7e3;font-weight:bold">in</span> special_tokens:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> token <span style="color:#91d7e3;font-weight:bold">in</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>get_vocab():
</span></span><span style="display:flex;"><span>            special_token_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            special_token_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>    tests[<span style="color:#a6da95">&#34;special_tokens&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">any</span>(special_token_tests)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_historical_accuracy</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test historical accuracy of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test prompts for different historical periods</span>
</span></span><span style="display:flex;"><span>    period_prompts <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;1500-1600&#34;</span>: <span style="color:#a6da95">&#34;In the year 1550, the gentleman said&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;1600-1700&#34;</span>: <span style="color:#a6da95">&#34;In the year 1650, the gentleman said&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;1700-1800&#34;</span>: <span style="color:#a6da95">&#34;In the year 1750, the gentleman said&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;1800-1850&#34;</span>: <span style="color:#a6da95">&#34;In the year 1834, the gentleman said&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> period, prompt <span style="color:#91d7e3;font-weight:bold">in</span> period_prompts<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>            generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">30</span>)
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Check for period-appropriate language</span>
</span></span><span style="display:flex;"><span>            period_terms <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#34;1500-1600&#34;</span>: [<span style="color:#a6da95">&#34;ye&#34;</span>, <span style="color:#a6da95">&#34;hath&#34;</span>, <span style="color:#a6da95">&#34;doth&#34;</span>, <span style="color:#a6da95">&#34;thou&#34;</span>, <span style="color:#a6da95">&#34;thee&#34;</span>],
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#34;1600-1700&#34;</span>: [<span style="color:#a6da95">&#34;hath&#34;</span>, <span style="color:#a6da95">&#34;doth&#34;</span>, <span style="color:#a6da95">&#34;thou&#34;</span>, <span style="color:#a6da95">&#34;thee&#34;</span>, <span style="color:#a6da95">&#34;verily&#34;</span>],
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#34;1700-1800&#34;</span>: [<span style="color:#a6da95">&#34;hath&#34;</span>, <span style="color:#a6da95">&#34;doth&#34;</span>, <span style="color:#a6da95">&#34;thou&#34;</span>, <span style="color:#a6da95">&#34;thee&#34;</span>, <span style="color:#a6da95">&#34;indeed&#34;</span>],
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#34;1800-1850&#34;</span>: [<span style="color:#a6da95">&#34;indeed&#34;</span>, <span style="color:#a6da95">&#34;verily&#34;</span>, <span style="color:#a6da95">&#34;whilst&#34;</span>, <span style="color:#a6da95">&#34;pray&#34;</span>]
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            found_terms <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">sum</span>(<span style="color:#f5a97f">1</span> <span style="color:#c6a0f6">for</span> term <span style="color:#91d7e3;font-weight:bold">in</span> period_terms[period] <span style="color:#c6a0f6">if</span> term <span style="color:#91d7e3;font-weight:bold">in</span> generated<span style="color:#91d7e3;font-weight:bold">.</span>lower())
</span></span><span style="display:flex;"><span>            tests[<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;period_</span><span style="color:#a6da95">{</span>period<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> found_terms <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>            tests[<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;period_</span><span style="color:#a6da95">{</span>period<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test London-specific knowledge</span>
</span></span><span style="display:flex;"><span>    london_prompts <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Thames flowed through&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;Westminster Hall was&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Tower of London&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;Cheapside was filled with&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    london_tests <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> prompt <span style="color:#91d7e3;font-weight:bold">in</span> london_prompts:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>            generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">20</span>)
</span></span><span style="display:flex;"><span>            london_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#91d7e3">len</span>(generated) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#91d7e3">len</span>(prompt))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>            london_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests[<span style="color:#a6da95">&#34;london_knowledge&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">any</span>(london_tests)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_linguistic_quality</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test linguistic quality of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test prompts for linguistic quality</span>
</span></span><span style="display:flex;"><span>    quality_prompts <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The gentleman walked through the garden&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;In the morning, the sun rose&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The old man sat by the fire&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The young woman read her book&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    quality_tests <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> prompt <span style="color:#91d7e3;font-weight:bold">in</span> quality_prompts:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>            generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">30</span>)
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Check for basic linguistic quality</span>
</span></span><span style="display:flex;"><span>            sentences <span style="color:#91d7e3;font-weight:bold">=</span> generated<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#a6da95">&#39;.&#39;</span>)
</span></span><span style="display:flex;"><span>            quality_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#91d7e3">len</span>(sentences) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>            quality_tests<span style="color:#91d7e3;font-weight:bold">.</span>append(<span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests[<span style="color:#a6da95">&#34;linguistic_quality&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">any</span>(quality_tests)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test coherence</span>
</span></span><span style="display:flex;"><span>    coherence_prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;The gentleman walked through the garden and&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, coherence_prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">50</span>)
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;coherence&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">len</span>(generated) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#91d7e3">len</span>(coherence_prompt)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;coherence&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_performance_metrics</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test performance metrics of the model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    tests <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test inference speed</span>
</span></span><span style="display:flex;"><span>    test_prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;In the year 1834, London was&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        start_time <span style="color:#91d7e3;font-weight:bold">=</span> time<span style="color:#91d7e3;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_historical_text(model, tokenizer, test_prompt, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">50</span>)
</span></span><span style="display:flex;"><span>        end_time <span style="color:#91d7e3;font-weight:bold">=</span> time<span style="color:#91d7e3;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        inference_time <span style="color:#91d7e3;font-weight:bold">=</span> end_time <span style="color:#91d7e3;font-weight:bold">-</span> start_time
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;inference_speed&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> inference_time <span style="color:#91d7e3;font-weight:bold">&lt;</span> <span style="color:#f5a97f">5.0</span>  <span style="color:#6e738d;font-style:italic"># Should complete within 5 seconds</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;inference_speed&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Test memory usage</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">psutil</span>
</span></span><span style="display:flex;"><span>        process <span style="color:#91d7e3;font-weight:bold">=</span> psutil<span style="color:#91d7e3;font-weight:bold">.</span>Process()
</span></span><span style="display:flex;"><span>        memory_usage <span style="color:#91d7e3;font-weight:bold">=</span> process<span style="color:#91d7e3;font-weight:bold">.</span>memory_info()<span style="color:#91d7e3;font-weight:bold">.</span>rss <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">1024</span> <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">1024</span>  <span style="color:#6e738d;font-style:italic"># MB</span>
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;memory_usage&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> memory_usage <span style="color:#91d7e3;font-weight:bold">&lt;</span> <span style="color:#f5a97f">8000</span>  <span style="color:#6e738d;font-style:italic"># Should use less than 8GB</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        tests[<span style="color:#a6da95">&#34;memory_usage&#34;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">True</span>  <span style="color:#6e738d;font-style:italic"># Skip if psutil not available</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tests</span></span></code></pre></div><figcaption>
        <strong>Listing 12: Testing Published Models</strong>
    </figcaption>
</figure>
<p>Published model tests validate loading, generation, historical accuracy, and basic performance before and after publication.</p>
<h3 id="55-model-card-generation">5.5 Model Card Generation</h3>
<p>The model card serves as the primary documentation on Hugging Face Hub, making it the first thing users see when they discover your model. A well-crafted model card helps users understand what the model does, how to use it, and its limitations. The <code>generate_comprehensive_model_card()</code> function in <strong><code>10_scripts/publish_slm_to_huggingface.py</code></strong> creates this documentation automatically.</p>
<p><strong>What Makes an Effective Model Card:</strong></p>
<p>The model card for our historical language models includes several key sections that provide users with everything they need to get started. At a minimum, include:</p>
<ol>
<li>
<p><strong>Model Description &amp; Key Features</strong>: A clear explanation that the model was trained from scratch (not fine-tuned), emphasizing the 117M parameter SLM variant and 354M parameter Regular Model, with details about the custom 30,000-token vocabulary and 150+ historical special tokens.</p>
</li>
<li>
<p><strong>Setup Instructions</strong>: Platform-specific guidance for creating virtual environments (Linux/macOS/Windows), installing dependencies (<code>transformers</code>, <code>torch</code>, <code>accelerate</code>), and handling different accelerators (CPU, NVIDIA CUDA, AMD ROCm).</p>
</li>
<li>
<p><strong>Quick Start Code</strong>: Auto-device detection that works across CPU, CUDA, and ROCm with sensible generation parameters (<code>temperature=0.8</code>, <code>top_p=0.95</code>, <code>repetition_penalty=1.2</code>).</p>
</li>
<li>
<p><strong>Training Details</strong>: Architecture specifics (GPT-2 Small/Medium), training infrastructure (2x GPU with Distributed Data Parallel), performance metrics (training loss, MFU utilization), and data sources (218+ historical sources spanning 1500-1850).</p>
</li>
<li>
<p><strong>Example Prompts</strong>: Period-specific prompts demonstrating different historical eras (Tudor, Stuart, Georgian, Victorian) and London-specific contexts (Thames, Westminster, Parliament).</p>
</li>
<li>
<p><strong>Testing &amp; Validation</strong>: Instructions for running the automated test suite (<strong><code>test_published_models.py</code></strong>) and interactive testing with custom prompts.</p>
</li>
<li>
<p><strong>Troubleshooting</strong>: Common issues and solutions for PyTorch installation, GPU detection, and memory constraints.</p>
</li>
<li>
<p><strong>Citation &amp; License</strong>: BibTeX citation format and MIT license information.</p>
</li>
</ol>
<p><strong>Key Implementation Details:</strong></p>
<p>The model card generation follows Hugging Face conventions with YAML frontmatter specifying license, library, pipeline type, language, and tags. The script emphasizes that models were <strong>trained from scratch</strong> (not fine-tuned) and provides device-agnostic code examples that run on CPU, CUDA, and ROCm.</p>
<p>The card also includes detailed model selection guidance comparing the SLM (faster, lower memory) versus the Regular Model (higher quality, more parameters), helping users choose the right model for their use case - whether that&rsquo;s quick experimentation, educational purposes, or production deployment.</p>
<blockquote>
<p><strong>💻 Complete Implementation</strong>: See <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/10_scripts/publish_slm_to_huggingface.py"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong><code>10_scripts/publish_slm_to_huggingface.py</code></strong>
	</span>
</a> and <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/10_scripts/publish_to_huggingface.py"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong><code>10_scripts/publish_to_huggingface.py</code></strong>
	</span>
</a> for the full model card generation implementation.</p></blockquote>
<blockquote>
<p><strong>👀 Live Model Cards</strong>: View the published cards at <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-slm
	</span>
</a> and <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-llm
	</span>
</a>.</p></blockquote>
<blockquote>
<p><strong>📝 Documentation</strong>: See <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/HUGGINGFACE_PUBLISHING.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		HUGGINGFACE_PUBLISHING.md
	</span>
</a> and <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/DEPLOYMENT_GUIDE.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		DEPLOYMENT_GUIDE.md
	</span>
</a> for complete publishing and deployment workflows.</p></blockquote>
<h3 id="56-local-deployment-options">5.6 Local Deployment Options</h3>
<p>Finally, <a href="#listing13" class="listing-ref">Listing 13</a> sketches how you might wrap a trained model into a simple REST API or CLI. These patterns are intentionally minimal, meant to help you connect the dots between the inference utilities in <strong><code>06_inference/</code></strong> and real applications (dashboards, notebooks, small services).</p>
<figure id="listing13"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">setup_local_deployment</span>(model, tokenizer, deployment_type<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;api&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Set up local deployment for historical language model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> deployment_type <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;api&#39;</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> setup_api_deployment(model, tokenizer)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> deployment_type <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;cli&#39;</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> setup_cli_deployment(model, tokenizer)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> deployment_type <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;notebook&#39;</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> setup_notebook_deployment(model, tokenizer)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">raise</span> <span style="color:#f5a97f">ValueError</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Unknown deployment type: </span><span style="color:#a6da95">{</span>deployment_type<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">setup_api_deployment</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Set up REST API deployment&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">flask</span> <span style="color:#8bd5ca">import</span> Flask, request, jsonify
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    app <span style="color:#91d7e3;font-weight:bold">=</span> Flask(<span style="color:#f4dbd6">__name__</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#8aadf4;font-weight:bold">@app.route</span>(<span style="color:#a6da95">&#39;/generate&#39;</span>, methods<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#39;POST&#39;</span>])
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_text</span>():
</span></span><span style="display:flex;"><span>        data <span style="color:#91d7e3;font-weight:bold">=</span> request<span style="color:#91d7e3;font-weight:bold">.</span>get_json()
</span></span><span style="display:flex;"><span>        prompt <span style="color:#91d7e3;font-weight:bold">=</span> data<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;prompt&#39;</span>, <span style="color:#a6da95">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>        max_length <span style="color:#91d7e3;font-weight:bold">=</span> data<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;max_length&#39;</span>, <span style="color:#f5a97f">100</span>)
</span></span><span style="display:flex;"><span>        temperature <span style="color:#91d7e3;font-weight:bold">=</span> data<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;temperature&#39;</span>, <span style="color:#f5a97f">0.3</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Generate text</span>
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>            outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(
</span></span><span style="display:flex;"><span>                inputs,
</span></span><span style="display:flex;"><span>                max_length<span style="color:#91d7e3;font-weight:bold">=</span>max_length,
</span></span><span style="display:flex;"><span>                temperature<span style="color:#91d7e3;font-weight:bold">=</span>temperature,
</span></span><span style="display:flex;"><span>                do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span>                pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id
</span></span><span style="display:flex;"><span>            )
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        generated_text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> jsonify({
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;generated_text&#39;</span>: generated_text,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;prompt&#39;</span>: prompt,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;parameters&#39;</span>: {
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#39;max_length&#39;</span>: max_length,
</span></span><span style="display:flex;"><span>                <span style="color:#a6da95">&#39;temperature&#39;</span>: temperature
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#8aadf4;font-weight:bold">@app.route</span>(<span style="color:#a6da95">&#39;/health&#39;</span>, methods<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#39;GET&#39;</span>])
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">health_check</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> jsonify({<span style="color:#a6da95">&#39;status&#39;</span>: <span style="color:#a6da95">&#39;healthy&#39;</span>, <span style="color:#a6da95">&#39;model_loaded&#39;</span>: <span style="color:#f5a97f">True</span>})
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> app
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">setup_cli_deployment</span>(model, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Set up command-line interface deployment&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">argparse</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">main</span>():
</span></span><span style="display:flex;"><span>        parser <span style="color:#91d7e3;font-weight:bold">=</span> argparse<span style="color:#91d7e3;font-weight:bold">.</span>ArgumentParser(description<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Historical Language Model CLI&#39;</span>)
</span></span><span style="display:flex;"><span>        parser<span style="color:#91d7e3;font-weight:bold">.</span>add_argument(<span style="color:#a6da95">&#39;--prompt&#39;</span>, <span style="color:#91d7e3">type</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">str</span>, required<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>, help<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Text prompt&#39;</span>)
</span></span><span style="display:flex;"><span>        parser<span style="color:#91d7e3;font-weight:bold">.</span>add_argument(<span style="color:#a6da95">&#39;--max_length&#39;</span>, <span style="color:#91d7e3">type</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">int</span>, default<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">100</span>, help<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Maximum length&#39;</span>)
</span></span><span style="display:flex;"><span>        parser<span style="color:#91d7e3;font-weight:bold">.</span>add_argument(<span style="color:#a6da95">&#39;--temperature&#39;</span>, <span style="color:#91d7e3">type</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">float</span>, default<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.3</span>, help<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Temperature&#39;</span>)
</span></span><span style="display:flex;"><span>        parser<span style="color:#91d7e3;font-weight:bold">.</span>add_argument(<span style="color:#a6da95">&#39;--interactive&#39;</span>, action<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;store_true&#39;</span>, help<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Interactive mode&#39;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        args <span style="color:#91d7e3;font-weight:bold">=</span> parser<span style="color:#91d7e3;font-weight:bold">.</span>parse_args()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> args<span style="color:#91d7e3;font-weight:bold">.</span>interactive:
</span></span><span style="display:flex;"><span>            run_interactive_mode(model, tokenizer)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            generate_and_print(model, tokenizer, args<span style="color:#91d7e3;font-weight:bold">.</span>prompt, args<span style="color:#91d7e3;font-weight:bold">.</span>max_length, args<span style="color:#91d7e3;font-weight:bold">.</span>temperature)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> main</span></span></code></pre></div><figcaption>
        <strong>Listing 13: Local Deployment Setup</strong>
    </figcaption>
</figure>
<p>Local deployment options: REST API, CLI, or notebook integration for different workflows.</p>
<h2 id="6-quality-assurance-and-validation">6. Quality Assurance and Validation</h2>
<p>Before wrapping up, let&rsquo;s look at the quality assurance systems that ensure the models behave reliably across different scenarios.</p>
<h3 id="61-automated-quality-checks">6.1 Automated Quality Checks</h3>
<p>The system includes automated quality checks that validate model performance and reliability: <a href="#listing14" class="listing-ref">Listing 14</a></p>
<figure id="listing14"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">run_quality_checks</span>(model, tokenizer, device<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Run quality checks on historical language model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    quality_checks <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;model_integrity&#39;</span>: check_model_integrity(model),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;tokenizer_consistency&#39;</span>: check_tokenizer_consistency(tokenizer),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;generation_quality&#39;</span>: check_generation_quality(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;historical_accuracy&#39;</span>: check_historical_accuracy(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;performance_metrics&#39;</span>: check_performance_metrics(model, tokenizer, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;memory_usage&#39;</span>: check_memory_usage(model, device),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;error_handling&#39;</span>: check_error_handling(model, tokenizer, device)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Generate quality report</span>
</span></span><span style="display:flex;"><span>    quality_report <span style="color:#91d7e3;font-weight:bold">=</span> generate_quality_report(quality_checks)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> quality_checks, quality_report
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">check_model_integrity</span>(model):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Check model integrity and consistency&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    checks <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;parameter_count&#39;</span>: check_parameter_count(model),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;weight_distribution&#39;</span>: check_weight_distribution(model),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;gradient_flow&#39;</span>: check_gradient_flow(model),
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;activation_patterns&#39;</span>: check_activation_patterns(model)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> checks
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">check_generation_quality</span>(model, tokenizer, device):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Check quality of generated text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    test_prompts <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;In the year of our Lord 1750, London was&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Thames flowed through the heart of&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;Merchants and tradesmen plied their wares&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Great Fire of 1666 had changed&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;Parliament sat in Westminster, making laws&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    quality_scores <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> prompt <span style="color:#91d7e3;font-weight:bold">in</span> test_prompts:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Generate text</span>
</span></span><span style="display:flex;"><span>        generated <span style="color:#91d7e3;font-weight:bold">=</span> generate_text(model, tokenizer, prompt, device)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Check quality metrics</span>
</span></span><span style="display:flex;"><span>        quality_score <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;coherence&#39;</span>: assess_coherence(generated),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;grammatical_correctness&#39;</span>: assess_grammatical_correctness(generated),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;historical_accuracy&#39;</span>: assess_historical_accuracy(generated, {}),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;linguistic_quality&#39;</span>: assess_linguistic_quality(generated),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;relevance&#39;</span>: assess_relevance(generated, prompt)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        quality_scores<span style="color:#91d7e3;font-weight:bold">.</span>append(quality_score)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> quality_scores</span></span></code></pre></div><figcaption>
        <strong>Listing 14: Quality Assurance Checks</strong>
    </figcaption>
</figure>
<p>Quality checks cover model integrity, generation quality, historical accuracy, performance, and error handling, ensuring the models behave reliably across different scenarios.</p>
<h3 id="62-continuous-integration-and-testing">6.2 Continuous Integration and Testing</h3>
<p>If you want to wire this into a lightweight CI gate, keep it simple and CPU-friendly. The goal is not to re-run full benchmarks in CI - it&rsquo;s to catch obvious regressions (can the model load, can it generate, do the evaluators still run).</p>
<p><strong>Minimal CI smoke checks (suggested):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 1) Run a fast, local evaluation pass (no external APIs)</span>
</span></span><span style="display:flex;"><span>python 05_evaluation/run_evaluation.py --mode quick --device cpu
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 2) Run a local inference smoke test from a checkpoint (replace with your path)</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_pytorch.py --checkpoint &lt;path-to-checkpoint.pt&gt; --prompt <span style="color:#a6da95">&#34;In the year 1834, London was&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 3) Optional: test the published model (requires downloading from Hugging Face)</span>
</span></span><span style="display:flex;"><span>python 06_inference/test_published_models.py --model_name bahree/london-historical-slm</span></span></code></pre></div>
<h2 id="7-summary">7. Summary</h2>
<p>We&rsquo;ve now completed the full cycle of building language models from scratch. This final part has shown how to transform trained models into working systems that can be evaluated, tested, and deployed for real-world use. The journey that began in <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1
	</span>
</a> with using published models, continued through <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a>&rsquo;s data collection and tokenization, and <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a>&rsquo;s training architecture, now concludes with evaluation and deployment - the critical final steps that make models usable.</p>
<p><strong>What we&rsquo;ve built:</strong></p>
<p>The evaluation, testing, and deployment pipeline provides a practical approach for bringing historical language models from research to deployment. We&rsquo;ve created specialized assessment metrics that go beyond standard LLM evaluation to catch historical inaccuracies, temporal inconsistencies, and period-inappropriate language. The testing infrastructure ensures reliability across different scenarios, while multiple deployment options make the models accessible to researchers, educators, and developers worldwide.</p>
<p><strong>Current Deployment Status:</strong></p>
<ul>
<li><strong>PyTorch Checkpoint Inference</strong>: Fully working with both SLM and Regular models</li>
<li><strong>Hugging Face Model Inference</strong>: SLM published and available, Regular model ready</li>
<li><strong>Local Testing</strong>: Both inference methods tested and validated on a remote Ubuntu machine</li>
<li><strong>Documentation</strong>: Complete guides and examples for all inference methods</li>
<li><strong>Performance</strong>: Clean logging, proper model detection, accurate parameter counts</li>
</ul>
<p><strong>The Complete Pipeline:</strong></p>
<p>This four-part series has demonstrated the complete LLM development lifecycle:</p>
<ol>
<li>
<p><strong>Data Collection</strong> (<a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a>): We gathered 218+ historical sources spanning 1500-1850, processed them through a sophisticated cleaning pipeline, and created a 500M+ character corpus of authentic historical English.</p>
</li>
<li>
<p><strong>Custom Tokenization</strong> (<a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a>): We built a specialized BPE tokenizer with 30,000 vocabulary tokens and 150+ special tokens that understand historical language patterns, London geography, and period-specific terminology.</p>
</li>
<li>
<p><strong>Model Training</strong> (<a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a>): We implemented custom GPT architectures, optimized for multi-GPU training, and successfully trained two models - an SLM (117M parameters) and a Regular model (354M parameters) - both capable of generating authentic historical text.</p>
</li>
<li>
<p><strong>Evaluation &amp; Deployment</strong> (This Part): We built comprehensive evaluation frameworks that assess historical accuracy, linguistic quality, and temporal consistency. We created a testing infrastructure for reliability and deployed models to the Hugging Face Hub for community access.</p>
</li>
</ol>
<p><strong>The Learning Journey:</strong></p>
<p>What started as a learning project has become a complete, working system that demonstrates every aspect of LLM development - from raw data collection through model deployment. The principles and techniques we&rsquo;ve covered scale from the 500M-character corpus to production-scale systems, and the evaluation frameworks we&rsquo;ve built can be adapted to any domain-specific language modeling task.</p>
<p>Whether you&rsquo;re a researcher exploring historical linguistics, an educator teaching AI concepts, or a developer building specialized language models, this series provides the complete toolkit for understanding and implementing LLM development from scratch. The models are published, the code is available, and the journey from data to deployment is complete.</p>
<blockquote>
<p><strong>🔗 GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete training infrastructure (<strong><code>04_training/</code></strong>), model architecture (<strong><code>config.py</code></strong>), and evaluation/deployment (<strong><code>05_evaluation/</code></strong>, <strong><code>06_inference/</code></strong>, <strong><code>10_scripts/</code></strong>)</p>
<p><strong>🟥 Series Posts</strong>: <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1 - Using the Published Historical Models
	</span>
</a> | <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2 - Data Collection &amp; Custom Tokenizer
	</span>
</a> | <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3 - Training Architecture &amp; GPU Optimization
	</span>
</a> | Part 4 (this post)</p>
<p><strong>🟧 Published Models</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SLM Model
	</span>
</a> | <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Regular Model
	</span>
</a> - Ready-to-use historical language models on Hugging Face</p>
<p><strong>📗 Book Reference</strong>: <a
	
		href = "https://a.co/d/gr87rem"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts</p></blockquote>
<h2 id="8-resources">8. Resources</h2>
<p>If you want to reproduce the full pipeline (or adapt it to your own domain), these are the most useful starting points:</p>
<ul>
<li><strong>Public GitHub repo</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a></li>
<li><strong>Evaluation guide</strong>: <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/EVALUATION_GUIDE.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://github.com/bahree/helloLondon/blob/main/08_documentation/EVALUATION_GUIDE.md
	</span>
</a></li>
<li><strong>Hugging Face publishing guide</strong>: <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/HUGGINGFACE_PUBLISHING.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://github.com/bahree/helloLondon/blob/main/08_documentation/HUGGINGFACE_PUBLISHING.md
	</span>
</a></li>
<li><strong>Deployment guide</strong>: <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/08_documentation/DEPLOYMENT_GUIDE.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://github.com/bahree/helloLondon/blob/main/08_documentation/DEPLOYMENT_GUIDE.md
	</span>
</a></li>
<li><strong>Published models</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-slm
	</span>
</a> | <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-llm
	</span>
</a></li>
</ul>
<h2 id="references">References</h2>
<ol>
<li>Vaswani et al. (2017) - Attention Is All You Need: <a
	
		href = "https://arxiv.org/abs/1706.03762"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1706.03762
	</span>
</a></li>
<li>Radford et al. (2019) - Language Models are Unsupervised Multitask Learners: <a
	
		href = "https://www.semanticscholar.org/paper/Language-Models-are-Unsupervised-Multitask-Learners-Radford-Wu/9405cc0d6169988371b2755e573cc28650d14dfe"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://www.semanticscholar.org/paper/Language-Models-are-Unsupervised-Multitask-Learners-Radford-Wu/9405cc0d6169988371b2755e573cc28650d14dfe
	</span>
</a></li>
<li>Lin (2004) - ROUGE: A Package for Automatic Evaluation of Summaries: <a
	
		href = "https://aclanthology.org/W04-1013/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/W04-1013/
	</span>
</a></li>
<li>Papineni et al. (2002) - BLEU: A Method for Automatic Evaluation of Machine Translation: <a
	
		href = "https://aclanthology.org/P02-1040/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/P02-1040/
	</span>
</a></li>
<li>Hendrycks et al. (2021) - Measuring Massive Multitask Language Understanding (MMLU): <a
	
		href = "https://arxiv.org/abs/2009.03300"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2009.03300
	</span>
</a></li>
<li>Zellers et al. (2019) - HellaSwag: Can a Machine Really Finish Your Sentence?: <a
	
		href = "https://arxiv.org/abs/1905.07830"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1905.07830
	</span>
</a></li>
<li>Liu et al. (2023) - G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment: <a
	
		href = "https://arxiv.org/abs/2303.16634"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2303.16634
	</span>
</a></li>
</ol>
<h2 id="acknowledgments">Acknowledgments</h2>
<p>This project builds upon the excellent work of the open-source community. Special thanks to <a
	
		href = "https://github.com/haykgrigo3/TimeCapsuleLLM"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		haykgrigo3&rsquo;s TimeCapsuleLLM
	</span>
</a> for the initial inspiration and framework for historical language model training, and to <a
	
		href = "https://github.com/karpathy/nanoGPT"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Andrej Karpathy&rsquo;s nanoGPT
	</span>
</a> for the foundational GPT architecture and training methodology. The project extends these foundations with specialized adaptations for historical text, including custom tokenizers, advanced data filtering, evaluation frameworks, and educational deployment infrastructure.</p>
]]></content:encoded>
    </item>
    <item>
      <title>🏛️Building LLMs from Scratch - Part 3: Training Architecture &amp; GPU Optimization</title>
      <link>/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/</link>
      <pubDate>Sat, 01 Nov 2025 00:00:00 +0000</pubDate>
      <guid>/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/</guid>
      <description>Complete guide to training custom GPT models with multi-GPU setup, checkpointing, and performance monitoring. Learn by building with working code and real training results.</description>
      <content:encoded><![CDATA[<p><strong>TL;DR</strong></p>
<p>In this third part of our 4-part series on building language models from scratch, I explore the complete training infrastructure that transforms our clean historical data and custom tokenizer into working language models.</p>
<ul>
<li><a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong>Part 1</strong>
	</span>
</a> How to build a Large Language Model from Scratch - covered using the published model</li>
<li><a
	
		href = "https://blog.desigeek.com/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong>Part 2</strong>
	</span>
</a> Building LLMs from Scratch - Part 2: Data Collection &amp; Custom Tokenizers - detailed data collection and custom tokenizer development.</li>
</ul>
<p>Here, we build the complete training pipeline from a custom GPT architecture through deployment-ready checkpoints.</p>
<p>This post demonstrates how to design custom model architectures, optimize GPU utilization, and implement comprehensive training pipelines that transform our 500M+ character historical corpus into two working language models.</p>
<blockquote>
<p><strong>⚠️ Educational Purpose</strong>: This is a learning project designed to teach LLM development concepts. For production-scale LLMs, you&rsquo;ll need significantly larger datasets, more sophisticated infrastructure, and additional considerations that are not covered in this post.</p></blockquote>
<p>As outlined in <a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 1
	</span>
</a>, both the SLM (117M parameters) and the regular Model (354M parameters) use the same training code and pipeline (<code>04_training/train_model_slm.py</code> and <code>04_training/train_model.py</code>) with different configurations defined in <code>config.py</code>. The training infrastructure, GPU optimization, checkpointing, and WandB integration are identical - only the model architecture parameters differ.</p>
<p>Both PyTorch checkpoint inference and Hugging Face model inference are fully working and available. Both the SLM and the Regular model are published on <a
	
		href = "https://huggingface.co/bahree"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Hugging Face Hub
	</span>
</a>. Local PyTorch checkpoints can be used directly for inference with the <code>inference_pytorch.py</code> script.</p>
<blockquote>
<p><strong>🔗 GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete training infrastructure (<code>04_training/</code>), model architecture (<code>config.py</code>), and GPU configuration (<code>08_documentation/GPU_TUNING.md</code>)</p>
<p><strong>🧱 Series Posts</strong>: <a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 1 – Using the Published Historical Models
	</span>
</a> | <a
	
		href = "https://blog.desigeek.com/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 2 – Data Collection &amp; Custom Tokenizer
	</span>
</a> | Part 3 (this post) | <a
	
		href = "https://blog.desigeek.com/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 4 – Evaluation &amp; Deployment
	</span>
</a></p></blockquote>
<blockquote>
<p><strong>🤗 Published Models</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SLM Model
	</span>
</a> | <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Regular Model
	</span>
</a> - Ready-to-use historical language models on Hugging Face</p></blockquote>
<blockquote>
<p><strong>📚 Book Reference</strong>: <a
	
		href = "https://a.co/d/ffzkJ7T"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts.</p></blockquote>
<h2 id="1-the-training-challenge-from-data-to-working-models">1. The Training Challenge: From Data to Working Models</h2>
<p>Now that we have our clean historical corpus and custom tokenizer from <a
	
		href = "https://blog.desigeek.com/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 2
	</span>
</a>, we need to transform this data into working language models. This isn&rsquo;t just about running training scripts – it&rsquo;s about designing an architecture that can learn from historical text, optimizing for the unique patterns of 1500-1850 English, and building infrastructure to handle the computational demands of language model training.</p>
<p>The challenge with historical language modeling isn&rsquo;t just having enough data - it&rsquo;s having the right architecture and training process that can learn from the complex linguistic patterns in historical texts. Unlike modern text, historical English contains archaic vocabulary, period-specific terminology, and cultural references that require specialized attention mechanisms and training strategies.</p>
<h3 id="11-high-level-training-process-overview">1.1 High-Level Training Process Overview</h3>
<p>The model training pipeline transforms our clean historical data and custom tokenizer into working language models through several key stages:</p>
<ol>
<li><em>Model Architecture Design</em> - involves a custom GPT implementation optimized for historical text patterns</li>
<li><em>GPU Configuration</em> covers - multi-GPU training with precision optimization and memory management</li>
<li><em>Training Infrastructure</em> - includes distributed training, checkpointing, and experiment tracking</li>
<li><em>Performance Optimization</em> - encompasses mixed precision, compilation, and hardware-specific tuning</li>
<li><em>Model Validation</em> - covers testing and evaluation of trained models.</li>
</ol>
<p><a href="#fig1" class="figure-ref">Figure 1</a> below illustrates this complete training pipeline:</p>
<figure class="align-center " id="fig1">
    <pre class="mermaid">graph TD
    A[📚 Clean Historical Corpus&lt;br/&gt;500M+ characters] --&gt; B[🔤 Custom Tokenizer&lt;br/&gt;30K vocab + 150+ special tokens]
    B --&gt; C[🏗️ Model Architecture&lt;br/&gt;Custom GPT for Historical Text]
    
    C --&gt; D[⚙️ GPU Configuration&lt;br/&gt;Multi-GPU + Precision Optimization]
    D --&gt; D1[Mixed Precision&lt;br/&gt;bf16/fp16]
    D --&gt; D2[Torch Compile&lt;br/&gt;JIT optimization]
    D --&gt; D3[Memory Management&lt;br/&gt;Gradient checkpointing]
    
    D1 --&gt; E[🏋️ Training Process&lt;br/&gt;60K iterations, checkpointing]
    D2 --&gt; E
    D3 --&gt; E
    
    E --&gt; E1[SLM: 117M params&lt;br/&gt;7-8 hours training]
    E --&gt; E2[Regular: 354M params&lt;br/&gt;28-32 hours training]
    
    E --&gt; E3[WandB Integration&lt;br/&gt;Experiment tracking]
    E --&gt; E4[Checkpointing&lt;br/&gt;Resume capability]
    E --&gt; E5[Multi-GPU Support&lt;br/&gt;Distributed training]
    
    E1 --&gt; F[📊 Model Evaluation&lt;br/&gt;Historical accuracy testing]
    E2 --&gt; F
    E3 --&gt; F
    E4 --&gt; F
    E5 --&gt; F
    
    F --&gt; G{Quality OK?}
    G --&gt;|Yes| H[🚀 Deployment&lt;br/&gt;Hugging Face + Local Inference]
    G --&gt;|No| I[🔄 Retrain/Adjust]
    I --&gt; E
    H --&gt; J[💬 Text Generation&lt;br/&gt;Historical language output]
    
    style A fill:#e1f5fe
    style C fill:#f3e5f5
    style H fill:#e8f5e8
    style J fill:#fff3e0</pre>
    <figcaption>Figure 1: Complete Training Pipeline</figcaption>
</figure>
<p>We will explore each of these components in detail, starting with the model architecture design, but first, let&rsquo;s discuss why I chose PyTorch as the framework for this project.</p>
<h3 id="12-using-pytorch">1.2 Using PyTorch</h3>
<p>I chose PyTorch for this project based on three key factors: educational accessibility, integration with the research ecosystem, and practical convenience. PyTorch provides many components out of the box - transformer blocks, attention layers, feed-forward networks, training loops, and CUDA support - which makes it much easier for learners building their first language model.</p>
<p>From a technical perspective, PyTorch&rsquo;s memory management and GPU optimization features-including automatic mixed precision, gradient checkpointing, and efficient attention implementations well-suited for the resource-intensive task of training language models on historical text.</p>
<p>PyTorch&rsquo;s recent developments, such as <strong><code>torch.compile</code></strong>, <strong><code>FlashAttention</code></strong> kernels, and <strong><code>SDPA</code></strong> operator (scaled dot-product attention), provide significant performance improvements, making training more efficient. These improvements enhance both speed and memory efficiency, which are critical for scaling LLMs. Of course, in our case, we&rsquo;re building a working toy example rather than scaling to production levels, and these optimizations help keep training times reasonable on available hardware.</p>
<p><strong>What about other Frameworks?</strong></p>
<p>I also considered TensorFlow and JAX, but neither seemed right for <strong><code>helloLondon</code></strong>; TensorFlow&rsquo;s API felt too complex, specifically from a beginner’s perspective. JAX has excellent performance and a clean, functional approach, but it&rsquo;s more research-focused and has a smaller ecosystem, which would make it harder to follow along and experiment with.</p>
<h2 id="2-model-architecture-overview">2. Model Architecture Overview</h2>
<h3 id="21-understanding-the-gpt-architecture">2.1 Understanding the GPT Architecture</h3>
<p>Our custom GPT (Generative Pre-trained Transformer) is a decoder-only transformer model designed for autoregressive language modeling on historical text. The architecture consists of four core components, each serving a distinct purpose in the sequence-to-sequence prediction pipeline. These are: token embeddings, position embeddings, causal self-attention mechanisms, and the language modeling head. Let us double-click into each component to understand its role and implementation.</p>
<h4 id="211-token-embeddings">2.1.1 Token Embeddings</h4>
<p>Token embeddings convert discrete token IDs from our 30,000-token historical vocabulary into dense, continuous vector representations. Each token (whether it&rsquo;s a word, subword unit, or special token) is mapped to a point in a high-dimensional space (768 dimensions for SLM, 1024 for the regular model).</p>
<p>This is implemented as a simple lookup table - <strong><code>wte = torch.nn.Embedding(config.vocab_size, config.n_embd)</code></strong>. When processing the token sequence, we look up the corresponding vector for each token ID. These embeddings are learned during training - the model learns which tokens should be close together in this vector space based on their co-occurrence patterns in historical text.</p>
<p>For historical language models, this is particularly valuable because rare historical terms (like &ldquo;yeoman&rdquo; or &ldquo;guildhall&rdquo;) get their own representations that can capture contextual relationships with related terms from that era.</p>
<h4 id="212-position-embeddings">2.1.2 Position Embeddings</h4>
<p>Position embeddings encode each token&rsquo;s absolute position within the sequence. This is crucial because, unlike recurrent models, transformer architectures have no inherent notion of temporal order or sequence position - they process all tokens in parallel. Let us double-click into the problem why.</p>
<p>Think of it like reading words without any sense of order. The words &ldquo;<em>The cat chased the mouse</em>&rdquo; would be indistinguishable from &ldquo;<em>Mouse the chased cat the</em>&rdquo; - you&rsquo;d see the same words but lose all meaning because you don&rsquo;t know which word came first, second, or third. Transformers face exactly this problem because they process all words simultaneously rather than sequentially, unlike older RNN models.</p>
<p>To help the model understand word order, we add position embeddings to the token embeddings. This way, each token&rsquo;s representation includes information about both &ldquo;what&rdquo; the token is and &ldquo;where&rdquo; it appears in the sequence. We use learned position embeddings (as opposed to fixed sinusoidal patterns): <strong><code>wpe = torch.nn.Embedding(config.block_size, config.n_embd)</code></strong>. For the SLM with a 512-token context window, we learn 512 different position vectors (one for each possible position). Similarly, the regular model with a 1024-token context learns 1024 position vectors.</p>
<p>Position embeddings work like giving each word a &ldquo;timestamp&rdquo; or &ldquo;address&rdquo; that tells the model where it sits in the sequence:</p>
<ol>
<li><strong>Token embedding</strong> says: &ldquo;This is the word &lsquo;cat&rsquo;&rdquo; → converts to a vector like <code>[0.2, -0.5, 0.8, ...]</code></li>
<li><strong>Position embedding</strong> says: &ldquo;This word is at position 3&rdquo; → adds another vector like <code>[0.1, 0.3, -0.2, ...]</code></li>
<li><strong>Combined</strong>: The model sees both &ldquo;what&rdquo; the word is AND &ldquo;where&rdquo; it appears</li>
</ol>
<p>The embedding vectors are combined element-wise: <strong><code>x = token_emb + position_emb</code></strong>. This allows the model to understand both what each token is (via the token embedding) and where it appears in the sequence (via the position embedding).</p>
<p>Our model uses <strong>learned</strong> position embeddings, meaning during training the model discovers that:</p>
<ul>
<li>Position 1 tends to be capitalized (start of sentence)</li>
<li>Position 512 might be mid-sentence (needs different handling)</li>
<li>Certain positions in historical documents have patterns (formal openings, closings, etc.)</li>
</ul>
<p>This is different from <strong>fixed</strong> sinusoidal embeddings (used in the original Transformer paper), which use a mathematical formula to encode positions. Learned embeddings are generally better because they adapt to specific patterns in the training data.</p>
<p>In historical texts, word order is crucial for understanding meaning. Consider &ldquo;The King granted the land&rdquo; versus &ldquo;The land granted the King&rdquo; - same words, completely different meanings. Historical legal documents and Victorian-era writings often have precise word order that changes legal or semantic meaning. Position embeddings ensure the model can distinguish between these critical variations.</p>
<h4 id="213-causal-self-attention">2.1.3 Causal Self-Attention</h4>
<p>Causal self-attention is the mechanism that allows each position in the sequence to selectively attend to previous positions. The <em>&ldquo;causal&rdquo;</em> constraint ensures the model can only look at past tokens (not future ones), which is essential for autoregressive generation.</p>
<p>When you read a sentence, you naturally use context from earlier words to understand later ones. If you see &ldquo;The King granted the land to his loyal&hellip;&rdquo;, you can predict that &ldquo;servant,&rdquo; &ldquo;knight,&rdquo; or &ldquo;subject&rdquo; might come next because you remember what came before. The model needs to do the same thing - use previous words to predict the next word.</p>
<p>However, there&rsquo;s a crucial constraint: during training, when predicting word 7, the model must <em>only</em> see words 1-6, never word 8 or beyond. This &ldquo;causal&rdquo; (cause-and-effect) constraint ensures the model learns realistic patterns - in the real world, you can&rsquo;t use future information to predict the present.</p>
<h5 id="how-attention-works">How Attention Works</h5>
<p>Think of attention as a sophisticated &ldquo;relevance detector&rdquo;. When the model is processing the word &ldquo;loyal&rdquo; in our example above, it needs to look back and ask: &ldquo;<em>Which previous words are most relevant for understanding this context?</em>&rdquo; The attention mechanism computes a weighted sum of all previous token representations, where the weights are determined by how relevant each previous token is to the current one.</p>
<p>This is done through three learned linear projections that create different &ldquo;views&rdquo; of each word:</p>
<ul>
<li><strong>Query (Q)</strong>: &ldquo;What am I looking for?&rdquo; - The current word asks a question</li>
<li><strong>Key (K)</strong>: &ldquo;What do I have to offer?&rdquo; - Previous words advertise their content</li>
<li><strong>Value (V)</strong>: &ldquo;What information should I contribute?&rdquo; - The actual information to pass forward</li>
</ul>
<p>Let us see a practical example to help us grok the concept. Consider the historical phrase: &ldquo;<em>The alderman of Cheapside, having served the city faithfully, was <strong>granted</strong>&hellip;</em>&rdquo;</p>
<p>When processing &ldquo;granted&rdquo; the attention mechanism:</p>
<ol>
<li>Creates a Query from &ldquo;granted&rdquo; asking &ldquo;what context do I need?&rdquo;</li>
<li>Compares this Query against Keys from all previous words</li>
<li>Finds high relevance with &ldquo;alderman&rdquo; (who is being granted something) and &ldquo;faithfully&rdquo; (why the grant is happening)</li>
<li>Uses these attention weights to pull relevant Values from those words</li>
<li>Combines this information to understand better &ldquo;granted&rdquo; in context</li>
</ol>
<p>The attention score between token i and token j is computed as:</p>
<p>$$\text{Attention}(Q_i, K_j) = \text{softmax}\left(\frac{Q_i K_j^T}{\sqrt{d_k}}\right)V_j$$</p>
<p>Breaking this down:</p>
<ul>
<li>$Q_i K_j^T$ computes how well the query from token <em><strong>i</strong></em> matches the key from token <em><strong>j</strong></em> (higher = more relevant)</li>
<li>$1/\sqrt{d_k}$ is a scaling factor that prevents scores from getting too large (which would make softmax too &ldquo;sharp&rdquo;)</li>
<li>$\text{softmax}$ converts scores into probabilities that sum to <strong>1</strong> (so each word gets a weighted &ldquo;vote&rdquo;)</li>
<li>Finally, we use these weights to combine the Values from all previous tokens</li>
</ul>
<p>The $1/\sqrt{d_k}$ scaling factor (where $d_k = 64$ for our SLM, so $\sqrt{64} = 8$) prevents the dot products from growing too large with high-dimensional embeddings, ensuring stable gradients during training. The softmax ensures the weights sum to <strong>1</strong>, creating a proper probability distribution over the previous tokens.</p>
<h5 id="why-this-matters-for-historical-text">Why This Matters for Historical Text?</h5>
<p>Historical documents present unique challenges that make attention particularly valuable. Consider a legal document from 1750: <em>&ldquo;John Smith, yeoman of the parish of St. Giles, being of sound mind and body, doth hereby bequeath&hellip;&rdquo;</em></p>
<p>The attention mechanism enables the model to:</p>
<ul>
<li>Connect &ldquo;doth bequeath&rdquo; back to &ldquo;John Smith&rdquo; across multiple clauses</li>
<li>Understand that &ldquo;yeoman&rdquo; modifies &ldquo;John Smith&rdquo; even though they&rsquo;re separated</li>
<li>Learn that &ldquo;doth&rdquo; (archaic) and &ldquo;does&rdquo; (modern) serve similar grammatical functions</li>
<li>Recognize that formal legal phrasing follows specific patterns</li>
</ul>
<p>For our historical language models, this attention mechanism learns which historical terms and phrases co-occur and relate to one another contextually - crucial for understanding historical documents where terminology and phrasing differ from modern English. The model learns to attend to relevant historical context, enabling it to generate coherent text that maintains period-appropriate language patterns and references.</p>
<h4 id="214-language-modeling-head">2.1.4 Language Modeling Head</h4>
<p>The language modeling head (also called the &ldquo;output projection&rdquo; or <strong>lm_head</strong>) is the final translator that turns the rich internal representation (after all the attention + MLP refinements) back into a decision: &ldquo;Given everything I&rsquo;ve seen so far, what is the most likely next token?&rdquo; It does this by mapping each hidden vector at every position into a vector of length equal to the vocabulary size (30,000 in our historical tokenizer). Each element of that output vector is a <em>logit</em> - an unnormalized score indicating how likely the model thinks the token is to be the next one.</p>
<p>Implementation is intentionally simple: <strong><code>lm_head = torch.nn.Linear(n_embd, vocab_size)</code></strong>. We don&rsquo;t put an activation function after it because we want raw, unconstrained scores. Those scores then flow into:</p>
<ul>
<li><strong>Inference:</strong> Apply softmax -&gt; probabilities -&gt; sample or greedy pick</li>
<li><strong>Training:</strong> Feed logits + target token IDs into cross-entropy loss -&gt; gradients flow backward</li>
</ul>
<p>You can think of logits as <em>evidence totals</em>. The softmax transforms those evidence values into a normalized probability distribution that the model can sample from. High logit = more supporting evidence; low logit = less.</p>
<p><strong>Step-by-step (Inference vs Training):</strong></p>
<ol>
<li>Hidden state at last position (e.g., index 511) enters <code>lm_head</code>.</li>
<li>Linear projection produces a 30,000-dimensional logit vector.</li>
<li>In inference: <code>probs = softmax(logits / temperature)</code>; optionally apply <code>top-k</code>/<code>top-p</code> filtering.</li>
<li>Sample (or argmax) a token → append to sequence → repeat.</li>
<li>In training: Cross-entropy compares logits to the true next token; loss scalar backpropagates through the head into all prior layers.</li>
</ol>
<p>Because our vocabulary mixes common function words (&ldquo;the&rdquo;, &ldquo;and&rdquo;, &ldquo;of&rdquo;) with rare era-specific tokens (&ldquo;yeoman&rdquo;, &ldquo;guildhall&rdquo;, &ldquo;paternoster&rdquo;, &ldquo;quoth&rdquo;), the head must reliably distinguish both frequent and infrequent patterns. Rare historical tokens need <em>consistent</em> representations from embedding -&gt; transformer -&gt; head so they are not forgotten. If their logits remained perpetually low, the model would never learn to generate them in authentic contexts.</p>
<p>Logits (not probabilities) inside the model - We retain logits (raw, unnormalized scores) instead of immediately converting to probabilities because they yield numerically stable loss computation - PyTorch efficiently fuses <code>log_softmax</code> with negative log-likelihood - allow cleaner gradient flow before any normalization (we only invoke softmax when we actually need a distribution), and enable flexible post-processing (temperature scaling, top-k or top-p filtering, repetition penalties) directly in score space without forcing an extra probability recomputation step.</p>
<p>We reuse the input embedding matrix for the output projection to keep input and output semantics aligned and reduce parameter and memory traffic. This concept is called Weight Typing, which we will cover in detail in <a
	
		href = "#-222-weight-tying"
	

	

	>
	
	<span>
		Section 2.2.2  -  Weight Tying
	</span>
</a>.</p>
<p>We share the embedding and output projection weights (<code>self.transformer.wte.weight = self.lm_head.weight</code>) so input token interpretation and next-token scoring occur in the <em>same semantic space</em>.</p>
<p>Using the shared embedding matrix $E$ (shape $(V,d)$), the logits are computed with $\text{logits} = h \cdot E^T$, reusing the same rows used for token lookup. This saves parameters (~23.0M SLM, ~30.7M Regular), keeps gradients for rare historical tokens coupled, and reduces memory traffic (Press &amp; Wolf, 2017; Inan et al., 2016). See Section 2.2.2 for detailed mechanics, benefits, and the historian/scribe analogy.</p>
<p>In short, the <code>lm_head</code> converts rich contextual understanding into next-token scores; with weight tying (details in Section 2.2.2) it stays efficient and semantically consistent.</p>
<h4 id="215-the-complete-flow">2.1.5 The Complete Flow</h4>
<p>The complete forward pass through our GPT model works as follows:</p>
<ol>
<li><strong>Input</strong>: A sequence of token IDs (batch × sequence_length, e.g., 512 tokens)</li>
<li><strong>Token Embedding</strong>: Convert each token ID to a dense vector (768 or 1024 dimensions)</li>
<li><strong>Position Embedding</strong>: Add position information to each token</li>
<li><strong>Transformer Blocks</strong>: Pass through n_layer transformer blocks (12 for SLM, 24 for regular model), each containing:
<ul>
<li>Layer normalization</li>
<li>Causal self-attention (with multiple heads)</li>
<li>Residual connection</li>
<li>Layer normalization</li>
<li>Feed-forward MLP</li>
<li>Residual connection</li>
</ul>
</li>
<li><strong>Final Layer Norm</strong>: Normalize the final hidden states</li>
<li><strong>Language Head</strong>: Project to vocabulary logits (30,000 dimensions)</li>
<li><strong>Output</strong>: Probability distribution over next token</li>
</ol>
<p>This architecture design is conventional and follows the GPT-style pattern established by OpenAI&rsquo;s GPT models. The traditional design is intentional - it allows for clear, educational learning from the implementation while being configured to work seamlessly with our historical tokenizer from Part 2.</p>
<p><a href="#fig2" class="figure-ref">Figure 2</a> below illustrates the complete architecture for the SLM:</p>
<figure class="align-center " id="fig2">
    <pre class="mermaid">graph TD
    A[Input Tokens&lt;br/&gt;512 tokens] --&gt; B[Token Embedding&lt;br/&gt;30K vocab → 768 dim]
    A --&gt; C[Position Embedding&lt;br/&gt;512 pos → 768 dim]
    B --&gt; D[Add Embeddings]
    C --&gt; D
    D --&gt; E[Layer Norm]
    E --&gt; F[Transformer Block 1&lt;br/&gt;12 heads, 768 dim]
    F --&gt; G[Transformer Block 2&lt;br/&gt;12 heads, 768 dim]
    G --&gt; H[...]
    H --&gt; I[Transformer Block 12&lt;br/&gt;12 heads, 768 dim]
    I --&gt; J[Final Layer Norm]
    J --&gt; K[Language Head&lt;br/&gt;768 → 30K vocab]
    K --&gt; L[Output Logits&lt;br/&gt;30K probabilities]
    
    subgraph &#34;Key Specifications&#34;
        M[Layers: 12&lt;br/&gt;Heads: 12&lt;br/&gt;Embedding: 768&lt;br/&gt;Context: 512&lt;br/&gt;Parameters: 117M&lt;br/&gt;Training: 7-8 hours&lt;br/&gt;MFU: 8-9%]
    end
    
    style A fill:#e1f5fe
    style L fill:#e8f5e8
    style M fill:#fff3e0</pre>
    <figcaption>Figure 2: SLM Architecture (117M Parameters)</figcaption>
</figure>
<p>The Regular model, as shown in <a href="#fig3" class="figure-ref">Figure 3</a> below, follows the same architectural pattern as the SLM but with increased capacity: 24 transformer layers instead of 12, 16 attention heads instead of 12, and 1024-dimensional embeddings instead of 768.</p>
<p>This represents a ~3x increase in parameters (354M vs 117M), ~2x more attention heads, and ~33% larger embedding dimensions, providing significantly more computational power for learning complex historical language patterns.</p>
<figure class="align-center " id="fig3">
    <pre class="mermaid">graph TD
    A[Input Tokens&lt;br/&gt;1024 tokens] --&gt; B[Token Embedding&lt;br/&gt;30K vocab → 1024 dim]
    A --&gt; C[Position Embedding&lt;br/&gt;1024 pos → 1024 dim]
    B --&gt; D[Add Embeddings]
    C --&gt; D
    D --&gt; E[Layer Norm]
    E --&gt; F[Transformer Block 1&lt;br/&gt;16 heads, 1024 dim]
    F --&gt; G[Transformer Block 2&lt;br/&gt;16 heads, 1024 dim]
    G --&gt; H[...]
    H --&gt; I[Transformer Block 24&lt;br/&gt;16 heads, 1024 dim]
    I --&gt; J[Final Layer Norm]
    J --&gt; K[Language Head&lt;br/&gt;1024 → 30K vocab]
    K --&gt; L[Output Logits&lt;br/&gt;30K probabilities]
    
    subgraph &#34;Key Specifications&#34;
        M[Layers: 24&lt;br/&gt;Heads: 16&lt;br/&gt;Embedding: 1024&lt;br/&gt;Context: 1024&lt;br/&gt;Parameters: 354M&lt;br/&gt;Training: 28-32 hours&lt;br/&gt;MFU: 15-20%]
    end
    
    style A fill:#e1f5fe
    style L fill:#e8f5e8
    style M fill:#fff3e0</pre>
    <figcaption>Figure 3: Regular Model Architecture (354M Parameters)</figcaption>
</figure>
<h3 id="22-simplegpt-class">2.2 SimpleGPT Class</h3>
<p>Now that we&rsquo;ve covered the theory behind the GPT architecture, let&rsquo;s examine the actual implementation. The <strong><code>SimpleGPT</code></strong> class is at the heart of our implementation - it&rsquo;s the core class that brings together all the components discussed in section 2.1 into a working language model. The class inherits from PyTorch&rsquo;s <code>**torch.nn.Module**</code>, which is the base class for all neural network components in PyTorch. This gives us access to automatic differentiation, GPU support, and other PyTorch features.</p>
<h4 id="221-the-__init__-method">2.2.1 The <code>__init__</code> method</h4>
<p>The <code>__init__</code> method is the constructor that assembles our entire language model from individual components. First, it stores all hyperparameters (such as vocabulary size, embedding dimensions, and the number of layers) in a configuration object that the rest of the model can reference. Next, it creates the embedding layers - one that converts our 30,000 historical tokens into dense vectors, and another that encodes position information. Hence, the model knows where each word appears in the sequence.</p>
<p>Next, it builds the transformer blocks - the core processing units that do the heavy lifting. Each block contains self-attention mechanisms and feed-forward networks that learn to understand relationships between words. The method also initializes the language modeling head, the final layer that converts all internal processing back into probabilities for which word should come next.</p>
<p>Finally, it sets up proper weight initialization to ensure the model starts with good random weights (not too big, not too small), and implements weight tying between the input embeddings and the output layer. This clever technique reduces the number of parameters while improving training efficiency by sharing weights between the first and last layers.</p>
<p>This is important because if the weights are too large, the model&rsquo;s gradients can explode during training, leading to unstable learning. If they start too small, gradients can vanish, rendering the model unable to learn. Our initialization ensures the model begins in the &ldquo;Goldilocks zone&rdquo; - just right for effective learning. Without this, even a perfectly designed architecture might fail to train properly.</p>
<p>Now, let me show you the actual implementation. The code in <a href="#listing1" class="listing-ref">Listing 1</a> demonstrates how we implement the core GPT architecture:</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">SimpleGPT</span>(torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Simple GPT model based on nanoGPT
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    This class implements a decoder-only transformer model optimized for 
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    historical text generation. It inherits from PyTorch&#39;s Module class
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    to get automatic differentiation and GPU support.
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, config):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>()<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()  <span style="color:#6e738d;font-style:italic"># Initialize the parent PyTorch Module class</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>config <span style="color:#91d7e3;font-weight:bold">=</span> config  <span style="color:#6e738d;font-style:italic"># Store all model hyperparameters</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Create the main transformer components using ModuleDict</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># ModuleDict allows us to organize related layers together</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>transformer <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>ModuleDict(<span style="color:#91d7e3">dict</span>(
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Token Embedding Layer (wte = &#34;word token embedding&#34;)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Converts each token ID to a high-dimensional vector</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Input: token IDs (integers 0 to vocab_size-1)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Output: dense vectors of size $n_{embd}$ (e.g., 768 dimensions)</span>
</span></span><span style="display:flex;"><span>            wte <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Embedding(config<span style="color:#91d7e3;font-weight:bold">.</span>vocab_size, config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd),
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Position Embedding Layer (wpe = &#34;word position embedding&#34;) </span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Encodes where each token appears in the sequence</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Input: position indices (0 to block_size-1)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Output: dense vectors of size $n_{embd}$</span>
</span></span><span style="display:flex;"><span>            wpe <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Embedding(config<span style="color:#91d7e3;font-weight:bold">.</span>block_size, config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd),
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Dropout Layer for regularization</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Randomly sets some inputs to zero during training to prevent overfitting</span>
</span></span><span style="display:flex;"><span>            drop <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Dropout(config<span style="color:#91d7e3;font-weight:bold">.</span>dropout),
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Stack of Transformer Blocks (h = &#34;hidden layers&#34;)</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Each SimpleBlock contains self-attention and feed-forward layers</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># We create n_layer blocks (e.g., 12 for SLM, 24 for regular model)</span>
</span></span><span style="display:flex;"><span>            h <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>ModuleList([SimpleBlock(config) <span style="color:#c6a0f6">for</span> _ <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(config<span style="color:#91d7e3;font-weight:bold">.</span>n_layer)]),
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Final Layer Normalization</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Normalizes the output before the language modeling head</span>
</span></span><span style="display:flex;"><span>            ln_f <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>LayerNorm(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, bias<span style="color:#91d7e3;font-weight:bold">=</span>config<span style="color:#91d7e3;font-weight:bold">.</span>bias),
</span></span><span style="display:flex;"><span>        ))
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Language Modeling Head</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Converts the final hidden states back to vocabulary space</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Input: hidden states of size $n_{embd}$</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Output: logits for each token in vocabulary ($vocab_{size}$ logits)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>lm_head <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, config<span style="color:#91d7e3;font-weight:bold">.</span>vocab_size, bias<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Initialize all weights using our custom initialization method</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># This ensures the model starts with good random weights</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>apply(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>_init_weights)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Weight Tying: Share weights between input embeddings and output layer</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># This technique improves training efficiency and model performance</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># by ensuring the same representation space is used for input and output</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>transformer<span style="color:#91d7e3;font-weight:bold">.</span>wte<span style="color:#91d7e3;font-weight:bold">.</span>weight <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>lm_head<span style="color:#91d7e3;font-weight:bold">.</span>weight</span></span></code></pre></div><figcaption>
        <strong>Listing 1: SimpleGPT Model Architecture</strong>
    </figcaption>
</figure>
<h4 id="222-weight-tying">2.2.2 Weight Tying</h4>
<p>The tied weights between the embedding layer and language modeling head (<code>self.transformer.wte.weight = self.lm_head.weight</code>) are a crucial optimization for our historical language model. In a typical neural network, you&rsquo;d have two separate weight matrices - one for converting input tokens to embeddings, and another for converting hidden states back to vocabulary probabilities. Weight tying means we use the <em>same</em> weight matrix for both operations.</p>
<p>Think of it like this: instead of having two different dictionaries (one for reading, one for writing), we use the same dictionary for both. The same table that maps &ldquo;alderman&rdquo; → [0.2, -0.5, 0.8, &hellip;] is used whether the model is reading &ldquo;alderman&rdquo; as input or trying to generate &ldquo;alderman&rdquo; as output.</p>
<p>Without weight tying, the model would have two separate weight matrices - one for converting input tokens to embeddings, and another for converting hidden states back to vocabulary probabilities. This means the model could learn that &ldquo;alderman&rdquo; means one thing when it sees it as input, but something slightly different when it tries to generate it as output. For rare historical terms, this inconsistency can cause the model to &ldquo;forget&rdquo; how to use words it has seen before properly.</p>
<p>Historical vocabulary contains many rare terms such as &ldquo;quoth&rdquo; &ldquo;alderman&rdquo; and &ldquo;paternoster&rdquo; that appear infrequently in our training data. Without weight tying, the model might learn different representations for the same word when it sees it as input versus when it generates it as output. This inconsistency can cause the model to struggle with rare historical terms.</p>
<p>When the model sees &ldquo;alderman&rdquo; in the input, it learns a specific representation of it. Later, when it needs to generate &ldquo;alderman&rdquo; in the output, it uses that same learned representation, ensuring consistency and improving the model&rsquo;s ability to generate coherent historical language with proper terminology.</p>
<p><strong>Mechanics (matrix reuse)</strong>  A <em>single</em> matrix $E \in \mathbb{R}^{(V \times d)}$ serves both roles: row lookup for input embeddings and column interaction for output scoring. The language head reuses it to compute logits via:</p>
<p>$$\text{logits} = h \cdot E^T$$</p>
<p>where $h$ is the hidden state at each position, this keeps the input interpretation and output prediction within the same semantic geometry - no second projection to drift or disagree.</p>
<p><strong>Why it helps</strong> Parameter savings (~23.0M SLM, ~30.7M Regular) lower memory footprint and bandwidth. Gradients for predicting a rare token (e.g., <em>yeoman</em>, <em>guildhall</em>, <em>paternoster</em>) directly refine the very rows used to embed it on future inputs - improving both recall and generation. Shared weights mildly regularize against the two spaces drifting apart and empirically improve perplexity for mid-scale autoregressive models (Press &amp; Wolf, 2017; Inan et al., 2016).</p>
<p><strong>Analogy</strong> If the transformer stack is a panel of historians debating context, the language modeling head is the scribe choosing the next historically plausible word. Weight tying means the scribe and historians consult the <em>same dictionary</em> - no translation mismatch between how words are read and how they&rsquo;re proposed.</p>
<p><strong>Practical notes</strong> Avoid inflating vocabulary unnecessarily (cost scales with $V$); tied weights do not remove the need for careful rare token coverage in the corpus; and if later adding adapters or LoRA heads, remember that tying interacts with how those layers inject low-rank updates.</p>
<p>Now that we understand how the model efficiently handles vocabulary, let&rsquo;s examine the core processing units that transform these embeddings into meaningful representations.</p>
<h3 id="23-transformer-block-design">2.3 Transformer Block Design</h3>
<p>Each transformer block implements the standard attention and feed-forward pattern, but with optimizations for historical text processing. Let us look at the code real quick and then get into a little more detail.</p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">SimpleBlock</span>(torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Simple transformer block&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, config):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>()<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ln_1 <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>LayerNorm(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, bias<span style="color:#91d7e3;font-weight:bold">=</span>config<span style="color:#91d7e3;font-weight:bold">.</span>bias)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>attn <span style="color:#91d7e3;font-weight:bold">=</span> SimpleCausalSelfAttention(config)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ln_2 <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>LayerNorm(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, bias<span style="color:#91d7e3;font-weight:bold">=</span>config<span style="color:#91d7e3;font-weight:bold">.</span>bias)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>mlp <span style="color:#91d7e3;font-weight:bold">=</span> SimpleMLP(config)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        x <span style="color:#91d7e3;font-weight:bold">=</span> x <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>attn(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ln_1(x))
</span></span><span style="display:flex;"><span>        x <span style="color:#91d7e3;font-weight:bold">=</span> x <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>mlp(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ln_2(x))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> x</span></span></code></pre></div><figcaption>
        <strong>Listing 2: Transformer Block Implementation</strong>
    </figcaption>
</figure>
<p>This code implements a single transformer block, which, as we know, is the fundamental building unit of our GPT model.</p>
<h4 id="231-self-attention-step">2.3.1 Self-Attention Step</h4>
<p>The self-attention step (<strong><code>x = x + self.attn(self.ln_1(x))</code></strong>) is the block that first normalizes the input with LayerNorm, then applies self-attention to understand relationships between words. The <code>+</code> creates a &ldquo;residual connection&rdquo; that helps information flow through the network.</p>
<p>As we discussed in <a
	
		href = "#214-gpt-architecture-components"
	

	

	>
	
	<span>
		section 2.1.4
	</span>
</a>, self-attention is the &ldquo;magic&rdquo; of transformers, allowing each token to decide how much attention to pay to every other token in the sequence. Our implementation uses multiple attention heads (12 for SLM, 16 for Regular) that operate in parallel, with each head learning to focus on different types of relationships - syntactic, semantic, or positional. Causal masking ensures that during training, the model learns to predict the next token based solely on the preceding context, which is essential for coherent text generation.</p>
<p>The residual connection (<strong><code>+</code></strong>) is crucial, as it allows the model to preserve the original token representation while adding contextual information from the attention. The pre-normalization approach (LayerNorm before attention) provides more stable training than post-normalization, especially important when working with the varied linguistic patterns found in historical text.</p>
<h4 id="232-feed-forward-step">2.3.2 Feed-Forward Step</h4>
<p>After attention, we have the feed-forward step (<strong><code>x = x + self.mlp(self.ln_2(x))</code></strong>), which first normalizes the attended information with LayerNorm, then passes it through a multi-layer perceptron (MLP) that transforms and processes it.</p>
<p>The MLP typically consists of two linear layers with a non-linear activation function (like GELU) between them, allowing the model to learn complex non-linear transformations of the attended features. This step is crucial because attention can only perform linear transformations on the input representations; the feed-forward network adds the necessary non-linearity, enabling the model to learn complex patterns and relationships in the historical text. Another residual connection preserves the original information, ensuring that the model can always fall back to the pre-attention representation if needed.</p>
<h4 id="233-understanding-the-feed-forward-mlp-sublayer">2.3.3 Understanding the Feed-Forward (MLP) Sublayer</h4>
<p>Directly beneath the <code>SimpleBlock</code> code above, you see the line <code>self.mlp = SimpleMLP(config)</code>. After attention has mixed information across positions, the model passes each token embedding through a position-wise feed-forward network (the MLP). Unlike attention, it does not look at other tokens; it refines the representation of each token independently, given the contextualized features attention just produced. In practice, this is where raw contextual patterns are distilled into richer semantic, stylistic, and morphological signals.</p>
<p><a href="#fig4" class="figure-ref">Figure 4</a> below visualizes how a single transformer block routes data through normalization, attention, and the feed-forward expansion/contraction before returning an upgraded representation via the residual path:</p>
<figure class="align-center " id="fig4">
    <pre class="mermaid">graph TB
    A[Input Embeddings&lt;br/&gt;batch, seq, emb] --&gt; LN1[LayerNorm 1]
    LN1 --&gt; ATTN[Multi-Head Attention&lt;br/&gt;query, key, value]
    ATTN --&gt; DROPA[Dropout]
    DROPA --&gt; RES1[Residual Add&lt;br/&gt;x + attn_out]
    RES1 --&gt; LN2[LayerNorm 2]
    RES1 --&gt; LN2
    LN2 --&gt; EXPAND[Linear Expand&lt;br/&gt;emb → 4*emb]
    EXPAND --&gt; GELU[GELU Activation]
    GELU --&gt; PROJECT[Linear Project&lt;br/&gt;4*emb → emb]
    PROJECT --&gt; DROPM[Dropout]
    DROPM --&gt; RES2[Residual Add&lt;br/&gt;res1 + mlp_out]
    RES2 --&gt; OUT[Block Output&lt;br/&gt;Updated embeddings]
    style A fill:#e1f5fe
    style ATTN fill:#f3e5f5
    style EXPAND fill:#fff3e0
    style PROJECT fill:#fff3e0
    style RES2 fill:#e8f5e8</pre>
    <figcaption>Figure 4: Internal Flow of a Transformer Block</figcaption>
</figure>
<p>Conceptually, the MLP is a two-step projection: first, an expansion into a higher-dimensional &ldquo;workspace&rdquo; with a non-linear activation, then a projection back down so the residual can safely merge with the original stream.</p>
<p>For our SLM, 768 dimensions expand to 3072 and then contract back to 768; for the larger model, 1024 dimensions expand to 4096. This temporary widening allows the network to express combinations of features that a purely linear transform could not capture. It is the difference between merely routing information and actually transforming it.</p>
<p>Here is the representative structure shown in <a href="#listing3" class="listing-ref">Listing 3</a>:</p>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">SimpleMLP</span>(torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, config):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>()<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>fc_in  <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, <span style="color:#f5a97f">4</span> <span style="color:#91d7e3;font-weight:bold">*</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>act    <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>GELU()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>fc_out <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(<span style="color:#f5a97f">4</span> <span style="color:#91d7e3;font-weight:bold">*</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>drop   <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Dropout(config<span style="color:#91d7e3;font-weight:bold">.</span>dropout)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>drop(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>fc_out(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>act(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>fc_in(x))))</span></span></code></pre></div><figcaption>
        <strong>Listing 3: Feed-Forward (MLP) Sublayer Implementation</strong>
    </figcaption>
</figure>
<p><strong>Why expand then shrink?</strong></p>
<p>The widened hidden space allows the model to form intermediate feature bundles (e.g., tense, register, archaic morphology) that do not cleanly live in the original lower-dimensional basis. The contraction enforces a stable interface for the residual path and keeps the total parameter count manageable. Removing the expansion would noticeably degrade expressiveness; removing the contraction would balloon memory use and break architectural symmetry.</p>
<p>In our context, the historical model internalizes regularities like mapping &ldquo;hath&rdquo; and &ldquo;doth&rdquo; into modern tense abstractions while still preserving period flavor; it encodes stylistic shifts between court proceedings, religious prose, and narrative storytelling; it stabilizes inconsistent orthography and variant spellings so downstream layers predict coherent continuations instead of brittle echoes. Attention tells the model where to look; the MLP decides how to reinterpret what it saw.</p>
<p>Focusing only on attention gives an incomplete mental model of transformers. More than half of the parameters and a large fraction of the FLOPs sit in these feed-forward layers. Under-sized MLPs lead to shallow pattern memorization - models that can repeat phrases but cannot generalize style or adapt archaic forms to new contexts. Properly scaled MLP width (the common ×4 expansion) is a proven sweet spot: smaller factors underfit; much larger ones give diminishing returns at this scale (see scaling law discussions in <a
	
		href = "https://arxiv.org/abs/2001.08361"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Kaplan et al. 2020
	</span>
</a>).</p>
<p><strong>A useful mental analogy:</strong> attention is the lively debate in a hall; the MLP is each participant stepping aside to integrate what was heard into their own refined understanding before the next round of discussion. When you see <code>x = x + self.mlp(self.ln_2(x))</code>, that addition represents the moment a token&rsquo;s contextual representation is upgraded. Without this transformation, the model would &ldquo;hear&rdquo; context but fail to internalize it, producing shallow, literal continuations rather than fluent, period-authentic prose.</p>
<p>In our <code>helloLondon</code> models, the MLP is therefore essential for converting raw multi-head attention patterns into durable historical linguistic competence - one of the quiet reasons the generated text feels coherent rather than stitched together.</p>
<p>Each block in our model (12 for SLM, 24 for Regular) applies this same pattern, allowing the model to build an increasingly sophisticated understanding of historical language patterns as text flows through the layers.</p>
<p>Each transformer block applies layer normalization before both the self-attention mechanism and the feed-forward network, followed by residual connections. This pre-normalization approach (as opposed to post-normalization) has been shown to provide more stable training, especially important when working with the varied linguistic patterns found in historical text.</p>
<h4 id="234-activation-choice-matters">2.3.4 Activation choice matters</h4>
<p>The activation function determines how the neural network processes information at each layer. Think of it as a &ldquo;decision maker&rdquo; that decides how much of each input signal to pass through to the next layer. The most common activation functions are ReLU (Rectified Linear Unit) and GELU (Gaussian Error Linear Unit).</p>
<p>ReLU is simple and fast: it passes positive values unchanged and sets negative values to zero (<code>f(x) = max(0, x)</code>). However, ReLU can be &ldquo;harsh&rdquo; - it completely cuts off negative signals, leading to &ldquo;dead neurons&rdquo; that never activate again. GELU is smoother and more sophisticated: it uses a Gaussian distribution to determine how much of each input to pass through (<code>f(x) = x * Φ(x)</code> where Φ is the cumulative distribution function of a standard normal distribution). This creates a smooth, differentiable function that allows for more nuanced information processing.</p>
<p>GELU offers smoother gradients and better calibration for language than plain ReLU. The smoother nature of GELU helps the model learn more subtle patterns in historical text, where the relationships between words and phrases can be complex and nuanced. Alternatives like SwiGLU can yield marginal gains in perplexity but increase implementation complexity - valuable in frontier systems, optional in educational builds like helloLondon. Modest dropout in the MLP further improves generalization on a corpus that, while sizable, is still modest relative to billion-token modern pretraining regimes.</p>
<h4 id="235-pre-vs-post-normalization">2.3.5 Pre vs Post-normalization</h4>
<p>In pre-normalization, we normalize the input before processing it (like we do here). In post-normalization, we&rsquo;d process first, then normalize the output. Pre-normalization is like checking that your ingredients are properly prepared before cooking, while post-normalization is like seasoning after cooking - both work, but pre-normalization tends to yield more consistent results.</p>
<p>This matters because historical texts contain complex syntactic structures and long-range dependencies that require sophisticated attention mechanisms. The residual connections ensure that information can flow directly through the network, helping the model learn to preserve important historical context across long sequences while still allowing the attention mechanism to focus on relevant historical details.</p>
<h3 id="24-causal-self-attention-for-historical-sequences">2.4 Causal Self-Attention for Historical Sequences</h3>
<p>The attention mechanism is crucial for understanding the complex relationships in historical text. Our implementation is based on the original transformer architecture from <a
	
		href = "https://arxiv.org/abs/1706.03762"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		&ldquo;Attention Is All You Need&rdquo;
	</span>
</a> (Vaswani et al., 2017), but optimized for historical language patterns.</p>
<h4 id="understanding-multi-head-attention">Understanding Multi-Head Attention</h4>
<p>Multi-head attention runs several attention “heads” in parallel, allowing the model to focus on different aspects of a sequence simultaneously (syntax, semantics, and position). Compared to a single head, this parallelism yields richer representations—think multiple specialists examining the same text. In our setup, the SLM uses 12 heads and the Regular model 16, scaling capacity with model size. Empirically, heads tend to specialize (e.g., subject–verb agreement, word relations, word order), as observed by <strong><a
	
		href = "https://arxiv.org/abs/1906.04341"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Clark et al. (2019) - &ldquo;What Does BERT Look At?&rdquo;
	</span>
</a></strong>.</p>
<p>Research by <strong><a
	
		href = "https://arxiv.org/abs/2001.08361"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Kaplan et al. (2020) Scaling Laws for Neural Language Models
	</span>
</a></strong> shows that the optimal number of attention heads scales with model size. For our 117M-parameter SLM, 12 heads provide sufficient parallel processing capacity, while our 354M-parameter Regular model benefits from 16 heads to capture more complex attention patterns.</p>
<p>The attention mechanism has $O(n^2)$ complexity with respect to sequence length. This means that doubling our sequence length from 512 to 1024 tokens is a quadratic jump and requires <strong>4x</strong> more memory for attention computations. This is why we carefully balance sequence length with available GPU memory and why techniques like <a
	
		href = "https://arxiv.org/abs/2205.14135"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		FlashAttention
	</span>
</a> (Dao et al., 2022) are so important for memory efficiency.</p>
<p><strong>How the attention mechanism works in practice:</strong></p>
<p>The code in <a href="#listing4" class="listing-ref">Listing 4</a> shows how we implement the attention mechanism that we&rsquo;ve been discussing. Here&rsquo;s what happens step by step:</p>
<ol>
<li>
<p><strong>Input Processing</strong>: The model receives a batch of sequences (B = batch size, T = sequence length, C = embedding dimension). For example, with our SLM: B=4, T=512, C=768.</p>
</li>
<li>
<p><strong>Query, Key, Value Generation</strong>: The input embeddings are transformed into three different representations - Query (Q), Key (K), and Value (V) - using a single linear layer that outputs 3×768 dimensions, then splits them.</p>
</li>
<li>
<p><strong>Multi-Head Reshaping</strong>: Each of Q, K, V is reshaped to separate the 12 attention heads, so each head gets its own 64-dimensional subspace (768 ÷ 12 = 64).</p>
</li>
<li>
<p><strong>Attention Computation</strong>: The scaled dot-product attention is computed, where each word &ldquo;looks at&rdquo; all previous words (causal masking) and decides how much attention to pay to each.</p>
</li>
<li>
<p><strong>Output Assembly</strong>: All attention head outputs are combined back into a single representation and projected through a final linear layer.</p>
</li>
</ol>
<p>This implementation is optimized for historical text processing, using PyTorch&rsquo;s efficient <code>scaled_dot_product_attention</code> function with causal masking to ensure the model can only attend to previous tokens, not future ones.</p>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">SimpleCausalSelfAttention</span>(torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Simple causal self-attention&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, config):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>()<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">assert</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd <span style="color:#91d7e3;font-weight:bold">%</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_head <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Key, query, value projections for all heads, but in a batch</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>c_attn <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, <span style="color:#f5a97f">3</span> <span style="color:#91d7e3;font-weight:bold">*</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, bias<span style="color:#91d7e3;font-weight:bold">=</span>config<span style="color:#91d7e3;font-weight:bold">.</span>bias)
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Output projection</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>c_proj <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd, bias<span style="color:#91d7e3;font-weight:bold">=</span>config<span style="color:#91d7e3;font-weight:bold">.</span>bias)
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Regularization</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>attn_dropout <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Dropout(config<span style="color:#91d7e3;font-weight:bold">.</span>dropout)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>resid_dropout <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>Dropout(config<span style="color:#91d7e3;font-weight:bold">.</span>dropout)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head <span style="color:#91d7e3;font-weight:bold">=</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_head
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_embd <span style="color:#91d7e3;font-weight:bold">=</span> config<span style="color:#91d7e3;font-weight:bold">.</span>n_embd
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dropout <span style="color:#91d7e3;font-weight:bold">=</span> config<span style="color:#91d7e3;font-weight:bold">.</span>dropout
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Batch size, sequence length, embedding dimensionality ($n_{embd}$)</span>
</span></span><span style="display:flex;"><span>        B, T, C <span style="color:#91d7e3;font-weight:bold">=</span> x<span style="color:#91d7e3;font-weight:bold">.</span>size()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Calculate query, key, values for all heads in batch </span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># and move head forward to be the batch dim</span>
</span></span><span style="display:flex;"><span>        q, k, v <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>c_attn(x)<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_embd, dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>)
</span></span><span style="display:flex;"><span>        k <span style="color:#91d7e3;font-weight:bold">=</span> k<span style="color:#91d7e3;font-weight:bold">.</span>view(B, T, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head, C <span style="color:#91d7e3;font-weight:bold">//</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head)<span style="color:#91d7e3;font-weight:bold">.</span>transpose(<span style="color:#f5a97f">1</span>, <span style="color:#f5a97f">2</span>)  <span style="color:#6e738d;font-style:italic"># (B, nh, T, hs)</span>
</span></span><span style="display:flex;"><span>        q <span style="color:#91d7e3;font-weight:bold">=</span> q<span style="color:#91d7e3;font-weight:bold">.</span>view(B, T, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head, C <span style="color:#91d7e3;font-weight:bold">//</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head)<span style="color:#91d7e3;font-weight:bold">.</span>transpose(<span style="color:#f5a97f">1</span>, <span style="color:#f5a97f">2</span>)  <span style="color:#6e738d;font-style:italic"># (B, nh, T, hs)</span>
</span></span><span style="display:flex;"><span>        v <span style="color:#91d7e3;font-weight:bold">=</span> v<span style="color:#91d7e3;font-weight:bold">.</span>view(B, T, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head, C <span style="color:#91d7e3;font-weight:bold">//</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head)<span style="color:#91d7e3;font-weight:bold">.</span>transpose(<span style="color:#f5a97f">1</span>, <span style="color:#f5a97f">2</span>)  <span style="color:#6e738d;font-style:italic"># (B, nh, T, hs)</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Causal self-attention; Self-attend:</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># (B, nh, T, hs) x (B, nh, hs, T) -&gt; (B, nh, T, T)</span>
</span></span><span style="display:flex;"><span>        y <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>functional<span style="color:#91d7e3;font-weight:bold">.</span>scaled_dot_product_attention(q, k, v, attn_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">None</span>, dropout_p<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dropout <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>training <span style="color:#c6a0f6">else</span> <span style="color:#f5a97f">0</span>, is_causal<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        y <span style="color:#91d7e3;font-weight:bold">=</span> y<span style="color:#91d7e3;font-weight:bold">.</span>transpose(<span style="color:#f5a97f">1</span>, <span style="color:#f5a97f">2</span>)<span style="color:#91d7e3;font-weight:bold">.</span>contiguous()<span style="color:#91d7e3;font-weight:bold">.</span>view(B, T, C)  <span style="color:#6e738d;font-style:italic"># Re-assemble all head outputs side by side</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Output projection</span>
</span></span><span style="display:flex;"><span>        y <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>resid_dropout(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>c_proj(y))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> y</span></span></code></pre></div><figcaption>
        <strong>Listing 4: Causal Self-Attention Implementation</strong>
    </figcaption>
</figure>
<p>The attention mechanism computes attention as show below:</p>
<p>$$\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$</p>
<p>Where $Q$, $K$, and $V$ are the query, key, and value matrices, respectively.</p>
<p>In our case, with 768-dimensional embeddings and 12 heads, each head operates on 64-dimensional subspaces ($d_k = 768 / 12 = 64$), providing sufficient representational capacity for each type of historical relationship while maintaining computational efficiency.</p>
<p>In addition, the $\sqrt{d_k}$ scaling factor ($\sqrt{64} = 8$) prevents the dot products from becoming too large, ensuring stable gradient flow during training.</p>
<p><strong>In plain English, please!</strong></p>
<p>Think of attention like a spotlight that can shine on different parts of a sentence. When the model is trying to understand the word &ldquo;he&rdquo; in a historical document, it needs to look back through the text to find who &ldquo;he&rdquo; refers to. The attention mechanism is like having multiple spotlights (our 12 or 16 attention heads) that can each focus on different aspects - each might look for people&rsquo;s names, another for relationships, and another for locations.</p>
<p>The mathematical formula we showed above is how the model calculates the amount of attention to pay to each word. The scaling factor ($$\sqrt(64) = 8) is like adjusting the brightness of the spotlight – it prevents the model from being &ldquo;blinded&rdquo; by very bright spots and helps it focus on the right amount of information.</p>
<p><strong>Does this matter for historical text?</strong></p>
<p>Historical documents are particularly challenging because they often feature complex sentence structures and references spanning long distances. For example, in a court record, you might see &ldquo;The defendant, John Smith, was accused of theft. He claimed innocence throughout the trial.&rdquo; The model needs to understand that &ldquo;He&rdquo; refers to &ldquo;John Smith,&rdquo; even though there are several words between them. The attention mechanism enables the model to make these connections, generating coherent text that maintains proper historical context and references.</p>
<p>This is certainly required for language modeling, given the complex structures in which later words reference earlier ones, and understanding the full context is essential for proper interpretation. The attention mechanism enables the model to learn long-range dependencies, allowing it to generate coherent text across extended sequences. For historical texts specifically, this becomes even more important because archaic language patterns and historical references often span longer distances than those in modern texts.</p>
<h3 id="25-model-configuration">2.5 Model Configuration</h3>
<p>The model architecture uses a centralized configuration, where each parameter is selected based on research findings and practical constraints for historical text processing. The SLM architecture uses five key parameters, each representing a design choice with specific trade-offs between computational efficiency and learning capacity.</p>
<table>
  <thead>
      <tr>
          <th>Parameter</th>
          <th>Value</th>
          <th>Purpose</th>
          <th>Trade-off</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>n_layer</code></td>
          <td>12</td>
          <td>Number of transformer blocks (model depth)</td>
          <td>More layers = better learning, but slower training</td>
      </tr>
      <tr>
          <td><code>n_head</code></td>
          <td>12</td>
          <td>Number of attention heads (parallel processing)</td>
          <td>More heads = better attention, but more computation</td>
      </tr>
      <tr>
          <td><code>n_embd</code></td>
          <td>768</td>
          <td>Embedding dimension (token representation)</td>
          <td>Larger = richer representations, but more memory</td>
      </tr>
      <tr>
          <td><code>max_length</code></td>
          <td>512</td>
          <td>Context window size (sequence length)</td>
          <td>Longer = more context, but quadratic memory growth</td>
      </tr>
      <tr>
          <td><code>vocab_size</code></td>
          <td>30K</td>
          <td>Vocabulary size (tokenizer compatibility)</td>
          <td>Larger = more words, but more parameters</td>
      </tr>
  </tbody>
</table>
<p>These parameters work together to create a model that effectively processes historical text while remaining computationally manageable.</p>
<h4 id="layer-count-n_"><strong>Layer Count (n_layer: 12)</strong></h4>
<p>The 12-layer architecture balances representational capacity with computational efficiency for historical text processing. Shallow layers (1-3) learn basic token patterns and grammatical structures, middle layers (4-8) capture complex syntactic relationships and historical language patterns, and deep layers (9-12) understand high-level semantic relationships and historical context.</p>
<p>This depth follows GPT-2 Small&rsquo;s 12-layer architecture, which delivers strong performance while remaining computationally manageable on available hardware.</p>
<h4 id="attention-heads-n_"><strong>Attention Heads (n_head: 12)</strong></h4>
<p>Multi-head attention allows the model to attend to different types of relationships simultaneously – for example, temporal (chronological order), social (class hierarchies), geographical (London landmarks), and linguistic (archaic patterns). The 12-head architecture balances parallel processing capability with computational efficiency for historical text understanding.</p>
<h4 id="embedding-dimension-n_"><strong>Embedding Dimension (n_embd: 768)</strong></h4>
<p>The 768-dimensional embedding space can represent complex historical concepts, such as archaic terms (&ldquo;yeoman&rdquo;, &ldquo;paternoster row&rdquo;, &ldquo;hath&rdquo;), while maintaining computational efficiency. This dimension is commonly used in transformer architectures, including BERT-base and GPT-2 Medium.</p>
<blockquote>
<p><strong>Why 768 became standard:</strong> As a side note, in case you are seeing a lot of 768 lately, there are a good set of reasons for this. Beyond its divisibility ($768 ÷ 12 = 64$ per attention head), 768 aligns with GPU memory architecture - it&rsquo;s a multiple of 256 (3 × 256), which matches common GPU memory bus widths and cache line sizes. This makes matrix operations more efficient on modern GPUs, as the hardware can process data in optimal chunks. Additionally, 768 provides sufficient representational capacity without the memory overhead of larger dimensions like 1024, making it practical for training on consumer hardware while still capturing complex linguistic relationships.</p></blockquote>
<h4 id="context-window-n_"><strong>Context Window (n_positions: 512)</strong></h4>
<p>We use a 512-token context window as a practical balance between historical coherence and available compute for a learning-focused setup. While many of our working snippets (e.g., diary passages, sections of legal records, or literary excerpts) comfortably fit within 512 tokens, full historical documents can be much longer. The 512 window keeps attention costs manageable (quadratic in sequence length) while covering typical training segments we use.</p>
<p>Both models use the same 30K vocabulary from our custom historical tokenizer, ensuring consistent tokenization across model variants.</p>
<h2 id="3-gpu-configuration-and-perf-optimization">3. GPU Configuration and Perf. Optimization</h2>
<p>The training system is designed to maximize GPU utilization while maintaining training stability. Understanding GPU architecture and memory management is crucial for efficient language model training, especially when working with historical text that requires significant computational resources.</p>
<h3 id="31-gpu-architecture-and-memory-management-for-language-model-training">3.1 GPU Architecture and Memory Management for Language Model Training</h3>
<p>Training on historical text benefits from sensible GPU settings even for a small, learning-focused model. We keep to practical, low-risk optimizations (precision choice, batch/sequence trade-offs, memory-aware attention) and accept some trial and error—reserving heavier systems engineering for larger setups.</p>
<p>The main universal factors are:</p>
<ol>
<li>Attention scales quadratically with sequence length, so longer contexts get expensive fast.</li>
<li>Natural language variability (syntax, vocabulary, style) demands sufficient model capacity and stable optimization.</li>
<li>Real‑world data quality (formatting, noise) can destabilize training, requiring robust error handling and memory management.</li>
</ol>
<p>For historical text specifically, archaic vocabulary, period terminology, and cultural references introduce patterns absent from modern corpora. OCR artifacts and uneven formatting in digitized sources add noise beyond what’s typical in contemporary datasets.</p>
<h4 id="311-gpu-memory-hierarchy-and-optimization-strategies">3.1.1 GPU memory hierarchy and optimization strategies</h4>
<p>Modern GPUs use a hierarchical memory system that significantly impacts training performance: fast but tiny registers and shared memory sit closest to the compute; a larger L2 cache buffers traffic; and global memory holds parameters and activations. Attention often ends up memory-bound, so moving less data (via AMP, Flash/SDPA kernels, and sensible sequence/batch sizes) is as important as raw FLOPs.</p>
<p>For language model training, the key optimization is managing the <em>memory bandwidth bottleneck</em>. Attention operations are often memory-bound rather than compute-bound, meaning performance is limited by how quickly data can be moved between memory levels rather than by computational power. If we are not careful, it is quite easy to run into memory issues, as shown in <a href="#fig5" class="figure-ref">Figure 5</a> below.</p>
<figure>
<img src="images/mem1.png" alt="Out of memory error Screenshot" title="OOM error" id="fig5">
<figcaption><strong>Figure 5:</strong>OOM error</figcaption>
</figure>
<p>And it is not restricted to training only; even on checkpoints that are saved, we can also encounter memory issues, as shown in <a href="#fig6" class="figure-ref">Figure 6</a>.</p>
<figure>
<img src="images/oom-checkpoint-eval.png" alt="Out of memory error - checkpoing evals Screenshot" title="OOM checkpoint eval" id="fig6">
<figcaption><strong>Figure 6:</strong>OOM checkpoint eval</figcaption>
</figure>
<p><strong>Mixed precision training and memory optimization</strong></p>
<p>Training large language models requires careful memory management, especially when working with limited GPU resources. Our training system uses several optimization techniques to maximize memory efficiency while maintaining training stability.</p>
<p><strong>GPU detection and basic configuration:</strong></p>
<p>The training system needs to work across different hardware setups, from single consumer GPUs to multi-GPU servers. Our approach uses a centralized configuration system that automatically adapts to available hardware.</p>
<p>The actual GPU detection in <code>train_model_slm.py</code> is quite straightforward - it checks for distributed training environment variables (<code>RANK</code>, <code>LOCAL_RANK</code>, <code>WORLD_SIZE</code>) and sets up basic multi-GPU support if available. The system also detects GPU capabilities, such as bfloat16 support, and enables appropriate optimizations. This allows the same training script to work across different hardware setups, though the real complexity comes from the trial-and-error process of stabilizing training.</p>
<figure id="listing5"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># GPU configuration (from config.py)</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>gpu_config <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;auto_detect&#34;</span>: <span style="color:#f5a97f">True</span>,  <span style="color:#6e738d;font-style:italic"># Automatically detect available GPUs</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;max_gpus&#34;</span>: <span style="color:#f5a97f">0</span>,  <span style="color:#6e738d;font-style:italic"># Maximum number of GPUs to use (0 = no limit, use all available)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;min_gpu_memory_gb&#34;</span>: <span style="color:#f5a97f">8</span>,  <span style="color:#6e738d;font-style:italic"># Minimum GPU memory required (GB)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;preferred_gpu_types&#34;</span>: [<span style="color:#a6da95">&#34;A30&#34;</span>, <span style="color:#a6da95">&#34;A40&#34;</span>, <span style="color:#a6da95">&#34;A100&#34;</span>, <span style="color:#a6da95">&#34;V100&#34;</span>, <span style="color:#a6da95">&#34;RTX4090&#34;</span>, <span style="color:#a6da95">&#34;RTX4080&#34;</span>],
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;fallback_to_cpu&#34;</span>: <span style="color:#f5a97f">True</span>,  <span style="color:#6e738d;font-style:italic"># Fall back to CPU if no suitable GPUs found</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;force_single_gpu&#34;</span>: <span style="color:#f5a97f">False</span>,  <span style="color:#6e738d;font-style:italic"># Force single GPU even if multiple available</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;force_multi_gpu&#34;</span>: <span style="color:#f5a97f">False</span>,  <span style="color:#6e738d;font-style:italic"># Force multi-GPU even if only one available</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;gpu_memory_fraction&#34;</span>: <span style="color:#f5a97f">0.9</span>,  <span style="color:#6e738d;font-style:italic"># Fraction of GPU memory to use (0.0-1.0)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;allow_growth&#34;</span>: <span style="color:#f5a97f">True</span>,  <span style="color:#6e738d;font-style:italic"># Allow GPU memory growth</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;log_device_placement&#34;</span>: <span style="color:#f5a97f">False</span>  <span style="color:#6e738d;font-style:italic"># Log device placement for debugging</span>
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><figcaption>
        <strong>Listing 5: GPU Configuration and Detection System</strong>
    </figcaption>
</figure>
<p>The configuration in <a href="#listing5" class="listing-ref">Listing 5</a> is defined in our centralized <code>config.py</code> file and provides settings for automatic GPU detection, memory management, and fallback options. While this looks comprehensive, the actual implementation is simpler - the training code primarily relies on PyTorch&rsquo;s built-in distributed training detection and basic device selection.</p>
<p><strong>The reality of training: Nearly 100 runs and many failures</strong></p>
<figure>
<img src="images/wandb.png" alt="WandB Screenshot" title="helloLondon training runs - WandB" id="fig7">
<figcaption><strong>Figure 7:</strong> helloLondon training runs - WandB</figcaption>
</figure>
<p><a href="#fig7" class="figure-ref">Figure 7</a> shows the actual training experience: 99 total runs with 24 completions. The failures were largely data-driven - OCR and encoding issues, uneven sequence lengths, and sensitivity to learning-rate warmup - and a few were plain memory pressure from early, less conservative settings. The code stabilized early; the data and knobs took time.</p>
<p>This iterative process is typical in language model development - the &ldquo;sophisticated&rdquo; system shown here is the result of learning from these failures and gradually improving the training pipeline. The successful runs exhibit stable loss curves and appropriate learning rate schedules, demonstrating that the final configuration performs well on historical text processing tasks.</p>
<p>Most importantly, this experience reinforces a fundamental truth in machine learning: <strong>data quality is still king</strong>. No amount of sophisticated architecture, GPU optimization, or training infrastructure can overcome poor data quality. The &ldquo;garbage in, garbage out&rdquo; principle remains as true for language models as it was for the earliest machine learning systems. Our 75% failure rate was primarily due to data issues – such as inconsistent formatting, OCR errors, and encoding problems - not technical limitations. This is why Part 2&rsquo;s focus on data cleaning and tokenization was so crucial to our success.</p>
<h3 id="32-precision-and-performance-configuration">3.2 Precision and Performance Configuration</h3>
<p>The system includes precision and performance configuration options that can be tuned based on available hardware. Mixed-precision training uses lower-precision (fp16/bf16) for most operations while keeping full precision for critical computations, providing significant memory savings and speed improvements with minimal impact on quality.</p>
<p><strong>Understanding fp16 and bf16: The Precision Trade-off</strong></p>
<p>To understand why precision matters for language model training, we need to look at how computers represent numbers. Standard floating-point numbers use 32 bits (float32), but we can use fewer bits to save memory and increase speed:</p>
<ul>
<li>
<p><strong>fp16 (Half Precision)</strong>: Uses 16 bits to represent numbers, cutting memory usage in half and enabling faster computation. However, it has a smaller range of representable numbers, which can cause &ldquo;overflow&rdquo; (numbers too large) or &ldquo;underflow&rdquo; (numbers too small) during training.</p>
</li>
<li>
<p><strong>bf16 (Brain Float 16)</strong>: Also uses 16 bits, but with a different bit layout that matches float32&rsquo;s exponent range. This means it can represent the same range of large and small numbers as float32, but with less precision for very small decimal values.</p>
</li>
</ul>
<p><strong>Why bf16 is better for language models:</strong></p>
<p>bf16 provides better numerical stability than fp16, especially for large language models, reducing the likelihood of overflow and underflow that can cause training instability. The key difference is that bf16 can represent the same range of numbers as float32 (from very small to very large), while fp16 has a much smaller range. This is crucial for language models because:</p>
<ol>
<li><strong>Gradient magnitudes vary widely</strong> - Some gradients are very small (close to zero) while others are large</li>
<li><strong>Attention weights</strong> - The softmax operations in attention can produce very small numbers that FP16 might round to zero</li>
<li><strong>Learning rate scaling</strong> - Modern optimizers like AdamW work with gradients of varying magnitudes</li>
</ol>
<p>When gradients become too small and are rounded to zero (underflow), the model stops learning effectively. When they become too large (overflow), training becomes unstable. bf16&rsquo;s wider range helps prevent both issues.</p>
<p><strong>Understanding precision and performance settings:</strong></p>
<p>The configuration in <a href="#listing6" class="listing-ref">Listing 6</a> toggles the levers that matter on consumer hardware: TF32 for faster matmuls, AMP (prefer bf16) for stability and memory cuts, <code>torch.compile</code> for an extra boost after warmup, and sequence/batch sizes sized to your VRAM. Used together, these commonly halve activation memory and yield 2-3x speedups versus full-precision baselines.</p>
<figure id="listing6"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Runtime/precision knobs (A30 optimized)</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;enable_tf32&#34;</span>: <span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;enable_amp&#34;</span>: <span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;amp_dtype&#34;</span>: <span style="color:#a6da95">&#34;bf16&#34;</span>,  <span style="color:#6e738d;font-style:italic"># bf16 on Ampere; fallback to fp16 if unsupported</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;enable_compile&#34;</span>: <span style="color:#f5a97f">True</span>,  <span style="color:#6e738d;font-style:italic"># torch.compile; set False to reduce memory usage</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Conservative baseline (for broad hardware) - uncomment to use:</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &#34;enable_tf32&#34;: False,</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &#34;enable_amp&#34;: True,</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &#34;amp_dtype&#34;: &#34;fp16&#34;,</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Sequence/batch control</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;max_length&#34;</span>: <span style="color:#f5a97f">1024</span>,  <span style="color:#6e738d;font-style:italic"># increase tokens per step when VRAM allows</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;batch_size&#34;</span>: <span style="color:#f5a97f">20</span>,    <span style="color:#6e738d;font-style:italic"># per-GPU batch; raise if VRAM allows</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Conservative sequence/batch - uncomment to use:</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &#34;max_length&#34;: 768,</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &#34;batch_size&#34;: 8,</span></span></span></code></pre></div><figcaption>
        <strong>Listing 6: Precision and Performance Configuration</strong>
    </figcaption>
</figure>
<h4 id="key-gpu-configuration-settings"><strong>Key GPU Configuration Settings</strong></h4>
<p>A few switches move the needle the most: enable TF32 on Ampere-class GPUs for a quick matrix-mul speedup; use AMP (bf16 where supported, fp16 otherwise) to halve activation memory; and turn on <code>torch.compile</code> if you can afford the warmup to get another 1.2-1.5x after a few hundred steps. Keep the sequence length in line with VRAM (~512 tokens for 8GB, ~1024 for 16GB+), and scale the per-GPU batch size accordingly (think hundreds of MB per batch at these widths). The repo includes sensible presets so you can start conservative and dial up.</p>
<h4 id="321-real-world-performance-results">3.2.1 Real-World Performance Results</h4>
<p>On 2x A30s, the SLM lands around mid-20s MFU with ~210 ms/iter and ~18 GB per GPU, converging from ~10.4 loss to the mid-3s over the full run. The clean BPE tokenizer and precision stack keep math efficient, and DDP delivers the expected speedup over a single device.</p>
<p><strong>Automatic Precision Detection and Memory Optimization:</strong></p>
<p>The system also includes automatic precision detection and memory optimization during model initialization. The code snippet below shows how the system automatically selects the optimal precision format based on available hardware capabilities:</p>
<figure id="listing7"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Precision / TF32 knobs from config</span>
</span></span><span style="display:flex;"><span>tf32 <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>slm_config<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#34;enable_tf32&#34;</span>, <span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>torch<span style="color:#91d7e3;font-weight:bold">.</span>backends<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>matmul<span style="color:#91d7e3;font-weight:bold">.</span>allow_tf32 <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">bool</span>(tf32)
</span></span><span style="display:flex;"><span>torch<span style="color:#91d7e3;font-weight:bold">.</span>backends<span style="color:#91d7e3;font-weight:bold">.</span>cudnn<span style="color:#91d7e3;font-weight:bold">.</span>allow_tf32 <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">bool</span>(tf32)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>    torch<span style="color:#91d7e3;font-weight:bold">.</span>set_float32_matmul_precision(<span style="color:#a6da95">&#39;high&#39;</span> <span style="color:#c6a0f6">if</span> tf32 <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#39;medium&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">pass</span>
</span></span><span style="display:flex;"><span>use_amp <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>slm_config<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#34;enable_amp&#34;</span>, <span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>amp_dtype_cfg <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>slm_config<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#34;amp_dtype&#34;</span>, <span style="color:#a6da95">&#34;bf16&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>lower()
</span></span><span style="display:flex;"><span>bf16_ok <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#91d7e3;font-weight:bold">and</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_bf16_supported()
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> use_amp:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> amp_dtype_cfg <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;bf16&#39;</span> <span style="color:#91d7e3;font-weight:bold">and</span> bf16_ok:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dtype <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;bfloat16&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dtype <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;float16&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dtype <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;float32&#39;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 7: Precision Detection and Memory Optimization</strong>
    </figcaption>
</figure>
<p>The TF32 configuration optimizes matrix operations for Ampere+ GPUs, delivering significant speedups while maintaining training stability.</p>
<h3 id="33-multi-gpu-training-with-distributed-data-parallel">3.3 Multi-GPU Training with Distributed Data Parallel</h3>
<p>The system supports multi-GPU training using PyTorch&rsquo;s DistributedDataParallel (DDP) - each GPU hosts a full model replica, processes different batches in parallel, and synchronizes gradients automatically. PyTorch handles the inter‑GPU communication, so on two GPUs, you typically see near‑linear speedup (~2×) for these model sizes.</p>
<p>Multi-GPU training improves throughput and shortens wall‑clock time by splitting work across devices. On our 2× A30 setup, we process 36 sequences in parallel (18 per GPU) instead of 18 on a single card, cutting Regular model training from ~56 hours to ~28–32 hours. It also offers operational flexibility: scale up or down based on the number of GPUs available.</p>
<p>However, multi-GPU training introduces several challenges that can limit performance gains. The primary bottleneck is <strong>inter-GPU communication</strong> - after each backward pass, gradients must be synchronized across all GPUs, which requires transferring large amounts of data. This communication overhead can become significant, especially with larger models and more GPUs.</p>
<p>The performance of multi-GPU training heavily depends on the interconnect between GPUs. On NVIDIA systems, <em>InfiniBand</em> provides the highest bandwidth and lowest latency for GPU-to-GPU communication, enabling near-linear scaling across many GPUs. <em>NVLink</em> (found on high-end NVIDIA GPUs such as A100 and H100) provides direct GPU-to-GPU connections with very high bandwidth, making it ideal for 2-8 GPU setups. <em>PCIe</em> connections are slower but more common in consumer and workstation systems.</p>
<p>In AMD systems, <em>Infinity Fabric</em> serves a role similar to NVLink, providing high-bandwidth interconnects between GPUs. AMD&rsquo;s <em>MI200</em> and <em>MI300</em> series GPUs include Infinity Fabric links that enable efficient multi-GPU communication.</p>
<p>In practice, scaling efficiency depends on the ratio of computation to communication. Our historical language models have relatively modest parameter counts (117M-354M), so communication overhead can be significant compared to computation time. This is why we see good scaling with 2 GPUs but diminishing returns with more GPUs - the communication overhead starts to dominate.</p>
<p>DDP is more efficient than naive data parallelism because it reduces communication overhead and enables larger effective batch sizes, as shown in <a href="#listing8" class="listing-ref">Listing 8</a> below.</p>
<figure id="listing8"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># DDP setup (process group already initialized in main())</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(os<span style="color:#91d7e3;font-weight:bold">.</span>environ<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;RANK&#39;</span>, <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)) <span style="color:#91d7e3;font-weight:bold">!=</span> <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp:
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_rank <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;RANK&#39;</span>])
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_local_rank <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;LOCAL_RANK&#39;</span>])
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_world_size <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;WORLD_SIZE&#39;</span>])
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>device <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#39;cuda:</span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_local_rank<span style="color:#a6da95">}</span><span style="color:#a6da95">&#39;</span>
</span></span><span style="display:flex;"><span>    torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>set_device(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>device)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>master_process <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_rank <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>seed_offset <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_rank
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>master_process <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">True</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>seed_offset <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_world_size <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1</span></span></span></code></pre></div><figcaption>
        <strong>Listing 8: Multi-GPU Training Setup</strong>
    </figcaption>
</figure>
<p><strong>What is &ldquo;rank&rdquo; and why does it matter?</strong></p>
<p>In distributed training, each GPU process gets a unique “rank.” Rank 0 acts as the coordinator (handles logging, checkpointing, and WandB), while the remaining ranks focus purely on computation. This avoids collisions - only one process touches files and dashboards - while every device contributes gradients.</p>
<p>This division of labor is crucial because it prevents conflicts. Without it, all processes would try to save checkpoints simultaneously, log to WandB at the same time, or write to the same files, causing errors and corruption.</p>
<p>The key to scaling efficiency is that each GPU works independently on different data batches, then synchronizes only the essential information (gradients). Here&rsquo;s how it works:</p>
<ol>
<li><strong>Parallel computation</strong>: Each GPU processes a different batch of data simultaneously</li>
<li><strong>Gradient synchronization</strong>: After each backward pass, gradients are averaged across all GPUs</li>
<li><strong>Independent updates</strong>: Each GPU updates its model copy with the averaged gradients</li>
</ol>
<p>This means that if you have 2 GPUs, you can process 2x the data in the same time, giving you roughly 2x the speed. With 4 GPUs, you get approximately 4x speedup. The &ldquo;near-linear&rdquo; part acknowledges that there&rsquo;s always some overhead from communication and synchronization, so that you might get 1.9x speedup instead of exactly 2x. Still, it&rsquo;s close enough to be very effective.</p>
<p>However, there&rsquo;s a practical limit to this approach. Beyond 8-16 GPUs, the communication overhead becomes so significant that you need more robust hardware (such as InfiniBand networks) and advanced systems engineering techniques (gradient compression, pipeline parallelism, model parallelism) to maintain efficiency. For truly large-scale training with hundreds of GPUs, you need specialized infrastructure and techniques that go far beyond what we&rsquo;re doing here.</p>
<p>This combination of distributed training and memory optimization enables us to train our historical language models efficiently, even on consumer hardware. The distributed setup provides fault tolerance and near-linear speedup, while the precision optimizations enable larger models and longer sequences on the same hardware.</p>
<h2 id="4-training-infrastructure-making-it-all-work-together">4. Training Infrastructure: Making It All Work Together</h2>
<p>As a reminder, as we saw earlier, the two model variants share the same training stack (scheduler, checkpointing, WandB, DDP). See Part 1 for the high‑level comparison; here are the training‑relevant differences only:</p>
<ul>
<li>SLM (117M): per‑GPU batch 18 → effective 36 on 2 GPUs; sequence length 512; ~7–8h on 2×A30</li>
<li>Regular (354M): per‑GPU batch 12 → effective 24 on 2 GPUs; sequence length 1024; ~28–32h on 2×A30</li>
</ul>
<h3 id="41-the-training-loop">4.1 The Training Loop</h3>
<p>The core training happens in the <code>train()</code> method, which implements a standard language model training loop with several key phases - outlined below.</p>
<h4 id="411-data-loading-and-preparation">4.1.1 Data Loading and Preparation</h4>
<p>The training loop starts by loading tokenized data using <code>get_batch('train')</code>, which reads from pre-processed binary files created during data preparation. This includes both training and validation data, with the tokenizer from <a
	
		href = "https://blog.desigeek.com/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 2: Data Collection &amp; Custom Tokenizers
	</span>
</a> handling the conversion between text and tokens.</p>
<p><strong>Main Training Loop Structure:</strong></p>
<figure id="listing9"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">train</span>(<span style="color:#91d7e3">self</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Main training loop&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Get initial batch</span>
</span></span><span style="display:flex;"><span>    X, Y <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>get_batch(<span style="color:#a6da95">&#39;train&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 1. Learning rate scheduling</span>
</span></span><span style="display:flex;"><span>        lr <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>get_lr(iter_num)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 2. Evaluation and checkpointing (every eval_interval steps)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> iter_num <span style="color:#91d7e3;font-weight:bold">%</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>eval_interval <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span>:
</span></span><span style="display:flex;"><span>            losses <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>estimate_loss()
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Save checkpoint if validation loss improved</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 3. Forward pass with mixed precision</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>amp<span style="color:#91d7e3;font-weight:bold">.</span>autocast():
</span></span><span style="display:flex;"><span>            logits, loss <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model(X, Y)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 4. Backward pass and optimization</span>
</span></span><span style="display:flex;"><span>        loss<span style="color:#91d7e3;font-weight:bold">.</span>backward()
</span></span><span style="display:flex;"><span>        torch<span style="color:#91d7e3;font-weight:bold">.</span>nn<span style="color:#91d7e3;font-weight:bold">.</span>utils<span style="color:#91d7e3;font-weight:bold">.</span>clip_grad_norm_(model<span style="color:#91d7e3;font-weight:bold">.</span>parameters(), <span style="color:#f5a97f">1.0</span>)
</span></span><span style="display:flex;"><span>        optimizer<span style="color:#91d7e3;font-weight:bold">.</span>step()
</span></span><span style="display:flex;"><span>        optimizer<span style="color:#91d7e3;font-weight:bold">.</span>zero_grad()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 5. Get next batch</span>
</span></span><span style="display:flex;"><span>        X, Y <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>get_batch(<span style="color:#a6da95">&#39;train&#39;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># 6. Logging and monitoring</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> iter_num <span style="color:#91d7e3;font-weight:bold">%</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>log_interval <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Log to WandB and console</span></span></span></code></pre></div><figcaption>
        <strong>Listing 9: Core Training Loop Structure</strong>
    </figcaption>
</figure>
<p><strong>Training Process Flow:</strong></p>
<p>Understanding how the training actually works requires seeing both the high-level flow and the technical details of each phase. <a href="#fig8" class="figure-ref">Figure 8</a> shows the complete training process flow.</p>
<figure class="align-center " id="fig8">
    <pre class="mermaid">graph TD
    A[Start Training] --&gt; B[Load Tokenized Data]
    B --&gt; C[Initialize Model &amp; Optimizer]
    C --&gt; D[Training Loop Start]
    D --&gt; E[Update Learning Rate]
    E --&gt; F{Evaluation Time?}
    F --&gt;|Yes| G[Run Validation]
    F --&gt;|No| H[Forward Pass]
    G --&gt; H
    H --&gt; I[Compute Loss]
    I --&gt; J[Backward Pass]
    J --&gt; K[Gradient Clipping]
    K --&gt; L[Update Weights]
    L --&gt; M[Zero Gradients]
    M --&gt; N[Log Metrics]
    N --&gt; O{Checkpoint?}
    O --&gt;|Yes| P[Save Model State]
    O --&gt;|No| Q[Load Next Batch]
    P --&gt; Q
    Q --&gt; R{Max Iterations?}
    R --&gt;|No| D
    R --&gt;|Yes| S[Save Final Model]
    S --&gt; T[End Training]</pre>
    <figcaption>Figure 8: Training Process Flow</figcaption>
</figure>
<p>Now that we have a high-level overview of the training process, let us dig deeper into each phase and see how it works in practice.</p>
<h4 id="412-data-loading">4.1.2 Data Loading</h4>
<p>Data loading reads pre-tokenized sequences from binary files (<code>.bin</code>) using <code>np.memmap</code> for memory efficiency. The initial tokenization process can take quite a long time on our 500M+ character corpus, but this is done only once and saved to disk. This optimization was crucial during our development process – given nearly 100 training runs and many failures, re-tokenizing the entire corpus each time would have been prohibitively slow. The system handles train/val splits (90/10 %) with random sampling per batch and uses <code>pin_memory()</code> and <code>non_blocking=True</code> for faster GPU transfers.</p>
<p>When we run this for the time time, it takes a long time to load and tokenize the training data corpus. We see this just startging in <a href="#fig9" class="figure-ref">Figure 9</a> below.</p>
<figure>
<img src="images/train16.png" alt="Tokenizer training data Screenshot" title="Tokenizer training data" id="fig9">
<figcaption><strong>Figure 9:</strong>Tokenizer training data</figcaption>
</figure>
<p>Batch sizes are optimized for our 2x A30 GPU setup: 18 per GPU for the SLM model (36 effective batch size) and 12 per GPU for the Regular model (24 effective batch size). These numbers balance memory usage with training stability – the SLM can handle larger batches thanks to its smaller 117M parameter count. In comparison, the Regular model&rsquo;s 354M parameters require smaller batches to fit in GPU memory.</p>
<p><a href="#fig10" class="figure-ref">Figure 10</a> below shows the dual GPU setup used for one of the training sessions for the regular mode.</p>
<figure>
<img src="images/gpu1.png" alt="GPU detail Screenshot" title="GPU details" id="fig10">
<figcaption><strong>Figure 10:</strong>GPU detail</figcaption>
</figure>
<h4 id="413-learning-rate-scheduling">4.1.3 Learning Rate Scheduling</h4>
<p>Learning Rate Scheduling uses cosine decay with warmup, a two-phase approach that helps prevent training instability. The warmup phase gradually increases the learning rate from 0 to the target value over 500 steps (SLM) or 1000 steps (Regular model), preventing the model from making large, destabilizing updates early in training.</p>
<p>After warmup, cosine decay smoothly reduces the learning rate following a cosine curve to 10% of the initial rate by the end of training. In case you are not familiar with Cosine decay, it is a scheduling strategy where the learning rate follows the shape of a cosine wave: starting at the maximum value after warmup, it decreases slowly at first, then more rapidly in the middle of training, and finally levels off gently near the minimum value.</p>
<p>Mathematically, this follows the curve <code>lr = min_lr + (max_lr - min_lr) × 0.5 × (1 + cos(π × progress))</code>, where <code>progress</code> goes from 0 (start of decay) to 1 (end of training). Unlike linear decay (which drops at a constant rate) or step decay (which drops abruptly at fixed intervals), cosine decay provides a smooth, natural reduction that helps the model explore the loss landscape more effectively early on, then refine its parameters more precisely as training progresses.</p>
<p>The initial learning rates are chosen based on model size: 3e-4 (0.0003) for the SLM model and 3e-5 (0.00003) for the Regular model. The 10x difference reflects the Regular model&rsquo;s larger parameter count (354M vs 117M) - larger models typically need smaller learning rates to prevent gradient explosion. The cosine decay ensures the model converges smoothly rather than oscillating around the minimum, which is crucial for the complex patterns in historical text.</p>
<figure id="listing10"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_lr</span>(<span style="color:#91d7e3">self</span>, it):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Learning rate schedule&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    warmup_iters <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">500</span>
</span></span><span style="display:flex;"><span>    lr_decay_iters <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>max_iters
</span></span><span style="display:flex;"><span>    min_lr <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>learning_rate <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">0.1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> it <span style="color:#91d7e3;font-weight:bold">&lt;</span> warmup_iters:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>learning_rate <span style="color:#91d7e3;font-weight:bold">*</span> (it <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>) <span style="color:#91d7e3;font-weight:bold">/</span> (warmup_iters <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> it <span style="color:#91d7e3;font-weight:bold">&gt;</span> lr_decay_iters:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> min_lr
</span></span><span style="display:flex;"><span>    decay_ratio <span style="color:#91d7e3;font-weight:bold">=</span> (it <span style="color:#91d7e3;font-weight:bold">-</span> warmup_iters) <span style="color:#91d7e3;font-weight:bold">/</span> (lr_decay_iters <span style="color:#91d7e3;font-weight:bold">-</span> warmup_iters)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">assert</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">&lt;=</span> decay_ratio <span style="color:#91d7e3;font-weight:bold">&lt;=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    coeff <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0.5</span> <span style="color:#91d7e3;font-weight:bold">*</span> (<span style="color:#f5a97f">1.0</span> <span style="color:#91d7e3;font-weight:bold">+</span> math<span style="color:#91d7e3;font-weight:bold">.</span>cos(math<span style="color:#91d7e3;font-weight:bold">.</span>pi <span style="color:#91d7e3;font-weight:bold">*</span> decay_ratio))
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> min_lr <span style="color:#91d7e3;font-weight:bold">+</span> coeff <span style="color:#91d7e3;font-weight:bold">*</span> (<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>learning_rate <span style="color:#91d7e3;font-weight:bold">-</span> min_lr)</span></span></code></pre></div><figcaption>
        <strong>Listing 10: Learning Rate Scheduling Function</strong>
    </figcaption>
</figure>
<p>The code in <a href="#listing10" class="listing-ref">Listing 10</a> shows how the learning rate schedule is implemented. The function takes the current iteration number and returns the appropriate learning rate based on whether we&rsquo;re in the warmup phase (linear increase) or decay phase (cosine curve). The <code>warmup_iters</code> parameter controls the warmup duration, while <code>min_lr</code> sets the final learning rate to 10% of the initial value.</p>
<blockquote>
<p>As a side note, in case you are curious about why a <strong>cosine decay</strong> specifically makes sense, then read on. The cosine function has unique mathematical properties that make it ideal for learning rate scheduling. Unlike linear decay (which drops too quickly) or exponential decay (which drops too slowly), cosine decay starts with a gentle slope that gradually steepens, then flattens out near the end. This creates a &ldquo;restart&rdquo; effect, allowing the model to escape local minima early in training and then fine-tune more precisely in later stages.</p></blockquote>
<blockquote>
<p>The smooth, continuous nature of cosine decay prevents the learning rate from changing too abruptly, which could destabilize training. Given the historical text&rsquo;s complex linguistic patterns, this gradual, adaptive approach helps the model learn both general language structures and specific historical vocabulary without getting stuck in suboptimal solutions.</p></blockquote>
<h4 id="414-evaluation">4.1.4 Evaluation</h4>
<p>We run evaluations at a regular interval after a certain number of steps, which are defined in the <strong><code>eval_interval()</code></strong> method (defaults to 500 for SLM, 1000 for Regular) and compute loss on both train and validation sets using the <strong><code>estimate_loss()</code></strong> method. The different intervals reflect the models&rsquo; training complexity: the SLM trains faster and benefits from more frequent checks, while the Regular model’s longer runs can use less frequent evaluation.</p>
<p>The <code>estimate_loss()</code> function monitors training progress without disrupting the learning process. To ensure consistent measurements, it temporarily switches the model to evaluation mode (<strong><code>model.eval()</code></strong>). In this mode, dropout layers stop randomly dropping neurons (using the full network capacity), and batch normalization uses running statistics rather than recomputing them from each batch. This means the same input produces the same output every time, unlike in training mode, where dropout introduces randomness for regularization.</p>
<p>Rather than computing loss on the entire dataset (which would be too slow), <code>estimate_loss()</code> samples <code>eval_iters</code> random batches (default 100) from both training and validation sets. It computes the loss for each batch and returns the average, providing a representative estimate of model performance while remaining computationally efficient.</p>
<p>The evaluation process uses <strong><code>torch.no_grad()</code></strong> to disable gradient computation during validation. Gradients are the partial derivatives that tell us how to adjust each model parameter to reduce loss - they&rsquo;re computed during the backward pass and stored for the optimizer. During evaluation, we don&rsquo;t need gradients because we&rsquo;re not updating weights; we&rsquo;re just measuring performance.</p>
<p>Disabling gradient computation serves two critical purposes. First, it prevents memory leaks by not storing gradients for validation computations - without this, GPU memory would gradually increase during evaluation and eventually cause out-of-memory errors. Second, it ensures accurate loss measurement by preventing any accidental gradient updates during the evaluation phase. The <code>no_grad()</code> context manager is essential for maintaining training stability and memory efficiency.</p>
<h4 id="415-forward-pass">4.1.5 Forward Pass</h4>
<p>A forward pass is when the model processes input data through its layers to produce a prediction. Think of it like asking the model a question: given a sequence of historical text tokens, &ldquo;what word should come next?&rdquo; The model flows the input forward through 12 transformer blocks (SLM) or 24 blocks (Regular), each applying self-attention (to understand relationships between words) and feed-forward operations (to transform and refine the representations). At the end, the model outputs a probability distribution over all possible next tokens.</p>
<p>The forward pass uses mixed precision training with <strong><code>torch.amp.autocast</code></strong> and bf16/fp16 data types, reducing memory usage by ~50% while maintaining training stability. Cross-entropy loss is computed by comparing the model&rsquo;s predicted probabilities with the actual next tokens in the training data; it measures how &ldquo;wrong&rdquo; the model&rsquo;s predictions are. The loss function handles variable sequence lengths by appropriately padding sequences. The mixed precision approach is particularly important for our historical text corpus, which contains long sequences that would otherwise exceed GPU memory limits.</p>
<h4 id="416-backward-pass">4.1.6 Backward Pass</h4>
<p>After the forward pass tells us how wrong the model is (via the loss), the backward pass figures out how to fix it. Using <strong><code>loss.backward()</code></strong>, PyTorch computes gradients for every parameter in the model - these gradients tell us the direction and magnitude of changes needed to reduce the loss. It&rsquo;s like having a GPS telling you which way to move and how far, but for 117 million (SLM) or 354 million (Regular) parameters simultaneously.</p>
<p>The system applies gradient clipping with <strong><code>torch.nn.utils.clip_grad_norm_</code></strong> using a maximum norm of 1.0. Sometimes gradients can become extremely large, especially when processing complex or unusual historical text patterns. Without clipping, these huge gradients would cause the model parameters to jump wildly, potentially making the model unstable or causing it to &ldquo;forget&rdquo; what it learned. Clipping acts like a safety valve, limiting the maximum size of parameter updates to keep training stable. Put simply, gradient clipping caps the overall (global) gradient norm at a threshold; if it exceeds the limit, gradients are rescaled so the update stays bounded. In our early runs, omitting clipping occasionally produced NaN losses; keeping <code>max_norm=1.0</code> eliminated those spikes.</p>
<p>After computing gradients, the system updates the model weights using the <strong><code>AdamW</code></strong> optimizer, which applies the gradients with momentum and adaptive learning rates for each parameter. The optimizer decouples weight decay (a regularization technique to prevent overfitting) from gradient updates, improving generalization. Finally, gradients are zeroed with <strong><code>optimizer.zero_grad(set_to_none=True)</code></strong> - this clears the gradient buffers before the next iteration, preventing them from accumulating across batches. The <code>set_to_none=True</code> option releases memory immediately rather than waiting for GC, improving memory efficiency.</p>
<h4 id="417-checkpointing">4.1.7 Checkpointing</h4>
<p>Checkpointing saves model state, optimizer state, iteration number, and best validation loss whenever validation performance improves, rather than at every evaluation. This selective saving strategy provides multiple benefits: it conserves disk space (our 354M-parameter model checkpoints are ~1.4GB each), reduces I/O overhead that can slow down training, and improves overall training time by 5-10% by eliminating redundant disk writes. The system maintains only the last 5 checkpoints (as configured in <code>config.py</code>), with PyTorch&rsquo;s <code>torch.save()</code> using compression to ensure efficient storage while preserving all necessary training state for resuming. We&rsquo;ll dive deeper into checkpointing strategies and implementation details in Section 6.</p>
<p>The training loop implements standard optimization practices, including dynamic learning rate scheduling, regular evaluation and checkpointing, and comprehensive logging to WandB (as detailed in Section 5). The system automatically saves checkpoints when validation loss improves, ensuring that the best model is always preserved. The learning rate schedule uses cosine decay with warmup, which is standard practice for transformer training.</p>
<blockquote>
<p><strong>📁 Full Implementation</strong>:</p>
<ul>
<li>SLM: <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/04_training/train_model_slm.py"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<code>04_training/train_model_slm.py</code>
	</span>
</a></li>
<li>Regular Model: <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/04_training/train_model.py"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<code>04_training/train_model.py</code>
	</span>
</a></li>
</ul></blockquote>
<h3 id="42-model-initialization-setting-up-the-training-foundation">4.2 Model Initialization: Setting Up the Training Foundation</h3>
<p>Before the training loop can begin, the system must properly initialize the model, optimizer, and training infrastructure. The <strong><code>init_model()</code></strong> method handles this setup, ensuring everything is configured correctly for efficient training.</p>
<h4 id="421-model-configuration-and-creation">4.2.1 Model Configuration and Creation</h4>
<p>The initialization process starts by loading metadata from the tokenized data to ensure the model architecture matches the training data. The system reads vocabulary size, block size, and other parameters from the <code>meta.pkl</code> file created during data preparation, ensuring consistency between the model and the data it will be trained on.</p>
<p>The model configuration is built from the SLM parameters defined in <code>config.py</code>, including the number of layers (12), attention heads (12), embedding dimensions (768), and other architectural choices. This configuration is then used to create the <code>SimpleGPT</code> model instance, which inherits from PyTorch&rsquo;s <code>nn.Module</code> and provides all the functionality we discussed in the architecture section.</p>
<h4 id="422-optimizer-setup-and-configuration">4.2.2 Optimizer Setup and Configuration:</h4>
<p>The optimizer is the algorithm that actually updates the model&rsquo;s parameters (weights and biases) during training. After the backward pass computes gradients (which tell us how to adjust each parameter), the optimizer applies those gradients to update the parameters and improve the model.</p>
<p>The system uses <strong>AdamW</strong> (Adam with Weight Decay), which is a popular optimizer for training transformers. AdamW combines the best of two approaches: Adam (which adapts the learning rate for each parameter individually, helping with convergence) and weight decay (a form of regularization that prevents overfitting by discouraging large parameter values).</p>
<p>However, not all parameters should be regularized the same way. The optimizer splits parameters into two groups for different weight decay:</p>
<ul>
<li><strong>2D parameters</strong> (weight matrices): These are the main &ldquo;learnable&rdquo; parts of the model - the connections between neurons in different layers. These receive weight decay (value 0.1) to prevent them from growing too large, which helps prevent overfitting.</li>
<li><strong>1D parameters</strong> (biases): These are additive constants that help shift the model&rsquo;s predictions. They don&rsquo;t receive weight decay (value 0.0) because regularizing biases doesn&rsquo;t help with overfitting and can actually hurt performance.</li>
</ul>
<p>This two-group approach follows standard practices for transformer training and ensures the model generalizes well to unseen historical text.</p>
<p>Modern PyTorch supports &ldquo;fused&rdquo; optimizer operations, which combine multiple steps into a single, faster GPU kernel. Instead of executing separate operations (unscale gradients, update parameters, update optimizer state), fused AdamW performs all three in a single optimized GPU operation. This can provide 10-20% speedup on modern GPUs. The system automatically detects whether your PyTorch version supports fused operations and uses them when available, falling back to the standard implementation otherwise.</p>
<p>Concretely, we use AdamW with the following settings for this project: <code>betas=(0.9, 0.95)</code>, <code>weight_decay=0.1</code>, and the learning rate provided by the scheduler (warmup + cosine decay). The AdamW <code>eps</code> parameter is left at the PyTorch default unless you change it in code. When available, the fused AdamW kernel is enabled automatically. See <a href="#listing11" class="listing-ref">Listing 11</a> for the exact call in <code>init_model()</code>.</p>
<h4 id="423-model-compilation-and-multi-gpu-setup">4.2.3 Model Compilation and Multi-GPU Setup</h4>
<p>Model compilation with PyTorch&rsquo;s <strong><code>torch.compile</code></strong> is similar to traditional code compilation, but with important differences. When you compile Python code (like using <code>gcc</code> for C), the compiler transforms the source code into optimized machine code once, which then runs faster. Similarly, <code>torch.compile</code> takes your model&rsquo;s computation graph and optimizes it, but it does this <strong>at runtime</strong> rather than ahead of time.</p>
<p>The compilation process analyzes your model&rsquo;s operations (matrix multiplications, attention layers, etc.) and generates optimized kernels tuned to your hardware. This includes <strong>operator fusion</strong> (combining multiple operations into single GPU kernels), <strong>memory layout optimization</strong> (arranging data for better cache usage), and <strong>kernel selection</strong> (choosing the fastest implementation for your specific GPU). The result is often 1.2-1.5x speedier training, but with an initial &ldquo;warmup&rdquo; cost: the first few forward/backward passes are slower while PyTorch analyzes the model and generates optimized code.</p>
<p>This differs from traditional compilation because the optimization happens dynamically based on actual input shapes and hardware capabilities, rather than being pre-computed. It&rsquo;s more like a JIT compiler that specializes your model&rsquo;s operations for the exact conditions it encounters during training.</p>
<p>For multi-GPU training, the model is wrapped with <code>DistributedDataParallel</code> (DDP), which enables parallel training across multiple GPUs. The DDP wrapper handles gradient synchronization and ensures that all GPUs work with identical model parameters throughout training.</p>
<figure id="listing11"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">init_model</span>(<span style="color:#91d7e3">self</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Initialize the model&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Initializing model...&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load metadata from tokenized data</span>
</span></span><span style="display:flex;"><span>    meta_path <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>data_dir <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#a6da95">&#34;meta.pkl&#34;</span>
</span></span><span style="display:flex;"><span>    meta_vocab_size <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> meta_path<span style="color:#91d7e3;font-weight:bold">.</span>exists():
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(meta_path, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            meta <span style="color:#91d7e3;font-weight:bold">=</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>load(f)
</span></span><span style="display:flex;"><span>        meta_vocab_size <span style="color:#91d7e3;font-weight:bold">=</span> meta[<span style="color:#a6da95">&#39;vocab_size&#39;</span>]
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Found vocab_size = </span><span style="color:#a6da95">{</span>meta_vocab_size<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create model configuration</span>
</span></span><span style="display:flex;"><span>    model_args <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">dict</span>(
</span></span><span style="display:flex;"><span>        n_layer<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_layer,        <span style="color:#6e738d;font-style:italic"># 12 for SLM</span>
</span></span><span style="display:flex;"><span>        n_head<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_head,          <span style="color:#6e738d;font-style:italic"># 12 for SLM  </span>
</span></span><span style="display:flex;"><span>        n_embd<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>n_embd,          <span style="color:#6e738d;font-style:italic"># 768 for SLM</span>
</span></span><span style="display:flex;"><span>        block_size<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>block_size,  <span style="color:#6e738d;font-style:italic"># 512 for SLM</span>
</span></span><span style="display:flex;"><span>        bias<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>bias,              <span style="color:#6e738d;font-style:italic"># False</span>
</span></span><span style="display:flex;"><span>        vocab_size<span style="color:#91d7e3;font-weight:bold">=</span>meta_vocab_size,  <span style="color:#6e738d;font-style:italic"># From tokenized data</span>
</span></span><span style="display:flex;"><span>        dropout<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>dropout         <span style="color:#6e738d;font-style:italic"># 0.1</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create and configure model</span>
</span></span><span style="display:flex;"><span>    gptconf <span style="color:#91d7e3;font-weight:bold">=</span> SimpleGPTConfig(<span style="color:#91d7e3;font-weight:bold">**</span>model_args)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model <span style="color:#91d7e3;font-weight:bold">=</span> SimpleGPT(gptconf)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model<span style="color:#91d7e3;font-weight:bold">.</span>to(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>device)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Initialize optimizer with proper parameter groups</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>optimizer <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model<span style="color:#91d7e3;font-weight:bold">.</span>configure_optimizers(
</span></span><span style="display:flex;"><span>        weight_decay<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.1</span>,
</span></span><span style="display:flex;"><span>        learning_rate<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>learning_rate,
</span></span><span style="display:flex;"><span>        betas<span style="color:#91d7e3;font-weight:bold">=</span>(<span style="color:#f5a97f">0.9</span>, <span style="color:#f5a97f">0.95</span>),
</span></span><span style="display:flex;"><span>        device_type<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cuda&#39;</span> <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#39;cuda&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>device <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#39;cpu&#39;</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Compile model for performance</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#91d7e3;font-weight:bold">and</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>slm_config<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#34;enable_compile&#34;</span>, <span style="color:#f5a97f">True</span>):
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Compiling model...&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>compile(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model, mode<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;reduce-overhead&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Wrap with DDP for multi-GPU training</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model <span style="color:#91d7e3;font-weight:bold">=</span> DDP(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model, device_ids<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp_local_rank])
</span></span><span style="display:flex;"><span>        param_count <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model<span style="color:#91d7e3;font-weight:bold">.</span>module<span style="color:#91d7e3;font-weight:bold">.</span>get_num_params()
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        param_count <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model<span style="color:#91d7e3;font-weight:bold">.</span>get_num_params()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Model initialized with </span><span style="color:#a6da95">{</span>param_count<span style="color:#a6da95">:</span><span style="color:#a6da95">,</span><span style="color:#a6da95">}</span><span style="color:#a6da95"> parameters&#34;</span>)</span></span></code></pre></div><figcaption>
        <strong>Listing 11: Model Initialization Process</strong>
    </figcaption>
</figure>
<p>While our model is a relatively simple toy example focused on a single domain (historical London text), proper initialization remains important to avoid common training issues. The vocabulary size must match our custom historical tokenizer, the sequence length needs to work with our tokenized data, and the model architecture should be appropriate for the text patterns we&rsquo;re learning.</p>
<p>The initialization process ensures these basic requirements are met before training begins, preventing issues such as vocabulary mismatches or memory allocation problems that could lead to training failures. This careful setup was helpful during our development process, where we ran nearly 100 training experiments. Proper initialization helped us avoid some basic configuration errors and focus on the actual training challenges.</p>
<p>Reproducibility and random seeds: To make runs repeatable on the same hardware, we set a deterministic seed per process using <code>torch.manual_seed(1337 + seed_offset)</code>, where <code>seed_offset</code> is the DDP rank (0 for single‑GPU). This gives consistent data shuffling and initialization across restarts while keeping each process distinct under DDP. Note that some CUDA kernels (and AMP/bf16) can introduce non‑determinism; for strict determinism, you may also configure PyTorch’s deterministic flags at the cost of performance.</p>
<h2 id="5-wandb-integration">5. WandB Integration</h2>
<p><strong>Weights &amp; Biases (WandB)</strong> is an experiment tracking and monitoring platform designed specifically for machine learning projects. Think of it as a &ldquo;black box&rdquo; for your training runs - it automatically records everything that happens during training so you can understand what worked, what didn&rsquo;t, and why.</p>
<p>Training a language model is a long‑running experiment. Without live telemetry, we are flying blind and can’t tell whether learning is stable, whether hardware is saturated, or whether runs are comparable. WandB gives real‑time visibility, remote monitoring, and reproducibility. It records loss, learning rate, and perplexity over time; captures GPU utilization and iteration latency; logs configuration and artifacts; and lets you compare runs side‑by‑side to understand which settings worked.</p>
<p>The system includes WandB integration for experiment tracking and monitoring, with automatic configuration logging, real-time metric tracking (including loss, perplexity, and learning rate), model checkpoint integration, experiment comparison across different training runs, and resource monitoring (GPU utilization and memory usage). This integration helps track and compare different training runs, identify better configurations, and reproduce successful experiments.</p>
<p><strong>Understanding WandB integration:</strong></p>
<p><a href="#listing12" class="listing-ref">Listing 12</a> logs the signals you need to fly by instruments: loss and perplexity trends for learning, the LR schedule to confirm warmup/decay, and hardware utilization and iteration timing for throughput and stability. It’s not just logging - it’s how you compare runs and catch issues early.</p>
<p>This real-time monitoring lets us spot problems early, compare different training approaches, and ensure our historical language model is learning properly over days or weeks.</p>
<figure id="listing12"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Log to WandB - loss first for better mobile UI</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>use_wandb:
</span></span><span style="display:flex;"><span>    wandb<span style="color:#91d7e3;font-weight:bold">.</span>log({
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;train/loss&#34;</span>: lossf,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;train/lr&#34;</span>: lr,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;train/iter&#34;</span>: iter_num,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;train/mfu&#34;</span>: running_mfu <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">100</span> <span style="color:#c6a0f6">if</span> running_mfu <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span> <span style="color:#c6a0f6">else</span> <span style="color:#f5a97f">0</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;train/dt_ms&#34;</span>: dt <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">1000</span>,
</span></span><span style="display:flex;"><span>    })</span></span></code></pre></div><figcaption>
        <strong>Listing 12: WandB Integration and Logging</strong>
    </figcaption>
</figure>
<p>The system logs training loss, learning rate, iteration number, model flops utilization (MFU), and training time per iteration. These metrics provide comprehensive insight into training progress, efficiency, and potential issues.</p>
<p>The most useful dials to watch are training loss (should steadily trend from ~8–10 toward ~2–4), MFU (a proxy for GPU efficiency - single‑digit theoretical targets but mid‑20s achievable with good tuning), the learning‑rate curve (warmup then cosine decay), and iteration time (a practical signal for throughput and stalls).</p>
<p>Both SLM and Regular model training runs complete 60,000 iterations, providing consistent training depth across both model variants. <a href="#fig11" class="figure-ref">Figure 11</a> below shows the complete training experience for our Regular model (354M parameters), demonstrating both the console output and WandB&rsquo;s comprehensive monitoring capabilities.</p>
<figure>
<img src="images/train17-regular.png" alt="Complete training run output showing console logs and WandB summary" title="Complete training run with WandB monitoring" id="fig11">
<figcaption><strong>Figure 11:</strong>Complete training run output showing console logs and WandB monitoring for Regular model (354M parameters)</figcaption>
</figure>
<p>Whilst it might be obvious, the screenshot in <a href="#fig11" class="figure-ref">Figure 11</a> captures the final moments of a successful 60,000-iteration training run, showing both the real-time console output and WandB&rsquo;s comprehensive run summary. In this run, the logs reveal the training progression through the final iterations (59,850 to 60,000), with training loss steadily decreasing from 3.0575 to 2.7063, demonstrating healthy convergence.</p>
<p>The WandB run summary provides the complete picture: a final training loss of 2.70315, a validation loss of 3.61921, and a validation perplexity of 37.31, all indicating successful model training. The system automatically saved the final checkpoint and cleaned up old checkpoints, while WandB captured the entire training journey with detailed metrics tracking. This comprehensive monitoring approach ensures we can both track progress in real time and analyze the full training history afterward.</p>
<h2 id="6-checkpointing-and-model-persistence">6. Checkpointing and Model Persistence</h2>
<p>Checkpointing is one of the most critical aspects of training large language models, especially for historical text, where training can take days or weeks. A robust checkpointing system ensures that training progress is never lost due to hardware failures, power outages, or other interruptions. In this section, we&rsquo;ll explore the comprehensive checkpointing system built for the <strong><code>helloLondon</code></strong> project, covering everything from basic checkpoint creation to advanced resume functionality.</p>
<h3 id="61-checkpoint-system">6.1 Checkpoint System</h3>
<p>The training system implements a practical checkpointing system that preserves all aspects of training state, ensuring that training can be resumed from exactly where it left off. This is particularly important for any complex model, where training can take a long time.</p>
<p>Each checkpoint packages four essentials: the model weights (so learning is preserved), the optimizer state (so momentum and adaptive stats resume cleanly), the current iteration (so schedules pick up in the right place), and the best validation loss to date (so we only promote genuinely better models). Together, these let you stop and restart without losing training dynamics.</p>
<p>The code in <a href="#listing13" class="listing-ref">Listing 13</a> shows how these components are saved when validation loss improves:</p>
<figure id="listing13"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> losses[<span style="color:#a6da95">&#39;val&#39;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;</span> best_val_loss:
</span></span><span style="display:flex;"><span>    best_val_loss <span style="color:#91d7e3;font-weight:bold">=</span> losses[<span style="color:#a6da95">&#39;val&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> iter_num <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span>:
</span></span><span style="display:flex;"><span>        checkpoint <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;model&#39;</span>: raw_model<span style="color:#91d7e3;font-weight:bold">.</span>state_dict(),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;optimizer&#39;</span>: <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>optimizer<span style="color:#91d7e3;font-weight:bold">.</span>state_dict(),
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;iter_num&#39;</span>: iter_num,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#39;best_val_loss&#39;</span>: best_val_loss,
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        checkpoint_path <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>output_dir <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#39;checkpoint-</span><span style="color:#a6da95">{</span>iter_num<span style="color:#a6da95">}</span><span style="color:#a6da95">.pt&#39;</span>
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Saving checkpoint to </span><span style="color:#a6da95">{</span>checkpoint_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        torch<span style="color:#91d7e3;font-weight:bold">.</span>save(checkpoint, checkpoint_path)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Clean up old checkpoints - keep only the last 3</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>cleanup_old_checkpoints()</span></span></code></pre></div><figcaption>
        <strong>Listing 13: Checkpointing and Model Persistence</strong>
    </figcaption>
</figure>
<p>The checkpointing system uses a simple yet effective approach: it saves checkpoints only when the validation loss improves, rather than at every evaluation. This approach serves multiple purposes. First, it ensures we&rsquo;re always keeping the best-performing model, not just the most recent one. Second, it significantly reduces I/O overhead during training, as checkpoint saves can be expensive operations (our 354M parameter model checkpoints are ~1.4GB each). Third, it prevents disk space issues by avoiding the accumulation of suboptimal checkpoints. This selective checkpointing approach can improve overall training time by 5-10% by eliminating redundant disk writes.</p>
<h3 id="62-checkpoint-management-and-cleanup">6.2 Checkpoint Management and Cleanup</h3>
<p>Since our 354M parameter model checkpoints are ~1.4GB each, we need to clean up old checkpoints to avoid running out of disk space. The system automatically keeps only the last 5 checkpoints and deletes older ones (as defined in <code>config.py</code>). The cleanup function in <a href="#listing14" class="listing-ref">Listing 14</a> finds all checkpoint files, sorts them by modification time (newest first), and deletes everything except the most recent 5. Only the master process handles cleanup to avoid race conditions in multi-GPU setups.</p>
<figure id="listing14"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">cleanup_old_checkpoints</span>(<span style="color:#91d7e3">self</span>, keep_last<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">5</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Clean up old checkpoints, keeping only the last N&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>master_process:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span>  <span style="color:#6e738d;font-style:italic"># Only the master process should clean up</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Find all checkpoint files</span>
</span></span><span style="display:flex;"><span>        checkpoint_files <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">list</span>(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>output_dir<span style="color:#91d7e3;font-weight:bold">.</span>glob(<span style="color:#a6da95">&#34;checkpoint-*.pt&#34;</span>))
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(checkpoint_files) <span style="color:#91d7e3;font-weight:bold">&lt;=</span> keep_last:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span>  <span style="color:#6e738d;font-style:italic"># Not enough checkpoints to clean up</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Sort by modification time (newest first)</span>
</span></span><span style="display:flex;"><span>        checkpoint_files<span style="color:#91d7e3;font-weight:bold">.</span>sort(key<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#c6a0f6">lambda</span> x: x<span style="color:#91d7e3;font-weight:bold">.</span>stat()<span style="color:#91d7e3;font-weight:bold">.</span>st_mtime, reverse<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Keep the newest ones, delete the rest</span>
</span></span><span style="display:flex;"><span>        files_to_delete <span style="color:#91d7e3;font-weight:bold">=</span> checkpoint_files[keep_last:]
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> file_path <span style="color:#91d7e3;font-weight:bold">in</span> files_to_delete:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>                file_path<span style="color:#91d7e3;font-weight:bold">.</span>unlink()
</span></span><span style="display:flex;"><span>                logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Deleted old checkpoint: </span><span style="color:#a6da95">{</span>file_path<span style="color:#91d7e3;font-weight:bold">.</span>name<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>                logger<span style="color:#91d7e3;font-weight:bold">.</span>warning(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Failed to delete checkpoint </span><span style="color:#a6da95">{</span>file_path<span style="color:#91d7e3;font-weight:bold">.</span>name<span style="color:#a6da95">}</span><span style="color:#a6da95">: </span><span style="color:#a6da95">{</span>e<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>warning(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Checkpoint cleanup failed: </span><span style="color:#a6da95">{</span>e<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)</span></span></code></pre></div><figcaption>
        <strong>Listing 14: Checkpoint Cleanup and Management</strong>
    </figcaption>
</figure>
<h3 id="63-resume-training-functionality">6.3 Resume Training Functionality</h3>
<p>The ability to resume training from any checkpoint is useful when training gets interrupted. This functionality lets you pick up where you left off, whether the interruption was a few minutes or longer.</p>
<p>The resume functionality loads a checkpoint file and restores the training state: the model weights, optimizer state, current iteration number, and best validation loss. If checkpoint loading fails, the code falls back to starting from scratch.</p>
<p>When loading checkpoints, the code handles two practical considerations. First, the <strong><code>map_location=self.device</code></strong> parameter ensures the checkpoint loads onto the correct device (CPU or GPU), which matters if you&rsquo;re resuming on different hardware or after a restart. Second, for multi-GPU setups using DistributedDataParallel, the model is wrapped in a <code>.module</code> attribute, so the code uses <code>raw_model = self.model.module if self.ddp else self.model</code> to access the actual model underneath.</p>
<figure id="listing15"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">resume_from_checkpoint_file</span>(<span style="color:#91d7e3">self</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Resume training from a checkpoint file&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>resume_from_checkpoint:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    checkpoint_path <span style="color:#91d7e3;font-weight:bold">=</span> Path(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>resume_from_checkpoint)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> checkpoint_path<span style="color:#91d7e3;font-weight:bold">.</span>exists():
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>error(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Checkpoint file not found: </span><span style="color:#a6da95">{</span>checkpoint_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Resuming from checkpoint: </span><span style="color:#a6da95">{</span>checkpoint_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load checkpoint</span>
</span></span><span style="display:flex;"><span>        checkpoint <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>load(checkpoint_path, map_location<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>device)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load model state</span>
</span></span><span style="display:flex;"><span>        raw_model <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model<span style="color:#91d7e3;font-weight:bold">.</span>module <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>ddp <span style="color:#c6a0f6">else</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>model
</span></span><span style="display:flex;"><span>        raw_model<span style="color:#91d7e3;font-weight:bold">.</span>load_state_dict(checkpoint[<span style="color:#a6da95">&#39;model&#39;</span>])
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Model state loaded successfully&#34;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load optimizer state</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>optimizer<span style="color:#91d7e3;font-weight:bold">.</span>load_state_dict(checkpoint[<span style="color:#a6da95">&#39;optimizer&#39;</span>])
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Optimizer state loaded successfully&#34;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Get iteration number and best validation loss</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>start_iter <span style="color:#91d7e3;font-weight:bold">=</span> checkpoint<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;iter_num&#39;</span>, <span style="color:#f5a97f">0</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>best_val_loss <span style="color:#91d7e3;font-weight:bold">=</span> checkpoint<span style="color:#91d7e3;font-weight:bold">.</span>get(<span style="color:#a6da95">&#39;best_val_loss&#39;</span>, <span style="color:#f5a97f">1e9</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Resuming from iteration: </span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>start_iter<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Best validation loss so far: </span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>best_val_loss<span style="color:#a6da95">:</span><span style="color:#a6da95">.4f</span><span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">Exception</span> <span style="color:#c6a0f6">as</span> e:
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>error(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Failed to load checkpoint: </span><span style="color:#a6da95">{</span>e<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Starting training from scratch...&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>start_iter <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>best_val_loss <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1e9</span></span></span></code></pre></div><figcaption>
        <strong>Listing 15: Resume Training from Checkpoint</strong>
    </figcaption>
</figure>
<p>The function loads the model weights, optimizer state, iteration number, and best validation loss from the checkpoint file, then continues training from where it left off. If the checkpoint file doesn&rsquo;t exist or can&rsquo;t be loaded, it logs an error and starts training from scratch. Since our Regular model takes 28-32 hours to train, resuming from a checkpoint saves significant time when training is interrupted by power outages, crashes, or manual stops.</p>
<h2 id="7-training-launch-and-management">7. Training Launch and Management</h2>
<h3 id="71-multi-gpu-training-with-torchrun">7.1 Multi-GPU Training with torchrun</h3>
<p>For a single GPU, you can run the training script directly — a single Python process will use that device. To use multiple GPUs, launch training with <code>torchrun</code>, which spawns one worker process per GPU and lets the code initialize <code>DistributedDataParallel</code> (DDP). This enables larger effective batch sizes and faster wall‑clock training while keeping weights synchronized across devices; set <code>--nproc_per_node</code> to the number of GPUs you want to use (for example, <code>--nproc_per_node=2</code>).</p>
<p><code>torchrun</code> is PyTorch&rsquo;s recommended launcher for distributed training: it initializes the distributed backend and sets environment variables (<code>RANK</code>, <code>LOCAL_RANK</code>, <code>WORLD_SIZE</code>) to keep workers in sync. With <code>torchrun --nproc_per_node=N</code> (where <code>N</code> is the number of GPUs to use — it can be less than the total GPUs available), batches are sharded across the chosen GPUs and gradients are synchronized after each backward pass, which often gives near‑linear speedups on a small multi‑GPU node.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Single GPU (even with multiple available)</span>
</span></span><span style="display:flex;"><span>python train_model_slm.py
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Multi-GPU with near-linear speedup</span>
</span></span><span style="display:flex;"><span>torchrun --nproc_per_node<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span> train_model_slm.py</span></span></code></pre></div>
<p>The training script handles DDP (<code>DistributedDataParallel</code>) via <code>train_model_slm.py</code> for gradient sync and batch distribution across GPUs. <a href="#fig12" class="figure-ref">Figure 12</a> below shows an example where we have dual GPUs and both are being used.</p>
<figure>
<img src="images/train16-4.png" alt="Multiple GPU used for training Screenshot" title="Multiple GPU used for training" id="fig12">
<figcaption><strong>Figure 12:</strong> Multiple GPU used for training</figcaption>
</figure>
<p>Note that if you run <code>python train_model_slm.py</code> on a multi‑GPU machine, only one GPU is used; the others remain idle. To use more than one GPU, we must use <code>torchrun</code>.</p>
<h3 id="72-training-monitoring">7.2 Training Monitoring</h3>
<p>Training is monitored locally via structured console logs and remotely via WandB. The snippet in <a href="#listing16" class="listing-ref">Listing 16</a> records loss, learning rate, timing, and MFU at a configurable interval and, when enabled, streams the same metrics to WandB for side‑by‑side run comparison.</p>
<figure id="listing16"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Timing and logging</span>
</span></span><span style="display:flex;"><span>t1 <span style="color:#91d7e3;font-weight:bold">=</span> time<span style="color:#91d7e3;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>dt <span style="color:#91d7e3;font-weight:bold">=</span> t1 <span style="color:#91d7e3;font-weight:bold">-</span> t0
</span></span><span style="display:flex;"><span>t0 <span style="color:#91d7e3;font-weight:bold">=</span> t1
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> iter_num <span style="color:#91d7e3;font-weight:bold">%</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>log_interval <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">and</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>master_process:
</span></span><span style="display:flex;"><span>    lossf <span style="color:#91d7e3;font-weight:bold">=</span> loss<span style="color:#91d7e3;font-weight:bold">.</span>item()
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> local_iter_num <span style="color:#91d7e3;font-weight:bold">&gt;=</span> <span style="color:#f5a97f">5</span>:
</span></span><span style="display:flex;"><span>        mfu <span style="color:#91d7e3;font-weight:bold">=</span> raw_model<span style="color:#91d7e3;font-weight:bold">.</span>estimate_mfu(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>batch_size, dt)
</span></span><span style="display:flex;"><span>        running_mfu <span style="color:#91d7e3;font-weight:bold">=</span> mfu <span style="color:#c6a0f6">if</span> running_mfu <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1.0</span> <span style="color:#c6a0f6">else</span> <span style="color:#f5a97f">0.9</span><span style="color:#91d7e3;font-weight:bold">*</span>running_mfu <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">0.1</span><span style="color:#91d7e3;font-weight:bold">*</span>mfu
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;iter </span><span style="color:#a6da95">{</span>iter_num<span style="color:#a6da95">}</span><span style="color:#a6da95">: loss </span><span style="color:#a6da95">{</span>lossf<span style="color:#a6da95">:</span><span style="color:#a6da95">.4f</span><span style="color:#a6da95">}</span><span style="color:#a6da95">, time </span><span style="color:#a6da95">{</span>dt<span style="color:#91d7e3;font-weight:bold">*</span><span style="color:#f5a97f">1000</span><span style="color:#a6da95">:</span><span style="color:#a6da95">.2f</span><span style="color:#a6da95">}</span><span style="color:#a6da95">ms, mfu </span><span style="color:#a6da95">{</span>running_mfu<span style="color:#91d7e3;font-weight:bold">*</span><span style="color:#f5a97f">100</span><span style="color:#a6da95">:</span><span style="color:#a6da95">.2f</span><span style="color:#a6da95">}</span><span style="color:#a6da95">%&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Log to WandB - loss first for better mobile UI</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>use_wandb:
</span></span><span style="display:flex;"><span>        wandb<span style="color:#91d7e3;font-weight:bold">.</span>log({
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#34;train/loss&#34;</span>: lossf,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#34;train/lr&#34;</span>: lr,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#34;train/iter&#34;</span>: iter_num,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#34;train/mfu&#34;</span>: running_mfu <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">100</span> <span style="color:#c6a0f6">if</span> running_mfu <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">0</span> <span style="color:#c6a0f6">else</span> <span style="color:#f5a97f">0</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6da95">&#34;train/dt_ms&#34;</span>: dt <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">1000</span>,
</span></span><span style="display:flex;"><span>        })</span></span></code></pre></div><figcaption>
        <strong>Listing 16: Training Monitoring and Logging</strong>
    </figcaption>
</figure>
<p>Together, console logs and WandB provide real‑time visibility and reproducible experiment tracking; <a href="#fig13" class="figure-ref">Figure 13</a> below shows an example of the console logs; see Section 5 for setup and dashboards.</p>
<figure>
<img src="images/train16-7.png" alt="Console training logs showing iteration, loss, step time, and MFU with checkpoint saves" title="Console training logs: iteration, loss, step time, MFU, and checkpoint saves" id="fig13">
<figcaption><strong>Figure 13:</strong> Console training logs: iteration, loss, step time, MFU, and checkpoint saves</figcaption>
</figure>
<h2 id="8-model-file-formats-and-conversion">8. Model File Formats and Conversion</h2>
<p>Training produces PyTorch checkpoint files (<code>.pt</code>) that contain model weights, optimizer state, and training metadata — everything needed to resume training. These checkpoints are covered in detail in <a
	
		href = "#6-checkpointing-and-model-persistence"
	

	

	>
	
	<span>
		Section 6
	</span>
</a>.</p>
<p>For sharing models and standard deployment workflows, we convert PyTorch checkpoints into the Hugging Face repository format. This conversion creates a portable, standardized model package that can be loaded with standard Hugging Face APIs.</p>
<h3 id="81-converting-pytorch-checkpoints-to-hugging-face-format">8.1 Converting PyTorch Checkpoints to Hugging Face Format</h3>
<p>The Hugging Face repository format is a standardized directory structure containing:</p>
<ul>
<li><strong><code>config.json</code></strong>: Architecture definition (layers, heads, embedding dimensions, vocabulary size, sequence length). Allows <code>AutoModelForCausalLM</code> to reconstruct the model architecture without custom code.</li>
<li><strong><code>model.safetensors</code></strong>: Model weights in SafeTensors format (memory-mapped, secure loading). Contains only model parameters, no optimizer state — suitable for inference workloads.</li>
<li><strong><code>generation_config.json</code></strong>: Default text generation parameters (max_new_tokens, temperature, top_p, repetition_penalty). Can be overridden at runtime.</li>
<li><strong>Tokenizer files</strong> (<code>tokenizer.json</code>, <code>vocab.json</code>, <code>merges.txt</code>, <code>special_tokens_map.json</code>, <code>tokenizer_config.json</code>): Serialized tokenizer with vocabulary, merge rules, normalization, and special tokens matching the training configuration.</li>
</ul>
<p>The conversion code in <a href="#listing17" class="listing-ref">Listing 17</a> loads a PyTorch checkpoint, extracts model weights and config, handles <code>torch.compile</code> naming prefixes if present, and saves the model and tokenizer in Hugging Face format.</p>
<figure id="listing17"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> GPT2LMHeadModel
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">convert_pytorch_to_huggingface</span>(pytorch_checkpoint_path, output_dir, tokenizer):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Convert PyTorch checkpoint to Hugging Face format&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load PyTorch checkpoint</span>
</span></span><span style="display:flex;"><span>    checkpoint <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>load(pytorch_checkpoint_path, map_location<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;cpu&#39;</span>)
</span></span><span style="display:flex;"><span>    model_state <span style="color:#91d7e3;font-weight:bold">=</span> checkpoint[<span style="color:#a6da95">&#39;model&#39;</span>]
</span></span><span style="display:flex;"><span>    config <span style="color:#91d7e3;font-weight:bold">=</span> checkpoint[<span style="color:#a6da95">&#39;config&#39;</span>]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Handle torch.compile prefixes</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">any</span>(key<span style="color:#91d7e3;font-weight:bold">.</span>startswith(<span style="color:#a6da95">&#39;_orig_mod.&#39;</span>) <span style="color:#c6a0f6">for</span> key <span style="color:#91d7e3;font-weight:bold">in</span> model_state<span style="color:#91d7e3;font-weight:bold">.</span>keys()):
</span></span><span style="display:flex;"><span>        clean_state <span style="color:#91d7e3;font-weight:bold">=</span> {}
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> key, value <span style="color:#91d7e3;font-weight:bold">in</span> model_state<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>            clean_state[key[<span style="color:#f5a97f">10</span>:]] <span style="color:#91d7e3;font-weight:bold">=</span> value <span style="color:#c6a0f6">if</span> key<span style="color:#91d7e3;font-weight:bold">.</span>startswith(<span style="color:#a6da95">&#39;_orig_mod.&#39;</span>) <span style="color:#c6a0f6">else</span> value
</span></span><span style="display:flex;"><span>        model_state <span style="color:#91d7e3;font-weight:bold">=</span> clean_state
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Convert to Hugging Face format</span>
</span></span><span style="display:flex;"><span>    hf_model <span style="color:#91d7e3;font-weight:bold">=</span> GPT2LMHeadModel(config)
</span></span><span style="display:flex;"><span>    hf_model<span style="color:#91d7e3;font-weight:bold">.</span>load_state_dict(model_state)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Save in Hugging Face format</span>
</span></span><span style="display:flex;"><span>    hf_model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(output_dir)
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(output_dir)</span></span></code></pre></div><figcaption>
        <strong>Listing 17: PyTorch → Hugging Face Conversion (essentials)</strong>
    </figcaption>
</figure>
<p>The conversion handles a few practical details. If the model was compiled with <code>torch.compile</code>, parameter names are prefixed with <code>_orig_mod.</code>, which the code strips to match Hugging Face module names. <code>GPT2LMHeadModel(config)</code> instantiates a GPT-2-style architecture that matches the checkpoint&rsquo;s layer structure, and <code>load_state_dict()</code> loads the weights with automatic shape validation. The <code>save_pretrained()</code> method writes all required files to disk.</p>
<p>File sizes: PyTorch checkpoints are ~450MB (SLM) and ~1.4GB (Regular model); the Hugging Face format reduces this slightly by excluding the optimizer state. The tokenizer adds ~15MB to the repository.</p>
<h2 id="9-inference-options">9. Inference Options</h2>
<p>Inference can run directly from PyTorch checkpoints or from Hugging Face models. PyTorch checkpoints are convenient during development since you can test any training checkpoint without conversion. Hugging Face models use standard <code>from_pretrained()</code> APIs and are better suited for sharing and deployment workflows.</p>
<figure id="listing18"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Option 1: PyTorch checkpoint inference (direct from training)</span>
</span></span><span style="display:flex;"><span>python <span style="color:#f5a97f">06</span>_inference<span style="color:#91d7e3;font-weight:bold">/</span>inference_pytorch<span style="color:#91d7e3;font-weight:bold">.</span>py \
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">--</span>checkpoint <span style="color:#f5a97f">09</span>_models<span style="color:#91d7e3;font-weight:bold">/</span>checkpoints<span style="color:#91d7e3;font-weight:bold">/</span>slm<span style="color:#91d7e3;font-weight:bold">/</span>checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">60001.</span>pt \
</span></span><span style="display:flex;"><span>  <span style="color:#91d7e3;font-weight:bold">--</span>prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Option 2: Hugging Face model inference (published models)</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoTokenizer, AutoModelForCausalLM
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>)
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(<span style="color:#a6da95">&#34;In the year 1834, I walked through the streets...&#34;</span>, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)
</span></span><span style="display:flex;"><span>outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(inputs[<span style="color:#a6da95">&#39;input_ids&#39;</span>], max_new_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">50</span>, do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>result <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)</span></span></code></pre></div><figcaption>
        <strong>Listing 18: Inference Options</strong>
    </figcaption>
</figure>
<p>Both methods load in seconds and generate ~50–100 tokens/sec on typical consumer GPUs (2–4GB VRAM for SLM, 6–8GB for the Regular model). Use PyTorch checkpoints for development and training comparisons; use Hugging Face models for production deployment and sharing. For interactive testing with published models, see <a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 1
	</span>
</a>.</p>
<h2 id="10-summary">10. Summary</h2>
<p>We built a training‑ready GPT pipeline for historical text, end‑to‑end: a clear decoder‑only architecture, pragmatic GPU/precision tuning, DDP for scale, resilient checkpointing/resume, WandB tracking, and clean hand‑off of artifacts (PyTorch checkpoints → Hugging Face export).</p>
<p>Outcome: two working models on the Part 2 corpus - 117M (SLM) and 354M (Regular) - ready for inference now and for evaluation/deployment in Part 4.</p>
<blockquote>
<p><strong>🔗 GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete training infrastructure (<code>04_training/</code>), model architecture (<code>config.py</code>), and GPU configuration (<code>08_documentation/GPU_TUNING.md</code>)</p></blockquote>
<blockquote>
<p><strong>🧱 Series Posts</strong>: <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1 – Using the Published Historical Models
	</span>
</a> | <a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2 – Data Collection &amp; Custom Tokenizer
	</span>
</a> | Part 3 (this post) | <a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4 – Evaluation &amp; Deployment
	</span>
</a></p></blockquote>
<blockquote>
<p><strong>🤗 Published Models</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SLM Model
	</span>
</a> | <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Regular Model
	</span>
</a> - Ready-to-use historical language models on HuggingFace</p></blockquote>
<blockquote>
<p><strong>📚 Book Reference</strong>: <a
	
		href = "https://a.co/d/ffzkJ7T"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts.</p></blockquote>
<hr>
<p><strong>Ready for Part 4?</strong> Part 4 covers model evaluation, testing, and deployment strategies that turn your trained models into working systems ready for real-world use.</p>
<h2 id="references">References</h2>
<div class="references" style="font-size:0.85em">
<ol>
<li>Vaswani et al. (2017) – Attention Is All You Need: <a
	
		href = "https://arxiv.org/abs/1706.03762"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1706.03762
	</span>
</a></li>
<li>Radford et al. (2019) – Language Models are Unsupervised Multitask Learners: <a
	
		href = "https://www.semanticscholar.org/paper/Language-Models-are-Unsupervised-Multitask-Learners-Radford-Wu/9405cc0d6169988371b2755e573cc28650d14dfe"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://www.semanticscholar.org/paper/Language-Models-are-Unsupervised-Multitask-Learners-Radford-Wu/9405cc0d6169988371b2755e573cc28650d14dfe
	</span>
</a></li>
<li>Brown et al. (2020) – Language Models are Few-Shot Learners: <a
	
		href = "https://arxiv.org/abs/2005.14165"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2005.14165
	</span>
</a></li>
<li>Kaplan et al. (2020) – Scaling Laws for Neural Language Models: <a
	
		href = "https://arxiv.org/abs/2001.08361"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2001.08361
	</span>
</a></li>
<li>Hoffmann et al. (2022) – Training Compute-Optimal LLMs (Chinchilla): <a
	
		href = "https://arxiv.org/abs/2203.15556"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2203.15556
	</span>
</a></li>
<li>Chowdhery et al. (2022) – PaLM: Scaling Language Modeling with Pathways: <a
	
		href = "https://arxiv.org/abs/2204.02311"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2204.02311
	</span>
</a></li>
<li>Clark et al. (2019) – What Does BERT Look At?: <a
	
		href = "https://arxiv.org/abs/1906.04341"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1906.04341
	</span>
</a></li>
<li>Voita et al. (2019) – Analyzing Multi‑Head Self‑Attention: <a
	
		href = "https://arxiv.org/abs/1905.09418"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1905.09418
	</span>
</a></li>
<li>Dao et al. (2022) – FlashAttention: <a
	
		href = "https://arxiv.org/abs/2205.14135"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2205.14135
	</span>
</a></li>
<li>Micikevicius et al. (2018) – Mixed Precision Training: <a
	
		href = "https://arxiv.org/abs/1710.03740"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1710.03740
	</span>
</a></li>
<li>Rajbhandari et al. (2020) – ZeRO: <a
	
		href = "https://arxiv.org/abs/1910.02054"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1910.02054
	</span>
</a></li>
<li>Paszke et al. (2019) – PyTorch: <a
	
		href = "https://arxiv.org/abs/1912.01703"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1912.01703
	</span>
</a></li>
<li>Kingma &amp; Ba (2014) – Adam: A Method for Stochastic Optimization: <a
	
		href = "https://arxiv.org/abs/1412.6980"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1412.6980
	</span>
</a></li>
<li>Loshchilov &amp; Hutter (2017) – AdamW Decoupled Weight Decay Regularization : <a
	
		href = "https://arxiv.org/abs/1711.05101"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1711.05101
	</span>
</a></li>
<li>Smith &amp; Topin (2017) – Super‑Convergence: Very Fast Training of Neural Networks Using Large Learning Rates: <a
	
		href = "https://arxiv.org/abs/1708.07120"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1708.07120
	</span>
</a></li>
<li>Goyal et al. (2017) – Accurate, Large Minibatch SGD: <a
	
		href = "https://arxiv.org/abs/1706.02677"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1706.02677
	</span>
</a></li>
<li>Sergeev &amp; Del Balso (2018) – Horovod: <a
	
		href = "https://arxiv.org/abs/1802.05799"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1802.05799
	</span>
</a></li>
<li>Pope et al. (2022) – Efficiently Scaling Transformer Inference: <a
	
		href = "https://arxiv.org/abs/2211.05102"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2211.05102
	</span>
</a></li>
<li>Jawahar et al. (2019) – What does BERT learn about the structure of language?: <a
	
		href = "https://aclanthology.org/P19-1356.pdf"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/P19-1356.pdf
	</span>
</a></li>
<li>Mikolov et al. (2013) – Word2vec: <a
	
		href = "https://arxiv.org/abs/1301.3781"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1301.3781
	</span>
</a></li>
<li>Pennington et al. (2014) – GloVe:  <a
	
		href = "https://aclanthology.org/D14-1162/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/D14-1162/
	</span>
</a></li>
<li>Devlin et al. (2018) – BERT <a
	
		href = "https://arxiv.org/abs/1810.04805"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1810.04805
	</span>
</a></li>
<li>Press &amp; Wolf (2017) – Using the Output Embedding to Improve Language Models: <a
	
		href = "https://arxiv.org/abs/1608.05859"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1608.05859
	</span>
</a></li>
<li>Inan et al. (2016) – Tying Word Vectors and Word Classifiers: <a
	
		href = "https://arxiv.org/abs/1611.01462"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1611.01462
	</span>
</a></li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>🏛️Building LLMs from Scratch - Part 2: Data Collection &amp; Custom Tokenizers</title>
      <link>/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/</link>
      <pubDate>Sun, 12 Oct 2025 00:00:00 +0000</pubDate>
      <guid>/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/</guid>
      <description>Deep dive into data collection, cleaning pipelines, and custom tokenizer development for authentic historical text processing. Complete 4-part series with working code.</description>
      <content:encoded><![CDATA[<p><strong>TL;DR</strong></p>
<p>In this second part of our 4-part series on building language models from scratch, I explore the two foundational areas of LLM development: data collection and custom tokenizer creation. <a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 1 - Building LLM from Scratch
	</span>
</a> covered using the published model; here, we build the complete pipeline from raw historical documents to a custom tokenizer that understands archaic English, London geography, and period-specific terminology.</p>
<p>The challenge with historical LLMs isn&rsquo;t just having enough data—it&rsquo;s having the <em>right</em> data processed to preserve linguistic nuances across different historical periods. This post demonstrates how to transform over 218 historical sources into a corpus of more than 500 million characters using a specialized tokenizer for authentic historical text generation.</p>
<blockquote>
<p><strong>⚠️ Educational Purpose</strong>: This is a learning project designed to teach LLM development concepts. For production-scale LLMs, you&rsquo;ll need significantly larger datasets, more sophisticated infrastructure, and additional considerations that are not covered in this post.</p></blockquote>
<h2 id="1-the-historical-language-modeling-challenge">1. The Historical Language Modeling Challenge</h2>
<p>Building a language model for historical text presents unique challenges. Historical English from 1500 to 1850 contains linguistic patterns, vocabulary, and cultural references that modern tokenizers have never encountered. Standard tokenizers like <a
	
		href = "https://github.com/openai/tiktoken"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		TikToken
	</span>
</a> fragment archaic words like &ldquo;quoth&rdquo; and &ldquo;hast&rdquo; into multiple subword tokens, destroying semantic meaning crucial for historical text generation.</p>
<p>A simple phrase like <strong><code>Quoth the alderman, 'Tis a fair day at Newgate</code></strong> becomes dozens of meaningless fragments, losing both historical context and linguistic coherence. This fragmentation is why we built a custom tokenizer trained specifically on historical English patterns, ensuring the model can generate coherent, historically accurate text.</p>
<p>As a reminder, both the SLM (117M parameters) and Regular Model (354M parameters) utilize the same training code and infrastructure, including GPU optimization, checkpointing, and WandB integration. The only difference lies in the model architecture parameters, which are specified in <code>config.py</code>.</p>
<blockquote>
<p><strong>🔗 GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete source code for data collection (<code>02_data_collection/</code>) and tokenizer training (<code>03_tokenizer/</code>). We will see the relevant code snippets in this post show key concepts—see the full implementation in the repository.</p></blockquote>
<blockquote>
<p><strong>🧱 Series Posts</strong>: <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1 – Using the Published Historical Models
	</span>
</a> | Part 2 (this post) | <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3 – Training Architecture &amp; GPU Optimization
	</span>
</a> | <a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4 – Evaluation &amp; Deployment
	</span>
</a></p></blockquote>
<p><strong>What will you learn?</strong></p>
<p>This project provides hands-on experience with real-world LLM development challenges, including data collection from over 218 historical sources, cleaning OCR errors and encoding issues, and developing custom tokenizers for historical text. Unlike theoretical tutorials, you receive complete, runnable code that demonstrates actual trade-offs and decisions—such as choosing BPE over WordPiece or handling different file formats—that you&rsquo;d encounter in any serious LLM project.</p>
<p>While operating at a learning scale, the principles taught here directly apply to larger systems. Data collection patterns, cleaning strategies, and tokenizer design principles scale from our 500M character corpus to the 500B+ character datasets used in production models.</p>
<h2 id="11-high-level-process-overview">1.1 High-Level Process Overview</h2>
<p>The complete pipeline transforms raw historical documents into a working language model through five key stages:</p>
<ol>
<li><strong>Data Collection</strong>: 218+ historical sources (1500-1850), including literature, newspapers, court records, and personal diaries</li>
<li><strong>Cleaning Pipeline</strong>: Handles multiple file formats (PDF, HTML, XML, TXT) while removing OCR artifacts and preserving authentic historical language</li>
<li><strong>Quality Validation</strong>: Removes duplicates, filters non-English content, and ensures only meaningful historical text reaches the final corpus</li>
<li><strong>Custom Tokenizer Training</strong>: BPE-based tokenizer with ~150 special tokens capturing archaic pronouns, historical landmarks, and period-specific terminology</li>
<li><strong>Model Training</strong>: Two language models (SLM 117M and Regular 354M parameters) trained on the same historical corpus</li>
</ol>
<p>The result is a system capable of generating authentic historical text that captures the linguistic patterns and cultural context of 1500-1850 English. <a href="#fig1" class="figure-ref">Figure 1</a> illustrates this complete pipeline:</p>
<figure class="align-center " id="fig1">
    <pre class="mermaid">graph TD
    A[📚 218+ Historical Sources&lt;br/&gt;1500-1850] --&gt; B[🔍 Data Collection&lt;br/&gt;Download and Filter]
    B --&gt; C[🧹 5-Phase Cleaning Pipeline&lt;br/&gt;Format-Specific Processing]
    C --&gt; D[📊 Quality Validation&lt;br/&gt;Duplicate and Language Detection]
    D --&gt; E[📝 500M+ Character Corpus&lt;br/&gt;Clean Historical Text]
    E --&gt; F[🔤 Custom Tokenizer Training&lt;br/&gt;BPE with 150+ Special Tokens]
    F --&gt; G[🤖 Language Model Training&lt;br/&gt;SLM 117M + Regular 354M]
    
    style A fill:#e1f5fe
    style E fill:#f3e5f5
    style F fill:#fff3e0</pre>
    <figcaption>Figure 1: Complete Historical Text Processing Pipeline</figcaption>
</figure>
<h2 id="2-data-collection-the-foundation-of-historical-language-modeling">2. Data Collection: The Foundation of Historical Language Modeling</h2>
<p>Let us dig deeper into steps 1-4: data collection, cleaning, validation, and corpus creation. The data collection system processes over 218 sources spanning the years 1500-1850 to create a corpus of over 500 million characters of authentic historical English text. But collecting historical data isn&rsquo;t just about downloading files - it&rsquo;s about handling the sheer variety of formats and quality levels that historical documents present.</p>
<p>Historical documents come in all shapes and sizes - scanned books with OCR errors, HTML pages with messy markup, XML archives with rich metadata, and plain text files with inconsistent encoding. This is especially true for the earlier periods, when the quality of the documents can vary significantly, and most modern techniques for processing them struggle to cope. This data diversity requires a cleaning pipeline that transforms raw historical documents into training data while preserving the authentic language patterns of 1500-1850 English.</p>
<h3 id="21-system-architecture-processing-218-historical-sources">2.1 System Architecture: Processing 218+ Historical Sources</h3>
<p>The data collection system employs a modular architecture, with <strong><code>historical_data_collector.py</code></strong> serving as the primary orchestration engine, coordinating with a <strong><code>data_sources.json</code></strong> configuration file that contains metadata for over 218 historical sources. This enables easy management and updates without code changes.</p>
<p>Supporting scripts include <strong><code>add_data_source.py</code></strong> for interactive source addition with built-in validation, and <strong><code>generate_report.py</code></strong> for comprehensive reporting and analysis across multiple output formats.</p>
<p>The <strong><code>data_sources.json</code></strong> file contains metadata for each source, including time periods, formats, licensing, and processing priorities. Each entry includes:</p>
<ul>
<li><strong><code>time_period</code></strong> (e.g., [1690, 1800] for London Lives)</li>
<li><strong><code>format</code></strong> (XML, HTML, PDF)</li>
<li><strong><code>priority</code></strong> (high/medium/low)</li>
<li><strong><code>search_terms</code></strong> for collection guidance</li>
</ul>
<p>Our data sources span multiple categories, each contributing unique perspectives to the historical corpus.</p>
<ul>
<li>
<p><strong>Project Gutenberg:</strong> This provides foundational literature with 8+ carefully selected texts, using relaxed quality criteria that accept texts with as low as 40% meaningful words to capture the full spectrum of historical writing styles.</p>
</li>
<li>
<p><strong>Historical Archives:</strong> Historical Archives like <em>London Lives</em> (240,000 pages of personal records) and <em>Old Bailey</em> (197,000+ trial transcripts) offer rich historical content and were initially enabled in our data collection.</p>
<ul>
<li>Note: I was using the aggressive cleaning earlier (enabled using the <code>aggressive_cleaning</code> flag designed to remove structured legal data and semantic markup), and discovered that it was too aggressive and caused generation quality issues. After initial training runs revealed repetitive and incoherent text patterns, I turned off these sources. Enabling this back might be an exercise for you to try.</li>
</ul>
</li>
<li>
<p><strong>Archive.org:</strong> Archive.org has an API access that can be used for file filtering, and this makes it relatively straightforward.</p>
</li>
<li>
<p><strong>The National Archives (TNA):</strong> TNA records contribute government correspondence and official documents that provide the institutional context for historical events.</p>
</li>
<li>
<p><strong>British History Online:</strong> Finally, these supplements our collection with historical surveys and period documents that offer scholarly perspectives on the time periods we&rsquo;re modeling.</p>
</li>
</ul>
<p>However, each source type presents unique technical challenges that require specialized processing approaches. One example is Project Gutenberg, which contains files with standardized headers and footers that must be removed. (As a side note, I really appreciate the effort that has gone into this to make this formatting consistent, which makes the process of this relatively straightforward.)</p>
<p>On the other hand, PDF files often suffer from OCR errors, especially for older documents that contain corrupted historical language, requiring sophisticated text correction algorithms to restore proper spelling and grammar from scanned documents. The figure below shows one example of how older documents look. This example is &ldquo;The abridgment of the charter of the city of London&rdquo; from 1680.</p>
<figure>
<img src="images/charter-city-of-london.png" alt="The abridgment of the charter of the city of London" title="The abridgment of the charter of the city of London">
<figcaption><strong>Figure 2:</strong> The abridgment of the charter of the city of London (1680) - showing faded text and ink blots typical of historical documents</figcaption>
</figure>
<p>As you can see, the text is faded, has ink blots, and the font style is very different from modern text. OCR software often misinterprets characters in such documents, resulting in numerous errors, as illustrated in the image below. These OCR artifacts can severely degrade the quality of our training data if not properly addressed.</p>
<figure>
<img src="images/charter-city-of-london-ocr.png" alt="OCR - Charter of the city of London" title="OCR - Charter of the city of London">
<figcaption><strong>Figure 3:</strong> OCR errors in the charter document - showing how optical character recognition struggles with historical fonts and document quality</figcaption>
</figure>
<p><strong>HTML files</strong> from sources like Archive.org contain navigation elements, advertisements, and modern web markup that contaminate the historical corpus, demanding careful content extraction that preserves only the meaningful historical text.</p>
<p><strong>XML archives</strong> like London Lives and Old Bailey require specialized parsing to extract meaningful text while preserving semantic markup that provides context about speakers, dates, and document structure - a delicate balance between removing technical artifacts and maintaining historical authenticity.</p>
<p><strong>Government records</strong> from TNA often contain bureaucratic formatting, form fields, and institutional language that need careful filtering to extract the human stories and historical narratives.</p>
<p><strong>British History Online</strong> documents present challenges with academic formatting, footnotes, and scholarly apparatus that must be processed to maintain readability while preserving the scholarly context that makes them valuable for historical language modeling.</p>
<h3 id="22-cleaning-pipeline">2.2 Cleaning Pipeline</h3>
<p>I implement a 5-stage cleaning pipeline that helps transform the raw historical documents into training-ready text. Each phase addresses specific challenges that would otherwise contaminate our language model training.</p>
<h4 id="221-stage-1-file-discovery--initial-filtering">2.2.1 Stage 1: File Discovery &amp; Initial Filtering</h4>
<p>Historical archives often contain files in various formats, which may be missing proper file extensions or have non-standard naming conventions. Many files contain non-English content that would contaminate our English historical corpus. Additionally, many sources employ their own templates and standards for this purpose. To resolve this, we first implement a simple file detection and naming cleanup, as shown in <a href="#listing1" class="listing-ref">Listing 1</a>. The code itself is simple and self-explanatory.</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">detect_file_type</span>(file_path: <span style="color:#91d7e3">str</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Detect file type based on extension and content analysis&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Extension-based detection</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith((<span style="color:#a6da95">&#39;.txt&#39;</span>, <span style="color:#a6da95">&#39;.txt.utf-8&#39;</span>, <span style="color:#a6da95">&#39;_txt.utf-8&#39;</span>)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;text&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith((<span style="color:#a6da95">&#39;.pdf&#39;</span>,)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;pdf&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith((<span style="color:#a6da95">&#39;.html&#39;</span>, <span style="color:#a6da95">&#39;.htm&#39;</span>)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;html&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith((<span style="color:#a6da95">&#39;.xml&#39;</span>,)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;xml&#39;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Content-based detection for files without extensions</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>        content <span style="color:#91d7e3;font-weight:bold">=</span> f<span style="color:#91d7e3;font-weight:bold">.</span>read(<span style="color:#f5a97f">1024</span>)  <span style="color:#6e738d;font-style:italic"># Read first 1KB</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#ed8796">b</span><span style="color:#a6da95">&#39;&lt;html&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> content<span style="color:#91d7e3;font-weight:bold">.</span>lower() <span style="color:#91d7e3;font-weight:bold">or</span> <span style="color:#ed8796">b</span><span style="color:#a6da95">&#39;&lt;!doctype&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> content<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;html&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> <span style="color:#ed8796">b</span><span style="color:#a6da95">&#39;&lt;?xml&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> content<span style="color:#91d7e3;font-weight:bold">.</span>lower():
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;xml&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> content<span style="color:#91d7e3;font-weight:bold">.</span>isascii() <span style="color:#91d7e3;font-weight:bold">and</span> <span style="color:#ed8796">b</span><span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\x00</span><span style="color:#a6da95">&#39;</span> <span style="color:#91d7e3;font-weight:bold">not</span> <span style="color:#91d7e3;font-weight:bold">in</span> content:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;text&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;binary&#39;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 1: File Type Detection Function</strong>
    </figcaption>
</figure>
<p>When we run this locally, we will see the flow as outlined below, which illustrates how the detection works. This, of course, can be made more robust for non-English characters, but for now, we reject these.</p>
<p><strong>File Type Detection Flow:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>📁 Raw Files (218+ sources)
</span></span><span style="display:flex;"><span>    ↓
</span></span><span style="display:flex;"><span>🔍 File Type Detection
</span></span><span style="display:flex;"><span>    ├── .txt, .txt.utf-8, _txt.utf-8 → Text Processing
</span></span><span style="display:flex;"><span>    ├── .pdf → PDF Processing  
</span></span><span style="display:flex;"><span>    ├── .html, .htm → HTML Processing
</span></span><span style="display:flex;"><span>    ├── .xml → XML Processing (Old Bailey, London Lives)
</span></span><span style="display:flex;"><span>    └── No Extension → Content Detection
</span></span><span style="display:flex;"><span>        ├── HTML-like content → HTML Processing
</span></span><span style="display:flex;"><span>        ├── Text-like content → Text Processing
</span></span><span style="display:flex;"><span>        └── Binary/Unknown → REJECTED
</span></span><span style="display:flex;"><span>    ↓
</span></span><span style="display:flex;"><span>🚫 Filename Language Check
</span></span><span style="display:flex;"><span>    ├── Non-English characters → REJECTED (logged)
</span></span><span style="display:flex;"><span>    └── English/Latin → Continue</span></span></code></pre></div>
<p>Historical archives often lack standardized file extensions and contain content in languages other than English. Our two-stage detection ensures we capture valuable historical documents while filtering out irrelevant files, preventing both data loss and processing waste.</p>
<h4 id="222-stage-2-format-specific-content-extraction">2.2.2 Stage 2: Format-Specific Content Extraction</h4>
<p>Each file format requires specialized processing due to its unique contamination sources, including Project Gutenberg headers, PDF OCR errors, HTML navigation elements, and XML structural markup. Our format-specific extraction functions clean these artifacts while preserving authentic historical content.</p>
<h5 id="text-files-txt-txtutf-8"><strong>Text Files (.txt, .txt.utf-8)</strong></h5>
<p>Project Gutenberg texts contain standardized headers and footers that would confuse our language model. The cleaning process removes these while preserving the actual historical content. The code snippet in <a href="#listing2" class="listing-ref">Listing 2</a> demonstrates this approach and is quite straightforward. Of course, this can be made more robust, but this works well for our selected texts.</p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">clean_gutenberg_text</span>(text: <span style="color:#91d7e3">str</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Clean Project Gutenberg text by removing headers/footers and metadata&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    lines <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span>)
</span></span><span style="display:flex;"><span>    cleaned_lines <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    in_content <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> line <span style="color:#91d7e3;font-weight:bold">in</span> lines:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Skip Gutenberg headers (before &#34;*** START OF&#34;)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#34;*** START OF&#34;</span> <span style="color:#91d7e3;font-weight:bold">in</span> line:
</span></span><span style="display:flex;"><span>            in_content <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">True</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">continue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Skip Gutenberg footers (after &#34;*** END OF&#34;)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#34;*** END OF&#34;</span> <span style="color:#91d7e3;font-weight:bold">in</span> line:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Skip metadata lines</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> line<span style="color:#91d7e3;font-weight:bold">.</span>startswith((<span style="color:#a6da95">&#39;Title:&#39;</span>, <span style="color:#a6da95">&#39;Author:&#39;</span>, <span style="color:#a6da95">&#39;Release Date:&#39;</span>, <span style="color:#a6da95">&#39;Language:&#39;</span>)):
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">continue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Skip empty lines at start</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> in_content <span style="color:#91d7e3;font-weight:bold">and</span> <span style="color:#91d7e3;font-weight:bold">not</span> line<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">continue</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> in_content:
</span></span><span style="display:flex;"><span>            cleaned_lines<span style="color:#91d7e3;font-weight:bold">.</span>append(line)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(cleaned_lines)<span style="color:#91d7e3;font-weight:bold">.</span>strip()</span></span></code></pre></div><figcaption>
        <strong>Listing 2: Project Gutenberg Text Cleaning Function</strong>
    </figcaption>
</figure>
<p><strong>Real Example - Before Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Title: A Journal of the Plague Year
</span></span><span style="display:flex;"><span>Author: Daniel Defoe
</span></span><span style="display:flex;"><span>Release Date: March 2003
</span></span><span style="display:flex;"><span>Language: English
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>*** START OF THE PROJECT GUTENBERG EBOOK A JOURNAL OF THE PLAGUE YEAR ***
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>It was about the beginning of September 1664, that I, among the rest of my neighbours, heard in ordinary discourse that the plague was returned again in Holland...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>*** END OF THE PROJECT GUTENBERG EBOOK A JOURNAL OF THE PLAGUE YEAR ***</span></span></code></pre></div>
<p><strong>After Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>It was about the beginning of September 1664, that I, among the rest of my neighbours, heard in ordinary discourse that the plague was returned again in Holland...</span></span></code></pre></div>
<p>Without this cleaning, the model would learn to generate Gutenberg headers and metadata instead of authentic historical text, contaminating the training data with modern digital artifacts.</p>
<h5 id="pdf-files"><strong>PDF Files</strong></h5>
<p>PDF files from historical archives often contain OCR errors and digital artifacts that require correction. The cleaning process in <a href="#listing3" class="listing-ref">Listing 3</a> addresses these issues while preserving historical content, removing page numbers and all-caps headers. While not perfect, it significantly improves text quality.</p>
<p>The OCR correction rules are based on common patterns in historical documents and can be refined for specific datasets. Libraries like <code>PyMuPDF</code> or <code>pdfplumber</code> extract text, while regex-based cleaning corrects common OCR errors and removes digital stamps. More advanced techniques, such as layout analysis or AI-based OCR correction, can further enhance this process.</p>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">clean_pdf_text</span>(text: <span style="color:#91d7e3">str</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Clean PDF text by removing OCR artifacts and digital stamps&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove page numbers: [Page 123], standalone numbers</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\[Page \d+\]&#39;</span>, <span style="color:#a6da95">&#39;&#39;</span>, text)
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;^\d+$&#39;</span>, <span style="color:#a6da95">&#39;&#39;</span>, text, flags<span style="color:#91d7e3;font-weight:bold">=</span>re<span style="color:#91d7e3;font-weight:bold">.</span>MULTILINE)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove library stamps: Internet Archive, Google, etc.</span>
</span></span><span style="display:flex;"><span>    stamps <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;Internet Archive&#39;</span>, <span style="color:#a6da95">&#39;Google Books&#39;</span>, <span style="color:#a6da95">&#39;HathiTrust&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;Digitized by Google&#39;</span>, <span style="color:#a6da95">&#39;Scanned by Google&#39;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> stamp <span style="color:#91d7e3;font-weight:bold">in</span> stamps:
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(stamp, <span style="color:#a6da95">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Fix common OCR artifacts</span>
</span></span><span style="display:flex;"><span>    ocr_fixes <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b0\b&#39;</span>: <span style="color:#a6da95">&#39;O&#39;</span>,  <span style="color:#6e738d;font-style:italic"># 0 → O</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b1\b&#39;</span>: <span style="color:#a6da95">&#39;I&#39;</span>,  <span style="color:#6e738d;font-style:italic"># 1 → I  </span>
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b5\b&#39;</span>: <span style="color:#a6da95">&#39;S&#39;</span>,  <span style="color:#6e738d;font-style:italic"># 5 → S</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b8\b&#39;</span>: <span style="color:#a6da95">&#39;B&#39;</span>,  <span style="color:#6e738d;font-style:italic"># 8 → B</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\brn\b&#39;</span>: <span style="color:#a6da95">&#39;m&#39;</span>, <span style="color:#6e738d;font-style:italic"># rn → m</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\bcl\b&#39;</span>: <span style="color:#a6da95">&#39;d&#39;</span>  <span style="color:#6e738d;font-style:italic"># cl → d</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> pattern, replacement <span style="color:#91d7e3;font-weight:bold">in</span> ocr_fixes<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(pattern, replacement, text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove all-caps lines (usually headers)</span>
</span></span><span style="display:flex;"><span>    lines <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span>)
</span></span><span style="display:flex;"><span>    cleaned_lines <span style="color:#91d7e3;font-weight:bold">=</span> [line <span style="color:#c6a0f6">for</span> line <span style="color:#91d7e3;font-weight:bold">in</span> lines <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> line<span style="color:#91d7e3;font-weight:bold">.</span>isupper() <span style="color:#91d7e3;font-weight:bold">or</span> <span style="color:#91d7e3">len</span>(line) <span style="color:#91d7e3;font-weight:bold">&lt;</span> <span style="color:#f5a97f">10</span>]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(cleaned_lines)</span></span></code></pre></div><figcaption>
        <strong>Listing 3: PDF Text Cleaning Function</strong>
    </figcaption>
</figure>
<p><strong>Real Example - Before Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>[Page 1]
</span></span><span style="display:flex;"><span>INTERNET ARCHIVE
</span></span><span style="display:flex;"><span>A JOURNAL OF THE PLAGUE YEAR
</span></span><span style="display:flex;"><span>BY DANIEL DEFOE
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>It was about the beginning of September 1664, that I, among the rest of my neighbours, heard in ordinary discourse that the plague was returned again in Holland. For it was indeed a very terrible time, and the people began to be very much alarmed at it.</span></span></code></pre></div>
<p><strong>After Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>A JOURNAL OF THE PLAGUE YEAR
</span></span><span style="display:flex;"><span>BY DANIEL DEFOE
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>It was about the beginning of September 1664, that I, among the rest of my neighbours, heard in ordinary discourse that the plague was returned again in Holland. For it was indeed a very terrible time, and the people began to be very much alarmed at it.</span></span></code></pre></div>
<p>OCR errors can significantly impact the quality of model training. For example, if <code>London</code> appears as <code>L0nd0n</code> due to OCR errors, the model won&rsquo;t learn the correct spelling and will generate nonsensical text when asked about historical London. The correction process ensures our model learns authentic historical language patterns rather than digital artifacts, which is crucial for generating coherent and historically accurate text.</p>
<h5 id="html-files"><strong>HTML Files</strong></h5>
<p>HTML files from historical websites and digital archives contain markup that needs to be stripped while preserving the actual text content. We use the <code>BeautifulSoup</code> library in <a href="#listing4" class="listing-ref">Listing 4</a> to clean the HTML structure and extract only the meaningful text.</p>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">clean_html_text</span>(html_content: <span style="color:#91d7e3">str</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Clean HTML content by removing markup and extracting text&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">bs4</span> <span style="color:#8bd5ca">import</span> BeautifulSoup
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    soup <span style="color:#91d7e3;font-weight:bold">=</span> BeautifulSoup(html_content, <span style="color:#a6da95">&#39;html.parser&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove unwanted elements</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> element <span style="color:#91d7e3;font-weight:bold">in</span> soup([<span style="color:#a6da95">&#39;script&#39;</span>, <span style="color:#a6da95">&#39;style&#39;</span>, <span style="color:#a6da95">&#39;nav&#39;</span>, <span style="color:#a6da95">&#39;header&#39;</span>, <span style="color:#a6da95">&#39;footer&#39;</span>, 
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;aside&#39;</span>, <span style="color:#a6da95">&#39;menu&#39;</span>, <span style="color:#a6da95">&#39;form&#39;</span>, <span style="color:#a6da95">&#39;input&#39;</span>, <span style="color:#a6da95">&#39;button&#39;</span>]):
</span></span><span style="display:flex;"><span>        element<span style="color:#91d7e3;font-weight:bold">.</span>decompose()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove wiki-specific elements</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> element <span style="color:#91d7e3;font-weight:bold">in</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all([<span style="color:#a6da95">&#39;div&#39;</span>, <span style="color:#a6da95">&#39;span&#39;</span>], class_<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#39;navbox&#39;</span>, <span style="color:#a6da95">&#39;infobox&#39;</span>, <span style="color:#a6da95">&#39;sidebar&#39;</span>]):
</span></span><span style="display:flex;"><span>        element<span style="color:#91d7e3;font-weight:bold">.</span>decompose()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove navigation elements</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> element <span style="color:#91d7e3;font-weight:bold">in</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all([<span style="color:#a6da95">&#39;div&#39;</span>, <span style="color:#a6da95">&#39;ul&#39;</span>], class_<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#39;breadcrumb&#39;</span>, <span style="color:#a6da95">&#39;navigation&#39;</span>, <span style="color:#a6da95">&#39;menu&#39;</span>]):
</span></span><span style="display:flex;"><span>        element<span style="color:#91d7e3;font-weight:bold">.</span>decompose()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Extract text content</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>get_text(separator<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39; &#39;</span>, strip<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Clean up excessive whitespace</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\s+&#39;</span>, <span style="color:#a6da95">&#39; &#39;</span>, text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text<span style="color:#91d7e3;font-weight:bold">.</span>strip()</span></span></code></pre></div><figcaption>
        <strong>Listing 4: HTML Text Cleaning Function</strong>
    </figcaption>
</figure>
<p><strong>Real Example - Before Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">&lt;!DOCTYPE html&gt;</span>
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">html</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">head</span>&gt;&lt;<span style="color:#c6a0f6">title</span>&gt;London History&lt;/<span style="color:#c6a0f6">title</span>&gt;&lt;/<span style="color:#c6a0f6">head</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">nav</span>&gt;Home | About | Contact&lt;/<span style="color:#c6a0f6">nav</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">header</span>&gt;London Historical Society&lt;/<span style="color:#c6a0f6">header</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">div</span> <span style="color:#8aadf4">class</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;content&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#c6a0f6">h1</span>&gt;The Great Fire of London&lt;/<span style="color:#c6a0f6">h1</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#c6a0f6">p</span>&gt;In the year 1666, a great fire consumed much of London...&lt;/<span style="color:#c6a0f6">p</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#c6a0f6">div</span>&gt;
</span></span><span style="display:flex;"><span>&lt;<span style="color:#c6a0f6">footer</span>&gt;© 2024 London Historical Society&lt;/<span style="color:#c6a0f6">footer</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#c6a0f6">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#c6a0f6">html</span>&gt;</span></span></code></pre></div>
<p><strong>After Cleaning:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>The Great Fire of London in the year 1666, a great fire consumed much of London...</span></span></code></pre></div>
<p>HTML tags and navigation elements would contaminate training, causing the model to generate markup instead of historical text. Our cleaning process extracts meaningful content while preserving natural flow and structure.</p>
<h5 id="xml-files-historical-archives"><strong>XML Files (Historical Archives):</strong></h5>
<p>XML files from historical archives, such as the Old Bailey and London Lives, use specific schemas that require specialized parsing. Old Bailey employs <strong>TEI (Text Encoding Initiative)</strong> with <code>TEI.2</code> elements, while London Lives uses semantic markup (<code>name</code>, <code>geo</code>, <code>occupation</code>). These structured formats contain authentic historical language with rich metadata, as shown in <a href="#listing5" class="listing-ref">Listing 5</a>.</p>
<figure id="listing5"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">extract_old_bailey_text</span>(soup) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Extract text from Old Bailey XML using TEI schema structure&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    extracted_text <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for TEI.2 elements (Old Bailey schema)</span>
</span></span><span style="display:flex;"><span>    tei_elements <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;TEI.2&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> tei_elements:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Extract trial accounts (main narrative content)</span>
</span></span><span style="display:flex;"><span>        trial_accounts <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;div1&#39;</span>, {<span style="color:#a6da95">&#39;type&#39;</span>: <span style="color:#a6da95">&#39;trialAccount&#39;</span>})
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> trial <span style="color:#91d7e3;font-weight:bold">in</span> trial_accounts:
</span></span><span style="display:flex;"><span>            trial_text <span style="color:#91d7e3;font-weight:bold">=</span> extract_trial_narrative(trial)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> trial_text:
</span></span><span style="display:flex;"><span>                extracted_text<span style="color:#91d7e3;font-weight:bold">.</span>append(trial_text)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Extract front matter (session information)</span>
</span></span><span style="display:flex;"><span>        front_matter <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;div1&#39;</span>, {<span style="color:#a6da95">&#39;type&#39;</span>: <span style="color:#a6da95">&#39;frontMatter&#39;</span>})
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> front <span style="color:#91d7e3;font-weight:bold">in</span> front_matter:
</span></span><span style="display:flex;"><span>            front_text <span style="color:#91d7e3;font-weight:bold">=</span> extract_front_matter_narrative(front)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> front_text:
</span></span><span style="display:flex;"><span>                extracted_text<span style="color:#91d7e3;font-weight:bold">.</span>append(front_text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(extracted_text)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">extract_london_lives_text</span>(soup) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Extract text from London Lives XML using semantic markup schema&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    extracted_text <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check for London Lives specific elements (name, geo, occupation, date)</span>
</span></span><span style="display:flex;"><span>    name_elements <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;name&#39;</span>)
</span></span><span style="display:flex;"><span>    geo_elements <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;geo&#39;</span>)
</span></span><span style="display:flex;"><span>    occupation_elements <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;occupation&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> name_elements <span style="color:#91d7e3;font-weight:bold">and</span> geo_elements <span style="color:#91d7e3;font-weight:bold">and</span> occupation_elements:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Extract paragraphs with semantic markup</span>
</span></span><span style="display:flex;"><span>        paragraphs <span style="color:#91d7e3;font-weight:bold">=</span> soup<span style="color:#91d7e3;font-weight:bold">.</span>find_all(<span style="color:#a6da95">&#39;p&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> para <span style="color:#91d7e3;font-weight:bold">in</span> paragraphs:
</span></span><span style="display:flex;"><span>            p_text <span style="color:#91d7e3;font-weight:bold">=</span> extract_paragraph_with_semantic_markup(para)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> p_text<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>                extracted_text<span style="color:#91d7e3;font-weight:bold">.</span>append(p_text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(extracted_text)</span></span></code></pre></div><figcaption>
        <strong>Listing 5: XML Text Extraction Functions</strong>
    </figcaption>
</figure>
<p><strong>Real Example - Old Bailey XML (Before Processing):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;trial&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;frontmatter&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;session&gt;</span>Session 1<span style="color:#c6a0f6">&lt;/session&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;date&gt;</span>1674-04-15<span style="color:#c6a0f6">&lt;/date&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;location&gt;</span>Old Bailey<span style="color:#c6a0f6">&lt;/location&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;/frontmatter&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;proceedings&gt;</span>
</span></span><span style="display:flex;"><span>The prisoner being brought to the bar, and the indictment being read, he pleaded Not Guilty. The witnesses being sworn, the first witness deposed that on the 15th day of April last, he saw the prisoner in the company of several suspicious persons...
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;/proceedings&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">&lt;/trial&gt;</span></span></span></code></pre></div>
<p><strong>After Processing:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Session 1 1674-04-15 Old Bailey The prisoner being brought to the bar, and the indictment being read, he pleaded Not Guilty. The witnesses being sworn, the first witness deposed, that on the 15th day of April last, he saw the prisoner in the company of several suspicious persons...</span></span></code></pre></div>
<p>These XML files contain the most authentic historical language in our entire dataset. The Old Bailey trials show how people actually spoke in court during the 17th-19th centuries, while London Lives reveals the everyday language used in personal records and official documents. This authentic historical language is very useful for training a model that can generate historically accurate text, as it provides the model with genuine examples of how people wrote and spoke during different historical periods.</p>
<h4 id="223-stage-3-text-normalization">2.2.3 Stage 3: Text Normalization</h4>
<p>After extraction, text normalization ensures consistency and compatibility with the training data. Historical documents contain encoding issues, inconsistent formatting, and special characters that confuse the model. Our normalization process fixes these issues and breaks long lines to fit within the model&rsquo;s context window. This is critical because lines exceeding the context window appear as incomplete sentences to the transformer, severely degrading generation quality due to the attention mechanism&rsquo;s inability to process fragmented text.</p>
<p>Inconsistent encoding and formatting can severely confuse the language model during training. For example, if some files use smart quotes (&quot;) and others use straight quotes (&quot;), the model might not learn that they represent the same concept, leading to inconsistent and potentially incorrect text generation. Normalization ensures that the model observes consistent patterns across all training data, which is crucial for learning coherent language patterns and generating high-quality historical text.</p>
<p>The code snippet in <a href="#listing6" class="listing-ref">Listing 6</a> demonstrates how we implement this normalization, which is quite straightforward.</p>
<figure id="listing6"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">normalize_text</span>(text: <span style="color:#91d7e3">str</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Normalize text for consistent training data&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">unicodedata</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Fix common encoding issues</span>
</span></span><span style="display:flex;"><span>    encoding_fixes <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€™&#39;</span>: <span style="color:#a6da95">&#34;&#39;&#34;</span>,  <span style="color:#6e738d;font-style:italic"># Smart apostrophe</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€œ&#39;</span>: <span style="color:#a6da95">&#39;&#34;&#39;</span>,  <span style="color:#6e738d;font-style:italic"># Smart quote left</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€&#39;</span>: <span style="color:#a6da95">&#39;&#34;&#39;</span>,   <span style="color:#6e738d;font-style:italic"># Smart quote right</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€&#34;&#39;</span>: <span style="color:#a6da95">&#39;—&#39;</span>,  <span style="color:#6e738d;font-style:italic"># Em dash</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€¢&#39;</span>: <span style="color:#a6da95">&#39;•&#39;</span>,  <span style="color:#6e738d;font-style:italic"># Bullet point</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;â€¦&#39;</span>: <span style="color:#a6da95">&#39;…&#39;</span>,  <span style="color:#6e738d;font-style:italic"># Ellipsis</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> old, new <span style="color:#91d7e3;font-weight:bold">in</span> encoding_fixes<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(old, new)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Normalize Unicode (NFC)</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> unicodedata<span style="color:#91d7e3;font-weight:bold">.</span>normalize(<span style="color:#a6da95">&#39;NFC&#39;</span>, text)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Break long lines for training compatibility (max 2000 chars)</span>
</span></span><span style="display:flex;"><span>    lines <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span>)
</span></span><span style="display:flex;"><span>    normalized_lines <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> line <span style="color:#91d7e3;font-weight:bold">in</span> lines:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(line) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">2000</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Split at sentence boundaries</span>
</span></span><span style="display:flex;"><span>            sentences <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;(?&lt;=[.!?])\s+&#39;</span>, line)
</span></span><span style="display:flex;"><span>            current_line <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">for</span> sentence <span style="color:#91d7e3;font-weight:bold">in</span> sentences:
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(current_line <span style="color:#91d7e3;font-weight:bold">+</span> sentence) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">2000</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">if</span> current_line:
</span></span><span style="display:flex;"><span>                        normalized_lines<span style="color:#91d7e3;font-weight:bold">.</span>append(current_line<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>                    current_line <span style="color:#91d7e3;font-weight:bold">=</span> sentence
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>                    current_line <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#a6da95">&#34; &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> sentence <span style="color:#c6a0f6">if</span> current_line <span style="color:#c6a0f6">else</span> sentence
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> current_line:
</span></span><span style="display:flex;"><span>                normalized_lines<span style="color:#91d7e3;font-weight:bold">.</span>append(current_line<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            normalized_lines<span style="color:#91d7e3;font-weight:bold">.</span>append(line)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Normalize line endings and whitespace</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(normalized_lines)
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;[ \t]+&#39;</span>, <span style="color:#a6da95">&#39; &#39;</span>, text)  <span style="color:#6e738d;font-style:italic"># Multiple spaces/tabs to single space</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>sub(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\n\s*\n&#39;</span>, <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span>, text)  <span style="color:#6e738d;font-style:italic"># Multiple newlines to double newline</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text<span style="color:#91d7e3;font-weight:bold">.</span>strip()</span></span></code></pre></div><figcaption>
        <strong>Listing 6: Text Normalization Function</strong>
    </figcaption>
</figure>
<p><strong>Real Example - Before Normalization:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>The year was 1666, and the plague had come to London. â€œIt was indeed a very terrible time,â€ wrote one observer. The streets were filled with the sounds of horse-drawn carriages and the cries of the afflicted.</span></span></code></pre></div>
<p><strong>After Normalization:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>The year was 1666, and the plague had come to London. &#34;It was indeed a very terrible time,&#34; wrote one observer. The streets were filled with the sounds of horse-drawn carriages and the cries of the afflicted.</span></span></code></pre></div>
<h4 id="224-stage-4-quality-validation">2.2.4 Stage 4: Quality Validation</h4>
<p>Not all extracted text is suitable for training. Some files contain duplicates, non-English content, or poor-quality text that would degrade model performance. We need a comprehensive validation system that ensures only high-quality, relevant text is included in our training corpus.</p>
<p>The key challenge is striking a balance between quality standards and historical value. A strict approach might reject valuable historical documents that have some OCR issues, while a lenient approach might include too much low-quality content, which can degrade model training. To address this, I implemented a <strong>tiered quality threshold system</strong> that applies different standards based on content type:</p>
<ul>
<li><strong>General Content</strong>: 200+ chars, 50+ words, 50% meaningful words</li>
<li><strong>Project Gutenberg</strong>: 200+ chars, 50+ words, 40% meaningful words (relaxed for historical value)</li>
<li><strong>Historical Documents</strong>: 1000+ chars, 100+ words, 30% meaningful words (very relaxed for historical value)</li>
</ul>
<p>This tiered approach ensures that we capture valuable historical content while maintaining quality standards, filtering out duplicates, non-English content, and low-quality text, thereby preserving the integrity of useful historical documents. Again, these implementations are quite simple, in the context of a toy project, but can be made more robust. The code itself is quite straightforward, as shown in <a href="#listing7" class="listing-ref">Listing 7</a>.</p>
<figure id="listing7"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">analyze_text_quality</span>(text: <span style="color:#91d7e3">str</span>, source_type: <span style="color:#91d7e3">str</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;general&#39;</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">dict</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Analyze text quality and determine if it should be included in training corpus&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">hashlib</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">re</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Length validation</span>
</span></span><span style="display:flex;"><span>    char_count <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">len</span>(text)
</span></span><span style="display:flex;"><span>    word_count <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">len</span>(text<span style="color:#91d7e3;font-weight:bold">.</span>split())
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># OCR artifact detection using regex patterns</span>
</span></span><span style="display:flex;"><span>    ocr_patterns <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;long_capitals&#39;</span>: <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;[A-Z]{5,}\s+[A-Z]{5,}&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;spaced_letters&#39;</span>: <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b[A-Za-z]\s+[A-Za-z]\s+[A-Za-z]\s+[A-Za-z]\b&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;special_chars&#39;</span>: <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;[!@#$%^&amp;*()]{3,}&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;mixed_alphanumeric&#39;</span>: <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;\b\d+[A-Za-z]+\d+\b&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;long_non_word&#39;</span>: <span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;[^\w\s]{10,}&#39;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    ocr_issues <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> pattern_name, pattern <span style="color:#91d7e3;font-weight:bold">in</span> ocr_patterns<span style="color:#91d7e3;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> re<span style="color:#91d7e3;font-weight:bold">.</span>search(pattern, text):
</span></span><span style="display:flex;"><span>            ocr_issues<span style="color:#91d7e3;font-weight:bold">.</span>append(pattern_name)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Advertisement detection</span>
</span></span><span style="display:flex;"><span>    ad_patterns <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;this day is published&#39;</span>, <span style="color:#a6da95">&#39;just ready&#39;</span>, <span style="color:#a6da95">&#39;elegantly bound&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;now ready&#39;</span>, <span style="color:#a6da95">&#39;new novels&#39;</span>, <span style="color:#a6da95">&#39;advertisements&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;price \d+s&#39;</span>, <span style="color:#a6da95">&#39;paternoster row&#39;</span>, <span style="color:#a6da95">&#39;corner of&#39;</span>, <span style="color:#a6da95">&#39;publishers&#39;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    ad_count <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">sum</span>(<span style="color:#f5a97f">1</span> <span style="color:#c6a0f6">for</span> pattern <span style="color:#91d7e3;font-weight:bold">in</span> ad_patterns <span style="color:#c6a0f6">if</span> re<span style="color:#91d7e3;font-weight:bold">.</span>search(pattern, text, re<span style="color:#91d7e3;font-weight:bold">.</span>IGNORECASE))
</span></span><span style="display:flex;"><span>    ad_density <span style="color:#91d7e3;font-weight:bold">=</span> ad_count <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">max</span>(word_count, <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Meaningful word ratio calculation</span>
</span></span><span style="display:flex;"><span>    words <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>split()
</span></span><span style="display:flex;"><span>    meaningful_words <span style="color:#91d7e3;font-weight:bold">=</span> [w <span style="color:#c6a0f6">for</span> w <span style="color:#91d7e3;font-weight:bold">in</span> words <span style="color:#c6a0f6">if</span> w<span style="color:#91d7e3;font-weight:bold">.</span>isalpha() <span style="color:#91d7e3;font-weight:bold">and</span> <span style="color:#91d7e3">len</span>(w) <span style="color:#91d7e3;font-weight:bold">&gt;</span> <span style="color:#f5a97f">2</span>]
</span></span><span style="display:flex;"><span>    meaningful_ratio <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">len</span>(meaningful_words) <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#91d7e3">max</span>(<span style="color:#91d7e3">len</span>(words), <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Quality thresholds based on source type</span>
</span></span><span style="display:flex;"><span>    thresholds <span style="color:#91d7e3;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;general&#39;</span>: {<span style="color:#a6da95">&#39;min_chars&#39;</span>: <span style="color:#f5a97f">200</span>, <span style="color:#a6da95">&#39;min_words&#39;</span>: <span style="color:#f5a97f">50</span>, <span style="color:#a6da95">&#39;min_meaningful_ratio&#39;</span>: <span style="color:#f5a97f">0.50</span>},
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;gutenberg&#39;</span>: {<span style="color:#a6da95">&#39;min_chars&#39;</span>: <span style="color:#f5a97f">200</span>, <span style="color:#a6da95">&#39;min_words&#39;</span>: <span style="color:#f5a97f">50</span>, <span style="color:#a6da95">&#39;min_meaningful_ratio&#39;</span>: <span style="color:#f5a97f">0.40</span>},
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;historical&#39;</span>: {<span style="color:#a6da95">&#39;min_chars&#39;</span>: <span style="color:#f5a97f">1000</span>, <span style="color:#a6da95">&#39;min_words&#39;</span>: <span style="color:#f5a97f">100</span>, <span style="color:#a6da95">&#39;min_meaningful_ratio&#39;</span>: <span style="color:#f5a97f">0.30</span>}
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    threshold <span style="color:#91d7e3;font-weight:bold">=</span> thresholds<span style="color:#91d7e3;font-weight:bold">.</span>get(source_type, thresholds[<span style="color:#a6da95">&#39;general&#39;</span>])
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Quality scoring</span>
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">100</span>
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">-=</span> <span style="color:#91d7e3">len</span>(ocr_issues) <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">3</span>  <span style="color:#6e738d;font-style:italic"># OCR issues</span>
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">-=</span> ad_density <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">50</span>  <span style="color:#6e738d;font-style:italic"># Advertisement density</span>
</span></span><span style="display:flex;"><span>    score <span style="color:#91d7e3;font-weight:bold">-=</span> (<span style="color:#f5a97f">1</span> <span style="color:#91d7e3;font-weight:bold">-</span> meaningful_ratio) <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">20</span>  <span style="color:#6e738d;font-style:italic"># Meaningful word ratio</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check if text meets quality thresholds</span>
</span></span><span style="display:flex;"><span>    meets_thresholds <span style="color:#91d7e3;font-weight:bold">=</span> (
</span></span><span style="display:flex;"><span>        char_count <span style="color:#91d7e3;font-weight:bold">&gt;=</span> threshold[<span style="color:#a6da95">&#39;min_chars&#39;</span>] <span style="color:#91d7e3;font-weight:bold">and</span>
</span></span><span style="display:flex;"><span>        word_count <span style="color:#91d7e3;font-weight:bold">&gt;=</span> threshold[<span style="color:#a6da95">&#39;min_words&#39;</span>] <span style="color:#91d7e3;font-weight:bold">and</span>
</span></span><span style="display:flex;"><span>        meaningful_ratio <span style="color:#91d7e3;font-weight:bold">&gt;=</span> threshold[<span style="color:#a6da95">&#39;min_meaningful_ratio&#39;</span>] <span style="color:#91d7e3;font-weight:bold">and</span>
</span></span><span style="display:flex;"><span>        ad_density <span style="color:#91d7e3;font-weight:bold">&lt;</span> <span style="color:#f5a97f">0.1</span>  <span style="color:#6e738d;font-style:italic"># Less than 10% advertisement content</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;char_count&#39;</span>: char_count,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;word_count&#39;</span>: word_count,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;meaningful_ratio&#39;</span>: meaningful_ratio,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;ocr_issues&#39;</span>: ocr_issues,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;ad_density&#39;</span>: ad_density,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;score&#39;</span>: score,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;meets_thresholds&#39;</span>: meets_thresholds,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#39;content_hash&#39;</span>: hashlib<span style="color:#91d7e3;font-weight:bold">.</span>md5(text<span style="color:#91d7e3;font-weight:bold">.</span>encode())<span style="color:#91d7e3;font-weight:bold">.</span>hexdigest()
</span></span><span style="display:flex;"><span>    }</span></span></code></pre></div><figcaption>
        <strong>Listing 7: Text Quality Analysis Function</strong>
    </figcaption>
</figure>
<p><strong>Content Quality Validation</strong></p>
<p>Our validation system employs multiple detection mechanisms to ensure training corpus quality:</p>
<ul>
<li><strong>OCR Artifact Detection</strong>: Regex patterns identify common digitization errors, including misread headers, character separation failures, scanning artifacts, alphanumeric misinterpretations, and corrupted text regions</li>
<li><strong>Advertisement Filtering</strong>: Pattern matching detects commercial content using phrases like &ldquo;this day is published&rdquo;, &ldquo;just ready&rdquo;, &ldquo;elegantly bound&rdquo;, and price references</li>
<li><strong>Quality Scoring</strong>: A 100-point system deducts points for OCR artifacts (-3 each), advertisement density (-50), and low meaningful word ratios (-20)</li>
</ul>
<p>This multi-layered approach balances quality standards with preservation of valuable historical content, ensuring the model trains on authentic historical language while filtering out contamination sources.</p>
<h4 id="225-stage-5-final-processing-and-corpus-creation">2.2.5 Stage 5: Final Processing and Corpus Creation</h4>
<p>After cleaning and validation, we create a final training corpus optimized for language model training. This requires intelligent segmentation that breaks long texts into manageable chunks while preserving the historical narrative flow, which is essential given the context window limits (e.g., 2048 tokens). The code snippet in <a href="#listing8" class="listing-ref">Listing 8</a> demonstrates this final processing stage.</p>
<figure id="listing8"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_comprehensive_corpus</span>(cleaned_files: <span style="color:#91d7e3">list</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Create final training corpus with intelligent segmentation&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    corpus_parts <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> file_path <span style="color:#91d7e3;font-weight:bold">in</span> cleaned_files:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            content <span style="color:#91d7e3;font-weight:bold">=</span> f<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Split into training segments</span>
</span></span><span style="display:flex;"><span>        segments <span style="color:#91d7e3;font-weight:bold">=</span> split_into_training_segments(content)
</span></span><span style="display:flex;"><span>        corpus_parts<span style="color:#91d7e3;font-weight:bold">.</span>extend(segments)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create final corpus</span>
</span></span><span style="display:flex;"><span>    final_corpus <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(corpus_parts)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Save to file</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(<span style="color:#a6da95">&#39;london_historical_corpus_comprehensive.txt&#39;</span>, <span style="color:#a6da95">&#39;w&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>        f<span style="color:#91d7e3;font-weight:bold">.</span>write(final_corpus)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> final_corpus
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">split_into_training_segments</span>(text: <span style="color:#91d7e3">str</span>, max_length: <span style="color:#91d7e3">int</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">2000</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">list</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Split text into training segments while preserving narrative flow&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># First split on double newlines (paragraphs)</span>
</span></span><span style="display:flex;"><span>    paragraphs <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    segments <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    current_segment <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> paragraph <span style="color:#91d7e3;font-weight:bold">in</span> paragraphs:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(current_segment <span style="color:#91d7e3;font-weight:bold">+</span> paragraph) <span style="color:#91d7e3;font-weight:bold">&lt;=</span> max_length:
</span></span><span style="display:flex;"><span>            current_segment <span style="color:#91d7e3;font-weight:bold">+=</span> paragraph <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> current_segment:
</span></span><span style="display:flex;"><span>                segments<span style="color:#91d7e3;font-weight:bold">.</span>append(current_segment<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>            current_segment <span style="color:#91d7e3;font-weight:bold">=</span> paragraph <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#39;</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">&#39;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> current_segment:
</span></span><span style="display:flex;"><span>        segments<span style="color:#91d7e3;font-weight:bold">.</span>append(current_segment<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Further split long segments at sentence boundaries</span>
</span></span><span style="display:flex;"><span>    final_segments <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> segment <span style="color:#91d7e3;font-weight:bold">in</span> segments:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(segment) <span style="color:#91d7e3;font-weight:bold">&gt;</span> max_length:
</span></span><span style="display:flex;"><span>            sentences <span style="color:#91d7e3;font-weight:bold">=</span> re<span style="color:#91d7e3;font-weight:bold">.</span>split(<span style="color:#ed8796">r</span><span style="color:#a6da95">&#39;(?&lt;=[.!?])\s+&#39;</span>, segment)
</span></span><span style="display:flex;"><span>            current_segment <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">for</span> sentence <span style="color:#91d7e3;font-weight:bold">in</span> sentences:
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(current_segment <span style="color:#91d7e3;font-weight:bold">+</span> sentence) <span style="color:#91d7e3;font-weight:bold">&lt;=</span> max_length:
</span></span><span style="display:flex;"><span>                    current_segment <span style="color:#91d7e3;font-weight:bold">+=</span> sentence <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; &#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">if</span> current_segment:
</span></span><span style="display:flex;"><span>                        final_segments<span style="color:#91d7e3;font-weight:bold">.</span>append(current_segment<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>                    current_segment <span style="color:#91d7e3;font-weight:bold">=</span> sentence <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; &#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> current_segment:
</span></span><span style="display:flex;"><span>                final_segments<span style="color:#91d7e3;font-weight:bold">.</span>append(current_segment<span style="color:#91d7e3;font-weight:bold">.</span>strip())
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            final_segments<span style="color:#91d7e3;font-weight:bold">.</span>append(segment)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Filter out segments that are too short</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> [seg <span style="color:#c6a0f6">for</span> seg <span style="color:#91d7e3;font-weight:bold">in</span> final_segments <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(seg) <span style="color:#91d7e3;font-weight:bold">&gt;=</span> <span style="color:#f5a97f">50</span>]</span></span></code></pre></div><figcaption>
        <strong>Listing 8: Corpus Creation and Segmentation Functions</strong>
    </figcaption>
</figure>
<p>During my local runs, this final processing stage generated a comprehensive corpus of over 500 million characters across ~250,000 segments, with an average segment length of around 2,000 characters. The success rate of files making it into the final corpus ranged from 70% to 90%, depending on the quality and availability of the source.</p>
<p><strong>Final Corpus Statistics:</strong></p>
<ul>
<li><strong>Total Sources Processed</strong>: 218+ historical sources</li>
<li><strong>Final Corpus Size</strong>: 500M+ characters</li>
<li><strong>Training Segments</strong>: ~250,000 segments</li>
<li><strong>Average Segment Length</strong>: ~2,000 characters</li>
<li><strong>Success Rate</strong>: 70-90% (depending on source availability)</li>
</ul>
<h3 id="23-detailed-data-processing-flow">2.3 Detailed Data Processing Flow</h3>
<p>Building on the high-level flow and having reviewed each of the areas, the detailed flow below illustrates the complete data cleaning process, including rejection paths, error handling, and statistics tracking. This is intended to provide a bird&rsquo;s-eye view of the entire process.</p>
<figure class="align-center " id="fig4">
    <pre class="mermaid">graph TD
    A[📁 Raw Files] --&gt; B{File Type Detection}
    
    B --&gt;|.txt, .txt.utf-8| C[📄 Text File]
    B --&gt;|.pdf| D[📄 PDF File]
    B --&gt;|.html, .htm| E[📄 HTML File]
    B --&gt;|.xml| F[📄 XML File]
    B --&gt;|No Extension| G{Content Detection}
    
    G --&gt;|HTML-like| E
    G --&gt;|Text-like| C
    G --&gt;|Binary/Unknown| REJECT1[❌ REJECTED]
    
    C --&gt; H[🧹 clean_gutenberg_text]
    D --&gt; I[🔧 extract_text_from_pdf]
    E --&gt; J[🧹 clean_html_text]
    F --&gt; K{XML Type Detection}
    
    I --&gt; L[🧹 clean_pdf_text]
    
    K --&gt;|Old Bailey| M[🔧 extract_old_bailey_text]
    K --&gt;|London Lives| N[🔧 extract_london_lives_text]
    
    M --&gt; O[🧹 clean_old_bailey_text]
    N --&gt; P[🧹 clean_london_lives_text]
    
    H --&gt; Q[🔧 normalize_text]
    L --&gt; Q
    J --&gt; Q
    O --&gt; Q
    P --&gt; Q
    
    Q --&gt; R[🔍 Duplicate Detection]
    R --&gt;|Duplicate| REJECT2[❌ REJECTED - Duplicate]
    R --&gt;|Unique| S[🌍 Language Detection]
    
    S --&gt;|Non-English| REJECT3[❌ REJECTED - Non-English]
    S --&gt;|English| T[📊 Quality Analysis]
    
    T --&gt; U{Quality Check}
    U --&gt;|Poor Quality| REJECT4[❌ REJECTED - Poor Quality]
    U --&gt;|Good Quality| V[💾 Save to Processed Directory]
    
    V --&gt; W[📊 Update Statistics]
    W --&gt; X[✅ Successfully Processed]
    
    REJECT1 --&gt; Y[📝 Log Rejection Reason]
    REJECT2 --&gt; Y
    REJECT3 --&gt; Y
    REJECT4 --&gt; Y
    
    Y --&gt; Z[📊 Update Rejection Stats]
    
    style A fill:#e1f5fe
    style X fill:#c8e6c9
    style REJECT1 fill:#ffcdd2
    style REJECT2 fill:#ffcdd2
    style REJECT3 fill:#ffcdd2
    style REJECT4 fill:#ffcdd2
    style Y fill:#fff3e0
    style Z fill:#fff3e0</pre>
    <figcaption>Figure 4: Detailed Data Processing Pipeline</figcaption>
</figure>
<h3 id="25-corpus-creation-process">2.5 Corpus Creation Process</h3>
<p>After cleaning, the system creates the final training corpus through intelligent segmentation that preserves historical narrative flow:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>📁 Cleaned Files
</span></span><span style="display:flex;"><span>    ↓
</span></span><span style="display:flex;"><span>🔧 create_comprehensive_corpus()
</span></span><span style="display:flex;"><span>    ├── Read all cleaned_*.txt files
</span></span><span style="display:flex;"><span>    ├── Split into training segments (split_into_training_segments)
</span></span><span style="display:flex;"><span>    │   ├── Split on double newlines (paragraphs)
</span></span><span style="display:flex;"><span>    │   ├── Max length: 2000 characters
</span></span><span style="display:flex;"><span>    │   ├── Min length: 100 characters
</span></span><span style="display:flex;"><span>    │   └── Further split long segments at sentence boundaries
</span></span><span style="display:flex;"><span>    ├── Filter segments (min 50 characters)
</span></span><span style="display:flex;"><span>    └── Write to london_historical_corpus_comprehensive.txt</span></span></code></pre></div>
<p>The corpus creation process reads all cleaned text files and intelligently segments them into training-ready chunks. It first splits on double newlines to preserve paragraph boundaries, which are natural break points in historical text. Segments are constrained to a maximum of 2000 characters to fit within the model&rsquo;s context window, with a minimum of 100 characters to ensure substantial content. Long segments are further split at sentence boundaries to maintain readability. Finally, segments shorter than 50 characters are filtered out as they&rsquo;re unlikely to contain meaningful historical content.</p>
<p>Proper segmentation is crucial for training language models. The model needs to learn from coherent text segments that maintain historical narrative flow while fitting within its context window. Splitting on paragraph boundaries preserves the natural structure of historical documents, while sentence-level splitting ensures that very long paragraphs don&rsquo;t exceed the model&rsquo;s processing capabilities. This approach maximizes the model&rsquo;s ability to learn from authentic historical language patterns while maintaining training efficiency.</p>
<h3 id="26-outcome-training-ready-corpus">2.6 Outcome: Training-Ready Corpus</h3>
<p>The result is a <strong>clean, historically faithful corpus</strong> containing over 500 million characters of authentic historical English spanning 350 years of London history from 1500-1850. The corpus comprises high-quality text with minimal OCR artifacts, preserving historical language patterns and a rich cultural context that reflects the social, political, and economic realities of various historical periods. The text has been intelligently segmented for optimal language model training, with careful attention to maintaining the natural flow of historical narratives while ensuring compatibility with modern training techniques.</p>
<p>This corpus serves as the essential foundation for training our specialized historical tokenizer and language model, ensuring the model learns authentic historical English rather than modern text patterns. By providing the model with genuine examples of how people wrote and spoke during different historical periods, we enable it to generate text that captures the linguistic nuances, cultural references, and historical context that make historical language modeling both challenging and rewarding.</p>
<p><strong>💻 Try It Yourself:</strong> The complete implementation, including all the data collection scripts, cleaning algorithms, and quality validation systems described in this section, is available in the <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		helloLondon GitHub repository
	</span>
</a>. The repository includes detailed documentation, example usage, and step-by-step guides for setting up your own historical language model training pipeline.</p>
<p>Now that we have examined the data collection and cleaning process, we can proceed to the next steps: creating a custom historical tokenizer and preparing for model training.</p>
<h2 id="3-custom-historical-tokenizer-the-key-to-authentic-historical-text-generation">3. Custom Historical Tokenizer: The Key to Authentic Historical Text Generation</h2>
<p>Creating a custom tokenizer is crucial for generating effective historical text. This section examines the necessity of a custom tokenizer, the challenges presented by historical language, and our chosen architecture. The tokenizer preserves the semantic meaning of historical words and phrases, enabling coherent and contextually accurate historical narratives.</p>
<p>Standard tokenizers like GPT-2&rsquo;s fragment archaic words like &ldquo;quoth&rdquo; and &ldquo;hast&rdquo; into multiple subword tokens, destroying semantic meaning crucial for historical text generation.</p>
<p><strong>Real Example - Standard Tokenizer vs. Our Custom Tokenizer:</strong></p>
<p><strong>Standard GPT-2 Tokenizer:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&#34;Quoth the alderman, &#39;Tis a fair day at Newgate&#34;
</span></span><span style="display:flex;"><span>→ [&#39;Qu&#39;, &#39;oth&#39;, &#39; the&#39;, &#39; ald&#39;, &#39;erman&#39;, &#39;,&#39;, &#39; &#39;, &#39;&#39;&#39;, &#39;T&#39;, &#39;is&#39;, &#39; a&#39;, &#39; fair&#39;, &#39; day&#39;, &#39; at&#39;, &#39; New&#39;, &#39;gate&#39;]</span></span></code></pre></div>
<p><strong>Our Custom Historical Tokenizer:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&#34;Quoth the alderman, &#39;Tis a fair day at Newgate&#34;
</span></span><span style="display:flex;"><span>→ [&#39;&lt;|quoth|&gt;&#39;, &#39; the&#39;, &#39; alderman&#39;, &#39;,&#39;, &#39; &#39;, &#39;&#39;&#39;, &#39;&lt;|tis|&gt;&#39;, &#39; a&#39;, &#39; fair&#39;, &#39; day&#39;, &#39; at&#39;, &#39; &lt;|newgate|&gt;&#39;]</span></span></code></pre></div>
<p>The standard tokenizer breaks historical language into 18 meaningless fragments, losing semantic meaning and historical context. Our custom tokenizer reduces this to 12 meaningful tokens, preserving authentic historical language patterns essential for coherent text generation.</p>
<p>A tokenizer that fragments historical language destroys the model&rsquo;s ability to learn authentic patterns. The model needs to perceive &ldquo;quoth&rdquo; as a single concept, rather than fragmented subwords, to capture the linguistic nuances of different historical periods.</p>
<h3 id="31-what-happens-with-off-the-shelf-tokenizers">3.1 What Happens with Off-the-Shelf Tokenizers</h3>
<p>What would happen if we used standard tokenizers like tiktoken or GPT-2&rsquo;s tokenizer?</p>
<p>Standard tokenizers would force the model to waste capacity reconstructing fragmented historical words from subwords rather than learning historical language patterns. The model might learn to generate &ldquo;Qu&rdquo; + &ldquo;oth&rdquo; but struggle to use &ldquo;quoth&rdquo; in new contexts. Historical phrases like &ldquo;methinks&rdquo; would split into meaningless fragments, losing semantic coherence. London geography becomes particularly problematic, as place names like &ldquo;Newgate&rdquo; fragment, making spatial relationships harder to understand.</p>
<p><strong>Generation Quality Issues:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># What you&#39;d get with standard tokenizer:</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;Quoth the alderman, &#39;Tis a fair day at Newgate&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">→</span> Generates: <span style="color:#a6da95">&#34;Qu oth the ald erman, &#39;T is a fair day at New gate&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">→</span> Result: Broken, unreadable historical text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># What you get with our custom tokenizer:</span>
</span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;Quoth the alderman, &#39;Tis a fair day at Newgate&#34;</span>  
</span></span><span style="display:flex;"><span><span style="color:#ed8796">→</span> Generates: <span style="color:#a6da95">&#34;Quoth the alderman, &#39;Tis a fair day at Newgate&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">→</span> Result: Authentic, coherent historical text</span></span></code></pre></div>
<p>A vocabulary that&rsquo;s too small (10K tokens) would fragment even more historical words, making the problem worse, while a vocabulary that&rsquo;s too large (100K+ tokens) would overfit to rare historical terms, wasting capacity on words that appear only once. Our choice of 30K tokens provides a balanced approach that captures common historical patterns without overfitting, ensuring the model learns the most important historical language patterns efficiently.</p>
<p><strong>Real-World Example:</strong>
With a standard tokenizer, our model might generate:</p>
<blockquote>
<p><code>&quot;The ald erman walk ed to New gate where he saw the pris oner&quot;</code></p></blockquote>
<p>With our custom tokenizer, it generates:</p>
<blockquote>
<p><code>&quot;The alderman walked to Newgate where he saw the prisoner&quot;</code></p></blockquote>
<p>The difference in historical text authenticity is significant between the two approaches.</p>
<h3 id="32-tokenizer-architecture">3.2 Tokenizer Architecture</h3>
<p>I had started with the easier WordPiece tokenizer (more of an accident rather than by design). Still, I realized later that it was unsuitable for historical text due to the <code>##</code> subword prefix artifacts. We need a tokenizer that can handle historical English efficiently while preserving semantic meaning, unlike standard tokenizers like GPT-2&rsquo;s WordPiece approach, which fragments historical language and, as a result, destroys the linguistic patterns we want to preserve. After some experimentation, I settled on a custom Byte Pair Encoding (BPE) tokenizer trained specifically on historical English.</p>
<p>BPE is a subword tokenization algorithm that learns to break text into meaningful subword units by iteratively finding the most frequent character pairs in the training corpus and merging them into single tokens. The process begins with individual characters and gradually evolves into common words and phrases.</p>
<p>For example, if <code>&quot;th&quot;</code> appears frequently in our historical corpus, BPE will learn to treat it as a single token rather than separate <code>&quot;t&quot;</code> and <code>&quot;h&quot;</code> tokens. This is particularly valuable for historical English, where words like <code>&quot;thou&quot;</code>, <code>&quot;thee&quot;</code>, and <code>&quot;thine&quot;</code> share common prefixes and suffixes.</p>
<h4 id="321-tokenizer-training-process">3.2.1 Tokenizer Training Process</h4>
<p>The BPE training algorithm analyzes our entire historical corpus to identify the most frequent character combinations, building a vocabulary that&rsquo;s optimized for historical language patterns. We start with a base alphabet (comprising all letters) and special tokens, then iteratively merge the most frequent pairs until we reach our target vocabulary size of 30,000 tokens. This ensures that common historical words, such as <code>&quot;quoth&quot;</code>, <code>&quot;hast&quot;</code>, and <code>&quot;methinks&quot;</code>, are treated as single tokens, while still allowing for the handling of rare or unknown words by breaking them into learned subword units.</p>
<p>The training process is computationally efficient and produces a tokenizer that&rsquo;s specifically tuned to the linguistic patterns found in our historical corpus.</p>
<p>In this case, we don&rsquo;t have to reinvent the wheel and use the Hugging Face <code>tokenizers</code> library, which provides a modular approach to building custom tokenizers. The library is organized into several key components: <code>models</code> define the core tokenization algorithm (BPE, WordPiece, Unigram), <code>pre_tokenizers</code> handle initial text splitting, <code>normalizers</code> clean and standardize text, <code>trainers</code> configure the learning process, and <code>processors</code> handle special token insertion. This modular design enables us to mix and match components to create a tokenizer tailored to our specific use case.</p>
<p>The <code>models</code> module offers several tokenization algorithms: <code>BPE()</code> for Byte Pair Encoding (what we use), <code>WordPiece()</code> for Google&rsquo;s WordPiece algorithm, <code>Unigram()</code> for Google&rsquo;s Unigram language model, and <code>WordLevel()</code> for simple word-level tokenization.</p>
<p>Each has different strengths - BPE is efficient and handles unknown words well, WordPiece is used by BERT but creates <code>##</code> artifacts, Unigram is more flexible but computationally expensive, and WordLevel is simple but creates very large vocabularies.</p>
<p>Let us look at the code in <a href="#listing9" class="listing-ref">Listing 9</a> for training our custom historical BPE tokenizer:</p>
<figure id="listing9"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">train_tokenizer</span>(<span style="color:#91d7e3">self</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Train a custom tokenizer for historical English&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Import the tokenizers library components</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">tokenizers</span> <span style="color:#8bd5ca">import</span> Tokenizer, models, pre_tokenizers, processors, trainers
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">tokenizers.normalizers</span> <span style="color:#8bd5ca">import</span> Sequence, NFD, StripAccents
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#a6da95">&#34;Training custom historical tokenizer...&#34;</span>)
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Corpus: </span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>corpus_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Target vocabulary: </span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>vocab_size<span style="color:#a6da95">:</span><span style="color:#a6da95">,</span><span style="color:#a6da95">}</span><span style="color:#a6da95"> tokens&#34;</span>)
</span></span><span style="display:flex;"><span>    logger<span style="color:#91d7e3;font-weight:bold">.</span>info(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Output directory: </span><span style="color:#a6da95">{</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>output_dir<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Initialize BPE tokenizer (not WordPiece)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># models.BPE() creates a Byte Pair Encoding model that will learn subword patterns</span>
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> Tokenizer(models<span style="color:#91d7e3;font-weight:bold">.</span>BPE())
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Normalizers for historical text - preserve case for better text reconstruction</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Normalizers clean and standardize text before tokenization</span>
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>normalizer <span style="color:#91d7e3;font-weight:bold">=</span> Sequence([
</span></span><span style="display:flex;"><span>        NFD(),           <span style="color:#6e738d;font-style:italic"># Unicode normalization - converts characters to canonical form</span>
</span></span><span style="display:flex;"><span>        StripAccents()   <span style="color:#6e738d;font-style:italic"># Remove accents - converts &#34;café&#34; to &#34;cafe&#34;</span>
</span></span><span style="display:flex;"><span>    ])
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Pre-tokenizer for historical English - use simple whitespace splitting</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Pre-tokenizers split text into initial segments before the main tokenization</span>
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>pre_tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> pre_tokenizers<span style="color:#91d7e3;font-weight:bold">.</span>Sequence([
</span></span><span style="display:flex;"><span>        pre_tokenizers<span style="color:#91d7e3;font-weight:bold">.</span>WhitespaceSplit(),  <span style="color:#6e738d;font-style:italic"># Split on whitespace</span>
</span></span><span style="display:flex;"><span>        pre_tokenizers<span style="color:#91d7e3;font-weight:bold">.</span>Punctuation()       <span style="color:#6e738d;font-style:italic"># Split punctuation from words</span>
</span></span><span style="display:flex;"><span>    ])
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Special tokens for historical English</span>
</span></span><span style="display:flex;"><span>    special_tokens <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|startoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|pad|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|unk|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|mask|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Historical language tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|thou|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thee|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thy|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thine|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|hast|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|hath|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|doth|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|dost|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|quoth|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|tis|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|twas|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|twill|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># London geography tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|london|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thames|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|westminster|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|tower|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|newgate|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|southwark|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|cheapside|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|fleet|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|ludgate|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|aldgate|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Historical period tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|tudor|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|stuart|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|georgian|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|regency|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|victorian|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Social class tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|noble|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|gentleman|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|commoner|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|apprentice|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|yeoman|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Professional tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|apothecary|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|coachman|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|chimneysweep|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|baker|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|butcher|&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># BPE trainer configuration</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># The trainer defines how the BPE algorithm learns from our corpus</span>
</span></span><span style="display:flex;"><span>    trainer <span style="color:#91d7e3;font-weight:bold">=</span> trainers<span style="color:#91d7e3;font-weight:bold">.</span>BpeTrainer(
</span></span><span style="display:flex;"><span>        vocab_size<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>vocab_size,        <span style="color:#6e738d;font-style:italic"># Target vocabulary size (30,000 tokens) - balanced between coverage and efficiency</span>
</span></span><span style="display:flex;"><span>        special_tokens<span style="color:#91d7e3;font-weight:bold">=</span>special_tokens,     <span style="color:#6e738d;font-style:italic"># Pre-defined tokens that are always included</span>
</span></span><span style="display:flex;"><span>        min_frequency<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>,                   <span style="color:#6e738d;font-style:italic"># Minimum frequency prevents vocabulary pollution from OCR errors</span>
</span></span><span style="display:flex;"><span>        show_progress<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>,                <span style="color:#6e738d;font-style:italic"># Display training progress</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Removed continuing_subword_prefix=&#34;##&#34; to eliminate WordPiece-style artifacts</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># This ensures pure BPE tokenization without ## symbols in generated text</span>
</span></span><span style="display:flex;"><span>        initial_alphabet<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#34;a&#34;</span>, <span style="color:#a6da95">&#34;b&#34;</span>, <span style="color:#a6da95">&#34;c&#34;</span>, <span style="color:#a6da95">&#34;d&#34;</span>, <span style="color:#a6da95">&#34;e&#34;</span>, <span style="color:#a6da95">&#34;f&#34;</span>, <span style="color:#a6da95">&#34;g&#34;</span>, <span style="color:#a6da95">&#34;h&#34;</span>, <span style="color:#a6da95">&#34;i&#34;</span>, <span style="color:#a6da95">&#34;j&#34;</span>, <span style="color:#a6da95">&#34;k&#34;</span>, <span style="color:#a6da95">&#34;l&#34;</span>, <span style="color:#a6da95">&#34;m&#34;</span>, 
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#34;n&#34;</span>, <span style="color:#a6da95">&#34;o&#34;</span>, <span style="color:#a6da95">&#34;p&#34;</span>, <span style="color:#a6da95">&#34;q&#34;</span>, <span style="color:#a6da95">&#34;r&#34;</span>, <span style="color:#a6da95">&#34;s&#34;</span>, <span style="color:#a6da95">&#34;t&#34;</span>, <span style="color:#a6da95">&#34;u&#34;</span>, <span style="color:#a6da95">&#34;v&#34;</span>, <span style="color:#a6da95">&#34;w&#34;</span>, <span style="color:#a6da95">&#34;x&#34;</span>, <span style="color:#a6da95">&#34;y&#34;</span>, <span style="color:#a6da95">&#34;z&#34;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#34;A&#34;</span>, <span style="color:#a6da95">&#34;B&#34;</span>, <span style="color:#a6da95">&#34;C&#34;</span>, <span style="color:#a6da95">&#34;D&#34;</span>, <span style="color:#a6da95">&#34;E&#34;</span>, <span style="color:#a6da95">&#34;F&#34;</span>, <span style="color:#a6da95">&#34;G&#34;</span>, <span style="color:#a6da95">&#34;H&#34;</span>, <span style="color:#a6da95">&#34;I&#34;</span>, <span style="color:#a6da95">&#34;J&#34;</span>, <span style="color:#a6da95">&#34;K&#34;</span>, <span style="color:#a6da95">&#34;L&#34;</span>, <span style="color:#a6da95">&#34;M&#34;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#34;N&#34;</span>, <span style="color:#a6da95">&#34;O&#34;</span>, <span style="color:#a6da95">&#34;P&#34;</span>, <span style="color:#a6da95">&#34;Q&#34;</span>, <span style="color:#a6da95">&#34;R&#34;</span>, <span style="color:#a6da95">&#34;S&#34;</span>, <span style="color:#a6da95">&#34;T&#34;</span>, <span style="color:#a6da95">&#34;U&#34;</span>, <span style="color:#a6da95">&#34;V&#34;</span>, <span style="color:#a6da95">&#34;W&#34;</span>, <span style="color:#a6da95">&#34;X&#34;</span>, <span style="color:#a6da95">&#34;Y&#34;</span>, <span style="color:#a6da95">&#34;Z&#34;</span>]
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Train the tokenizer on our historical corpus</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># This is where the BPE algorithm learns the optimal subword patterns</span>
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>train([<span style="color:#91d7e3">str</span>(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>corpus_path)], trainer)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> tokenizer</span></span></code></pre></div><figcaption>
        <strong>Listing 9: Custom Tokenizer Training Function</strong>
    </figcaption>
</figure>
<h4 id="322-tokenization-architecture-decisions">3.2.2 Tokenization Architecture Decisions</h4>
<p>Our custom historical tokenizer necessitated several critical design decisions to handle historical English effectively. We evaluated multiple tokenization approaches including <strong>Byte Pair Encoding (BPE)</strong> (<a
	
		href = "https://arxiv.org/abs/1508.07909"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Sennrich et al., 2016
	</span>
</a>), <strong>WordPiece</strong> (<a
	
		href = "https://research.google/pubs/pub37842/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Schuster &amp; Nakajima, 2012
	</span>
</a>), <strong>Unigram Language Model</strong> (<a
	
		href = "https://arxiv.org/abs/1804.10959"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Kudo, 2018
	</span>
</a>), <strong>SentencePiece</strong> (<a
	
		href = "https://arxiv.org/abs/1808.06226"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Kudo &amp; Richardson, 2018
	</span>
</a>), and traditional character-level and word-level tokenization. Each approach has distinct trade-offs: BPE produces clean subwords without special markers (used by GPT models), WordPiece adds <code>##</code> prefixes that contaminate generated text (used by BERT), Unigram uses probabilistic modeling but is computationally expensive, SentencePiece treats text as raw bytes and excels at multilingual scenarios, while character-level and word-level tokenization either produce impractically long sequences or massive vocabularies.</p>
<p>For historical text generation, BPE provides the optimal balance of clean output, efficient training, and effective vocabulary coverage, as demonstrated by <a
	
		href = "https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Radford et al., 2019
	</span>
</a> and <a
	
		href = "https://arxiv.org/abs/2112.10508"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Mielke et al., 2021
	</span>
</a>. We also preserve case throughout tokenization, since historical text often uses capitalization for semantic meaning (e.g., &ldquo;Thou&rdquo; vs. &ldquo;thou&rdquo;), and include over 150 carefully designed special tokens that capture historical language patterns, London geography, and social context. This combination ensures our tokenizer can effectively learn and generate authentic historical language while maintaining computational efficiency.</p>
<h3 id="33-special-token-design-capturing-historical-language-patterns">3.3 Special Token Design: Capturing Historical Language Patterns</h3>
<p>Historical English contains linguistic patterns, vocabulary, and cultural references that are no longer present in modern English. Standard tokenizers fragment these patterns, destroying the semantic meaning crucial for historical text generation. The solution here was to design 150 special tokens that capture the essence of historical English, organized into strategic categories that reflect the linguistic and cultural structure of 1500-1850 English.</p>
<figure id="listing10"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_special_tokens</span>() <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">list</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Create special tokens for historical English&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    special_tokens <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Basic control tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|startoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|pad|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|unk|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|mask|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Historical language tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|thou|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thee|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thy|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thine|&gt;&#34;</span>,  <span style="color:#6e738d;font-style:italic"># Second person pronouns</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|hast|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|hath|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|doth|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|dost|&gt;&#34;</span>,  <span style="color:#6e738d;font-style:italic"># Archaic verb forms</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|quoth|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|tis|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|twas|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|twill|&gt;&#34;</span>, <span style="color:#6e738d;font-style:italic"># Common contractions</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># London geography tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|london|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|thames|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|westminster|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|tower|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|newgate|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|southwark|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|cheapside|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|fleet|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|ludgate|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|aldgate|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Historical period tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|tudor|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|stuart|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|georgian|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|regency|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|victorian|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Social and professional tokens</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|noble|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|gentleman|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|commoner|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|apothecary|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|coachman|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;&lt;|merchant|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|court|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|jury|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|verdict|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|church|&gt;&#34;</span>, <span style="color:#a6da95">&#34;&lt;|parish|&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> special_tokens</span></span></code></pre></div><figcaption>
        <strong>Listing 10: Special Tokens Creation Function</strong>
    </figcaption>
</figure>
<h4 id="331-token-category-analysis">3.3.1 Token Category Analysis</h4>
<p>Our special token vocabulary spans ten carefully curated categories, each designed to capture essential aspects of historical London life. The largest categories focus on <strong>Historical Language</strong> (25 tokens) and <strong>London Geography</strong> (20 tokens), providing the linguistic and spatial foundation for authentic historical text generation. These tokens capture archaic pronouns like <code>&quot;thou&quot;</code> and <code>&quot;thee,&quot;</code> along with specific London locations like <code>&quot;Thames&quot;</code> and <code>&quot;Newgate&quot;</code> that were central to historical narratives.</p>
<p>The remaining categories address the social, professional, and cultural dimensions of historical society. <strong>Social Class</strong> and <strong>Professional</strong> tokens (35 tokens combined) reflect the highly stratified nature of historical London, enabling accurate dialogue between nobles, commoners, and various tradespeople. <strong>Legal and Judicial</strong> tokens support court proceedings from the Old Bailey, while <strong>Religious</strong> tokens capture the central role of faith in historical society. <strong>Temporal</strong>, <strong>Currency</strong>, and <strong>Transportation</strong> tokens (35 tokens combined) provide the temporal, economic, and logistical context that makes historical narratives authentic and believable.</p>
<h4 id="332-special-token-categories-visualization">3.3.2 Special Token Categories Visualization</h4>
<p>Let us visualize the special token categories and their relationships as shown below. These special tokens enable the model to understand and generate authentic historical language. Without them, the model would fragment historical concepts into meaningless subwords, losing the cultural and linguistic context that makes historical text generation both challenging and rewarding.</p>
<figure class="align-center " id="fig5">
    <pre class="mermaid">graph LR
    A[🔤 Special Tokens&lt;br/&gt;150+ Total] --&gt; B[📜 Historical Language&lt;br/&gt;25 tokens]
    A --&gt; C[🏛️ London Geography&lt;br/&gt;20 tokens]
    A --&gt; D[⏰ Historical Periods&lt;br/&gt;10 tokens]
    A --&gt; E[👥 Social Classes&lt;br/&gt;15 tokens]
    A --&gt; F[💼 Professions&lt;br/&gt;20 tokens]
    A --&gt; G[⚖️ Legal &amp; Judicial&lt;br/&gt;10 tokens]
    A --&gt; H[⛪ Religious&lt;br/&gt;10 tokens]
    A --&gt; I[🕐 Temporal&lt;br/&gt;15 tokens]
    A --&gt; J[💰 Currency &amp; Measurement&lt;br/&gt;10 tokens]
    A --&gt; K[🚗 Transportation&lt;br/&gt;10 tokens]

    B --&gt; B1[&#34;&lt;|thou|&gt;, &lt;|thee|&gt;, &lt;|hast|&gt;, &lt;|doth|&gt;, &lt;|quoth|&gt;&#34;]
    C --&gt; C1[&#34;&lt;|london|&gt;, &lt;|thames|&gt;, &lt;|newgate|&gt;, &lt;|westminster|&gt;&#34;]
    D --&gt; D1[&#34;&lt;|tudor|&gt;, &lt;|stuart|&gt;, &lt;|georgian|&gt;, &lt;|regency|&gt;&#34;]
    E --&gt; E1[&#34;&lt;|noble|&gt;, &lt;|gentleman|&gt;, &lt;|commoner|&gt;, &lt;|yeoman|&gt;&#34;]
    F --&gt; F1[&#34;&lt;|apothecary|&gt;, &lt;|coachman|&gt;, &lt;|chimneysweep|&gt;, &lt;|baker|&gt;&#34;]
    G --&gt; G1[&#34;&lt;|court|&gt;, &lt;|jury|&gt;, &lt;|verdict|&gt;, &lt;|prisoner|&gt;&#34;]
    H --&gt; H1[&#34;&lt;|church|&gt;, &lt;|parish|&gt;, &lt;|prayer|&gt;, &lt;|blessed|&gt;&#34;]
    I --&gt; I1[&#34;&lt;|morn|&gt;, &lt;|eve|&gt;, &lt;|season|&gt;, &lt;|year|&gt;&#34;]
    J --&gt; J1[&#34;&lt;|shilling|&gt;, &lt;|pound|&gt;, &lt;|yard|&gt;, &lt;|furlong|&gt;&#34;]
    K --&gt; K1[&#34;&lt;|coach|&gt;, &lt;|carriage|&gt;, &lt;|horse|&gt;, &lt;|vessel|&gt;&#34;]

    %% class definitions (custom palette matching your original)
    classDef cls_root fill:#e1f5fe,stroke:#81d4fa,color:#000;
    classDef cls_hist fill:#f3e5f5,stroke:#ce93d8,color:#000;
    classDef cls_geo fill:#e8f5e8,stroke:#a5d6a7,color:#000;
    classDef cls_period fill:#fff3e0,stroke:#ffe0b2,color:#000;
    classDef cls_social fill:#fce4ec,stroke:#f8bbd0,color:#000;
    classDef cls_prof fill:#f1f8e9,stroke:#c5e1a5,color:#000;
    classDef cls_legal fill:#e0f2f1,stroke:#80cbc4,color:#000;
    classDef cls_relig fill:#f9fbe7,stroke:#e6ee9c,color:#000;
    classDef cls_temp fill:#e3f2fd,stroke:#90caf9,color:#000;
    classDef cls_curr fill:#fef7e0,stroke:#ffe082,color:#000;
    classDef cls_trans fill:#f3e5f5,stroke:#e1bee7,color:#000;

    %% assign classes
    class A cls_root;
    class B cls_hist;
    class C cls_geo;
    class D cls_period;
    class E cls_social;
    class F cls_prof;
    class G cls_legal;
    class H cls_relig;
    class I cls_temp;
    class J cls_curr;
    class K cls_trans;</pre>
    <figcaption>Figure 5: Special Token Categories and Examples</figcaption>
</figure>
<h3 id="35-post-processing-and-hugging-face-integration">3.5 Post-Processing and Hugging Face Integration</h3>
<p>After training our custom tokenizer, we need to make it compatible with the broader machine learning ecosystem and ensure it works properly with language model training. Raw tokenizers can only convert text to tokens and back. Still, language models require additional functionality, such as special token handling, sequence padding, and integration with popular frameworks like Hugging Face Transformers.</p>
<p>The challenge, though, is that language model training requires specific formatting that raw tokenizers don&rsquo;t provide. For example, training sequences need to be wrapped with special start/end tokens (<code>&lt;|startoftext|&gt;</code> and <code>&lt;|endoftext|&gt;</code>), padded to consistent lengths for batch processing, and integrated with the rest of the ecosystem. In our case, we also want to utilize Hugging Face and its ecosystem, allowing us to leverage standard training scripts and model architectures. Without proper post-processing, our custom tokenizer would be incompatible with existing training infrastructure.</p>
<p>We add post-processing capabilities that wrap text sequences with control tokens and create Hugging Face-compatible tokenizer files, ensuring seamless integration with the broader machine learning ecosystem while preserving our historical text optimizations.</p>
<p>There are three key areas that we need to consider:</p>
<ul>
<li>
<p><strong>Understanding Post-Processing:</strong> The first step is adding a post-processor that automatically wraps every text sequence with special start and end tokens. This is crucial because language models must be able to identify where sequences begin and end during training. For example, when we tokenize <code>&quot;Hello world&quot;</code>, the post-processor automatically converts it to <code>&lt;|startoftext|&gt; Hello world &lt;|endoftext|&gt;</code>. This template processing ensures consistent formatting across all our training data.</p>
</li>
<li>
<p><strong>Hugging Face Integration:</strong> Next, we create a Hugging Face-compatible wrapper around our custom tokenizer. This wrapper maps our special tokens to the standard token types that Hugging Face expects: beginning-of-sequence (bos), end-of-sequence (eos), padding, unknown, and masking tokens. This mapping allows our custom tokenizer to work seamlessly with standard training scripts and model architectures.</p>
</li>
<li>
<p><strong>Special Token Functions:</strong> Each special token serves a specific purpose in language model training. The beginning-of-sequence token indicates when a new text starts, the end-of-sequence token marks the end of the text, padding tokens ensure all sequences in a batch have the same length, unknown tokens handle words not in our vocabulary, and masking tokens are used during training for masked language modeling tasks.</p>
</li>
</ul>
<p>The code in <a href="#listing11" class="listing-ref">Listing 11</a> demonstrates how we implement these post-processing steps and create a Hugging Face-compatible tokenizer:</p>
<figure id="listing11"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_huggingface_tokenizer</span>(tokenizer: Tokenizer, max_length: <span style="color:#91d7e3">int</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1024</span>) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> PreTrainedTokenizerFast:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Create Hugging Face compatible tokenizer&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> PreTrainedTokenizerFast
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add post-processor for sequence formatting</span>
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>post_processor <span style="color:#91d7e3;font-weight:bold">=</span> processors<span style="color:#91d7e3;font-weight:bold">.</span>TemplateProcessing(
</span></span><span style="display:flex;"><span>        single<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|startoftext|&gt; $A &lt;|endoftext|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        special_tokens<span style="color:#91d7e3;font-weight:bold">=</span>[
</span></span><span style="display:flex;"><span>            (<span style="color:#a6da95">&#34;&lt;|startoftext|&gt;&#34;</span>, <span style="color:#f5a97f">1</span>),
</span></span><span style="display:flex;"><span>            (<span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#f5a97f">0</span>),
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create Hugging Face tokenizer wrapper</span>
</span></span><span style="display:flex;"><span>    hf_tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> PreTrainedTokenizerFast(
</span></span><span style="display:flex;"><span>        tokenizer_object<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer,
</span></span><span style="display:flex;"><span>        bos_token<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|startoftext|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        eos_token<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        pad_token<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|pad|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        unk_token<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|unk|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        mask_token<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&lt;|mask|&gt;&#34;</span>,
</span></span><span style="display:flex;"><span>        model_max_length<span style="color:#91d7e3;font-weight:bold">=</span>max_length
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> hf_tokenizer</span></span></code></pre></div><figcaption>
        <strong>Listing 11: Hugging Face Tokenizer Integration</strong>
    </figcaption>
</figure>
<p>Without this integration, our custom tokenizer would be incompatible with standard language model training. The post-processor ensures proper sequence formatting, while the Hugging Face wrapper enables seamless integration with existing training infrastructure and model architectures. This makes our tokenizer compatible with standard training frameworks, allowing for easy sharing and deployment.</p>
<h3 id="36-testing-and-validation">3.6 Testing and Validation</h3>
<p>We need to ensure the tokenizer works correctly with historical text before using it for model training. This requires testing on diverse historical samples and validating both encoding and decoding accuracy. A simple way to do this is to encode a set of historical text samples, decode them back, and check if the original text is perfectly reconstructed. We also want to verify that special tokens are used correctly in the tokenized output.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">test_historical_tokenizer</span>(tokenizer: Tokenizer) <span style="color:#91d7e3;font-weight:bold">-&gt;</span> <span style="color:#91d7e3">dict</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;Test the trained tokenizer on historical text samples&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    test_texts <span style="color:#91d7e3;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;In the year of our Lord 1834, the streets of London were filled with the sounds of horse-drawn carriages.&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The gentleman from the country said, &#39;I have never seen such a sight in all my days.&#39;&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;The Thames flowed dark and mysterious through the heart of the city.&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6da95">&#34;It was the best of times, it was the worst of times.&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    results <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;perfect_reconstruction&#39;</span>: <span style="color:#f5a97f">0</span>, <span style="color:#a6da95">&#39;special_token_usage&#39;</span>: <span style="color:#f5a97f">0</span>, <span style="color:#a6da95">&#39;failed_tests&#39;</span>: []}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> i, text <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">enumerate</span>(test_texts):
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Encode and decode text</span>
</span></span><span style="display:flex;"><span>        encoded <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(text)
</span></span><span style="display:flex;"><span>        decoded <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(encoded<span style="color:#91d7e3;font-weight:bold">.</span>ids)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Check reconstruction accuracy</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> decoded<span style="color:#91d7e3;font-weight:bold">.</span>strip() <span style="color:#91d7e3;font-weight:bold">==</span> text<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            results[<span style="color:#a6da95">&#39;perfect_reconstruction&#39;</span>] <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            results[<span style="color:#a6da95">&#39;failed_tests&#39;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>append({<span style="color:#a6da95">&#39;index&#39;</span>: i, <span style="color:#a6da95">&#39;original&#39;</span>: text, <span style="color:#a6da95">&#39;decoded&#39;</span>: decoded})
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Check special token usage</span>
</span></span><span style="display:flex;"><span>        special_tokens <span style="color:#91d7e3;font-weight:bold">=</span> [token <span style="color:#c6a0f6">for</span> token <span style="color:#91d7e3;font-weight:bold">in</span> encoded<span style="color:#91d7e3;font-weight:bold">.</span>tokens <span style="color:#c6a0f6">if</span> token<span style="color:#91d7e3;font-weight:bold">.</span>startswith(<span style="color:#a6da95">&#39;&lt;|&#39;</span>) <span style="color:#91d7e3;font-weight:bold">and</span> token<span style="color:#91d7e3;font-weight:bold">.</span>endswith(<span style="color:#a6da95">&#39;|&gt;&#39;</span>)]
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> special_tokens:
</span></span><span style="display:flex;"><span>            results[<span style="color:#a6da95">&#39;special_token_usage&#39;</span>] <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> results</span></span></code></pre></div>
<p><strong>Test Results:</strong></p>
<ul>
<li><strong>Perfect Reconstruction</strong>: 99%+ accuracy on test cases</li>
<li><strong>Special Token Usage</strong>: 80%+ of test cases use special tokens</li>
<li><strong>Average Compression Ratio</strong>: ~0.3 tokens per word (highly efficient)</li>
<li><strong>Success Rate</strong>: 99%+ for historical text samples</li>
</ul>
<p>It is essential to conduct comprehensive testing to ensure the tokenizer operates reliably. In our case, the test cases cover different historical periods, writing styles, and linguistic patterns, giving us confidence that the tokenizer can handle the full range of historical text in our corpus. For a real-world LLM, this is, of course, more complex and would need to cover a broader set of areas.</p>
<p><strong>Tokenizer Performance Validation</strong></p>
<p>Not surprisingly, our custom tokenizer significantly outperforms standard approaches on historical text, as demonstrated by comprehensive metrics that compare it to GPT-2&rsquo;s tokenizer, as shown in the table below. These metrics indicate that our custom tokenizer significantly outperforms standard approaches for historical text. The improved compression ratio and reconstruction accuracy ensure that the model learns from authentic historical language rather than tokenization artifacts, which is crucial for generating coherent and historically accurate text.</p>
<table>
  <thead>
      <tr>
          <th>Metric</th>
          <th>Standard GPT-2</th>
          <th>Our Custom Tokenizer</th>
          <th>Improvement</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Vocabulary Size</strong></td>
          <td>50,257 tokens</td>
          <td>30,000 tokens</td>
          <td>40% smaller</td>
      </tr>
      <tr>
          <td><strong>Special Tokens</strong></td>
          <td>4 tokens</td>
          <td>150+ tokens</td>
          <td>37x more</td>
      </tr>
      <tr>
          <td><strong>Compression Ratio</strong></td>
          <td>~0.4 tokens/word</td>
          <td>~0.3 tokens/word</td>
          <td>25% better</td>
      </tr>
      <tr>
          <td><strong>Reconstruction Accuracy</strong></td>
          <td>95%</td>
          <td>99%+</td>
          <td>4% better</td>
      </tr>
      <tr>
          <td><strong>Historical Language Support</strong></td>
          <td>Poor</td>
          <td>Good</td>
          <td>N/A</td>
      </tr>
  </tbody>
</table>
<p>These metrics validate that our 30K token vocabulary provides optimal coverage for historical text while remaining manageable for small language models. The 150+ special tokens capture linguistic patterns of 1500-1850 English, and the 25% better compression ratio means historical text is represented more efficiently, allowing the model to process longer sequences. The 99%+ reconstruction accuracy ensures no information is lost during tokenization, while excellent performance on archaic vocabulary, period-specific terminology, and London geography demonstrates the tokenizer&rsquo;s effectiveness for historical language modeling.</p>
<h3 id="38-implementation-and-usage">3.8 Implementation and Usage</h3>
<p>The complete tokenizer implementation, including training scripts, testing utilities, and validation tools, is available in the <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		helloLondon GitHub repository
	</span>
</a>. The repository provides:</p>
<ul>
<li><strong>Training Code</strong>: Complete BPE tokenizer training with configurable vocabulary sizes and special token definitions.</li>
<li><strong>Testing Utilities</strong>: Comprehensive validation tools for testing tokenizer performance on historical text</li>
<li><strong>Integration Examples</strong>: Ready-to-use code for incorporating the tokenizer into your own projects</li>
<li><strong>Documentation</strong>: Detailed usage guides and API references</li>
</ul>
<p>This implementation demonstrates how to build production-ready tokenizers for specialized domains, with particular focus on historical language processing and integration with modern ML frameworks.</p>
<h2 id="4-current-limitations">4. Current Limitations</h2>
<p>This project is designed as a learning exercise for those new to AI and LLM development. While we&rsquo;ve built a functional system that demonstrates core concepts, this is not production-ready code and has several limitations that would need to be addressed for real-world deployment:</p>
<p><strong>Data Scale &amp; Quality:</strong></p>
<ul>
<li>Corpus size: Our 500M character corpus is tiny compared to production LLMs, which typically use 100x-1000x more data (50B-500B+ characters). This limits the model&rsquo;s ability to learn diverse patterns and reduces the quality of generated output.</li>
<li>Source diversity: With only 218 sources, we lack comprehensive historical coverage across the 1500-1850 span, potentially missing important linguistic evolution patterns and regional variations.</li>
<li>Geographic bias: Heavy focus on London may not accurately represent broader historical English patterns from other regions, limiting the model&rsquo;s generalizability.</li>
<li>Bias detection: We lack systematic approaches to identify or mitigate historical biases in the data, which could lead to the model perpetuating outdated or problematic language patterns.</li>
<li>Quality assessment: Our cleaning pipeline, while effective for common issues, overlooks many edge cases and artifacts that would require more sophisticated ML-based quality assessment in production.</li>
</ul>
<p><strong>Tokenizer &amp; Model Architecture:</strong></p>
<ul>
<li>Vocabulary size: Our 30K token vocabulary is small compared to modern models (which often use 50K-100K+ tokens), limiting the model&rsquo;s ability to represent diverse vocabulary efficiently.</li>
<li>Special tokens: The 150+ special tokens are manually curated rather than learned from data, which may miss important patterns that data-driven approaches would discover.</li>
<li>Context length: The 1024 token context window is very short compared to modern models (which often use 4K-32K+ tokens), limiting the model&rsquo;s ability to maintain coherence in longer texts.</li>
<li>Language support: No support for other languages or historical variants beyond English, significantly limiting the model&rsquo;s applicability.</li>
<li>Tokenization approach: While our BPE approach is clean and avoids WordPiece artifacts, it may not be optimal for all historical text patterns and could benefit from more sophisticated techniques.</li>
</ul>
<p><strong>Technical Infrastructure:</strong></p>
<ul>
<li>Error handling: Basic error handling with limited logging and monitoring makes it difficult to debug issues and track system health in production.</li>
<li>Testing: Minimal test coverage that excludes edge cases means many potential failure modes remain undetected until they occur in production.</li>
<li>Performance: No optimization for speed, memory, or distributed processing, making the system unsuitable for production-scale deployment.</li>
<li>Data management: Lacks data versioning and reproducibility guarantees, making it difficult to track changes and reproduce results across different environments.</li>
<li>Security: No security considerations for data handling and model deployment, creating potential vulnerabilities for sensitive historical data.</li>
<li>Compliance: Missing compliance considerations for GDPR, data privacy, and regulatory requirements, which are essential for production deployment.</li>
<li>Monitoring: No production monitoring, alerting, or observability features, making it impossible to detect and respond to issues in real-time.</li>
</ul>
<p>These limitations are intentional trade-offs made to keep the project manageable and focused on core learning objectives, but they represent significant gaps for production deployment.</p>
<h3 id="43-what-youd-need-for-production">4.3 What You&rsquo;d Need for Production</h3>
<p><strong>Data Engineering and Legal Framework</strong></p>
<p>Production systems require 100x-1000x more data from diverse sources, with ML-based quality assessment, bias detection, and filtering that goes far beyond our simple heuristics. You&rsquo;d need robust ETL pipelines with proper error handling and monitoring, as well as a comprehensive legal framework for copyright clearance, data licensing, and compliance management, which we haven&rsquo;t addressed.</p>
<p><strong>Model Architecture and Training</strong></p>
<p>Meaningful historical language understanding would require models with over 1 billion parameters, utilizing sophisticated training techniques, regularization, and optimization. You&rsquo;d need a comprehensive evaluation on diverse historical text tasks and domain-specific fine-tuning capabilities that our current system doesn&rsquo;t support.</p>
<p><strong>Infrastructure and Operations</strong></p>
<p>Production deployment requires a multi-GPU, multi-node distributed training infrastructure, production-grade model serving with load balancing and scaling, comprehensive monitoring and alerting systems, and end-to-end security for both data and model protection—none of which our learning-focused system currently provides.</p>
<p>This progression from data → tokenizer → training → deployment provides a complete methodology for building specialized historical language models.</p>
<h2 id="5-resources-and-further-reading">5. Resources and Further Reading</h2>
<ul>
<li><strong>GitHub Repository</strong>: <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete source code for data collection and tokenizer training</li>
<li><strong>Part 1</strong>: <a
	
		href = "https://blog.desigeek.com/post/2025/09/building-llm-from-scratch-part1/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Building LLMs from Scratch - Part 1
	</span>
</a> - Quick start and overview</li>
<li><strong>Documentation</strong>: Complete guides in the <code>08_documentation/</code> folder covering every aspect of the project</li>
<li><strong>Book Reference</strong>: <a
	
		href = "https://a.co/d/ffzkJ7T"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts</li>
</ul>
<h2 id="6-summary">6. Summary</h2>
<p>This post represents Part 2 of our learning journey into the fundamentals of LLM development. While we&rsquo;ve built a functional data collection and tokenization system demonstrating core concepts, the real value lies in understanding:</p>
<ul>
<li><strong>Data flow</strong> from raw sources to training-ready corpora</li>
<li><strong>Tokenization impact</strong> on model performance across different approaches</li>
<li><strong>Challenges</strong> in processing historical and domain-specific text</li>
<li><strong>Trade-offs</strong> between quality, scale, and complexity</li>
<li><strong>Debugging and improvement</strong> strategies for encountered problems</li>
</ul>
<p>The limitations we&rsquo;ve identified are great learning opportunities. Every production LLM started as a learning project, and every limitation teaches you something new about how these systems work. This foundation prepares us for the next phase of our journey.</p>
<hr>
<p><strong>Ready for Part 3?</strong> Part 3 will cover the custom GPT architecture, GPU optimization strategies, and training infrastructure that transforms our clean data and custom tokenizer into working language models—while maintaining the same educational focus on understanding the fundamentals.</p>
<blockquote>
<p><strong>🧱 Series Posts</strong>: <a
	
		href = "/post/2025/09/building-llm-from-scratch-part1/"
	

	

	>
	
	<span>
		Part 1 – Using the Published Historical Models
	</span>
</a> | Part 2 (this post) | <a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3 – Training Architecture &amp; GPU Optimization
	</span>
</a> | <a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4 – Evaluation &amp; Deployment
	</span>
</a></p></blockquote>
]]></content:encoded>
    </item>
    <item>
      <title>🏛️How to build a Large Language Model from Scratch - Part 1</title>
      <link>/post/2025/09/building-llm-from-scratch-part1/</link>
      <pubDate>Tue, 23 Sep 2025 00:00:00 +0000</pubDate>
      <guid>/post/2025/09/building-llm-from-scratch-part1/</guid>
      <description>Learn how to build LLMs from scratch using historical London texts (1500-1850). Complete 4-part series with working code, published models, and educational deployment. Part 1: Get started in minutes.</description>
      <content:encoded><![CDATA[<p><strong>TL;DR</strong></p>
<p>In this post, I show how to build a working LLM from scratch and show a complete end-to-end pipeline from data gathering to training to deployment of a language model. For this project I concentrate on Old English and only related to London, using historical London texts (1500-1850). To show the flexibility, I built <strong>two language models</strong> which are identical in architecture and the only differs is their size and parameters (117M vs 354M).</p>
<blockquote>
<p><strong>⚠️ Educational Purpose</strong>: This is a learning project designed to teach LLM development concepts. For production-scale LLMs, you&rsquo;ll need much larger datasets, more sophisticated infrastructure, and additional considerations not covered here.</p></blockquote>
<p>This guide shows you how to monitor training progression, perform rapid evaluations, test models from both PyTorch checkpoints and published Hugging Face repositories, and ultimately publish your own - supported by complete code, live model artifacts, and educational inference tooling.</p>
<p><strong>4-Part Series</strong>:</p>
<ul>
<li><strong>Part 1 (this): Quick start, inference, and overview</strong></li>
<li><strong><a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2: Data collection and custom tokenizers
	</span>
</a></strong></li>
<li><strong><a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3: Model architecture and GPU training
	</span>
</a></strong></li>
<li><strong><a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4: Evaluation and deployment
	</span>
</a></strong></li>
</ul>
<h2 id="1-overview">1. Overview</h2>
<p><em>Train AI models on 1500-1850 London texts. Complete 4-part series covering data collection, training, and deployment. Part 1: Quick start and overview.</em></p>
<blockquote>
<p><strong>📖 Want to understand the core LLM concepts?</strong> This series focuses on implementation and hands-on building. For a deeper understanding of foundational concepts like tokenizers, prompt engineering, RAG, responsible AI, fine-tuning, and more, check out my book <a
	
		href = "https://a.co/d/ffzkJ7T"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		<strong>Generative AI in Action</strong>
	</span>
</a>.</p></blockquote>
<blockquote>
<p>You can learn more about the book → <a
	
		href = "https://blog.desigeek.com/post/2024/10/book-release-genai-in-action/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		by clicking here
	</span>
</a>📘.</p></blockquote>
<h3 id="11-what-was-built">1.1 What was built?</h3>
<p>I found many folks don&rsquo;t understand what it entails to build an LLM, and where we do have guides, they only share piecemeal elements and nothing that is comprehensive for someone who is new to this. There are more detailed guides on fine-tuning existing models, but not much on the complete development pipeline. This series outlines that by walking through the process of creating specialized language models trained exclusively on historical London texts from 1500 to 1850.</p>
<p>I am mostly doing this for my own learning, and also sharing what I can. Many work-related details, for obvious reasons, I cannot share and discuss, but some small pet projects like this embody the same sentiment.</p>
<p>The <strong>helloLondon Historical Language Models</strong> represent a complete end-to-end implementation, from data collection through deployment. Rather than fine-tuning existing models, I chose to train from the ground up to eliminate modern biases and create models that genuinely understand historical language patterns, cultural contexts, and period-specific knowledge.</p>
<p><strong>Two Model Variants</strong>
I built two identical models with the same architecture, tokenizer, and training process. The only difference is the number of parameters: an SLM (117M parameters) optimized for learning and resource-constrained environments, and a Regular model (354M parameters) designed for higher-quality generation.</p>
<p>Both use identical code with different configuration files, allowing you to understand the impact of model size on performance and choose the right variant for your needs.</p>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Parameters</th>
          <th>Iterations</th>
          <th>Training Time*</th>
          <th>Use Case</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>SLM</strong> (Small)</td>
          <td>117M</td>
          <td>60,000</td>
          <td>~8-12 hours</td>
          <td>Fast inference, resource-constrained</td>
      </tr>
      <tr>
          <td><strong>Regular</strong> (Full)</td>
          <td>354M</td>
          <td>60,000</td>
          <td>~28-32 hours</td>
          <td>High-quality generation</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p><strong>Note:</strong> Technically speaking, both these models can be called classified as SLMs given they are 117M and 354M parameters; however, for the sake of this project, I call the smaller of the two the SLM and the other regular.</p></blockquote>
<h3 id="12-core-pipelines">1.2 Core Pipelines</h3>
<p>The complete development pipeline encompasses multiple critical stages that transform raw historical texts into working language models. The process starts with <strong>data collection</strong>, where we systematically gather and filter over 218 historical London sources spanning 1500–1850. This process ensures we capture authentic period language while minimizing modern biases that could contaminate our models.</p>
<p>Next, we develop a <strong>custom tokenization system</strong> specifically designed for historical English. This involves training a domain-specific tokenizer with a 30,000-token vocabulary plus 150+ special tokens that capture period language patterns, archaic spellings, and historical terminology that modern tokenizers often miss.</p>
<p>The <strong>model architecture</strong> phase implements GPT-style causal language models entirely from scratch, creating two variants with 117M and 354M parameters, respectively. Both models share identical architecture and training processes, allowing for direct comparison of performance versus computational requirements.</p>
<p>Our <strong>training infrastructure</strong> leverages modern multi-GPU training with Distributed Data Parallel (DDP), comprehensive checkpointing for restart resilience, and real-time monitoring through Weights &amp; Biases. This ensures reliable training even across extended periods and hardware failures.</p>
<p><strong>Evaluation</strong> goes beyond standard metrics to include historical accuracy probes, perplexity tracking, qualitative generation review, and early failure detection. We specifically test how well our models understand historical context, period-appropriate language, and London geography.</p>
<p>Finally, <strong>deployment</strong> includes publishing models to Hugging Face alongside unified local and cloud inference scripts, making the models immediately accessible to researchers and developers worldwide.</p>
<h3 id="13-hands-on-experience">1.3 Hands-On Experience</h3>
<p>Every aspect of this project is designed for practical implementation and learning. The <strong>working code</strong> covers every stage from data collection through tokenizer training, model training, evaluation, and publishing - all fully implemented and documented with clear instructions and examples.</p>
<p>I already have both the models published on Hugging Face; which allows for <strong>Live models</strong> are immediately available for use, allowing you to test published checkpoints instantly or retrain from scratch with a single command. This dual approach lets you either jump straight into experimentation or understand the complete development process.</p>
<p>The project works with <strong>real data</strong> - over 500 million characters of authentic historical English from 1500–1850, carefully filtered to minimize modern bias while preserving the rich linguistic patterns of the period. This is using genuine historical texts that provide authentic training material.</p>
<p>Everything is <strong>well-structured</strong> with clear documentation, error handling, reproducible configurations, and automated publishing workflows. The codebase follows good development practices, making it suitable for learning LLM development concepts.</p>
<p>This series is structured to take you through the complete LLM development pipeline:</p>
<table>
  <thead>
      <tr>
          <th>Part</th>
          <th>Focus</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Part 1</strong> (this post)</td>
          <td>Quick start and end-to-end overview</td>
          <td>Use published models, understand the complete pipeline, and get hands-on experience with working code and live models. The intent is that if you want to build this, you can follow the instructions and get a model in the end. If you want to understand more of the inner workings and details, then those will be covered in the subsequent blog posts.</td>
      </tr>
      <tr>
          <td><strong>Part 2</strong></td>
          <td>Data collection and custom tokenization</td>
          <td>Deep dive into gathering 218+ historical sources, cleaning pipelines, and building specialized tokenizers for historical language patterns.</td>
      </tr>
      <tr>
          <td><strong>Part 3</strong></td>
          <td>Model architecture and training infrastructure</td>
          <td>Technical implementation of custom GPT architectures, multi-GPU training, checkpointing, and performance optimization.</td>
      </tr>
      <tr>
          <td><strong><a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4
	</span>
</a></strong></td>
          <td>Evaluation and deployment</td>
          <td>Comprehensive testing frameworks, historical accuracy assessment, and deployment to Hugging Face.</td>
      </tr>
  </tbody>
</table>
<p>For this first part, you have two paths to choose from based on your goals and available time:</p>
<ul>
<li>
<p><strong>Option 1: Quick Start with Published Models</strong> - Jump straight into using the pre-trained models on Hugging Face for immediate testing and exploration. Perfect if you want to see results quickly and aren&rsquo;t concerned with the technical implementation details.</p>
</li>
<li>
<p><strong>Option 2: Build from Scratch</strong> - Dive deep into the complete codebase and build your own historical language model from the ground up. Ideal if you want to understand every aspect of the pipeline and learn how to create specialized LLMs.</p>
</li>
</ul>
<p>Let us start with option 1 - use the models.</p>
<h2 id="2-use-the-models---try-it-now-using-hugging-face">2. Use the models - Try it now using Hugging Face</h2>
<p>If you just want to get going and use the models and kick tires, the models are live on Hugging Face and ready to use.</p>
<ul>
<li><strong>SLM Model (117M parameters)</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		💡 https://huggingface.co/bahree/london-historical-slm
	</span>
</a></li>
<li><strong>Regular Model (354M parameters)</strong>: <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		💡 https://huggingface.co/bahree/london-historical-llm
	</span>
</a></li>
</ul>
<p>In addition, you can also explore the complete codebase and build your own historical language model from scratch. The entire pipeline is documented with working code, training scripts, and deployment guides, and is available on GitHub:</p>
<ul>
<li><strong>Github Repo 💻 &ndash;&gt; <a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		⚙️ github.com/bahree/helloLondon
	</span>
</a></strong>.</li>
</ul>
<p>If you want to quickly test the published models on Hugging Face (HF), you can do so in two ways: quick automated tests or interactive mode. This is the easiest way to get started and show that the models are fully working. You can either clone the repo and run the scripts or use the Python code snippet below.</p>
<p>If you don&rsquo;t have a development environment set up, you can follow the instructions in the GitHub repo to set up a conda environment with all dependencies. And just for the local testing, you can use CPU only, but for interactive mode, a GPU is recommended. Finally, you will need at a minimum the following Python packages shown in <a href="#listing1" class="listing-ref">Listing 1</a>. Note, these are also called out on the Hugging Face model page.</p>
<figure id="listing1"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>python -m pip install -U pip setuptools wheel
</span></span><span style="display:flex;"><span>python -m pip install <span style="color:#a6da95">&#34;transformers[torch]&#34;</span> accelerate safetensors</span></span></code></pre></div><figcaption>
        <strong>Listing 1: Install Required Dependencies</strong>
    </figcaption>
</figure>
<blockquote>
<p><em>Note:</em> It is recommended to use a virtual environment or conda environment to avoid dependency conflicts. See the GitHub repo for complete setup instructions.</p></blockquote>
<p>If you don&rsquo;t have the code repo yet, you can run the commands in <a href="#listing2" class="listing-ref">Listing 2</a> directly and run inference from Hugging Face.</p>
<p><strong>Python Code:</strong></p>
<figure id="listing2"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoTokenizer, AutoModelForCausalLM
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Load the published SLM model</span>
</span></span><span style="display:flex;"><span>model_name <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;bahree/london-historical-slm&#34;</span>
</span></span><span style="display:flex;"><span>tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Generate historical text</span>
</span></span><span style="display:flex;"><span>prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;In the year of our Lord 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(
</span></span><span style="display:flex;"><span>    inputs[<span style="color:#a6da95">&#34;input_ids&#34;</span>],
</span></span><span style="display:flex;"><span>    max_new_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">100</span>,
</span></span><span style="display:flex;"><span>    do_sample<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>,
</span></span><span style="display:flex;"><span>    temperature<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.3</span>,
</span></span><span style="display:flex;"><span>    top_p<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.9</span>,
</span></span><span style="display:flex;"><span>    top_k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">20</span>,
</span></span><span style="display:flex;"><span>    repetition_penalty<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1.2</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">print</span>(tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>decode(outputs[<span style="color:#f5a97f">0</span>], skip_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>))</span></span></code></pre></div><figcaption>
        <strong>Listing 2: Load and Test Published Model</strong>
    </figcaption>
</figure>
<h3 id="21-local-testing-with-the-complete-codebase">2.1 Local Testing with the Complete Codebase</h3>
<p>Now that you&rsquo;ve seen the models work directly from Hugging Face, let&rsquo;s explore the complete development experience by working with the actual codebase. This section walks you through testing the models locally using the same infrastructure that was used to train them.</p>
<p>The <code>helloLondon</code> repository contains everything needed to reproduce the entire pipeline - from data collection through model deployment. By running these tests locally, you&rsquo;ll get hands-on experience with the inference scripts and understand how the models integrate with the broader development workflow.</p>
<p>The following examples assume you&rsquo;ve cloned the repository and are running from the root directory. All scripts are designed to work out-of-the-box with the published models, giving you immediate access to the same testing infrastructure used during development. You can test the models using <a href="#listing3" class="listing-ref">Listing 3</a> or <a href="#listing4" class="listing-ref">Listing 4</a>.</p>
<figure id="listing3"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Test SLM model (117M parameters)</span>
</span></span><span style="display:flex;"><span>python 06_inference/test_published_models.py --model_type slm
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Test Regular model (354M parameters)  </span>
</span></span><span style="display:flex;"><span>python 06_inference/test_published_models.py --model_type regular</span></span></code></pre></div><figcaption>
        <strong>Listing 3: Test SLM Model</strong>
    </figcaption>
</figure>
<p>There is also an interactive mode where you can type in your own prompts and see the model generate text.</p>
<p><strong>Interactive Testing:</strong></p>
<figure id="listing4"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># SLM model - Interactive mode</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type slm --interactive
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Regular model - Interactive mode</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type regular --interactive
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Single prompt testing</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type slm --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type regular --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 4: Interactive Mode Testing</strong>
    </figcaption>
</figure>
<p>If everything works, you should see output similar to the following for the SLM model:</p>
<p><strong>Example Output ( Hugging Face SLM Example):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span><span style="color:#ed8796">🧪</span> Testing SLM Model: bahree<span style="color:#91d7e3;font-weight:bold">/</span>london<span style="color:#91d7e3;font-weight:bold">-</span>historical<span style="color:#91d7e3;font-weight:bold">-</span>slm
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">============================================================</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">📂</span> Loading model<span style="color:#91d7e3;font-weight:bold">...</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">✅</span> Model loaded <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#f5a97f">8.91</span> seconds
</span></span><span style="display:flex;"><span><span style="color:#ed8796">📊</span> Model Info:
</span></span><span style="display:flex;"><span>   Type: SLM
</span></span><span style="display:flex;"><span>   Description: Small Language Model (<span style="color:#f5a97f">117</span>M parameters)
</span></span><span style="display:flex;"><span>   Device: cuda
</span></span><span style="display:flex;"><span>   Vocabulary size: <span style="color:#f5a97f">30</span>,<span style="color:#f5a97f">000</span>
</span></span><span style="display:flex;"><span>   Max length: <span style="color:#f5a97f">512</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">---</span> Test <span style="color:#f5a97f">1</span><span style="color:#91d7e3;font-weight:bold">/</span><span style="color:#f5a97f">10</span> <span style="color:#91d7e3;font-weight:bold">---</span>
</span></span><span style="display:flex;"><span>Prompt: In the year <span style="color:#f5a97f">1834</span>, I walked through the streets of London <span style="color:#91d7e3;font-weight:bold">and</span> witnessed
</span></span><span style="display:flex;"><span>Generated: a scene <span style="color:#91d7e3;font-weight:bold">in</span> which some of those who did <span style="color:#91d7e3;font-weight:bold">not</span> incline to come <span style="color:#91d7e3;font-weight:bold">in</span> contact with him took part <span style="color:#91d7e3;font-weight:bold">in</span> his discourse<span style="color:#91d7e3;font-weight:bold">.</span> It was on this occasion that I perceived that he had been engaged <span style="color:#91d7e3;font-weight:bold">in</span> some new business connected with the house, but <span style="color:#c6a0f6">for</span> some days it had <span style="color:#91d7e3;font-weight:bold">not</span> taken place, nor did he appear so desirous of pursuing any further display of interest <span style="color:#91d7e3;font-weight:bold">.....</span>
</span></span><span style="display:flex;"><span>Time: <span style="color:#f5a97f">5.75</span>s</span></span></code></pre></div>
<p>Notice how the model captures:</p>
<ul>
<li><strong>Period-appropriate language</strong> (&ldquo;thank &rsquo;ee kindly,&rdquo; &ldquo;bade me go,&rdquo; &ldquo;spectacles&rdquo;)</li>
<li><strong>Historical dialogue patterns</strong> (formal speech, period-appropriate contractions)</li>
<li><strong>Historical context</strong> (West Indies, poor rates, needle work, pocket-book)</li>
<li><strong>Authentic historical narrative</strong> (detailed scene setting, period-appropriate social interactions)</li>
</ul>
<p>Now that we have tried using the model, let&rsquo;s explore option 2 and see how we can build it. Once you&rsquo;ve built your own model, you&rsquo;ll be able to test it using the checkpoints saved during training - see section 7.4 for detailed checkpoint testing instructions.</p>
<h2 id="3-build-the-models---from-scratch">3. Build the models - From Scratch</h2>
<p>Building a language model from scratch is both an art and a science - requiring careful orchestration of data, architecture, and training to create something that can genuinely understand and generate historical text. Unlike fine-tuning existing models, training from scratch gives us complete control over every aspect of the model&rsquo;s knowledge and behavior.</p>
<p>The journey from raw historical documents to a working language model involves six critical phases, each building upon the previous one. The flowchart below illustrates this complete end-to-end pipeline, showing how we transform 218+ historical sources into two specialized models that can generate authentic medieval London text.</p>
<figure class="align-center " id="fig1">
    <pre class="mermaid">graph TD
    A[📚 Historical Data Collection&lt;br/&gt;218+ sources, 1500-1850] --&gt; B[🧹 Data Cleaning &amp; Processing&lt;br/&gt;Text normalization, filtering]
    B --&gt; C[🔤 Custom Tokenizer Training&lt;br/&gt;30k vocab + 150+ special tokens]
    C --&gt; D[🏋️ Model Training&lt;br/&gt;Two Identical Models&lt;br/&gt;SLM: 117M / Regular: 354M]
    D --&gt; E[📊 Evaluation &amp; Testing&lt;br/&gt;Historical accuracy, ROUGE, MMLU]
    E --&gt; F[🚀 Deployment&lt;br/&gt;Hugging Face + Local Inference]
    
    G[📖 Building a Custom LLM] --&gt; A
    
    F --&gt; L[🎯 Use Cases&lt;br/&gt;Historical text generation&lt;br/&gt;Educational projects&lt;br/&gt;Research applications]
    
    style A fill:#e1f5fe
    style D fill:#f3e5f5
    style F fill:#e8f5e8
    style G fill:#fff3e0</pre>
    <figcaption>Figure 1: Complete LLM Development Pipeline</figcaption>
</figure>
<p>Now that we have a bird&rsquo;s eye view of the complete pipeline, let us get into the details and build the model from scratch. I am going to walk you through the complete process step-by-step.</p>
<p>I am also going to assume you have a basic understanding of Python, PyTorch, and command-line operations and have a more recent dev setup, including a relatively modern GPU (NVIDIA RTX 3060 or better recommended). For the sake of simplicity, I will show commands for Linux/macOS, but Windows users can easily adapt them.</p>
<p>Again, as a reminder, the ⚙️ <a
	
		href = "https://github.com/bahree/helloLondon/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		GitHub repo
	</span>
</a> has all the code and instructions you need to get started. You can clone the repo and follow along.</p>
<h2 id="4-environment-and-configuration-setup">4. Environment and Configuration Setup</h2>
<p>The foundation of any successful machine learning project lies in proper environment setup and configuration. This step involves creating a virtual environment, installing dependencies, and configuring the project structure. Understanding the key configuration files, directory organization, and overall project architecture is crucial - these elements form the backbone of the entire training process. Taking time to get this right upfront prevents countless headaches and debugging sessions later, ensuring smooth execution through all subsequent phases.</p>
<h3 id="41-key-configuration-files">4.1 Key Configuration Files</h3>
<ul>
<li><strong><code>config.py</code></strong>: Central configuration system (paths, training settings, tokenizer config)</li>
<li><strong><code>01_environment/setup_environment.py</code></strong>: Environment setup script (reads from config.py)</li>
<li><strong><code>requirements.txt</code></strong>: Python dependencies (auto-generated by setup script)</li>
</ul>
<h3 id="42-important-directories-created-by-setup">4.2 Important Directories (Created by Setup)</h3>
<ul>
<li><strong><code>helloLondon/</code></strong>: Virtual environment directory</li>
<li><strong><code>data/london_historical/</code></strong>: Historical text data storage</li>
<li><strong><code>09_models/checkpoints/</code></strong>: Model checkpoints during training</li>
<li><strong><code>09_models/tokenizers/</code></strong>: Custom tokenizer storage</li>
</ul>
<p>Now that we have that out of the way, let us run the setup commands as shown in <a href="#listing5" class="listing-ref">Listing 5</a>. This will clone the repo, set up the environment, and install all dependencies. For this to work you will already have git, python, and <code>python3-venv</code> installed. If you don&rsquo;t have these, please install them first.</p>
<blockquote>
<p>PS: See the <a
	
		href = "https://github.com/bahree/helloLondon/blob/main/README.md"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Training QuickStart guide
	</span>
</a> in the GitHub repo for more details.</p></blockquote>
<figure id="listing5"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Clone and setup environment</span>
</span></span><span style="display:flex;"><span>git clone https://github.com/bahree/helloLondon/
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">cd</span> helloLondon
</span></span><span style="display:flex;"><span>python 01_environment/setup_environment.py
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">source</span> activate_env.sh</span></span></code></pre></div><figcaption>
        <strong>Listing 5: Clone and Setup Repository</strong>
    </figcaption>
</figure>
<p>As you run the setup script, you should see output similar to the images shown below; the script will create a virtual environment, install dependencies, and set up necessary directories. And then you can activate the environment using the <code>source activate_env.sh</code> command.</p>
<figure>
<img src="images/env11.png" alt="Environment setup - 1 of 3" title="Environment setup - 1/3">
<figcaption><strong>Figure 2:</strong> Environment setup process - Step 1 of 3 showing virtual environment creation</figcaption>
</figure>
<figure>
<img src="images/env12.png" alt="Environment setup - 2 of 3" title="Environment setup - 2/3">
<figcaption><strong>Figure 3:</strong> Environment setup process - Step 2 of 3 showing dependency installation</figcaption>
</figure>
<figure>
<img src="images/env13.png" alt="Environment setup - 3 of 3" title="Environment setup - 3/3">
<figcaption><strong>Figure 4:</strong> Environment setup process - Step 3 of 3 showing final configuration</figcaption>
</figure>
<p>Now that the configuration and environment are set up, we can validate them by running the following command. This will check if everything is working and you have the necessary dependencies installed.</p>
<p>When one activates the environment using <strong><code>source activate_env.sh</code></strong>, you will see it in the console as shown below.</p>
<p>The default environment name is called <strong><code>helloLondon</code></strong>. If you want to change the environment name from <code>helloLondon</code> to something else, you can modify the <code>venv_name</code> field in <code>environment_config.json</code> before running the setup script. This will create a virtual environment with your preferred name.</p>
<figure id="listing6"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>python3 -c <span style="color:#a6da95">&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">from config import config
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(&#39;🔧 Configuration Overview&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(&#39;=&#39; * 50)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Project Root: {config.project_root}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Data Directory: {config.london_historical_data}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Tokenizer Directory: {config.london_tokenizer_dir}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Checkpoints Directory: {config.checkpoints_dir}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Virtual Environment: {config.project_root}/helloLondon&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Vocabulary Size: {config.tokenizer_config[\&#34;vocab_size\&#34;]:,} tokens&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Special Tokens: {len(config.tokenizer_config[\&#34;special_tokens\&#34;])} tokens&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;SLM Model: {config.slm_config[\&#34;model_name\&#34;]}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Training Epochs: {config.slm_config[\&#34;num_epochs\&#34;]}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Batch Size: {config.slm_config[\&#34;batch_size\&#34;]}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(f&#39;Max Length: {config.slm_config[\&#34;max_length\&#34;]}&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">print(&#39;\\n🎯 Configuration looks good!&#39;)
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">&#34;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 6: Validate Configuration</strong>
    </figcaption>
</figure>
<p>The following directory structure will be generated after executing the setup script. Please note that certain directories will remain empty until the data collection and training processes are initiated.</p>
<figure id="listing7"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span>helloLondon<span style="color:#91d7e3;font-weight:bold">/</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> data<span style="color:#91d7e3;font-weight:bold">/</span>london_historical<span style="color:#91d7e3;font-weight:bold">/</span>          <span style="color:#6e738d;font-style:italic"># Historical text data</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> london_historical_corpus_comprehensive<span style="color:#91d7e3;font-weight:bold">.</span>txt  <span style="color:#6e738d;font-style:italic"># Final training corpus</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> downloads<span style="color:#91d7e3;font-weight:bold">/</span>                   <span style="color:#6e738d;font-style:italic"># Raw downloaded data</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> processed<span style="color:#91d7e3;font-weight:bold">/</span>                   <span style="color:#6e738d;font-style:italic"># Cleaned and processed text</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">└──</span> <span style="color:#ed8796">📁</span> metadata<span style="color:#91d7e3;font-weight:bold">/</span>                    <span style="color:#6e738d;font-style:italic"># Data collection metadata</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> <span style="color:#f5a97f">09</span>_models<span style="color:#91d7e3;font-weight:bold">/</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> checkpoints<span style="color:#91d7e3;font-weight:bold">/</span>                 <span style="color:#6e738d;font-style:italic"># Regular model checkpoints (354M)</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">44000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">47000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">51000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">59000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">└──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">60001.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> checkpoints<span style="color:#91d7e3;font-weight:bold">/</span>slm<span style="color:#91d7e3;font-weight:bold">/</span>             <span style="color:#6e738d;font-style:italic"># SLM model checkpoints (117M)</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">52000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">60000.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">│</span>   <span style="color:#ed8796">└──</span> <span style="color:#ed8796">📄</span> checkpoint<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">60001.</span>pt
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>   <span style="color:#ed8796">└──</span> <span style="color:#ed8796">📁</span> tokenizers<span style="color:#91d7e3;font-weight:bold">/</span>london_historical_tokenizer<span style="color:#91d7e3;font-weight:bold">/</span>  <span style="color:#6e738d;font-style:italic"># Custom tokenizer</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>       <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>json           <span style="color:#6e738d;font-style:italic"># Tokenizer configuration</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>       <span style="color:#ed8796">├──</span> <span style="color:#ed8796">📄</span> vocab<span style="color:#91d7e3;font-weight:bold">.</span>json               <span style="color:#6e738d;font-style:italic"># Vocabulary mapping</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">│</span>       <span style="color:#ed8796">└──</span> <span style="color:#ed8796">📄</span> merges<span style="color:#91d7e3;font-weight:bold">.</span>txt               <span style="color:#6e738d;font-style:italic"># BPE merge rules</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">├──</span> <span style="color:#ed8796">📁</span> helloLondon<span style="color:#91d7e3;font-weight:bold">/</span>                     <span style="color:#6e738d;font-style:italic"># Virtual environment</span>
</span></span><span style="display:flex;"><span><span style="color:#ed8796">└──</span> <span style="color:#ed8796">📁</span> logs<span style="color:#91d7e3;font-weight:bold">/</span>                            <span style="color:#6e738d;font-style:italic"># Training logs and WandB data</span></span></span></code></pre></div><figcaption>
        <strong>Listing 7: Project Directory Structure</strong>
    </figcaption>
</figure>
<blockquote>
<p><strong>Prerequisites</strong>: Before proceeding with the following steps, please verify the following requirements:</p>
<ul>
<li><strong>Storage</strong>: Minimum 20GB of free disk space and stable internet connectivity for data acquisition</li>
<li><strong>Hardware</strong>: GPU with 8GB+ VRAM for SLM training, 16GB+ VRAM for Regular model training. Cloud users should select appropriate instance types</li>
<li><strong>Experiment Tracking</strong> (Optional but highly recommended): <a
	
		href = "https://wandb.ai/site"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Weights &amp; Biases
	</span>
</a> account with <code>WANDB_API_KEY</code> environment variable configured for comprehensive training monitoring</li>
<li><strong>Dependencies</strong>: Required data processing libraries (nltk, beautifulsoup4, etc.) will be automatically installed via the setup script</li>
</ul></blockquote>
<h2 id="5-data-collection">5. Data Collection</h2>
<p>The foundation of any language model lies in its training data. For our historical London models, we&rsquo;ve built a comprehensive data collection system that sources authentic text from <strong>218+ historical sources spanning 1500-1850</strong> - a remarkable 350-year window of London&rsquo;s linguistic evolution. This isn&rsquo;t just about downloading files; it&rsquo;s about curating a high-quality corpus that captures the authentic voice of historical London.</p>
<p>Our data collection pipeline automatically processes multiple formats (PDFs, HTML, XML, plain text) from diverse sources, including Project Gutenberg classics, Old Bailey trial records, London Lives manuscripts, and British History Online archives. The system includes sophisticated quality control measures: language detection to filter non-English content, OCR artifact correction, duplicate detection, and historical period validation to ensure every text genuinely represents the target era.</p>
<p>The result? A curated corpus of <strong>500M+ characters</strong> of authentic historical English text, ready to train models that understand not just the words, but the cultural context, social dynamics, and linguistic patterns of 18th and 19th-century London. Of course, you can always add your own data sources if you have them, and the system is designed to be extensible.</p>
<p>We can kick off the data collection process using <a href="#listing8" class="listing-ref">Listing 8</a>. This will be run from the project root directory.</p>
<figure id="listing8"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Download historical data with advanced filtering</span>
</span></span><span style="display:flex;"><span>python 02_data_collection/historical_data_collector.py --max_sources <span style="color:#f5a97f">100</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># The system automatically filters:</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># - Non-English content (Arabic, Chinese, etc.)</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># - Poor OCR quality scans and gibberish</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># - Advertisement-heavy commercial content  </span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># - Duplicate content and empty files</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># - Special handling for Project Gutenberg classics</span></span></span></code></pre></div><figcaption>
        <strong>Listing 8: Download Historical Data</strong>
    </figcaption>
</figure>
<p>This process may take some time, depending on your internet speed, the number of sources you choose to download and your system&rsquo;s performance. For me, on a very fast internet connection and a powerful machine this took typically 2-4 hours for downloading, and processing the full dataset. The script will save the cleaned and processed data in the <code>data/london_historical/</code> directory, creating a comprehensive historical corpus.</p>
<p>The data collection process creates a comprehensive historical corpus with the main training file <strong><code>london_historical_corpus_comprehensive.txt</code></strong> containing 270M+ characters (~258MB) of authentic historical text. The complete data directory spans approximately 1.2GB, including 521MB of raw downloaded sources, 263MB of processed and cleaned content, and 126MB of tokenized training sequences ready for model training. The image below shows the data collection in progress.</p>
<figure>
<img src="images/data3.png" alt="Data Collection in Progress" title="Data Collection in Progress">
<figcaption><strong>Figure 5:</strong> Data collection process in progress showing real-time processing of historical sources</figcaption>
</figure>
<p>The final corpus represents one of the largest collections of historical London text ever assembled for language model training, with authentic content spanning 350 years of linguistic evolution. The two images below show an example of one of my runs, one of them showing the final output of the data cleaning and outlining the statistics. And the second one shows the size of the data on disk.</p>
<figure>
<img src="images/data11.png" alt="Data Collection Summary" title="Data Collection Summary">
<figcaption><strong>Figure 6:</strong> Data collection summary showing final statistics and corpus composition</figcaption>
</figure>
<p>The total size at the end of the data. Note this does not include the Old Bailey and London Lives data.</p>
<figure>
<figure>
<img src="images/data12.png" alt="Total Data Size" title="Total Data Size">
<figcaption><strong>Figure 7:</strong> Total data size on disk showing comprehensive historical corpus</figcaption>
</figure>
<figcaption><strong>Figure 7:</strong> Total data size on disk showing the complete historical corpus storage requirements</figcaption>
</figure>
<p>Now that we have our data and have cleaned it. Let us build a custom tokenizer.</p>
<h2 id="6-train-custom-tokenizer">6. Train Custom Tokenizer</h2>
<p>With our cleaned historical corpus ready, we now need to create a custom tokenizer specifically designed for historical English. Standard tokenizers like GPT-2 are optimized for modern text and fail catastrophically with historical language - treating archaic words like &ldquo;quoth&rdquo; and &ldquo;hast&rdquo; as multiple subword fragments, losing both meaning and efficiency.</p>
<p>Our custom tokenizer uses Byte Pair Encoding (BPE) with a 30,000 vocabulary size and 150+ carefully designed special tokens that understand:</p>
<ul>
<li><strong>Historical Language</strong>: Archaic pronouns (<code>&lt;|thou|&gt;</code>, <code>&lt;|thee|&gt;</code>), verbs (<code>&lt;|hast|&gt;</code>, <code>&lt;|doth|&gt;</code>), and expressions (<code>&lt;|verily|&gt;</code>, <code>&lt;|forsooth|&gt;</code>)</li>
<li><strong>London Geography</strong>: Landmarks (<code>&lt;|thames|&gt;</code>, <code>&lt;|newgate|&gt;</code>, <code>&lt;|tower|&gt;</code>), streets (<code>&lt;|cheapside|&gt;</code>, <code>&lt;|fleet|&gt;</code>), and districts (<code>&lt;|southwark|&gt;</code>, <code>&lt;|westminster|&gt;</code>)</li>
<li><strong>Historical Context</strong>: Period markers (<code>&lt;|tudor|&gt;</code>, <code>&lt;|stuart|&gt;</code>, <code>&lt;|georgian|&gt;</code>), social classes (<code>&lt;|noble|&gt;</code>, <code>&lt;|commoner|&gt;</code>), and professions (<code>&lt;|apothecary|&gt;</code>, <code>&lt;|coachman|&gt;</code>)</li>
</ul>
<p>This specialized vocabulary ensures that common historical terms remain as single tokens rather than being fragmented, dramatically improving both training efficiency and text generation quality. We can kick off the tokenizer using <a href="#listing9" class="listing-ref">Listing 9</a>. Again, this will be run from the project root directory.</p>
<figure id="listing9"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Train historical tokenizer (30k vocabulary)</span>
</span></span><span style="display:flex;"><span>python 03_tokenizer/train_historical_tokenizer.py</span></span></code></pre></div><figcaption>
        <strong>Listing 9: Train Historical Tokenizer</strong>
    </figcaption>
</figure>
<p>The training process analyzes our 270M+ character corpus to learn optimal token boundaries, creating a tokenizer that understands the linguistic patterns of 1500-1850 English. The result is a highly efficient tokenizer with a compression ratio of ~0.3 tokens per character and 99%+ reconstruction accuracy - essential for training models that can generate authentic historical text.</p>
<p>Once the training is finished (and usually it is pretty quick - just a few minutes for our data size), we run a quick sanity test as the image below shows.</p>
<figure>
<figure>
<img src="images/tokenizer-8.png" alt="Custom Tokenizer Training" title="Custom Tokenizer Training">
<figcaption><strong>Figure 8:</strong> Custom tokenizer training progress showing vocabulary learning</figcaption>
</figure>
<figcaption><strong>Figure 8:</strong> Custom tokenizer training process showing BPE algorithm learning optimal token boundaries</figcaption>
</figure>
<p>Note that in testing, we might see a warning that the reconstruction differs; this is only because of the alphabet case being different and is expected. You can ignore this. An example of this is shown below.</p>
<figure>
<figure>
<img src="images/tokenizer-7.png" alt="Tokenizer reconstruction warning" title="Tokenizer reconstruction warning">
<figcaption><strong>Figure 9:</strong> Tokenizer reconstruction warning during training process</figcaption>
</figure>
<figcaption><strong>Figure 9:</strong> Tokenizer reconstruction warning showing expected case normalization differences</figcaption>
</figure>
<p><strong>Why the &ldquo;Reconstruction differs&rdquo; warning is actually beneficial:</strong></p>
<p>The reconstruction differences you see are not errors - they&rsquo;re the tokenizer working exactly as designed for optimal language model training. The tokenizer uses Byte Pair Encoding (BPE), which breaks complex words into smaller, reusable subword units (like &ldquo;Bourgh&rdquo; → &ldquo;bour ##gh&rdquo;), and normalizes text to lowercase to reduce vocabulary size. These &ldquo;differences&rdquo; are actually features that make the tokenizer more efficient and the resulting language model more capable of generating authentic historical text.</p>
<blockquote>
<p><strong>📖 For detailed technical explanation</strong>: Part 2 of this series covers the complete tokenizer architecture, BPE implementation, special token design, and why these reconstruction differences are essential for optimal language model training.</p></blockquote>
<p>Now that we have our data and the tokenizer is ready, it is time to train the model.</p>
<h2 id="7-train-the-model">7. Train the Model</h2>
<p>With our cleaned historical corpus and custom tokenizer in place, we can now train our language models. The training system is designed to build two identical models with different parameter counts, allowing you to choose between speed (SLM) and quality (Regular model) based on your needs.</p>
<p><strong>Training Architecture:</strong> Both models use a custom GPT architecture specifically optimized for historical text, featuring sophisticated attention mechanisms that understand the complex relationships in historical language. The system includes automatic GPU detection, multi-GPU support, and comprehensive monitoring to ensure optimal training performance.</p>
<p><strong>Training Process:</strong> The training system implements modern optimization techniques, including dynamic learning rate scheduling, automatic checkpointing, and real-time experiment tracking via WandB. The entire process is automated with intelligent configuration that adapts to your hardware setup, whether you&rsquo;re using a single GPU or multiple GPUs for distributed training.</p>
<p><strong>Performance Optimization:</strong> The system includes precision optimization (TF32, AMP) and memory management specifically tuned for historical text processing. Training typically takes 7-8 hours for the SLM and 28-32 hours for the Regular model on modern hardware, with comprehensive monitoring to track progress and identify any issues. Note, this time can vary significantly based on your hardware. The times mentioned here are based on dual NVIDIA A30s.</p>
<blockquote>
<p><strong>📖 For detailed technical implementation</strong>: Part 3 of this series covers the complete model architecture, GPU configuration, training infrastructure, and performance optimization strategies in detail.<br>
<strong>🧪 Ready to test your checkpoints?</strong> Once training completes, see section 7.4 for comprehensive instructions on testing your trained model checkpoints.</p></blockquote>
<h3 id="71-slm-training">7.1 SLM Training</h3>
<p>To kick off the training, the code is quite simple, as shown in <a href="#listing10" class="listing-ref">Listing 10</a>. Again, this would be from the project root folder. In my case, I am using <code>torchrun --nproc_per_node=2</code> because I have dual GPUs and I want to use both. If you only have a single GPU, you can just run the automatic GPU detection script. The <code>train_model_slm.py</code> script specifically trains the SLM (Small Language Model) with 117M parameters.</p>
<p><strong>Option A: Train SLM (117M parameters) - Faster, Good for Testing</strong></p>
<figure id="listing10"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Clean any existing tokenized data</span>
</span></span><span style="display:flex;"><span>rm -rf data/london_historical/tokenized_data/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Automatic GPU Detection (Recommended)</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">cd</span> 04_training
</span></span><span style="display:flex;"><span>./launch_slm_training.sh
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Manual Multi-GPU training</span>
</span></span><span style="display:flex;"><span>torchrun --nproc_per_node<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span> 04_training/train_model_slm.py --data_dir data/london_historical</span></span></code></pre></div><figcaption>
        <strong>Listing 10: Train SLM Model</strong>
    </figcaption>
</figure>
<p>Note: The first line <code>rm -rf data/london_historical/tokenized_data/</code> cleans any existing tokenized data to ensure a fresh start. This is important because the training system caches tokenized data for efficiency, and we want to ensure it uses the latest corpus and tokenizer settings rather than potentially outdated cached data. You want to do this only if you have more updated data from the previous steps.</p>
<p>Once the training starts, you will see a similar output as the one shown below.</p>
<figure>
<figure>
<img src="images/train16.png" alt="Starting model training" title="Starting model training">
<figcaption><strong>Figure 10:</strong> Model training initialization showing configuration and setup</figcaption>
</figure>
<figcaption><strong>Figure 10:</strong> Model training initialization showing tokenization and GPU setup process</figcaption>
</figure>
<p>Note the Tokenizing corpus line - this will take some time, depending on your data size and hardware. The tokenized data will be saved in <code>data/london_historical/tokenized_data/</code> for future runs, so subsequent training runs will be much faster. If you want to force re-tokenization, you can delete this directory and restart the training. And if you think this is hung, you can check the GPU usage using <code>nvtop</code> in a separate terminal.</p>
<p>And if you have configured WandB as recommended earlier, then you can log in to that dashboard and also monitor the training progress. This is quite handy when you are away from the machine and see how it is generally progressing.</p>
<figure>
<figure>
<img src="images/train6.png" alt="WanB Training progress" title="WanB Training progress">
<figcaption><strong>Figure 11:</strong> Weights & Biases training progress monitoring dashboard</figcaption>
</figure>
<figcaption><strong>Figure 11:</strong> Weights & Biases training dashboard showing real-time loss curves and performance metrics</figcaption>
</figure>
<p>WandB also provides valuable insights into your model&rsquo;s training performance through comprehensive visualizations. The dashboard shows the complete training journey, revealing how your model&rsquo;s loss decreased over time, whether the training plateaued, and how efficiently your hardware was utilized. These visualizations help you understand not just the final results, but the entire learning process - identifying if the model continued improving throughout training or if it reached a performance plateau.</p>
<p>While these metrics are incredibly useful for optimizing your training process, we&rsquo;ll dive deeper into interpreting these results and fine-tuning your training strategy in Part 3 of this series.</p>
<p><strong>SLM Results (117M parameters):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>wandb: Run history:
</span></span><span style="display:flex;"><span>wandb:       eval/iter   ▂▂▃▃▄▄▅▅▆▆▇▇██
</span></span><span style="display:flex;"><span>wandb: eval/train_loss  ███▇▇▇▇▇▇▇▇▇▇▇▇
</span></span><span style="display:flex;"><span>wandb:   eval/val_loss  ███████▇▇▇█▇▇▇▇
</span></span><span style="display:flex;"><span>wandb:    eval/val_ppl  █▇▇▇▇▇▆▆▆▆▆▆▆▆▆
</span></span><span style="display:flex;"><span>wandb:     train/dt_ms           █            █                
</span></span><span style="display:flex;"><span>wandb:      train/iter      ▂▂▂▂▂▃▃▃▃▃▃▄▄▄▄▄▄▅▅▅▅▅▅▅▆▆▆▆▇▇▇▇▇██
</span></span><span style="display:flex;"><span>wandb:      train/loss ▆▅▇▅▅▃▇▄▄█▅▄▅▄▃▇▄▄▅ ▃▃▂▄▅▂▅▂▄▅▃▃▄▅ ▄▃
</span></span><span style="display:flex;"><span>wandb:        train/lr ██████████▇▇▇▅▄▄▄▃▃▃▃▃▃▂▂▂▂▂▂           
</span></span><span style="display:flex;"><span>wandb:       train/mfu ▃▄▇▇█▄▄▆▆▇▅▂▅▆▆▇▇▂▄▅▇▇▇▆▆▇▇▇▇▅███▅▇▆▇▇ ▇
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>wandb: Run summary:
</span></span><span style="display:flex;"><span>wandb:       eval/iter 60000
</span></span><span style="display:flex;"><span>wandb: eval/train_loss 2.74369
</span></span><span style="display:flex;"><span>wandb:   eval/val_loss 3.44089
</span></span><span style="display:flex;"><span>wandb:    eval/val_ppl 31.21462
</span></span><span style="display:flex;"><span>wandb:     train/dt_ms 10217.92054
</span></span><span style="display:flex;"><span>wandb:      train/iter 60000
</span></span><span style="display:flex;"><span>wandb:      train/loss 2.87667
</span></span><span style="display:flex;"><span>wandb:        train/lr 3e-05
</span></span><span style="display:flex;"><span>wandb:       train/mfu 7.50594</span></span></code></pre></div>
<p>It&rsquo;s also helpful to monitor GPU usage during training. I recommend using <code>nvtop</code> (a GPU monitoring tool similar to <code>htop</code> but for NVIDIA GPUs) in a separate terminal to track memory usage, temperature, and utilization in real-time. The screenshot below shows the GPU monitoring during model training.</p>
<figure>
<figure>
<img src="images/train16-4.png" alt="GPU monitoring using nvtop" title="GPU monitoring using nvtop">
<figcaption><strong>Figure 12:</strong> GPU monitoring using nvtop showing real-time resource utilization</figcaption>
</figure>
<figcaption><strong>Figure 12:</strong> GPU monitoring during training showing memory usage, temperature, and utilization metrics</figcaption>
</figure>
<h3 id="72-understanding-checkpoints">7.2 Understanding Checkpoints</h3>
<p>Throughout training, the system automatically saves checkpoints - snapshots of your model&rsquo;s current state, including all learned parameters, optimizer state, and training progress. These checkpoints serve as safety nets, allowing you to resume training if interrupted, and provide multiple model versions to choose from. The final checkpoint (typically saved at the end of training) represents your fully trained model, ready for inference and deployment.</p>
<p>Checkpoints are saved in the <code>09_models/checkpoints/</code> directory, with separate subdirectories for each model type. SLM checkpoints are stored in <code>09_models/checkpoints/slm/</code> (e.g., <code>checkpoint-4000.pt</code>, <code>checkpoint-8000.pt</code>), while regular model checkpoints are saved directly in <code>09_models/checkpoints/</code> (e.g., <code>checkpoint-60001.pt</code>, <code>checkpoint-120000.pt</code>). The checkpoint filenames include the training step number, making it easy to identify the training progress and select the best-performing version for your needs.</p>
<p>These checkpoints enable two powerful capabilities that significantly enhance your training workflow. You can test your model&rsquo;s current performance at any point during training by running inference on intermediate checkpoints, allowing you to monitor progress without waiting for training to complete. Additionally, suppose training is interrupted due to power loss, system crash, or manual stop. In that case, you can resume from the last saved checkpoint exactly where you left off, saving both time and computational resources. This flexibility is particularly valuable for long training runs, enabling you to experiment with different model versions and recover from unexpected interruptions.</p>
<blockquote>
<p><strong>🧪 Ready to test your checkpoints?</strong> See section 7.4 for detailed instructions on testing your trained model checkpoints.</p></blockquote>
<h3 id="73-regular-model-training">7.3 Regular Model Training</h3>
<p>The Regular model training follows the same process as the SLM, using identical training infrastructure but with different configuration settings. The only differences are the training script (<code>train_model.py</code> instead of <code>train_model_slm.py</code>) and the model architecture parameters (354M parameters vs 117M).</p>
<figure id="listing11"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Clean any existing tokenized data</span>
</span></span><span style="display:flex;"><span>rm -rf data/london_historical/tokenized_data/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Automatic GPU Detection (Recommended)</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">cd</span> 04_training
</span></span><span style="display:flex;"><span>./launch_training.sh
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Manual Multi-GPU training</span>
</span></span><span style="display:flex;"><span>torchrun --nproc_per_node<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span> 04_training/train_model.py --data_dir data/london_historical</span></span></code></pre></div><figcaption>
        <strong>Listing 11: Train Regular Model</strong>
    </figcaption>
</figure>
<p><strong>Key Differences from SLM:</strong></p>
<ul>
<li><strong>Training script</strong>: <code>train_model.py</code> (instead of <code>train_model_slm.py</code>)</li>
<li><strong>Model size</strong>: 354M parameters (vs 117M for SLM)</li>
<li><strong>Training time</strong>: 28-32 hours (vs 7-8 hours for SLM)</li>
<li><strong>Memory usage</strong>: Higher VRAM requirements</li>
<li><strong>Performance</strong>: Better text quality, slower inference</li>
</ul>
<p>The training infrastructure, checkpointing, WandB integration, and all other features remain identical. The system automatically detects the model type and applies the appropriate configuration from <code>config.py</code>.</p>
<p><strong>Regular Model Results (354M parameters):</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>wandb: Run history:
</span></span><span style="display:flex;"><span>wandb:       eval/iter     ▂▂▂▂▂▃▃▃▃▃▃▄▄▄▄▅▅▅▅▅▅▅▆▆▆▆▆▇▇▇▇▇▇███
</span></span><span style="display:flex;"><span>wandb: eval/train_loss  █████████▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▆▇▆▆▆▆▆▆▆▆▆
</span></span><span style="display:flex;"><span>wandb:   eval/val_loss  ███████████████████████████████████▇███
</span></span><span style="display:flex;"><span>wandb:    eval/val_ppl  ████▇▇█▇▇▇▇▇▇▇▇▇▆▇▇▇▇▇▇▇▇▆▇▇▆▆▆▆▆▆▆▆
</span></span><span style="display:flex;"><span>wandb:     train/dt_ms                  █                      
</span></span><span style="display:flex;"><span>wandb:      train/iter      ▂▂▂▃▃▃▃▃▄▄▄▄▄▄▅▅▅▆▆▆▆▆▆▆▆▇▇▇▇▇▇▇███
</span></span><span style="display:flex;"><span>wandb:      train/loss ▇▆▆▇▇▅▅█▅▄▃▅▅▅▄▇▄▄▄▄▄▃▃▃▅▂▄▅▂▅▂▄▅▃▃▄▅ ▄▃
</span></span><span style="display:flex;"><span>wandb:        train/lr ▄██████▇▇▇▇▇▆▆▆▅▅▄▄▄▄▄▄▄▄▃▃▂▂▂          
</span></span><span style="display:flex;"><span>wandb:       train/mfu ▆▇█▅▄ ▄▆▆▆▇▃▃▂▂▆█▃▃▅▅▃█▅▄▆▇▇▇▇▄▅▃█▆▇█▄▃█
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>wandb: Run summary:
</span></span><span style="display:flex;"><span>wandb:       eval/iter 60000
</span></span><span style="display:flex;"><span>wandb: eval/train_loss 2.70315
</span></span><span style="display:flex;"><span>wandb:   eval/val_loss 3.61921
</span></span><span style="display:flex;"><span>wandb:    eval/val_ppl 37.30823
</span></span><span style="display:flex;"><span>wandb:     train/dt_ms 24681.64754
</span></span><span style="display:flex;"><span>wandb:      train/iter 60000
</span></span><span style="display:flex;"><span>wandb:      train/loss 2.70629
</span></span><span style="display:flex;"><span>wandb:        train/lr 0.0
</span></span><span style="display:flex;"><span>wandb:       train/mfu 7.20423</span></span></code></pre></div>
<h3 id="74-testing-your-checkpoints">7.4 Testing Your Checkpoints</h3>
<p>Once training is complete, you can immediately test your model using the checkpoints saved during training. This is one of the most exciting parts - seeing the model generate historical text for the first time! The PyTorch checkpoint approach provides immediate testing without any conversion needed, allowing you to test any checkpoint to monitor training progress while preserving the complete model state, including training metadata and optimizer state for fast, optimized inference.</p>
<p><strong>Direct PyTorch Checkpoint Testing:</strong>
Test your model directly from the training checkpoints without any conversion:</p>
<figure id="listing12"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Test SLM checkpoint (117M parameters)</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_pytorch.py <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --checkpoint 09_models/checkpoints/slm/checkpoint-4000.pt <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Test Regular model checkpoint (354M parameters)  </span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_pytorch.py <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --checkpoint 09_models/checkpoints/checkpoint-60001.pt <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  --prompt <span style="color:#a6da95">&#34;In the year 1834, I walked through the streets of London and witnessed&#34;</span></span></span></code></pre></div><figcaption>
        <strong>Listing 12: Test Model Checkpoints</strong>
    </figcaption>
</figure>
<p><strong>Expected Output:</strong>
Your trained model will generate authentic historical text like:</p>
<blockquote>
<p>&ldquo;In the year 1834, I walked through the streets of London and witnessed the most extraordinary sight. The Thames flowed dark beneath London Bridge, whilst carriages rattled upon the cobblestones with great urgency. Merchants called their wares from Cheapside to Billingsgate, and the smoke from countless chimneys did obscure the morning sun.&rdquo;</p></blockquote>
<p><strong>Testing Different Checkpoints:</strong>
You can test any checkpoint from your training run to see how the model improved over time. Try testing checkpoints from different training stages to observe the learning progression - early checkpoints will generate more random text, while later checkpoints will produce increasingly coherent historical language.</p>
<blockquote>
<p><strong>💡 Pro Tip</strong>: For published Hugging Face models and community access, see the Quick Start section earlier in this post, where we demonstrated the published SLM model.</p></blockquote>
<h2 id="8-publish-to-hugging-face">8. Publish to Hugging Face</h2>
<p>Once you&rsquo;ve successfully trained and tested your models, you can publish them to Hugging Face for community access and easy deployment. Publishing makes your models available to researchers, developers, and enthusiasts worldwide, while integrating them into the Hugging Face ecosystem for seamless use with the <code>transformers</code> library.</p>
<p><strong>Publishing Process:</strong>
The publishing code automatically handles the complete conversion process from PyTorch checkpoints to Hugging Face format, which is essential for making your trained models accessible to the broader community. This conversion transforms your local training artifacts into a standardized format that can be easily loaded by users worldwide.</p>
<p>The process includes converting model weights from PyTorch&rsquo;s <code>.pt</code> format to the more efficient <code>.safetensors</code> format, generating proper configuration files (<code>config.json</code>, <code>generation_config.json</code>) that define the model architecture and generation parameters, uploading the custom tokenizer and all necessary files to ensure complete functionality, creating comprehensive model cards with usage instructions and metadata for easy adoption, and setting up proper model repositories with versioning for educational deployment.</p>
<p>This conversion is necessary because PyTorch checkpoints are optimized for training workflows and contain additional information like optimizer states that aren&rsquo;t needed for inference, while the Hugging Face format is specifically designed for model sharing and deployment across different environments and hardware configurations.</p>
<p>We need to call the right script to publish the relevant model - either the SLM or the larger model. The publishing scripts will prompt you for your Hugging Face username and repository name, allowing you to customize where your models are published. The scripts automatically detect and use the latest checkpoint from your training run, so you can publish immediately after training completes.</p>
<blockquote>
<p><strong>💡 Quick Reference</strong>: If you want to test published models before publishing your own, see section 2 &ldquo;Use the models - Try it now using Hugging Face&rdquo; for immediate access to pre-trained models.</p></blockquote>
<p><strong>Prerequisites:</strong> You&rsquo;ll need a Hugging Face account and either set the <code>HF_TOKEN</code> environment variable or provide your token when prompted. The scripts will guide you through the publishing process step by step.</p>
<p><strong>Option A: Publish SLM (117M parameters)</strong></p>
<figure id="listing13"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Publish SLM to Hugging Face</span>
</span></span><span style="display:flex;"><span>python 10_scripts/publish_slm_to_huggingface.py</span></span></code></pre></div><figcaption>
        <strong>Listing 13: Publish SLM to Hugging Face</strong>
    </figcaption>
</figure>
<p><strong>Option B: Publish Regular Model (354M parameters)</strong></p>
<figure id="listing14"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Publish Regular model to Hugging Face  </span>
</span></span><span style="display:flex;"><span>python 10_scripts/publish_to_huggingface.py</span></span></code></pre></div><figcaption>
        <strong>Listing 14: Publish Regular Model to Hugging Face</strong>
    </figcaption>
</figure>
<p>If everything is working correctly and the models are published, you will see confirmation messages and upload progress. Here&rsquo;s what successful publishing looks like:</p>
<figure>
<figure>
<img src="images/hf05.png" alt="HF - SLM upload" title="HF - SLM upload">
<figcaption><strong>Figure 13:</strong> Hugging Face SLM model upload progress and confirmation</figcaption>
</figure>
<figcaption><strong>Figure 13:</strong> Hugging Face upload process for SLM model showing successful publishing workflow</figcaption>
</figure>
<p>And this is an example output for the Regular model:</p>
<figure>
<figure>
<img src="images/hf06-regular-model.png" alt="HF - Regular model upload" title="HF - Regular model upload">
<figcaption><strong>Figure 14:</strong> Hugging Face Regular model upload progress and confirmation</figcaption>
</figure>
<figcaption><strong>Figure 14:</strong> Hugging Face upload process for Regular model showing successful publishing workflow</figcaption>
</figure>
<p><strong>After Publishing:</strong>
Once published, your models will be available at:</p>
<ul>
<li><strong>SLM</strong>: <code>bahree/london-historical-slm</code></li>
<li><strong>Regular Model</strong>: <code>bahree/london-historical-llm</code></li>
</ul>
<p>Users can then easily load and use your models with just a few lines of code, making your historical language models accessible to the broader AI community for research, education, and creative applications.</p>
<p><strong>Testing Your Published Models:</strong>
Once published, you can test your models using the same inference methods shown in the Quick Start section:</p>
<figure id="listing15"><div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Test published SLM model (10 automated tests)</span>
</span></span><span style="display:flex;"><span>python 06_inference/test_published_models.py --model_type slm
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Interactive testing with published models</span>
</span></span><span style="display:flex;"><span>python 06_inference/inference_unified.py --published --model_type slm --interactive</span></span></code></pre></div><figcaption>
        <strong>Listing 15: Test Published Models</strong>
    </figcaption>
</figure>
<h2 id="10-what-weve-accomplished">10. What We&rsquo;ve Accomplished</h2>
<p>This comprehensive guide has taken you from raw historical documents to working language models that can generate authentic 18th and 19th-century London text. We&rsquo;ve built a complete pipeline that transforms 218+ historical sources into two specialized models - a fast SLM for experimentation and a powerful Regular model for high-quality generation. The entire system is fully functional, with both PyTorch checkpoint inference and Hugging Face model publishing working seamlessly, tested and validated on real hardware.</p>
<p>What makes this project interesting is that it&rsquo;s not just another language model - it&rsquo;s a complete educational journey that teaches you every aspect of building LLMs from scratch. From custom historical tokenizers that understand archaic English to sophisticated GPU optimization and deployment, you&rsquo;ve learned the full stack of modern language model development. The result is a system that preserves historical linguistic heritage while demonstrating cutting-edge AI techniques, making it valuable for researchers, educators, and anyone interested in the intersection of history and technology.</p>
<h2 id="11-the-journey-continues">11. The Journey Continues</h2>
<p>This is just the beginning. In the next three parts of this series, we&rsquo;ll dive deeper into the technical foundations:</p>
<p><strong><a
	
		href = "/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	>
	
	<span>
		Part 2
	</span>
</a></strong> explores historical data collection, showing how we curated 218+ authentic sources spanning 350 years of London&rsquo;s history, and how we built a custom tokenizer that truly understands historical English.</p>
<p><strong><a
	
		href = "/post/2025/11/building-llm-from-scratch-part3-model-architecture-gpu-training/"
	

	

	>
	
	<span>
		Part 3
	</span>
</a></strong> reveals the custom GPT architecture designed specifically for historical text, GPU optimization strategies, and training infrastructure.</p>
<p><strong><a
	
		href = "/post/2026/01/building-llm-from-scratch-part4-evaluation-deployment/"
	

	

	>
	
	<span>
		Part 4
	</span>
</a></strong> completes the journey with evaluation frameworks, testing strategies, and deployment techniques that transform your trained models into working systems.</p>
<p>Each part builds on what you&rsquo;ve learned here, taking you from high-level overview to deep technical implementation details.</p>
<h2 id="12-resources">12. Resources</h2>
<ul>
<li><strong>GitHub Repository</strong>:⚙️<a
	
		href = "https://github.com/bahree/helloLondon"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		github.com/bahree/helloLondon
	</span>
</a> - Complete codebase with all training scripts, inference tools, and documentation</li>
<li><strong>Hugging Face Models</strong>:
<ul>
<li>🤗 <a
	
		href = "https://huggingface.co/bahree/london-historical-slm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-slm
	</span>
</a> - Small Language Model (117M parameters)</li>
<li>🤗 <a
	
		href = "https://huggingface.co/bahree/london-historical-llm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		bahree/london-historical-llm
	</span>
</a> - Regular Model (354M parameters)</li>
</ul>
</li>
<li>📘<strong>Book Reference</strong>: <a
	
		href = "https://a.co/d/ffzkJ7T"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> - For deeper understanding of core LLM concepts</li>
<li>📖<strong>Documentation</strong>: Complete guides in the <code>08_documentation/</code> folder covering every aspect of the project</li>
</ul>
<h2 id="13-acknowledgments">13. Acknowledgments</h2>
<p>This project builds upon the excellent work of the open-source community. Special thanks to <a
	
		href = "https://github.com/haykgrigo3/TimeCapsuleLLM"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		haykgrigo3&rsquo;s TimeCapsuleLLM
	</span>
</a> for the initial inspiration and framework for historical language model training, and to <a
	
		href = "https://github.com/karpathy/nanoGPT"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Andrej Karpathy&rsquo;s nanoGPT
	</span>
</a> for the foundational GPT architecture and training methodology. The project extends these foundations with specialized adaptations for historical text, including custom tokenizers, advanced data filtering, and educational deployment infrastructure.</p>
<p>🙏</p>
<hr>
<p><strong>Ready to dive deeper?</strong> <a
	
		href = "https://blog.desigeek.com/post/2025/10/building-llm-from-scratch-part2-data-tokenizers/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Part 2: Data Collection &amp; Custom Tokenizers
	</span>
</a> covers the technical details of data collection, cleaning pipelines, and custom tokenizer development for authentic historical text processing.</p>
]]></content:encoded>
    </item>
    <item>
      <title>RustySnake - Classic Snake game to learn Rust</title>
      <link>/post/2025/03/rustysnake-snake-game-to-learn-rust/</link>
      <pubDate>Thu, 27 Mar 2025 00:00:00 +0000</pubDate>
      <guid>/post/2025/03/rustysnake-snake-game-to-learn-rust/</guid>
      <description>Learning Rust through a Snake game and LLMs</description>
      <content:encoded><![CDATA[<h3 id="1-overview">1. Overview</h3>
<p>Rust has been gaining attention recently due to its unique combination of performance, safety, and modern programming features. Its strict ownership model eliminates common memory issues like null pointer dereferencing and data races, providing a secure environment for developers. At the same time, its expressive syntax and focus on developer productivity make it a strong contender for systems programming. Its growing ecosystem and community continue to expand its capabilities into areas like web development and embedded systems, ensuring a confident and secure learning experience for developers.</p>
<p>When diving into Rust, a language lauded for its speed and safety, I wanted to start with a fun and educational project. Inspired by my experience with C and C++, I decided to learn Rust by implementing a classic snake game - a project that not only provides a fun learning experience but also reinforces an understanding of programming fundamentals. This journey has introduced me to Rust&rsquo;s unique features and reinforced my understanding of programming fundamentals, engaging and motivating me to learn more.</p>
<p>As I embarked on this journey, I also wanted to understand how LLMs can assist in grasping Rust in terms of syntax, layout, flow, and execution aspects. These models played a crucial role in helping me debug and resolve issues, providing the necessary support and guidance in my learning process. For this, I used the following different models. Except for GPT 4o, I also want to run the others on my home GPU cluster - again, to help experiment with different aspects of inference.</p>
<ul>
<li>Phi 4</li>
<li>Llama 3.3 70b Instruct</li>
<li>Qwen 2.5B Coder 32B</li>
<li>DeepSeek R1 Distill Llama 8B</li>
<li>GPT 4o</li>
</ul>
<h4 id="11-llm-performance-comparison">1.1. LLM Performance Comparison</h4>
<table>
  <thead>
      <tr>
          <th>Model</th>
          <th>Code Generation</th>
          <th>Error Explanation</th>
          <th>Documentation</th>
          <th>Overall Experience</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Phi 4</td>
          <td>✔️✔️✔️</td>
          <td>✔️✔️</td>
          <td>✔️✔️</td>
          <td>Good for simpler tasks</td>
      </tr>
      <tr>
          <td>Llama 3.3 70b</td>
          <td>✔️✔️✔️✔️</td>
          <td>✔️✔️✔️</td>
          <td>✔️✔️✔️</td>
          <td>Strong but inconsistent</td>
      </tr>
      <tr>
          <td>Qwen 2.5B Coder 32B</td>
          <td>✔️✔️✔️✔️</td>
          <td>✔️✔️✔️</td>
          <td>✔️✔️✔️✔️</td>
          <td>Excellent for code examples</td>
      </tr>
      <tr>
          <td>DeepSeek R1 Distill 8B</td>
          <td>✔️✔️</td>
          <td>✔️✔️</td>
          <td>✔️✔️</td>
          <td>Limited understanding of Rust</td>
      </tr>
      <tr>
          <td>GPT 4o</td>
          <td>✔️✔️✔️✔️✔️</td>
          <td>✔️✔️✔️✔️✔️</td>
          <td>✔️✔️✔️✔️✔️</td>
          <td>Comprehensive assistance throughout</td>
      </tr>
  </tbody>
</table>
<p>Of course, it is not an apples-to-apples comparison. Each started well but then quickly went downhill for various reasons. The only awesome one I finally ended up using all the way through was GPT4o.</p>
<h4 id="12-why-rust">1.2. Why Rust?</h4>
<p>Rust is designed to be a systems-level language with modern features. It eliminates common pitfalls like memory safety issues without compromising performance. As someone familiar with C and C++, I think this seems like a natural progression.</p>
<h3 id="2-the-snake-game-project">2. The Snake Game Project</h3>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>   /^\\/^\\
</span></span><span style="display:flex;"><span>  / o   o \\
</span></span><span style="display:flex;"><span> (    ^    )
</span></span><span style="display:flex;"><span>  \\_______/
</span></span><span style="display:flex;"><span>   |     |
</span></span><span style="display:flex;"><span>   |     |</span></span></code></pre></div>
<p><p>

    <figure>
        <img src="images/ascii-text-art.jpg" alt="Rust Snake"/>
        <figcaption>Rusty Snake</figcaption>
    </figure>

</p></p>
<p>The nostalgic, classic snake game provides a perfect playground for learning Rust’s syntax, constructs, and libraries. For the game, I started with the basic game but then soon added other things - I wanted to give the player a choice on the size of the play area (measured in number of characters as the rendering is in ASCII) and speed.</p>
<p>A crate in Rust is a collection of related code akin to a library in C++ or a module in Python. The project imports the following crates for different functionalities needed: </p>
<ul>
<li><strong><code>crossterm</code></strong>: For terminal UI and input handling.</li>
<li><strong><code>rand</code></strong>: For generating random numbers (e.g., food placement).</li>
</ul>
<p>Let us walk through some key implementation parts and draw parallels to C and C++ for easier comprehension.</p>
<h3 id="3-key-concepts-in-the-code">3. Key Concepts in the Code</h3>
<h4 id="31-structs-rusts-version-of-struct-in-c">3.1 Structs: Rust’s Version of <code>struct</code> in C</h4>
<p>Rust’s <code>struct</code> is similar to <code>struct</code> in C and C++ but with additional safety and functionality. Here’s how we define a <code>Point</code> struct to represent 2D coordinates:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#[derive(Debug, Clone, PartialEq)]</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">struct</span> <span style="color:#eed49f">Point</span> {
</span></span><span style="display:flex;"><span>    x: <span style="color:#ed8796">i32</span>,
</span></span><span style="display:flex;"><span>    y: <span style="color:#ed8796">i32</span>,
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p><strong>Parallel in C/C++</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#c6a0f6">struct</span> <span style="color:#eed49f">Point</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> x;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> y;
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
<p>In Rust, <code>#[derive(...)]</code> automatically implements traits like <code>Debug</code> (for debugging), <code>Clone</code> (for copying), and <code>PartialEq</code> (for comparisons).</p>
<h4 id="32-vectors-vect-dynamic-arrays">3.2 Vectors <code>Vec&lt;T&gt;</code>): Dynamic Arrays</h4>
<p>Rust uses <code>Vec&lt;T&gt;</code> for dynamically-sized arrays, similar to <code>std::vector</code> in C++.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> snake <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#8aadf4">vec!</span>[Point { x: <span style="color:#eed49f">width</span> <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">2</span>, y: <span style="color:#eed49f">height</span> <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">2</span> }];</span></span></code></pre></div>
<p><strong>Parallel in C++</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#91d7e3;font-weight:bold">::</span>vector<span style="color:#91d7e3;font-weight:bold">&lt;</span>Point<span style="color:#91d7e3;font-weight:bold">&gt;</span> snake <span style="color:#91d7e3;font-weight:bold">=</span> { Point{width <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">2</span>, height <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">2</span>} };</span></span></code></pre></div>
<ul>
<li>Unlike C++, Rust ensures safety with bounds checking and ownership rules.</li>
</ul>
<h4 id="33-ownership-and-borrowing">3.3 <strong>Ownership and Borrowing</strong></h4>
<p>Rust’s ownership model ensures memory safety without a garbage collector. When working with <code>snake</code>, you own or borrow the data.</p>
<p>Example:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> new_head <span style="color:#91d7e3;font-weight:bold">=</span> snake.last().unwrap().clone();</span></span></code></pre></div>
<p>Here, <code>.clone()</code> creates a deep copy of the last element to avoid ownership issues.</p>
<p><strong>Parallel in C++</strong>: Copying would be explicit but lacks ownership enforcement:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Point new_head <span style="color:#91d7e3;font-weight:bold">=</span> snake.back();</span></span></code></pre></div>
<h4 id="34-enums-and-pattern-matching">3.4 <strong>Enums and Pattern Matching</strong></h4>
<p>Rust’s pattern matching with <code>match</code> is more expressive than a <code>switch</code> statement in C++.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#c6a0f6">match</span> key_event.code {
</span></span><span style="display:flex;"><span>    KeyCode::Up <span style="color:#c6a0f6">if</span> direction.y <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> next_direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#f5a97f">0</span>, y: <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span> },
</span></span><span style="display:flex;"><span>    KeyCode::Down <span style="color:#c6a0f6">if</span> direction.y <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> next_direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#f5a97f">0</span>, y: <span style="color:#f5a97f">1</span> },
</span></span><span style="display:flex;"><span>    _ <span style="color:#91d7e3;font-weight:bold">=&gt;</span> {}
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p><strong>Parallel in C++</strong>:
A <code>switch</code> with additional <code>if</code> conditions might approximate this but would lack the same level of elegance.</p>
<h4 id="35-error-handling">3.5 <strong>Error Handling</strong></h4>
<p>Rust avoids exceptions by using the <code>Result</code> type for error handling, relying on explicit and predictable control flow to manage errors. This differs from other languages and ensures errors are handled explicitly rather than relying on potentially disruptive exception mechanisms, which can lead to unwieldy codebases in large systems.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#c6a0f6">fn</span> <span style="color:#8aadf4">main</span>() -&gt; <span style="color:#eed49f">crossterm</span>::<span style="color:#91d7e3">Result</span><span style="color:#91d7e3;font-weight:bold">&lt;</span>()<span style="color:#91d7e3;font-weight:bold">&gt;</span> {
</span></span><span style="display:flex;"><span>    terminal::enable_raw_mode()<span style="color:#91d7e3;font-weight:bold">?</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">..</span>.
</span></span><span style="display:flex;"><span>    terminal::disable_raw_mode()<span style="color:#91d7e3;font-weight:bold">?</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Ok</span>(())
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>The <code>?</code> operator propagates errors automatically, akin to C++&rsquo;s <code>std::optional</code> or manual error handling:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">!</span>enable_raw_mode()) <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>;</span></span></code></pre></div>
<h4 id="36-functional-features">3.6 <strong>Functional Features</strong></h4>
<p>Rust’s iterators and closures make code concise and expressive. For instance, generating random food coordinates:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#ed8796">let</span> new_food <span style="color:#91d7e3;font-weight:bold">=</span> Point {
</span></span><span style="display:flex;"><span>    x: <span style="color:#eed49f">rng</span>.gen_range(<span style="color:#f5a97f">1</span><span style="color:#91d7e3;font-weight:bold">..</span>width <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">1</span>),
</span></span><span style="display:flex;"><span>    y: <span style="color:#eed49f">rng</span>.gen_range(<span style="color:#f5a97f">1</span><span style="color:#91d7e3;font-weight:bold">..</span>height <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">1</span>),
</span></span><span style="display:flex;"><span>};</span></span></code></pre></div>
<p><strong>Parallel in C++</strong>:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Point new_food <span style="color:#91d7e3;font-weight:bold">=</span> { rand() <span style="color:#91d7e3;font-weight:bold">%</span> (width <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">2</span>) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>, rand() <span style="color:#91d7e3;font-weight:bold">%</span> (height <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">2</span>) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span> };</span></span></code></pre></div>
<p>Rust’s range syntax and <code>rand</code> crate simplify random number generation.</p>
<h3 id="4-code">4. Code</h3>
<p>The complete source code for this project is available on GitHub:
<a
	
		href = "https://github.com/bahree/rustysnake"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://github.com/bahree/rustysnake
	</span>
</a></p>
<p>The game loop handles input, updates the snake&rsquo;s position, checks for collisions, and renders the game state. The <code>crossterm</code> library provides a simple way to handle keyboard input and terminal rendering.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#c6a0f6">loop</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Handle input
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span> crossterm::event::poll(Duration::from_millis(game_speed))<span style="color:#91d7e3;font-weight:bold">?</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#ed8796">let</span> Event::Key(key_event) <span style="color:#91d7e3;font-weight:bold">=</span> event::read()<span style="color:#91d7e3;font-weight:bold">?</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">match</span> key_event.code {
</span></span><span style="display:flex;"><span>                KeyCode::Char(<span style="color:#a6da95">&#39;q&#39;</span>) <span style="color:#91d7e3;font-weight:bold">=&gt;</span> <span style="color:#c6a0f6">break</span>,
</span></span><span style="display:flex;"><span>                KeyCode::Up <span style="color:#c6a0f6">if</span> direction.y <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#f5a97f">0</span>, y: <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span> },
</span></span><span style="display:flex;"><span>                KeyCode::Down <span style="color:#c6a0f6">if</span> direction.y <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#f5a97f">0</span>, y: <span style="color:#f5a97f">1</span> },
</span></span><span style="display:flex;"><span>                KeyCode::Left <span style="color:#c6a0f6">if</span> direction.x <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>, y: <span style="color:#f5a97f">0</span> },
</span></span><span style="display:flex;"><span>                KeyCode::Right <span style="color:#c6a0f6">if</span> direction.x <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">=&gt;</span> direction <span style="color:#91d7e3;font-weight:bold">=</span> Point { x: <span style="color:#f5a97f">1</span>, y: <span style="color:#f5a97f">0</span> },
</span></span><span style="display:flex;"><span>                _ <span style="color:#91d7e3;font-weight:bold">=&gt;</span> {}
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Update snake position
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#ed8796">let</span> new_head <span style="color:#91d7e3;font-weight:bold">=</span> Point {
</span></span><span style="display:flex;"><span>        x: (snake[<span style="color:#f5a97f">0</span>].x <span style="color:#91d7e3;font-weight:bold">+</span> direction.x <span style="color:#91d7e3;font-weight:bold">+</span> width) <span style="color:#91d7e3;font-weight:bold">%</span> width,
</span></span><span style="display:flex;"><span>        y: (snake[<span style="color:#f5a97f">0</span>].y <span style="color:#91d7e3;font-weight:bold">+</span> direction.y <span style="color:#91d7e3;font-weight:bold">+</span> height) <span style="color:#91d7e3;font-weight:bold">%</span> height,
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Check collision with self
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span> snake.contains(<span style="color:#91d7e3;font-weight:bold">&amp;</span>new_head) {
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">break</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    snake.insert(<span style="color:#f5a97f">0</span>, new_head);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Check if snake ate food
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span> snake[<span style="color:#f5a97f">0</span>] <span style="color:#91d7e3;font-weight:bold">==</span> food {
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">// Generate new food
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        <span style="color:#c6a0f6">loop</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#ed8796">let</span> new_food <span style="color:#91d7e3;font-weight:bold">=</span> Point {
</span></span><span style="display:flex;"><span>                x: <span style="color:#eed49f">rng</span>.gen_range(<span style="color:#f5a97f">0</span><span style="color:#91d7e3;font-weight:bold">..</span>width),
</span></span><span style="display:flex;"><span>                y: <span style="color:#eed49f">rng</span>.gen_range(<span style="color:#f5a97f">0</span><span style="color:#91d7e3;font-weight:bold">..</span>height),
</span></span><span style="display:flex;"><span>            };
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">!</span>snake.contains(<span style="color:#91d7e3;font-weight:bold">&amp;</span>new_food) {
</span></span><span style="display:flex;"><span>                food <span style="color:#91d7e3;font-weight:bold">=</span> new_food;
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">break</span>;
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    } <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>        snake.pop();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>}</span></span></code></pre></div>
<p>Feel free to clone, fork, or contribute to the repository!</p>
<h4 id="41-running-the-game">4.1 Running the Game</h4>
<p>To run this on Windows:</p>
<ol>
<li>Install Rust with <code>rustup</code>.</li>
<li>Add dependencies to <code>Cargo.toml</code>:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[dependencies]
</span></span><span style="display:flex;"><span>crossterm = <span style="color:#a6da95">&#34;0.27&#34;</span>
</span></span><span style="display:flex;"><span>rand = <span style="color:#a6da95">&#34;0.8&#34;</span></span></span></code></pre></div>
</li>
<li>Build and run the project:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cargo run</span></span></code></pre></div>
</li>
<li>Compile the project:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cargo build --release</span></span></code></pre></div>
</li>
</ol>
<h4 id="42-controls">4.2 Controls</h4>
<table>
  <thead>
      <tr>
          <th>Key</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>Arrow Keys</code></td>
          <td>Move the snake</td>
      </tr>
      <tr>
          <td><code>+</code></td>
          <td>Increase game speed</td>
      </tr>
      <tr>
          <td><code>-</code></td>
          <td>Decrease game speed</td>
      </tr>
      <tr>
          <td><code>Spacebar</code></td>
          <td>Pause/Resume the game</td>
      </tr>
      <tr>
          <td><code>q</code></td>
          <td>Quit the game</td>
      </tr>
  </tbody>
</table>
<h4 id="43-gameplay">4.3 Gameplay</h4>
<ol>
<li>Select the <strong>boundary size</strong> and <strong>difficulty</strong> from the menu.</li>
<li>Use arrow keys to move the snake.</li>
<li>Eat the red food (<code>■</code>) to grow your snake and increase your score.</li>
<li>Avoid hitting the walls (<code>#</code>) or yourself!</li>
</ol>
<p><p>

    <figure>
        <img src="images/rusty2.jpg" alt="Screen Play Example"/>
        <figcaption>Snake Game</figcaption>
    </figure>

</p></p>
<h3 id="5-learnings">5. Learnings</h3>
<p>I encountered a few things while learning Rust through this snake game project. These are called out below. As with anything else, where things are different, one must forget what one knows and learn the new way of doing things.</p>
<ul>
<li><strong>Ownership model</strong>: Coming from C/C++, adjusting to Rust&rsquo;s strict borrowing rules required a shift in thinking</li>
<li><strong>Terminal UI limitations</strong>: Working with crossterm&rsquo;s terminal interface meant designing around ASCII graphics</li>
<li><strong>Game loop timing</strong>: Balancing responsiveness with performance required careful tuning</li>
<li><strong>Error propagation</strong>: Learning to use the <code>?</code> operator and <code>Result</code> types effectively took practice</li>
</ul>
<p>Understanding these differences was part of the learning process and helped solidify some of my understanding of Rust&rsquo;s principles.</p>
<h4 id="51-reflecting-on-rust">5.1 Reflecting on Rust</h4>
<p>Building this game helped me appreciate Rust’s features:</p>
<ul>
<li><strong>Memory Safety</strong>: Rust eliminates segmentation faults common in C and C++.</li>
<li><strong>Expressive Syntax</strong>: Iterators, pattern matching, and traits make Rust elegant and powerful.</li>
<li><strong>Community and Ecosystem</strong>: Libraries like <code>crossterm</code> and <code>rand</code> accelerate development.</li>
</ul>
<h4 id="52-acknowledgment">5.2 Acknowledgment</h4>
<p>Throughout this journey, I used the assistance of LLMs to clarify Rust concepts and draw parallels to languages I already know. This collaborative approach made learning smoother and deepened my understanding by offering different perspectives. The combination of hands-on learning and real-time assistance has been invaluable. </p>
<h3 id="6-final-thoughts">6 Final Thoughts</h3>
<p>Rust might feel different initially if you&rsquo;re from a C or C++ background, but its safety guarantees and modern features make it worthwhile. Building a snake game is just one of many ways to start.</p>
<p>What will you build next? 📎</p>
]]></content:encoded>
    </item>
    <item>
      <title>An introduction to Mixture of Experts (MoE)</title>
      <link>/post/2025/01/intro-to-mixture-of-experts/</link>
      <pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate>
      <guid>/post/2025/01/intro-to-mixture-of-experts/</guid>
      <description>In #AI, what are Mixture of Experts (MoEs) and how do they work?</description>
      <content:encoded><![CDATA[<p>AI is advancing at an unprecedented pace, with Mixture of Experts (MoE) models being one set of model architectures at the forefront of this revolution. These architectures enable breakthroughs in efficiency and scalability by leveraging a modular design where only a subset of specialized &ldquo;expert&rdquo; networks are activated for each input. MoE architectures have become a cornerstone in building ultra-large-scale models like GLaM and Switch Transformers.</p>
<p>Mixture of Experts (MoE) is an advanced machine learning architecture that lately has gained significance, particularly in the realm of #LLMs (large language models) and NNs (neural networks). In talking with many people about AI, I&rsquo;ve found that MoE as a topic comes up often, with many folks either not understanding what it is or their understanding of it being incorrect.</p>
<p>With recent announcements of trillion-parameter models and announcements from Microsoft, OpenAI, and Google, understanding MoEs is more important than ever. More recently, <a
	
		href = "https://huggingface.co/deepseek-ai/DeepSeek-V3"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		DeepSeek v3
	</span>
</a> is a great example of a model that uses MoEs to achieve state-of-the-art performance - where the language model has 671B total but only 37B activated for each token.</p>
<p>I this post I provide a high-level overview of MoEs, their core components, how they work and workflow. I also includes a simple toy example implementation to help grasp the core concepts.</p>
<h3 id="why-mixture-of-experts">Why Mixture of Experts?</h3>
<p>The central motivation behind MoE stems from the tension between growing model size and limiting computational resources. As we have seen in the recent past, increasing the parameter count of a model often yields better performance, especially in domains like natural language processing (NLP) and computer vision; however, this also drastically increases the cost of both training these models and computing cost for inference of these models. This massive computing cost is at the heart of what MoEs are addressing by offering a different paradigm known as conditional computation. MoE&rsquo;s activate only a small subset of specialized sub-networks (called experts) for each input or token) only to a small number of experts rather than processing it through every parameter in the network. This helps with three key aspects:</p>
<ul>
<li><strong>Increased Model Capacity</strong>: Because only a few experts are activated at a time, MoE architectures can pack many parameters (experts) without proportionally increasing the computational cost per input.</li>
<li><strong>Specialized Sub-networks</strong>: Different experts can learn specific patterns or token-level specializations, leading to better performance.</li>
<li><strong>Efficient Usage of Compute</strong>: MoE optimizes resources (compute) by activating only a small fraction of the entire model, leading to significant efficiency gains.</li>
</ul>
<p>This sparse activation strategy enables constructing models with billions (or even trillions) of parameters, making MoE a scalable solution for large-scale applications. Another benefit of MoE is its capacity for specialization. Different experts can learn distinct, context-dependent processing strategies, enabling the model to cover a broad set of input variations more effectively than a monolithic architecture.</p>
<p>Early MoE ideas trace back to model ensembling in classic machine learning. Still, MoE extends beyond ensembling by learning a parametric “router” (gating function) that dynamically decides which experts to use. Notable works like the Sparsely-Gated Mixture of Experts showed that MoE could massively scale model size while staying computationally efficient.</p>
<h3 id="core-components-of-an-moe-system">Core Components of an MoE System</h3>
<p>The fundamental building blocks of an MoE system are:</p>
<ol>
<li><strong>Experts</strong>: Specialized sub-models, typically feed-forward neural networks.</li>
<li><strong>Router</strong> or <strong>Gating Network</strong>: Determines which tokens are sent to which experts.</li>
<li><strong>Combiner</strong>: Aggregates the outputs from the selected experts.</li>
</ol>
<h4 id="experts">Experts</h4>
<p>Each expert is typically a neural sub-network replicated multiple times within the same model. Depending on the task, these experts are often implemented as independent feed-forward networks, MLP blocks, or convolutional layers. For large-scale language models, each expert usually mirrors the structure of the feed-forward component of a Transformer block.</p>
<p>Since multiple experts exist in parallel, each can potentially learn to handle different token types or data distributions. Contrary to common misconceptions, experts do not necessarily correspond to human-like semantic domains (e.g., “Expert #3 = Physics”). One might assume that certain experts correspond to high-level domains such as &ldquo;finance&rdquo; or &ldquo;medicine.&rdquo; Still, it is more common for the learned expertise to be more subtle and token-level, capturing idiosyncratic patterns that are not necessarily interpretable in a straightforward semantic way. Each expert ends up specializing in token-level or feature-level patterns that are discovered during training. For instance, in NLP, one expert might specialize in syntactic structures, while another focuses on semantic relationships.</p>
<p>Sometimes, certain layers (like embeddings or attention blocks) are shared among all experts, and only feed-forward layers are duplicated (as in Switch Transformers).
This partial sharing allows the model to keep some global representation while still having specialized processing in the experts.</p>
<h4 id="router">Router</h4>
<p>The router, often called the gating network, is a small module that predicts which experts should handle any given input. Its purpose is to determine which experts should handle a given input. Modern MoE designs are typically parameterized as a simple neural network (often a single linear layer + SoftMax) or a simple linear transform. The SoftMax output provides a probability distribution across the experts, indicating which should be &ldquo;activated&rdquo; for each input.</p>
<p>The router reads the input representation (e.g., the token embedding in an NLP model) and produces a probability distribution over the experts, typically with a SoftMax function. We then select the top-k experts (e.g., top-1, top-2) based on these probabilities - for each token, known as <strong>Top-k gating</strong>.</p>
<p>Practical implementations often limit the number of tokens an expert can process per batch (known as Expert Capacity). If too many tokens route to the same expert, some tokens may get dropped or rerouted, leading to training instability. This capacity limit helps prevent any single expert from monopolizing the model&rsquo;s processing and prevents load imbalances.</p>
<p>The gating network is trained jointly with the experts through back-propagation. As we outlined above, the gating process introduces discrete decisions into the computational graph, which can hamper backpropagation. To help counter this, additional techniques such as adding a small amount of noise to the logits (<strong>Noisy Top-k Gating</strong>) or using Soft MoE — are employed to smooth out these discrete selections, keep training stable, help provide smoother gradients, and encourage balanced expert utilization. Additional mechanisms like Expert Capacity limit how many tokens each expert can process, preventing load imbalances in which a single expert might receive most tokens.</p>
<h4 id="combiner">Combiner</h4>
<p>Once the chosen experts have computed their outputs for a given token, these outputs must be aggregated to produce a single vector that feeds into subsequent layers. The MoE architecture achieves this through a combiner, which typically performs a weighted sum of the experts&rsquo; outputs, using the gating probabilities as weights. In <strong>top-k gating</strong>, if $ k $ experts were activated, each expert’s output is multiplied by its corresponding probability from the router. The combiner then sums or otherwise fuses the results to form the token’s transformed representation. This consolidated output is passed into the rest of the model, such as attention blocks or additional Transformer layers.</p>
<p>In “Soft MoE” variants, we might use a soft combination, letting tokens pass to all experts but with different fractional weights—alleviating some routing discontinuities at the cost of higher computation.</p>
<h3 id="high-level-workflow">High-Level Workflow</h3>
<ol>
<li>Input is fed into the <em>gating network</em>, which produces a probability distribution (or <em>scores</em>) over experts.</li>
<li>The <em>top-k</em> experts are <em>activated</em> for each input (or token, in the case of language models).</li>
<li>The selected experts <em>process the input</em> in parallel.</li>
<li>A combiner fuses the experts’ outputs into a single vector.</li>
<li>The model produces the <em>final</em> output, which can then feed into other layers or tasks.</li>
</ol>
<p>Below is a simple flow diagram that shows how to visualize this:</p>
<pre class="mermaid">flowchart TD
    Input_Tokens --&gt; Gating_Network
    Gating_Network --&gt; Expert_1
    Gating_Network --&gt; Expert_2
    Gating_Network --&gt; Expert_3
    Expert_2 --&gt; Combiner
    Expert_3 --&gt; Combiner
    Combiner --&gt; Final_Output
</pre>
<p>The MoE module often appears in LLMs where a standard feed-forward layer normally would reside. For <strong>each layer</strong> designated as an MoE, the process begins by sending the hidden representations of each token to the router. The router computes gating probabilities across all experts, ranks the experts by these probabilities, and picks the top few. Each selected expert applies its transformation to the token&rsquo;s hidden state. Finally, the combiner merges these expert outputs with a weighted sum.</p>
<p>During training, the experts and the router are updated jointly through backpropagation. However, discrete gating can make gradient flow tricky since the top-k selection is not inherently differentiable. In practice, noise injection (Noisy Top-k Gating) or methods like Soft MoE can help approximate continuous gradients, ensuring that even experts with lower gating probabilities receive occasional training signals.</p>
<p>In addition, to avoid a scenario where one or two experts monopolize all tokens, an auxiliary load-balancing loss is often introduced to encourage more uniform usage. This might take the form of a penalty term that grows when the variance in expert usage is high, incentivizing the router to distribute tokens more evenly.</p>
<p>This mechanism occurs for every token in every layer designated as an MoE layer, which is why load balancing is so critical—without careful design, certain experts can receive far more tokens than others.</p>
<h3 id="what-is-the-difference-between-an-moe-and-an-ensemble">What is the Difference between an MoE and an Ensemble?</h3>
<p>It’s easy to confuse MoE with model ensembling - where multiple independently trained models vote or average predictions. MoE differs in a few critical ways:</p>
<ul>
<li>Dynamic and data-dependent routing: In classical ensembles, each model sees the same input, and a meta-learner or a simple average produces the final result. MoE, in contrast, uses a router that decides different subsets of the input for each expert. This dynamic routing allows MoE to specialize in different patterns or tokens.</li>
<li>Single model training: MoE typically trains all experts jointly in one go. Each expert does not have a separate training pass; they’re part of the same computation graph, sharing some parameters (like embeddings) and learning together. This is in contrast to ensembles, where each model is trained independently.</li>
<li>Fine-grained token specialization: Different tokens in the same sentence might get routed to different experts, enabling extremely fine-grained specialization. This is impossible in a traditional ensemble, where each model sees the entire input.</li>
</ul>
<h3 id="load-balancing-experts-and-training-pitfalls">Load Balancing Experts and Training Pitfalls</h3>
<p>A significant challenge in training MoE models is ensuring the balanced utilization of experts. Certain experts may become overburdened without proper load balancing, while others remain underutilized, leading to inefficient training and suboptimal performance. Gating can become highly imbalanced early in training, favoring a few experts. Common solutions for Load Balancing:</p>
<ul>
<li>Auxiliary Load Balancing Losses: Adding a regularization term to the loss function encourages the gating network to distribute inputs evenly across all experts.</li>
<li>Top-k Randomization: Instead of always selecting the top-k experts with the highest gating probabilities, randomizing the selection among the top candidates can prevent overloading.</li>
<li>Expert Capacity Constraints: Limiting the number of tokens an expert can process at a time can help ensure all experts are used during training.</li>
</ul>
<p>Over time, load-balancing losses/techniques help the distribution even out, but the model can remain fragile without careful hyperparameter tuning. Expert capacity is another important design choice. Since top-k selection may route too many tokens to the most popular experts, a capacity limit ensures each expert processes no more than a certain maximum number of tokens in one forward pass. The remaining tokens must be dropped or re-routed to other experts if an expert is at capacity. Both approaches come with trade-offs: dropping tokens entirely can lead to data inefficiency, whereas re-routing can add complexity and undermine the sparsity benefits that MoE aims to provide.</p>
<p>Load balancing - ensuring experts share the training load - remains one of the <em>biggest technical hurdles</em> with MoEs. Early in training, the gating network might discover that routing most tokens to the same expert (or a small number) might yield acceptable results, leaving other experts underutilized and effectively “dead.” This imbalance leads to suboptimal solutions - if only a few experts get trained on all tokens, you lose the advantages of specialization.</p>
<p>To mitigate this, MoE architectures often introduce additional techniques that nudge the model toward more even usage. One such technique is the auxiliary load-balancing loss. By monitoring how frequently each expert is selected, the model can be penalized if certain experts remain underutilized.  This can be thought of as an additional penalty term that measures how evenly tokens (or batches) are distributed across experts.</p>
<p>Another common approach is <strong>Noisy Top-k Gating</strong>, which injects a small amount of learnable or fixed noise into the gating logits before the softmax, making the gating probabilities slightly more random. This randomness allows less-popular experts to occasionally receive tokens, which can help them develop more useful specializations over time. The gating network thus learns not only to minimize the primary task loss but also to spread tokens more uniformly.</p>
<h4 id="sparsity-in-mixture-of-experts-moe-models">Sparsity in Mixture of Experts (MoE) Models</h4>
<p>Sparsity is one of MoE’s most valuable contributions to model efficiency. By only activating a small fraction of the total parameters for each input, the model maintains a much lower compute footprint than a dense model of the same overall size. This efficiency is crucial for scaling; while a trillion-parameter dense model may be prohibitively expensive to train and deploy, a trillion-parameter MoE model that only activates 1% of those parameters simultaneously becomes significantly more tractable.</p>
<p>That said, implementing sparsity at scale often requires specialized infrastructure. Frameworks like <code>GShard</code> or <code>Mesh-TensorFlow</code> are designed to handle data and model parallelism necessary for distributing the experts across GPU clusters. The overhead of routing tokens to the correct devices can become significant if the system is not carefully optimized. Researchers have also explored alternative gating mechanisms, such as Soft MoE, which approximates selection by routing every token to all experts in a soft, weighted fashion. While this approach can mitigate the fragility of discrete gates, it naturally increases computation since more experts perform computations at once.</p>
<p>Sparcity in MoE models offers several key advantages:</p>
<ul>
<li>Computational Efficiency: Sparsity dramatically reduces the number of FLOPs required to process each task.</li>
<li>Scalability: The sparse activation of experts enables MoE models to scale to a large number of experts without a corresponding linear increase in computational and memory requirements.</li>
<li>Increased Model Capacity: Sparsity allows MoE models to increase their overall parameter count and model capacity without significantly increasing the computational cost during training or inference.</li>
<li>Memory Efficiency: Operating sparsely, MoE models require less memory for activations and parameters.</li>
<li>Specialized Processing: Sparsity enables the model to route different inputs to the most relevant experts, allowing for more specialized and efficient processing of diverse inputs.</li>
</ul>
<h3 id="practical-applications-of-moe">Practical applications of MoE</h3>
<p>MoE architectures have already demonstrated clear benefits in many areas. Microsoft’s Z-code model (Machine Translation), for instance, leverages MoE to handle multilingual translation tasks at a massive scale, and Google’s Switch Transformers showed that sparse activation can reach higher quality at lower training cost than dense baselines on benchmarks such as GLUE and SuperGLUE. In computer vision, MoE modules have been integrated into Vision Transformers (V-MoEs) to achieve better image classification and detection accuracy, with each expert focusing on different aspects of the image representation. In multimodal learning, the capacity to handle diverse data types—such as text, images, and audio—makes MoE a natural fit because experts can adapt to different modalities or different subproblems within a single modality.</p>
<p>In the context of LLMs, systems like ChatGPT, Claude, and Gemini can benefit from MoE by leveraging different experts for different topics or query types - though specifics are often proprietary and not shared. MoE is particularly suited to multi-modal tasks involving text, images, and audio, as experts can specialize in different modalities or sub-modalities. This is valuable for text-to-image generation or video understanding.</p>
<p>Several emerging directions continue to push MoE research forward. Soft MoE (Zuo et al., 2022) is an example aiming to produce a fully differentiable version of sparsely gated Transformers. Another is Parameter-Efficient Sparsity Crafting (PESC), which seeks to retrofit existing dense models into a sparse MoE design without retraining from scratch. These innovations reflect ongoing efforts to refine the balance between sparse efficiency, training stability, and model reliability.</p>
<p>In production, deploying a large MoE model requires carefully coordinating hardware resources, data pipelines, and load-balancing techniques. Training an MoE system may involve more hyperparameters than a comparable dense model, including the number of experts, gating softmax temperature, top-k value, load-balancing penalty weights, and expert capacity. These factors can significantly affect performance, convergence speed, and final accuracy. When scaling an MoE model across multiple GPUs, designers must pay attention to network communication overhead. Token-based routing leads to collective operations that can become bottlenecks if not carefully optimized.</p>
<p>Despite these complexities, MoE’s flexibility and computational cost savings make it a compelling choice for handling highly varied or large-scale tasks. However, Fine-tuning MoE models can be more delicate than dense models because the gating distributions or specialized experts may not adapt smoothly to a new domain without carefully applied load-balancing strategies. There can also be interpretability challenges since the model’s internal “expert structure” does not always map neatly to skills.</p>
<h3 id="challenges-and-considerations">Challenges and Considerations</h3>
<p>We have touched on most of these in this blog post, but it is helpful to outline the key issues and considerations to be mindful of when using MoE-based models:</p>
<ul>
<li>Complexity: MoE models are significantly more complex (compared to traditional neural networks) and require substantial computational resources for training and inference.</li>
<li>Training Instability: MoE models can suffer from training instability due to the discrete nature of expert selection.</li>
<li>Load Balancing: Proper load balancing among experts is crucial for efficiently using model capacity and optimal performance.</li>
<li>Computational Overhead: The gating mechanism introduces additional computational overhead, potentially impacting training and inference times.</li>
<li>Interpretability Issues: The dynamic routing of inputs makes interpreting how MoE models arrive at their decisions challenging.</li>
<li>Hyperparameter Sensitivity: MoE models have several hyperparameters that must be tuned for optimal performance.</li>
</ul>
<p>Let’s define these mathematically before discussing practical considerations.</p>
<hr>
<h3 id="mathematical-formulation-of-moe">Mathematical Formulation of MoE</h3>
<p>Let $ x \in \mathbb{R}^d $ denote an input vector (a hidden representation from a preceding layer). We assume the system has $ N $ experts, with each expert $ E_i $ parameterized as a function $ E_i: \mathbb{R}^d \rightarrow \mathbb{R}^m $. The gating network $ G $ takes the same input $ x $ and outputs a vector in $\mathbb{R}^N$—essentially, a “score” or “weight” for each of the $ N $ experts. Finally, a combiner function $ C $ merges the experts’ outputs into a single output vector $ y \in \mathbb{R}^m $.</p>
<p>Formally, we can write:</p>
<ol>
<li><strong>Input</strong>: $ x \in \mathbb{R}^d $</li>
<li><strong>Experts</strong>: $ E_i: \mathbb{R}^d \rightarrow \mathbb{R}^m \quad \text{for}\ i = 1, \ldots, N $</li>
<li><strong>Gating Network</strong>: $ G: \mathbb{R}^d \rightarrow \mathbb{R}^N $</li>
<li><strong>Combiner</strong>: $ C: \mathbb{R}^{N \times m} \rightarrow \mathbb{R}^m $</li>
</ol>
<h4 id="top-k-gating">Top-k Gating</h4>
<p>Most contemporary MoE implementations use <em>top-k gating</em>, which activates only the $ k $ experts with the highest gating scores. In this scenario, the summation is performed only over those top-$ k $ indices. If we denote $\text{top-k}(G(x))$ as the set of indices corresponding to the $ k $ largest values of $ G(x) $, then</p>
<p>$
y = C\Bigl(\sum_{i \in \text{top-k}(G(x))} G(x)_i ,\cdot, E_i(x)\Bigr).
$</p>
<p>By pruning all but the top-$ k $ experts per input, this design enforces <em>sparse activation</em>: each input (or token) only “touches” $ k $ out of $ N $ experts at a time. This approach significantly reduces the computational load relative to using all $ N $ experts for every input.</p>
<h4 id="dense-gating-formulation">Dense Gating Formulation</h4>
<p>This refers to a version of MoE architecture in which <strong>all</strong> experts contribute to the final output for each input rather than filtering out all but the top-k experts. Here, the gating network assigns <em>continuous weights</em> to every expert, aggregating each expert’s weighted output into a final result. There is <em>no discrete selection</em> to zero out certain experts based on gating. Fundamentally, this is the opposite of top-K gating.</p>
<p>In the simplest version of MoE, where the gating network’s output is a set of continuous weights, the forward pass for one input $ x $ can be written as:</p>
<p>$ y = C\Bigl(\sum_{i=1}^N G(x)_i ,\cdot, E_i(x)\Bigr). $</p>
<p>Here, $ G(x)_i $ is the $i$-th component of the gating network’s output for $ x $ and represents the <em>weight</em> (or probability) assigned to expert $ i $. Intuitively, if $ G(x)_i $ is large, expert $ i $ contributes more to the final output $ y $. The function $ C $ often takes the form of a simple weighted sum or concatenation-and-projection step, depending on the specific design.</p>
<h4 id="load-balancing-and-expert-monopolization">Load Balancing and Expert Monopolization</h4>
<p>As we saw earlier, MoE architectures introduce an *<code>auxiliary load-balancing loss</code>*to load valance across all experts. This can be thought of as an additional penalty term that measures how evenly tokens (or batches) are distributed across experts. One common strategy is penalizing the variance in expert usage or encouraging each expert to receive roughly an equal proportion of examples. The gating network thus learns not only to minimize the primary task loss but also to spread tokens more uniformly.</p>
<p>Mathematically, a typical load-balancing loss might look like:</p>
<p>$ \mathcal{L} _ {\text{balance}} = \lambda \sum_{i=1}^N \Bigl(\frac{f_i}{\sum_j f_j} - \frac{1}{N}\Bigr)^2, $</p>
<p>where $ f_i $ is the total number of tokens assigned to expert $ i $ in a minibatch (or the sum of gating probabilities if you’re using a continuous measure), and $ \lambda $ is a hyperparameter controlling the strength of this penalty. This ensures the model is incentivized to explore and train all experts over time.</p>
<hr>
<h3 id="example-pytorch-implementation">Example PyTorch Implementation</h3>
<p>Here&rsquo;s a simplified Python implementation of an MoE model using PyTorch:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch.nn</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">nn</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">Expert</span>(nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, input_size, output_size):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>(Expert, <span style="color:#91d7e3">self</span>)<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>layer <span style="color:#91d7e3;font-weight:bold">=</span> nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(input_size, output_size)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>layer(x)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">GatingNetwork</span>(nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, input_size, num_experts):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>(GatingNetwork, <span style="color:#91d7e3">self</span>)<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>layer <span style="color:#91d7e3;font-weight:bold">=</span> nn<span style="color:#91d7e3;font-weight:bold">.</span>Linear(input_size, num_experts)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>softmax <span style="color:#91d7e3;font-weight:bold">=</span> nn<span style="color:#91d7e3;font-weight:bold">.</span>Softmax(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>softmax(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>layer(x))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">class</span> <span style="color:#eed49f">MixtureOfExperts</span>(nn<span style="color:#91d7e3;font-weight:bold">.</span>Module):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">__init__</span>(<span style="color:#91d7e3">self</span>, input_size, output_size, num_experts, top_k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">super</span>(MixtureOfExperts, <span style="color:#91d7e3">self</span>)<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#8aadf4">__init__</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>num_experts <span style="color:#91d7e3;font-weight:bold">=</span> num_experts
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k <span style="color:#91d7e3;font-weight:bold">=</span> top_k
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>experts <span style="color:#91d7e3;font-weight:bold">=</span> nn<span style="color:#91d7e3;font-weight:bold">.</span>ModuleList([Expert(input_size, output_size) <span style="color:#c6a0f6">for</span> _ <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(num_experts)])
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>gating <span style="color:#91d7e3;font-weight:bold">=</span> GatingNetwork(input_size, num_experts)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">forward</span>(<span style="color:#91d7e3">self</span>, x):
</span></span><span style="display:flex;"><span>        batch_size <span style="color:#91d7e3;font-weight:bold">=</span> x<span style="color:#91d7e3;font-weight:bold">.</span>size(<span style="color:#f5a97f">0</span>)
</span></span><span style="display:flex;"><span>        gating_probs <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>gating(x)
</span></span><span style="display:flex;"><span>        topk_vals, topk_inds <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>topk(gating_probs, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k, dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        expert_outputs <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>zeros(batch_size, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k, output_size, device<span style="color:#91d7e3;font-weight:bold">=</span>x<span style="color:#91d7e3;font-weight:bold">.</span>device)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k):
</span></span><span style="display:flex;"><span>            inds <span style="color:#91d7e3;font-weight:bold">=</span> topk_inds[:, i]
</span></span><span style="display:flex;"><span>            outputs_for_expert <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>stack([<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>experts[inds[b]](x[b]<span style="color:#91d7e3;font-weight:bold">.</span>unsqueeze(<span style="color:#f5a97f">0</span>)) <span style="color:#c6a0f6">for</span> b <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(batch_size)])
</span></span><span style="display:flex;"><span>            expert_outputs[:, i, :] <span style="color:#91d7e3;font-weight:bold">=</span> outputs_for_expert<span style="color:#91d7e3;font-weight:bold">.</span>squeeze(<span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        topk_vals_expanded <span style="color:#91d7e3;font-weight:bold">=</span> topk_vals<span style="color:#91d7e3;font-weight:bold">.</span>unsqueeze(<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        weighted_sum <span style="color:#91d7e3;font-weight:bold">=</span> expert_outputs <span style="color:#91d7e3;font-weight:bold">*</span> topk_vals_expanded
</span></span><span style="display:flex;"><span>        combined_output <span style="color:#91d7e3;font-weight:bold">=</span> weighted_sum<span style="color:#91d7e3;font-weight:bold">.</span>sum(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> combined_output
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Example Usage</span>
</span></span><span style="display:flex;"><span>input_size <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">10</span>
</span></span><span style="display:flex;"><span>output_size <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">5</span>
</span></span><span style="display:flex;"><span>num_experts <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">4</span>
</span></span><span style="display:flex;"><span>top_k <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> MixtureOfExperts(input_size, output_size, num_experts, top_k)
</span></span><span style="display:flex;"><span>sample_input <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>randn(<span style="color:#f5a97f">8</span>, input_size)
</span></span><span style="display:flex;"><span>output <span style="color:#91d7e3;font-weight:bold">=</span> model(sample_input)
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Output shape:&#34;</span>, output<span style="color:#91d7e3;font-weight:bold">.</span>shape)</span></span></code></pre></div>
<p>When you run this code, you should see the output shape printed as <code>torch.Size([8, 5])</code>. This confirms that the model routes each input to the most relevant experts, processes the input through those experts, and combines their contributions into a unified output.</p>
<h4 id="code-explanation">Code Explanation</h4>
<p>This code defines a simple MoE model with a single gating network and multiple experts. The <strong><code>Expert</code></strong> class represents a single sub-module, the expert, a linear layer that transforms the input and forms the backbone of the MoE architecture. The expert takes the input of size <code>input_size</code> and transforms it to <code>output_size</code> using a fully connected (<code>Linear</code>) layer. In the real world, each expert is designed to specialize in different transformations based on their training.</p>
<p>The <strong><code>GatingNetwork</code></strong> class is the router that computes gating probabilities for each expert and determines which experts to activate for a given input. It takes the input size and the number of experts as input and outputs a probability distribution over the experts (<code>num_experts</code>) using a linear layer followed by a <code>Softmax</code> function. Higher probability values indicate that the corresponding expert is more relevant to the input.</p>
<p>The <strong><code>MixtureOfExperts</code></strong> class combines the experts&rsquo; outputs based on the gating probabilities and returns the final output. It takes the input size, output size, number of experts, and the top-k value as input. The <code>forward</code> method computes the gating probabilities, selects the top-k experts based on these probabilities, and computes the weighted sum of the expert outputs to produce the final output. The <code>top_k</code> parameter controls how many experts are activated for each input.</p>
<ul>
<li>The input ( x ) is passed through the gating network to produce a probability distribution over the experts: <code>gating_probs = self.gating(x)</code></li>
<li>The gating network selects the indices of the <code>top_k</code> experts with the highest probabilities: <code>topk_vals, topk_inds = torch.topk(gating_probs, self.top_k, dim=1)</code></li>
<li>For each of the selected experts, process the input:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>expert_outputs <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>zeros(batch_size, <span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k, output_size, device<span style="color:#91d7e3;font-weight:bold">=</span>x<span style="color:#91d7e3;font-weight:bold">.</span>device)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>top_k):
</span></span><span style="display:flex;"><span>    inds <span style="color:#91d7e3;font-weight:bold">=</span> topk_inds[:, i]
</span></span><span style="display:flex;"><span>    outputs_for_expert <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>stack([<span style="color:#91d7e3">self</span><span style="color:#91d7e3;font-weight:bold">.</span>experts[inds[b]](x[b]<span style="color:#91d7e3;font-weight:bold">.</span>unsqueeze(<span style="color:#f5a97f">0</span>)) <span style="color:#c6a0f6">for</span> b <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(batch_size)])
</span></span><span style="display:flex;"><span>    expert_outputs[:, i, :] <span style="color:#91d7e3;font-weight:bold">=</span> outputs_for_expert<span style="color:#91d7e3;font-weight:bold">.</span>squeeze(<span style="color:#f5a97f">1</span>)</span></span></code></pre></div>
</li>
<li>Multiply each expert&rsquo;s output by its gating probability and sum them to form the final output:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>topk_vals_expanded <span style="color:#91d7e3;font-weight:bold">=</span> topk_vals<span style="color:#91d7e3;font-weight:bold">.</span>unsqueeze(<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>weighted_sum <span style="color:#91d7e3;font-weight:bold">=</span> expert_outputs <span style="color:#91d7e3;font-weight:bold">*</span> topk_vals_expanded
</span></span><span style="display:flex;"><span>combined_output <span style="color:#91d7e3;font-weight:bold">=</span> weighted_sum<span style="color:#91d7e3;font-weight:bold">.</span>sum(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)</span></span></code></pre></div>
<ul>
<li>The <code>topk_vals</code> (probabilities) are weights for the corresponding expert outputs.</li>
</ul>
</li>
</ul>
<p>The model processes a batch of inputs and returns the combined output with a shape matching <code>(batch_size, output_size)</code>. For the example above, <code>Output shape: torch.Size([8, 5])</code> confirms that the model routes each input to the most relevant experts, processes the input through those experts, and combines their contributions into a unified output.</p>
<h4 id="code-dependencies">Code Dependencies</h4>
<p>To run this code, you&rsquo;ll need the following dependencies:</p>
<ol>
<li>Python 3.6 or later (preferably 3.10 or higher)</li>
<li>PyTorch 1.0 or later</li>
</ol>
<p>Save the code to a file, e.g., <code>moe_example.py</code>, and run it:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>python moe_example.py</span></span></code></pre></div>
<p>I used Conda, which I prefer for managing Python environments. If you&rsquo;re using a virtual environment, you can adapt the installation commands accordingly. For Conda, you can create a new environment and install PyTorch using the following steps.</p>
<p>Start by creating a new conda environment with PyTorch dependencies. In  your terminal, execute the following commands:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Create a new conda environment named &#34;moe_env&#34;</span>
</span></span><span style="display:flex;"><span>conda create -n moe_example <span style="color:#f4dbd6">python</span><span style="color:#91d7e3;font-weight:bold">=</span>3.10 -y
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Activate the environment</span>
</span></span><span style="display:flex;"><span>conda activate moe_example</span></span></code></pre></div>
<p>Install PyTorch and necessary dependencies; adjust the CUDA version based on your system&rsquo;s GPU configuration. You can omit the <code>pytorch-cuda</code> package using a CPU-only setup.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># For CPU-only:</span>
</span></span><span style="display:flex;"><span>conda install pytorch torchvision torchaudio cpuonly -c pytorch -y
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># For GPU (use appropriate CUDA version, e.g., 11.7):</span>
</span></span><span style="display:flex;"><span>conda install pytorch torchvision torchaudio pytorch-cuda<span style="color:#91d7e3;font-weight:bold">=</span>11.7 -c pytorch -c nvidia -y
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Install additional dependencies if needed</span>
</span></span><span style="display:flex;"><span>pip install numpy</span></span></code></pre></div>
<p>This example demonstrates the basic structure of an MoE model, including the experts, gating network, and the MoE module that combines them. Of course, this is a toy version that helps understand the basic construct and does not include advanced features like load balancing or sparsity, etc.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Mixture of Experts offers a compelling framework for scaling neural networks and managing the trade-offs between model size, computational cost, and performance. By selectively activating only a subset of parameters for each input, MoE allows researchers and practitioners to build models with enormous capacity without incurring a proportionate computational penalty. Given the additional interest in inference optimization for LLMs and broadly with Transformer-based architecture, we expect to see further innovations and applications for MoE.</p>
<hr>
<h3 id="references">References</h3>
<ul>
<li>Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer - <a
	
		href = "https://arxiv.org/abs/1701.06538"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1701.06538
	</span>
</a></li>
<li>GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding - <a
	
		href = "https://arxiv.org/abs/2006.16668"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2006.16668
	</span>
</a></li>
<li>Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity - <a
	
		href = "https://arxiv.org/abs/2101.03961"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2101.03961
	</span>
</a></li>
<li>A Lightweight Mixture-of-Experts Neural Machine Translation Model with Stage-wise Training Strategy - <a
	
		href = "https://aclanthology.org/2024.findings-naacl.154/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/2024.findings-naacl.154/
	</span>
</a></li>
<li>Deep Mixture of Experts via Shallow Embedding - <a
	
		href = "https://arxiv.org/abs/1806.01531"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1806.01531
	</span>
</a></li>
<li>Mixture of Experts in Image Classification: What&rsquo;s the Sweet Spot? - <a
	
		href = "https://arxiv.org/abs/2411.18322v1"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2411.18322v1
	</span>
</a></li>
<li>Mesh-TensorFlow: Deep Learning for Supercomputers - <a
	
		href = "https://proceedings.neurips.cc/paper/2018/hash/3a37abdeefe1dab1b30f7c5c7e581b93-Abstract.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://proceedings.neurips.cc/paper/2018/hash/3a37abdeefe1dab1b30f7c5c7e581b93-Abstract.html
	</span>
</a></li>
<li>A Generalist Agent - <a
	
		href = "https://arxiv.org/abs/2205.06175"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2205.06175
	</span>
</a></li>
<li>Estimating or Propagating Gradients Through Stochastic Neurons for Conditional Computation - <a
	
		href = "https://arxiv.org/abs/1308.3432"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/1308.3432
	</span>
</a></li>
<li>From Sparse to Soft Mixtures of Experts - <a
	
		href = "https://arxiv.org/abs/2308.00951"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://arxiv.org/abs/2308.00951
	</span>
</a></li>
<li>Parameter-Efficient Sparsity Crafting for Large Language Models - <a
	
		href = "https://aclanthology.org/2024.emnlp-main.43/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://aclanthology.org/2024.emnlp-main.43/
	</span>
</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>Automating Hugo Deployments</title>
      <link>/post/2024/12/automating-hugo-deployments/</link>
      <pubDate>Mon, 23 Dec 2024 00:00:00 +0000</pubDate>
      <guid>/post/2024/12/automating-hugo-deployments/</guid>
      <description>Using GitHub Action, CloudPanel, Cloudflare Tunnel, and Tailscale</description>
      <content:encoded><![CDATA[<h3 id="1-a-little-background">1. A little background</h3>
<p>I have been meaning to automate the deployment of my blog post to a dev server (running locally) for a while, but I haven&rsquo;t had the time to get around to it until now. In addition to deploying this, the dev server also had several constraints. It is one of the machines at home and is not exposed directly to the internet. I also don&rsquo;t have any ports opened on the firewalls at home, which adds a few more interesting challenges. I have a server hosted online in one of the data centers, but I want to keep that for more &lsquo;production&rsquo; things.</p>
<p>Another thing I wanted to do was clean up the theme being used, as it wasn&rsquo;t compatible with the newer version of Hugo. The longer I was pinned to an older version, the more challenging it would be to update. Now, these are all done and working, and I wanted to share what I learned here—more so because I will likely need to find this at some point in the future, but it might also help someone else.</p>
<p>PS - If you don&rsquo;t notice much (any?) difference in the blog&rsquo;s look, that means it is a good thing; I worked through fixing the theme and upgrading to the latest version of Hugo (v0.140.0 at the time of writing this).</p>
<p>While cleaning up, I also got around to things I had wanted to do for a while. I wanted to see how to securely expose one of the machines I have at home online without opening up ports and things and serve several services—mostly for my own use.</p>
<h3 id="11-dev-server-setup-desktop_computer">1.1 Dev-Server Setup &#x1f5a5;&#xfe0f;</h3>
<p>I think outlining how this machine is set up would be helpful, as I am sure many others will be trying this. First, this machine runs locally on the home network, running a headless Ubuntu server. The environment that made this dev server unique was the combination of things - I use <a href="https://tailscale.com/" target="_blank">Tailscale</a>, <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/" target="_blank">Cloudflare Tunnels</a>, <a href="https://www.cloudpanel.io/" target="_blank">CloudPanel</a>, and as outlined earlier <a href="https://gohugo.io/" target="_blank">Hugo</a>. The code sits in a private Github repo, and the build-server is via Github Actions. I won&rsquo;t get into the details of each setup here as the intent of the blog post is not that, but I will share some context below.</p>
<h3 id="12-tailscale">1.2 Tailscale</h3>
<p>To make my life easier between the different machines and servers I have, running both locally at home and on the cloud, including bare-metal machines, I use <a href="https://tailscale.com/" target="_blank">Tailscale</a>.</p>
<p>Tailscale simplifies network management by creating a secure, zero-config mesh VPN based on the WireGuard protocol. Unlike traditional VPNs that route all traffic through a central server, Tailscale forms direct, encrypted connections between devices, regardless of their physical location or network configuration. This &ldquo;mesh&rdquo; approach offers significant performance benefits, reduced latency, and improved reliability. By assigning each device a stable IP address within a private network namespace, Tailscale eliminates the complexities of managing firewalls, port forwarding, and dynamic DNS, making it ideal for connecting personal devices, servers, and cloud instances into a cohesive, secure network.</p>
<p>What makes Tailscale work like magic is a feature they call <a href="https://tailscale.com/kb/1081/magicdns" target="_blank">MagicDNS</a>. This feature eliminates the need to remember IP addresses and SSH keys for different machines. Using Tailnet, I get a private VPN between different machines, and I can use that to connect to any of these without opening any ports, etc. This significantly simplifies the networking process and enhances security.</p>
<h3 id="13-cloudpanel">1.3 CloudPanel</h3>
<p>I use <a href="https://www.cloudpanel.io/" target="_blank">CloudPanel</a>, a free and open-source server control panel for managing web servers and applications. In the context of this blog post, CloudPanel provides a user-friendly web interface for creating and managing websites, databases, email accounts, and DNS records. It&rsquo;s a crucial part of the automation process, allowing for efficient server management and deployment. Its lightweight design and efficient resource utilization make it great for me.</p>
<h3 id="14-cloudflare-tunnels">1.4 Cloudflare Tunnels</h3>
<p>Cloudflare Tunnels play a crucial role in the deployment process. They offer a secure and efficient way to expose web services running on private networks without opening inbound ports on firewalls. By establishing an outbound-only connection between a lightweight daemon (<code>cloudflared</code>) running on your server and Cloudflare&rsquo;s global network, Tunnels create an encrypted tunnel that allows external users to access your services through Cloudflare&rsquo;s edge. This approach enhances security by eliminating the attack surface associated with open ports while leveraging Cloudflare&rsquo;s performance optimizations, such as caching, DDoS protection, and global CDN. It&rsquo;s a powerful tool for self-hosting applications, APIs, and websites from home networks or private infrastructure without compromising security.</p>
<h3 id="2-server-setup-keyboard">2. Server Setup &#x2328;&#xfe0f;</h3>
<p>To get this to work at a high level, we will do the following to get all of this working. This post won&rsquo;t get into all the details of setting up the prerequisites, and it assumes that you have the following:</p>
<ul>
<li>CloudPanel installed on your machine.</li>
<li>Cloudflare account with a Cloudflare Tunnel set up.</li>
<li>GitHub repo containing the Hugo project.</li>
<li>Tailscale for private networking - this is in addition to the HTTP traffic that Cloudflare handles.</li>
</ul>
<h3 id="21-create-a-static-site-in-cloudpanel">2.1. Create a Static Site in CloudPanel</h3>
<p>In CloudPanel, create a new “<strong>Site</strong>” (from the Dashboard). Select <code>Static</code>, and assign it a domain, e.g., <code>blog.desigeek.com</code>. CloudPanel will create a directory structure under something like:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>/home/[cloudpanel_username]/htdocs/blog.desigeek.com</span></span></code></pre></div>
<p><em>(Exact path can vary based on CloudPanel’s naming conventions.)</em></p>
<p>The figure below shows an example of how I set this up.
<p>

    <img src="images/cloudpanel-1.png" alt="Static site in Cloudpanel"/>

</p></p>
<h3 id="211-create-an-ssh-user">2.1.1 Create an SSH User</h3>
<p>If you haven’t already, go to CloudPanel’s “<strong>Users</strong>” section. You can create an <strong>SSH user</strong> (for example, <code>blog-ssh</code>). This user will be mapped to the same group or path used by CloudPanel so that it can write your site files.</p>
<p><strong>Important</strong>:</p>
<ul>
<li>Make sure the SSH user’s <strong>home directory</strong> either <em>is</em> or <em>can write to</em> the directory created earlier <code>/home/[cloudpanel_username]/htdocs/blog.desigeek.com</code>.</li>
<li>For our example, I created an SSH user called <code>blog-ssh</code>, and the user&rsquo;s group on Ubuntu was called <code>bahree-blog</code>.</li>
<li>Note: on some CloudPanel installations, the user’s primary group might be something else, not necessarily <code>bahree-blog</code>.</li>
</ul>
<p>The image below shows an example of how to set this up.
<p>

    <img src="images/cloudpanel-2.png" alt="Cloudpanel SSH User setting"/>

</p></p>
<h3 id="212-directory-paths-and-permissions">2.1.2. Directory Paths and Permissions</h3>
<p>Confirm that your SSH user (<code>blog-ssh</code>) can write into the <code>blog.desigeek.com</code> directory:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Example:</span>
</span></span><span style="display:flex;"><span>ls -ld /home/<span style="color:#91d7e3;font-weight:bold">[</span>cloudpanel_username<span style="color:#91d7e3;font-weight:bold">]</span>/htdocs/blog.desigeek.com
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Make sure the owner or group matches your user or a group the user is in.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># If needed:</span>
</span></span><span style="display:flex;"><span>sudo chown -R blog-ssh:bahree-blog /home/<span style="color:#91d7e3;font-weight:bold">[</span>cloudpanel_username<span style="color:#91d7e3;font-weight:bold">]</span>/htdocs/blog.desigeek.com
</span></span><span style="display:flex;"><span>sudo chmod -R <span style="color:#f5a97f">755</span> /home/<span style="color:#91d7e3;font-weight:bold">[</span>cloudpanel_username<span style="color:#91d7e3;font-weight:bold">]</span>/htdocs/blog.desigeek.com</span></span></code></pre></td></tr></table>
</div>
</div>
<p>(Adjust <code>bahree-blog</code> to the actual group that CloudPanel assigned.)</p>
<hr>
<h3 id="22-cloudflare-tunnel-configuration">2.2 Cloudflare Tunnel Configuration</h3>
<p>Cloudflare Tunnel is typically set up via Cloudflare’s admin dashboard or command line (see <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/" target="_blank">the docs</a> for more details).</p>
<p>We will map the domain <code>blog.desigeek.com</code> to the local server’s port 80 (or 443). Since CloudPanel listens on port 80/443, your tunnel might forward something like:</p>
<ul>
<li>Cloudflare subdomain → <code>localhost:80</code> on your server.</li>
</ul>
<p>Because Cloudflare is reverse proxying your site, you won’t need to open inbound ports on your server. Ensure your DNS is configured so that <code>blog.desigeek.com</code> points to the Cloudflare Tunnel.</p>
<p>The image below shows an example of what this would look like.
<p>

    <img src="images/cloudfare-1.png" alt="Cloudfare Tunnel example"/>

</p></p>
<hr>
<h3 id="23-setup-tailscale-secure-private-access">2.3. Setup Tailscale Secure Private Access</h3>
<p>To deploy the Hugo site on my home dev server, I need private remote access (SSH, SCP, rsync, etc.) between the GitHub build servers and the home server. I achieve this using Tailscale.</p>
<ol>
<li><strong>Install Tailscale</strong> on your server:
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -fsSL https://tailscale.com/install.sh | sh
</span></span><span style="display:flex;"><span>sudo tailscale up</span></span></code></pre></div>
</li>
</ol>
<p>The figure below shows an example of an ephemeral node, which created and deleted in approx. a minute.
<p>

    <img src="images/tailscale-1.png" alt="Tailscale ephemeral note"/>

</p></p>
<ol start="2">
<li>Our Build server is essentially spun up when something is committed in a specific branch, and as a result, we need to get that added to the Tailscale network - but it should not be permanent. Over time, that will pollute the tailnet and make things more difficult to manage. Tailscale has a perfect solution for this - <a href="https://tailscale.com/kb/1111/ephemeral-nodeshttps://tailscale.com/kb/1111/ephemeral-nodes" target="_blank">Ephemeral nodes</a>. These nodes, as the name suggests, make it easier to connect and then clean up short-lived devices such as containers, cloud functions, or CI/CD systems that spin up and spin down on a regular basis.</li>
</ol>
<p>To use this, we generate an <strong>Ephemeral Auth Key</strong> from the auth keys page of the Tailscale admin console. Given that the build server would need this, we store this key in GitHub as <code>TS_AUTH_KEY</code>.</p>
<p>This allows the ephemeral GitHub Actions runner to connect to my dev server privately, even when I don&rsquo;t have any inbound ports open.</p>
<hr>
<h3 id="24-nginx-configuration-via-cloudpanel">2.4. Nginx Configuration via CloudPanel</h3>
<p>CloudPanel typically manages the Nginx configuration for each “Site” automatically. It also handles SSL certificates, logs, etc. However, in this case, we are relying on Cloudflare Tunnel, and as a result, we do not need SSL termination on the server; Cloudflare can handle it and forward HTTP internally.
We therefore need to ensure that the config file (vhost) in CloudPanel is minimal. Below is an example of what I use:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#c6a0f6">server</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">listen</span> <span style="color:#f5a97f">80</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">listen</span> <span style="color:#a6da95">[::]:80</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">server_name</span> <span style="color:#a6da95">blog.desigeek.com</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">{{root}}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  {<span style="color:#8bd5ca">{nginx_access_log}}</span>
</span></span><span style="display:flex;"><span>  {<span style="color:#8bd5ca">{nginx_error_log}}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">location</span> ~ <span style="color:#8bd5ca">/.well-known</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">auth_basic</span> <span style="color:#eed49f">off</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">allow</span> <span style="color:#a6da95">all</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">{{settings}}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6da95">include</span> <span style="color:#a6da95">/etc/nginx/global_settings</span>;
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">index</span> <span style="color:#a6da95">index.html</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">location</span> ~<span style="color:#8bd5ca">*</span> <span style="color:#a6da95">^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf)</span>$ {
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">add_header</span> <span style="color:#a6da95">Access-Control-Allow-Origin</span> <span style="color:#a6da95">&#34;*&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">expires</span> <span style="color:#a6da95">max</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">access_log</span> <span style="color:#eed49f">off</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#8bd5ca">if</span> <span style="color:#a6da95">(-f</span> <span style="color:#f4dbd6">$request_filename</span><span style="color:#a6da95">)</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">break</span>;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p><strong>Note:</strong> Please note that <code>{{root}}</code> points to <code>/home/[cloudpanel_username]/htdocs/blog.desigeek.com</code>, which is set up in the CloudPanel site configuration.</p>
<p>The figure below shows what this would look like in CloudPanel.</p>
<h2 id="cloudpanel---vhost-editor"><p>

    <img src="images/cloudpanel-3.png" alt="CloudPanel - Vhost editor"/>

</p></h2>
<h3 id="3-github-actions-configuration">3. GitHub Actions Configuration</h3>
<p>We finally get to the main thing of building out GitHub actions that will trigger whenever something is checked in on a certain branch. All the other things until now were to essentially expose this dev machine externally (specifically the blog) and allow the build server to deploy to this dev server running at home. If you have a publicly accessible dev machine, much of this might not be required.</p>
<h3 id="31-storing-variables--secrets-in-github">3.1. Storing Variables &amp; Secrets in GitHub</h3>
<p>We start by storing the variables and secrets we need for this to work. The details are outlined below. To set these up, go to <strong>Settings → Secrets and variables → Actions</strong> in your GitHub repo.</p>
<p><strong>Variables (non-sensitive)</strong>:</p>
<ul>
<li><code>SERVER_HOSTNAME</code> = <code>my-dev-server</code> (or your Cloudflare Tunnel/hostname). If Tailscale is used, it might be the Tailscale IP/hostname.
<ul>
<li>Note: If you are using Tailscale, the notation of machine-name.local will not resolve and fail, even with MagicDNS enabled. It is best to use the machine name (e.g., <code>my-dev-server</code> and not <code>my-dev-server.local</code> or the IP address from the Tailnet)</li>
</ul>
</li>
<li><code>SERVER_USERNAME</code> = <code>blog-ssh</code> (the SSH user you created in CloudPanel).</li>
<li><code>HUGO_VERSION</code> = e.g., <code>0.140.0</code></li>
<li><code>HUGO_EXTENDED</code> = <code>true</code> (if you want the extended version, otherwise <code>false</code>).</li>
</ul>
<p><strong>Secrets (sensitive)</strong>:</p>
<ul>
<li><code>SSH_PASSWORD</code> = <em>the password for <code>blog-ssh</code></em>; this was either set or auto-generated when you created the account in CloudPanel.</li>
<li><code>TS_AUTH_KEY</code> = <em>Your Tailscale ephemeral auth key</em> (only if using Tailscale).</li>
</ul>
<p>The screenshot below shows what this would look like:
<p>

    <img src="images/gh-1.png" alt="GitHub Actions secrets and variables"/>

</p></p>
<h3 id="32-github-actions-workflow">3.2 GitHub Actions Workflow</h3>
<p>The code below outlines a sample workflow for this. It checks out the code, builds it with Hugo, and, once complete, copies it over to the dev server, where I can expose it securely to test it. To allow us to do this, we take a dependency on <code>sshpass</code> and <code>rsync</code>.</p>
<p>I do have a few things to check as this happens to ensure there aren&rsquo;t any failures. When Hugo is finished, we show some basic stats, such as the number of files and folders created. This is called <code>Show public directory stats</code>.</p>
<p>Another is to test the SSH connection, as without that, the build machine cannot talk to the dev server where the final site is getting deployed. This step is called <code>Test SSH Connection</code>. At a high level, here is the flow that this goes through:</p>
<ul>
<li>Check out the code</li>
<li>Installs Tailscale and connects as an ephemeral node</li>
<li>Builds with Hugo</li>
<li>Installs <code>sshpass</code> &amp; <code>rsync</code></li>
<li>Uses <code>rsync</code> to deploy to the server</li>
<li>Rolls back if the build fails</li>
<li>Logs out of Tailscale at the end</li>
</ul>
<p>Below is a sample <code>.github/workflows/deploy-hugo.yml</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#c6a0f6">name</span>: Deploy Hugo Site
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">on</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">push</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">branches</span>:
</span></span><span style="display:flex;"><span>      - main  <span style="color:#6e738d;font-style:italic"># or your chosen branch</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">jobs</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">deploy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">runs-on</span>: ubuntu-latest
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Gets non-sensitive values from &#34;Variables&#34; (not Secrets)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">env</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">SERVER_HOSTNAME</span>: ${{ vars.SERVER_HOSTNAME }}
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">SERVER_USERNAME</span>: ${{ vars.SERVER_USERNAME }}
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">HUGO_VERSION</span>: ${{ vars.HUGO_VERSION }}
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">HUGO_EXTENDED</span>: ${{ vars.HUGO_EXTENDED }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Checkout code
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">uses</span>: actions/checkout@v3
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic"># Optional Tailscale login if you&#39;re using Tailscale for SSH</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Setup Tailscale
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">uses</span>: tailscale/github-action@v3
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">authkey</span>: ${{ secrets.TS_AUTH_KEY }}
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">statedir</span>: /tmp/tailscale-state/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Setup Hugo
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">uses</span>: peaceiris/actions-hugo@v2
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">hugo-version</span>: ${{ env.HUGO_VERSION }}
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">extended</span>: ${{ env.HUGO_EXTENDED }}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Build Hugo site
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">id</span>: build-site
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          hugo --minify -d public</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Verify Hugo Output
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span>: steps.build-site.outcome == &#39;success&#39;
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          if [ ! -d &#34;public&#34; ] || [ -z &#34;$(ls -A public)&#34; ]; then
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            echo &#34;Error: &#39;public&#39; directory is empty or does not exist.&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            exit 1
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          fi
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          echo &#34;Public directory exists and is not empty.&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Show public directory stats
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span>: steps.build-site.outcome == &#39;success&#39;
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          cd public
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          echo &#34;Files: $(find . -type f | wc -l)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          echo &#34;Directories: $(find . -type d | wc -l)&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          echo &#34;Size: $(du -sm . | cut -f1) MB&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Install sshpass and rsync
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          sudo apt-get update
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          sudo apt-get install -y sshpass rsync</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Test SSH Connection
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          sshpass -p &#34;${{ secrets.SSH_PASSWORD }}&#34; \
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            ssh -o StrictHostKeyChecking=no ${{ env.SERVER_USERNAME }}@${{ env.SERVER_HOSTNAME }} \
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            &#39;echo &#34;SSH Connection Successful&#34;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Deploy via rsync
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span>: steps.build-site.outcome == &#39;success&#39;
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">env</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">SSH_PASSWORD</span>: ${{ secrets.SSH_PASSWORD }}
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          sshpass -p &#34;$SSH_PASSWORD&#34; rsync -az --delete \
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            -e &#34;ssh -o StrictHostKeyChecking=no&#34; \
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            public/ ${{ env.SERVER_USERNAME }}@${{ env.SERVER_HOSTNAME }}:/home/[cloudpanel_username]/htdocs/blog.desigeek.com</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Rollback on Failure
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span>: steps.build-site.outcome != &#39;success&#39;
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">uses</span>: appleboy/ssh-action@master
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">host</span>: ${{ env.SERVER_HOSTNAME }}
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">username</span>: ${{ env.SERVER_USERNAME }}
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">password</span>: ${{ secrets.SSH_PASSWORD }}
</span></span><span style="display:flex;"><span>          <span style="color:#c6a0f6">script</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            BACKUP_DIR=&#34;/home/[cloudpanel_username]/backups&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            SITE_DIR=&#34;/home/[cloudpanel_username]/htdocs/blog.desigeek.com&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            LATEST_BACKUP=$(ls -t &#34;$BACKUP_DIR&#34;/*.tar.gz | head -n 1)
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            if [[ -n &#34;$LATEST_BACKUP&#34; ]]; then
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">              echo &#34;Rolling back to: $LATEST_BACKUP&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">              rm -rf &#34;$SITE_DIR&#34;/*
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">              tar -xzvf &#34;$LATEST_BACKUP&#34; -C &#34;$SITE_DIR&#34; --strip-components=1
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">              echo &#34;Rollback successful!&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            else
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">              echo &#34;No backups found to rollback to!&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">            fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      - <span style="color:#c6a0f6">name</span>: Tailscale Logout
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span>: always()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">run</span>: |<span style="color:#6e738d">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          # If Tailscale requires sudo or operator privileges to log out,
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          # either configure passwordless sudo or skip this if ephemeral.
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d">          sudo tailscale logout</span></span></span></code></pre></div>
<blockquote>
<p><strong>Note</strong>: Update <code>/home/[cloudpanel_username]/htdocs/blog.desigeek.com</code> to match the actual path CloudPanel created. The same applies to the backup directory if you use that mechanism.</p></blockquote>
<p>The figures below show an example of when this was building, and one of the validations.
<p>

    <img src="images/gh-2.png" alt="Action running"/>

</p></p>
<p><p>

    <img src="images/gh-3.png" alt="Validation - Build was successful"/>

</p></p>
<hr>
<h3 id="4-tips--troubleshooting-rotating_light">4. Tips &amp; Troubleshooting &#x1f6a8;</h3>
<p>I wanted to share a few things I have learned as I went through and debugged this. Hopefully this will help someone else too.</p>
<ul>
<li>Permission Denied: Make sure your CloudPanel SSH user has <em>ownership</em> or <em>group-writable</em> permission on <code>/home/[cloudpanel_username]/htdocs/blog.desigeek.com</code>.</li>
<li>Rsync Overwrites: The <code>--delete</code> flag in rsync removes files on the server that no longer exist locally. Remove &lsquo;- delete &rsquo; if you don’t want that behavior.</li>
<li>Cloudflare Tunnel: If the site is not reachable at <code>blog.desigeek.com</code>, check your Cloudflare DNS config or the tunnel routing.</li>
<li>SSH: You might still rely on Tailscale or an open SSH port for SSH since Cloudflare typically only proxies HTTP/HTTPS.</li>
<li>Tailscale Logout: If you see “Access denied: logout access denied,” you can skip <code>tailscale logout</code> (ephemeral nodes eventually vanish) or configure passwordless sudo for Tailscale.</li>
</ul>
<hr>
<h3 id="5-conclusion">5. Conclusion</h3>
<p>By combining CloudPanel for site management, Cloudflare Tunnel for secure external traffic, Tailscale for private networking, and GitHub Actions for automated builds, you get a robust, convenient pipeline to deploy your Hugo blog. &#x1f60d;</p>
<ul>
<li>CloudPanel simplifies web server config and user management.</li>
<li>Cloudflare Tunnel ensures secure traffic without exposing direct ports.</li>
<li>Tailscale provides easy, private SSH if desired.</li>
<li>GitHub Actions automates the entire Hugo build and deployment process.</li>
</ul>
<p>Now, when you push changes to your repository, your CloudPanel-managed site updates automatically, so no manual FTP or SSH is required!</p>
<p>Happy Deploying! &#x263a;&#xfe0f;</p>
<hr>
<h3 id="further-reading">Further Reading</h3>
<ul>
<li><a
	
		href = "https://www.cloudpanel.io/documentation/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		CloudPanel Docs
	</span>
</a></li>
<li><a
	
		href = "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Cloudflare Tunnel Docs
	</span>
</a></li>
<li><a
	
		href = "https://gohugo.io/documentation/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Hugo Documentation
	</span>
</a></li>
<li><a
	
		href = "https://tailscale.com/kb/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Tailscale Documentation
	</span>
</a></li>
<li><a
	
		href = "https://docs.github.com/en/actions"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		GitHub Actions Docs
	</span>
</a></li>
</ul>
<hr>
]]></content:encoded>
    </item>
    <item>
      <title>Backing up TeslaMate data to OneDrive</title>
      <link>/post/2024/07/backing-teslamate-to-onedrive/</link>
      <pubDate>Fri, 05 Jul 2024 00:00:00 +0000</pubDate>
      <guid>/post/2024/07/backing-teslamate-to-onedrive/</guid>
      <description>Script showing to backup Teslamate docker data to onedrive and emailing the results</description>
      <content:encoded><![CDATA[<p>I have been running a couple of instances of Teslamate - one locally on a server at home and another in Azure in a Ubuntu VM (see 👉 <a
	
		href = "https://blog.desigeek.com/post/2021/12/how-to-run-teslamate-on-azure/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		this blog post
	</span>
</a> for details). I have been backing up the data to a NAS and then an offsite backup for the local instance. For the Azure instance, I have been running various backups during the day and backing up the data to OneDrive. This allows me to have a data backup in case the VM crashes or the data gets corrupted.</p>
<p>Work and AI have kept me busy, and I have not had a chance to write about it until now. I have a ton of storage on OneDrive, which seemed the most logical place to store it. Multiple options are available to back up data to OneDrive. The one that has been working consistently and well for me is <code>rclone</code>.</p>
<h2 id="what-is-rclone">What is Rclone?</h2>
<p><a
	
		href = "https://rclone.org"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Rclone
	</span>
</a> Rclone is an open-source command-line tool for managing files across various cloud storage services. It supports over 70 providers, including Google Drive, Amazon S3, Dropbox, and Microsoft OneDrive. Rclone allows users to perform operations like copying, moving, syncing, and deleting files and more advanced tasks such as encryption, compression, and chunking. Additionally, it can mount cloud storage as a local drive, facilitating seamless file access and management.</p>
<p>Known as the &ldquo;Swiss army knife of cloud storage,&rdquo; Rclone is highly versatile and can be integrated into scripts for automating complex file operations. Its ability to act as a bridge between different cloud providers makes it an invaluable tool for efficiently transferring files and managing cloud storage environments.</p>
<h3 id="step-1-setting-up-rclone">Step 1: Setting up Rclone</h3>
<p>The base assumption is that you have a working instance of Teslamate running in a docker container on a Linux machine. Rclone is also available for Windows and Mac, and you can follow the [instructions] (<a
	
		href = "https://rclone.org/install/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://rclone.org/install/
	</span>
</a>) to install it on those platforms. Let us start by outlining how to install <code>rclone</code>.</p>
<p>First, let&rsquo;s install <code>rclone</code> on your system. You can download and install it using the following commands:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>curl https://rclone.org/install.sh | sudo bash</span></span></code></pre></div>
<h3 id="step-11---configuring-rclone-with-onedrive-">Step 1.1 - Configuring Rclone with OneDrive ☁️</h3>
<p>After installing <code>rclone</code>, we need to configure it to use OneDrive. Rclone has <a
	
		href = "https://rclone.org/onedrive/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		detailed steps
	</span>
</a> on how to configure OneDrive. I show some of them here on how to run the following command to start the configuration process:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>rclone config</span></span></code></pre></div>
<p>You&rsquo;ll be prompted with several questions about setting up a new remote. Here is an example configuration for OneDrive:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>No remotes found - make a new one
</span></span><span style="display:flex;"><span>n) New remote
</span></span><span style="display:flex;"><span>r) Rename remote
</span></span><span style="display:flex;"><span>c) Copy remote
</span></span><span style="display:flex;"><span>s) Set configuration password
</span></span><span style="display:flex;"><span>q) Quit config
</span></span><span style="display:flex;"><span>n/r/c/s/q&gt; n
</span></span><span style="display:flex;"><span>name&gt; onedrive
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Type of storage to configure.
</span></span><span style="display:flex;"><span>Choose a number from below, or type in your own value.
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>24 / Microsoft OneDrive
</span></span><span style="display:flex;"><span>   \ &#34;onedrive&#34;
</span></span><span style="display:flex;"><span>...
</span></span><span style="display:flex;"><span>Storage&gt; 24
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Microsoft App Client Id
</span></span><span style="display:flex;"><span>Leave blank normally.
</span></span><span style="display:flex;"><span>client_id&gt; 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Microsoft App Client Secret
</span></span><span style="display:flex;"><span>Leave blank normally.
</span></span><span style="display:flex;"><span>client_secret&gt; 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Edit advanced config? (y/n)
</span></span><span style="display:flex;"><span>y) Yes
</span></span><span style="display:flex;"><span>n) No
</span></span><span style="display:flex;"><span>y/n&gt; n
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Use auto config?
</span></span><span style="display:flex;"><span> * Say Y if not sure
</span></span><span style="display:flex;"><span> * Say N if you are working on a remote or headless machine
</span></span><span style="display:flex;"><span>y) Yes
</span></span><span style="display:flex;"><span>n) No
</span></span><span style="display:flex;"><span>y/n&gt; y</span></span></code></pre></div>
<p>For the last prompt (auto config), if you are on a headless machine (e.g., you had SSH&rsquo;d into a server to set this up), you can say n, and it will give you a URL to open in a browser to authenticate - as these require OAuth2. On the host (from which you are connected), you can authenticate and get the token, which you can paste into the terminal window. If the browser does not open, you can open your local browser and navigate to <code>http://127.0.0.1:53682/auth</code>.</p>
<p>If you are on SSH, you might need to create an SSH tunnel over port 53682 to your local machine using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>ssh -L localhost:53682:localhost:53682 username@remote_server</span></span></code></pre></div>
<p>For more details on how to do this, refer to the <a
	
		href = "https://rclone.org/remote_setup/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		rclone remote setup
	</span>
</a> documentation.</p>
<p>It is important to remember the <strong>name</strong> you give to the remote, as you will need it in the backup script later. In our example, we called it <strong><code>onedrive</code></strong>.</p>
<p>Also, note that the <code>rclone</code> configuration is stored in the <code>.config</code> directory in the user&rsquo;s home directory. The configuration file is named <code>rclone.conf</code> and contains the details of the remotes you have configured. You can view the configuration file by running:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cat ~/.config/rclone/rclone.conf</span></span></code></pre></div>
<p>To test the configuration, you can run the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>rclone ls onedrive:</span></span></code></pre></div>
<p>This should list the files and directories in your OneDrive account. If you see the list of files, the configuration is successful. If you encounter any errors, review the configuration and ensure the authentication details are correct. For example, the image below shows the successful configuration of the OneDrive remote:
<p>

    <figure>
        <img src="images/cheking-rclone.jpg" alt="Remote OneDrive folder listing"/>
        <figcaption>Remote OneDrive folder listing</figcaption>
    </figure>

</p></p>
<h3 id="step-12---mounting-remote-directory-with-rclone-file_folder">Step 1.2 - Mounting Remote Directory with Rclone &#x1f4c1;</h3>
<p>Now that we have configured <code>rclone</code> with OneDrive, we can mount the remote directory to a local directory in the system. This allows us to access the remote files as if they were local files. We cannot use a regular mount command, as the rclone mount command is not a real drive but is a FUSE-based mount. To mount the remote directory, use the following command.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>rclone mount --config<span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/.config/rclone/rclone.conf --vfs-cache-mode full --allow-non-empty --allow-other --daemon --dir-perms <span style="color:#f5a97f">0777</span> car_backup:car_data2 /home/amit/onedrive</span></span></code></pre></div>
<p>Let&rsquo;s break down the command:</p>
<ul>
<li>In the above command, replace <code>/home/amit/.config/rclone/rclone.conf</code> with the path to your rclone configuration file.</li>
<li>Replace <code>car_backup:car_data2</code> with the remote name (configured in Sep 3) and the directory you want to mount.</li>
<li>The local directory <code>/home/amit/onedrive</code> is where the remote directory will be mounted (you might need to create a new folder for this). You can choose any local directory for this purpose.</li>
<li>In the above command, the <code>--daemon</code> flag runs the mount in the background, and the <code>--dir-perms 0777</code> flag sets the directory permissions.</li>
<li>The <code>--allow-non-empty</code> flag allows mounting over a non-empty directory.</li>
<li>And finally, the <code>--allow-other</code> flag allows other users to access the mounted directory.</li>
</ul>
<p>Once the command is executed, you should see the remote directory mounted to the local directory. You can access the files in the remote directory as local files. For example, you can list the files in the mounted directory using the <code>ls</code> command, as shown in the image below:</p>
<p><p>

    <figure>
        <img src="images/listing-mounted-drive.jpg" alt="Listing mounted OneDrive directory"/>
        <figcaption>Listing mounted OneDrive directory</figcaption>
    </figure>

</p></p>
<h3 id="step-13---persistent-mounting-with-rclone-">Step 1.3 - Persistent Mounting with Rclone 💾</h3>
<p>It is important to note that the <code>rclone mount</code> command is not persistent across reboots. So, if your VM reboots for some reason, you will lose this. To make the mount persistent, you can add the command to the system&rsquo;s startup scripts or use a tool like <code>systemd</code> to manage the mount as a service. We will create a <code>systemd</code> service file to manage the mount for this.</p>
<p>To do this, create a new service file using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo nano /etc/systemd/system/rclonemount.service</span></span></code></pre></div>
<p>Add the following content to the file:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">[</span>Unit<span style="color:#91d7e3;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Description</span><span style="color:#91d7e3;font-weight:bold">=</span>rclonemount
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">AssertPathIsDirectory</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/onedrive
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">After</span><span style="color:#91d7e3;font-weight:bold">=</span>network-online.target
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Wants</span><span style="color:#91d7e3;font-weight:bold">=</span>network-online.target
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">[</span>Service<span style="color:#91d7e3;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Type</span><span style="color:#91d7e3;font-weight:bold">=</span>simple
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">User</span><span style="color:#91d7e3;font-weight:bold">=</span>amit
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Group</span><span style="color:#91d7e3;font-weight:bold">=</span>amit
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">ExecStartPre</span><span style="color:#91d7e3;font-weight:bold">=</span>/bin/sleep <span style="color:#f5a97f">10</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">ExecStartPre</span><span style="color:#91d7e3;font-weight:bold">=</span>/bin/mkdir -p /home/amit/onedrive
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">ExecStartPre</span><span style="color:#91d7e3;font-weight:bold">=</span>/bin/chmod <span style="color:#f5a97f">777</span> /home/amit/onedrive
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">ExecStart</span><span style="color:#91d7e3;font-weight:bold">=</span>/usr/bin/rclone mount <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>        --config<span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/.config/rclone/rclone.conf <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>        --vfs-cache-mode full <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>        --allow-other <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>        --dir-perms <span style="color:#f5a97f">0777</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>        car_backup:/car_data2 /home/amit/onedrive
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">ExecStop</span><span style="color:#91d7e3;font-weight:bold">=</span>/bin/fusermount -uz /home/amit/onedrive
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Restart</span><span style="color:#91d7e3;font-weight:bold">=</span>always
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">RestartSec</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">10</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Environment</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f4dbd6">HOME</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">Environment</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f4dbd6">USER</span><span style="color:#91d7e3;font-weight:bold">=</span>amit
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">[</span>Install<span style="color:#91d7e3;font-weight:bold">]</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">WantedBy</span><span style="color:#91d7e3;font-weight:bold">=</span>default.target</span></span></code></pre></div>
<p>In the above service file, the <code>ExecStartPre`` commands create the directory and set the permissions before mounting the remote directory. The </code>ExecStart<code>command mounts the remote directory to the local directory. The</code>ExecStop<code>command unmounts the directory when the service is stopped. The</code>Restart<code>and</code>RestartSec` settings ensure the service is restarted in case of failure.</p>
<p>The image below shows the contents of the <code>rclonemount.service</code> file in my system:
<p>

    <figure>
        <img src="images/rclonemoun.service.jpg" alt="rclonemount.service contents"/>
        <figcaption>rclonemount.service contents</figcaption>
    </figure>

</p></p>
<p>After creating the service file, reload the systemd daemon and start the service using the following commands:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo systemctl daemon-reload</span></span></code></pre></div>
<p>To test the service, start it using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo systemctl start rclonemount</span></span></code></pre></div>
<p>And check the status of the service to ensure it is running:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo systemctl status rclonemount</span></span></code></pre></div>
<p>If everything is working correctly, you should see the status of the service as <code>active (running)</code>, as shown in the image below:
<p>

    <figure>
        <img src="images/rclonemoun.service-status.jpg" alt="rclonemount service status"/>
        <figcaption>rclonemount service status</figcaption>
    </figure>

</p></p>
<p>Note: Press Q to quit.</p>
<p>And finally, if the service is running without any errors, enable it to start at boot:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo systemctl <span style="color:#91d7e3">enable</span> rclonemount</span></span></code></pre></div>
<p>Of course, the service can be tested by rebooting the system and checking if the remote directory is mounted automatically.</p>
<p>Yay! Congratulations! You have successfully configured <code>rclone</code> to mount the remote directory as a service. The remote directory will now be mounted automatically on system startup. 👍</p>
<h3 id="step-2---basic-backup-script-hammer_and_wrench">Step 2 - Basic Backup Script &#x1f6e0;&#xfe0f;</h3>
<p>Now that we have <code>rclone</code> set up and mounted, we can create a basic backup script to back up the Teslamate data to OneDrive. The script will perform the following steps:</p>
<ol>
<li>Back up the Teslamate database.</li>
<li>Compress the backed-up file.</li>
<li>Move the compressed file to the remote backup directory using <code>rclone</code>.</li>
</ol>
<p>The will be a simple bash script that can be run manually or scheduled using a cron job. Here&rsquo;s an example of the basic backup script. Create a new file named <code>backup.sh</code> and add the following content to it:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#6e738d;font-style:italic"># Basic backup script</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">SOURCEDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/teslamate <span style="color:#6e738d;font-style:italic"># Source directory where the data is located</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">BACKUPDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/onedrive <span style="color:#6e738d;font-style:italic"># Remote directory mounted via rclone</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FILENAME</span><span style="color:#91d7e3;font-weight:bold">=</span>teslamate-<span style="color:#c6a0f6">$(</span>date +%Y-%m-%d-%H%M%S<span style="color:#c6a0f6">)</span>.bak.gz <span style="color:#6e738d;font-style:italic"># Date-time stamp filename</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Perform the database dump and compress it</span>
</span></span><span style="display:flex;"><span>docker compose <span style="color:#91d7e3">exec</span> -T database pg_dump -U teslamate teslamate | gzip &gt; <span style="color:#f4dbd6">$FILENAME</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Move the compressed file to the remote backup directory</span>
</span></span><span style="display:flex;"><span>rclone move <span style="color:#f4dbd6">$FILENAME</span> <span style="color:#f4dbd6">$BACKUPDIR</span></span></span></code></pre></div>
<p>You will need to replace the <code>SOURCEDIR</code> and <code>BACKUPDIR</code> with the actual directories on your system. The <code>FILENAME</code> variable creates a unique filename for the backup file using the current date and time. The script uses <code>pg_dump</code> to back up the Teslamate database and <code>rclone move</code> to move the compressed file to the remote directory.</p>
<p>This script assumes the following:</p>
<ul>
<li>You have Teslamate running in a docker container, and the database is named <code>teslamate</code>.</li>
<li>You have <code>rclone</code> configured with OneDrive and mounted the remote directory to a local directory.</li>
<li>You have the necessary permissions to access the source and destination directories.</li>
</ul>
<p>Make the script executable:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>chmod +x /home/amit/backup.sh</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Run the script manually to test it:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>/home/amit/backup.sh</span></span></code></pre></td></tr></table>
</div>
</div>
<p>You should see the backup file in the remote directory if the script runs successfully. You can check the remote directory using the ls command or by navigating to it.</p>
<h2 id="step-3---adding-logging-">Step 3 - Adding Logging 🗒️</h2>
<p>To make the script more informative, let&rsquo;s add logging. This will help us keep track of the backup process and identify any issues. We will use the same script as above as our starting point and add logging to it. We will log messages to a log file and display them on the console. Here&rsquo;s the updated script with logging:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#6e738d;font-style:italic"># Backup script with logging</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">SOURCEDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/teslamate <span style="color:#6e738d;font-style:italic"># Source directory where the data is located</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">BACKUPDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/onedrive <span style="color:#6e738d;font-style:italic"># Remote directory mounted via rclone</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FILENAME</span><span style="color:#91d7e3;font-weight:bold">=</span>teslamate-<span style="color:#c6a0f6">$(</span>date +%Y-%m-%d-%H%M%S<span style="color:#c6a0f6">)</span>.bak.gz <span style="color:#6e738d;font-style:italic"># Date-time stamp filename</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">LOGFILE</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/backup.log
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Function to log messages</span>
</span></span><span style="display:flex;"><span>log<span style="color:#91d7e3;font-weight:bold">()</span> <span style="color:#91d7e3;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">echo</span> <span style="color:#a6da95">&#34;</span><span style="color:#c6a0f6">$(</span>date <span style="color:#a6da95">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span style="color:#c6a0f6">)</span><span style="color:#a6da95"> - </span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">&#34;</span> | tee -a <span style="color:#f4dbd6">$LOGFILE</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Start of the script</span>
</span></span><span style="display:flex;"><span>log <span style="color:#a6da95">&#34;Starting backup script.&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Perform the database dump and compress it</span>
</span></span><span style="display:flex;"><span>docker compose <span style="color:#91d7e3">exec</span> -T database pg_dump -U teslamate teslamate | gzip &gt; <span style="color:#f4dbd6">$FILENAME</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$?</span> -eq <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Database dump and compression successful: </span><span style="color:#f4dbd6">$FILENAME</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Database dump and compression failed&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Move the compressed file to the remote backup directory</span>
</span></span><span style="display:flex;"><span>rclone move <span style="color:#f4dbd6">$FILENAME</span> <span style="color:#f4dbd6">$BACKUPDIR</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$?</span> -eq <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Successfully moved backup to </span><span style="color:#f4dbd6">$BACKUPDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Failed to move backup to </span><span style="color:#f4dbd6">$BACKUPDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>log <span style="color:#a6da95">&#34;Backup script completed successfully.&#34;</span></span></span></code></pre></div>
<p>As in the earlier step, run the script manually to test it and check the log file for messages.</p>
<h2 id="step-4---adding-email-notifications-">Step 4 - Adding Email Notifications 📧</h2>
<p>Finally, we want to be notified about the backup status. We can add email notifications to the script. We&rsquo;ll use <code>ssmtp</code> for this purpose. This simple mail transfer agent can send email notifications from the command line. We&rsquo;ll update the script to send an email notification after the backup is completed.</p>
<p>To send email notifications, you need to have an SMTP server configured. You can use your email provider&rsquo;s SMTP server for this purpose. Our first step is to set up <code>ssmtp</code> for email notifications</p>
<h3 id="step-41---installing-and-configuring-ssmtp">Step 4.1 - Installing and Configuring <code>ssmtp</code></h3>
<p>Install <code>ssmtp</code> on your system:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo apt-get install ssmtp</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Edit the <code>/etc/ssmtp/ssmtp.conf</code> file to add your SMTP server details:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo nano /etc/ssmtp/ssmtp.conf</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Add the following lines, replacing the placeholders with your actual details:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>root=your-email@example.com
</span></span><span style="display:flex;"><span>mailhub=smtp.your-email-provider.com:587
</span></span><span style="display:flex;"><span>AuthUser=your-email@example.com
</span></span><span style="display:flex;"><span>AuthPass=your-email-password
</span></span><span style="display:flex;"><span>UseSTARTTLS=YES
</span></span><span style="display:flex;"><span>TLS_CA_File=/dev/null
</span></span><span style="display:flex;"><span>FromLineOverride=YES</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Edit or create the <code>/etc/ssmtp/revaliases</code> file:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo nano /etc/ssmtp/revaliases</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Add the following line, replacing with your actual details:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>your-username:your-email@example.com:smtp.your-email-provider.com:587</span></span></code></pre></td></tr></table>
</div>
</div>
<p>To test the email configuration, we can create a simple script called  test_email.sh that sends a test email:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#6e738d;font-style:italic"># Test email script</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FROM_NAME</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Teslamate Server&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FROM_EMAIL</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;the-from-email-address&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">EMAIL</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;email-address-where-you-want-to-send-the-email&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">SUBJECT</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Test Email&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">BODY</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;This is a test email to verify the SMTP configuration.&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">echo</span> -e <span style="color:#a6da95">&#34;To: </span><span style="color:#f4dbd6">$EMAIL</span><span style="color:#a6da95">\nFrom: \&#34;</span><span style="color:#f4dbd6">$FROM_NAME</span><span style="color:#a6da95">\&#34; &lt;</span><span style="color:#f4dbd6">$FROM_EMAIL</span><span style="color:#a6da95">&gt;\nSubject: </span><span style="color:#f4dbd6">$SUBJECT</span><span style="color:#a6da95">\n\n</span><span style="color:#f4dbd6">$BODY</span><span style="color:#a6da95">&#34;</span> | ssmtp <span style="color:#f4dbd6">$EMAIL</span></span></span></code></pre></div>
<p>You should receive a test email at the specified address when you run the script. If you encounter any issues, review the configuration and ensure the SMTP server details are correct.</p>
<h3 id="step-42---timeout-for-email-notifications">Step 4.2 - Timeout for Email Notifications</h3>
<p>We can add a timeout to the email command to prevent the script from hanging indefinitely if the email-sending process takes too long. We&rsquo;ll use the <code>timeout</code> command to set the duration of the email sending. First, we must check if the <code>timeout</code> command is available on your system. You can check this by running the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>timeout --version</span></span></code></pre></div>
<p>If the <code>timeout</code> command is not available, you can install it using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install coreutils</span></span></code></pre></div>
<h3 id="step-43---updating-the-backup-script-with-email-notifications">Step 4.3 - Updating the Backup Script with Email Notifications</h3>
<p>Now that we have <code>ssmtp</code> set up for email notifications, we can update the backup script to send an email notification after the backup is completed. We&rsquo;ll add a function to send email notifications and call it at the end of the script. Here&rsquo;s the updated script that includes email notifications:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#6e738d;font-style:italic"># Backup script with logging and email notifications</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">SOURCEDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/teslamate <span style="color:#6e738d;font-style:italic"># Source directory where the data is located</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">BACKUPDIR</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/onedrive <span style="color:#6e738d;font-style:italic"># Remote directory mounted via rclone</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FILENAME</span><span style="color:#91d7e3;font-weight:bold">=</span>teslamate-<span style="color:#c6a0f6">$(</span>date +%Y-%m-%d-%H%M%S<span style="color:#c6a0f6">)</span>.bak.gz <span style="color:#6e738d;font-style:italic"># Date-time stamp filename</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">LOGFILE</span><span style="color:#91d7e3;font-weight:bold">=</span>/home/amit/backup.log
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">EMAIL</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;email-address-you-want-to-send-the-email-to&#34;</span> <span style="color:#6e738d;font-style:italic"># Replace with your email address</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FROM_NAME</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Teslamate Server&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FROM_EMAIL</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;your-email@example.com&#34;</span> <span style="color:#6e738d;font-style:italic"># Replace with the authorized sender email address</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">TIMEOUT_DURATION</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">10</span> <span style="color:#6e738d;font-style:italic"># Set timeout duration (in seconds) for sending email</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">RCLONE_CONFIG</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;/home/amit/.config/rclone/rclone.conf&#34;</span> <span style="color:#6e738d;font-style:italic"># Path to the rclone config file</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Initialize a variable to capture the entire log</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FULL_LOG</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Function to log messages</span>
</span></span><span style="display:flex;"><span>log<span style="color:#91d7e3;font-weight:bold">()</span> <span style="color:#91d7e3;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">echo</span> <span style="color:#a6da95">&#34;</span><span style="color:#c6a0f6">$(</span>date <span style="color:#a6da95">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span style="color:#c6a0f6">)</span><span style="color:#a6da95"> - </span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">&#34;</span> | tee -a <span style="color:#f4dbd6">$LOGFILE</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">FULL_LOG</span><span style="color:#91d7e3;font-weight:bold">+=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">\n&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Function to send email notification</span>
</span></span><span style="display:flex;"><span>send_email<span style="color:#91d7e3;font-weight:bold">()</span> <span style="color:#91d7e3;font-weight:bold">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">local</span> <span style="color:#f4dbd6">STATUS</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">local</span> <span style="color:#f4dbd6">SUBJECT</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Backup </span><span style="color:#a6da95">${</span><span style="color:#f4dbd6">STATUS</span><span style="color:#a6da95">}</span><span style="color:#a6da95"> - </span><span style="color:#c6a0f6">$(</span>date <span style="color:#a6da95">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span style="color:#c6a0f6">)</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">local</span> <span style="color:#f4dbd6">BODY</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;The backup script </span><span style="color:#a6da95">${</span><span style="color:#f4dbd6">STATUS</span><span style="color:#a6da95">}</span><span style="color:#a6da95"> on </span><span style="color:#c6a0f6">$(</span>date<span style="color:#c6a0f6">)</span><span style="color:#a6da95">.\n\nLog details:\n</span><span style="color:#f4dbd6">$FULL_LOG</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">echo</span> -e <span style="color:#a6da95">&#34;To: </span><span style="color:#f4dbd6">$EMAIL</span><span style="color:#a6da95">\nFrom: \&#34;</span><span style="color:#f4dbd6">$FROM_NAME</span><span style="color:#a6da95">\&#34; &lt;</span><span style="color:#f4dbd6">$FROM_EMAIL</span><span style="color:#a6da95">&gt;\nSubject: </span><span style="color:#f4dbd6">$SUBJECT</span><span style="color:#a6da95">\n\n</span><span style="color:#f4dbd6">$BODY</span><span style="color:#a6da95">&#34;</span> | timeout <span style="color:#f4dbd6">$TIMEOUT_DURATION</span> ssmtp <span style="color:#f4dbd6">$EMAIL</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">EMAIL_EXIT_CODE</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f4dbd6">$?</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Email command exit code: </span><span style="color:#f4dbd6">$EMAIL_EXIT_CODE</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$EMAIL_EXIT_CODE</span> -eq <span style="color:#f5a97f">124</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>        log <span style="color:#a6da95">&#34;Email sending timed out after </span><span style="color:#f4dbd6">$TIMEOUT_DURATION</span><span style="color:#a6da95"> seconds&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">elif</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$EMAIL_EXIT_CODE</span> -ne <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>        log <span style="color:#a6da95">&#34;Failed to send email&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>        log <span style="color:#a6da95">&#34;Email sent successfully&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Start of the script</span>
</span></span><span style="display:flex;"><span>log <span style="color:#a6da95">&#34;Starting backup script.&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Change to the source directory</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">cd</span> <span style="color:#f4dbd6">$SOURCEDIR</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$?</span> -eq <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Changed directory to </span><span style="color:#f4dbd6">$SOURCEDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Failed to change directory to </span><span style="color:#f4dbd6">$SOURCEDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    send_email <span style="color:#a6da95">&#34;Failed&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Perform the database dump and compress it</span>
</span></span><span style="display:flex;"><span>docker compose <span style="color:#91d7e3">exec</span> -T database pg_dump -U teslamate teslamate | gzip &gt; <span style="color:#f4dbd6">$FILENAME</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$?</span> -eq <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Database dump and compression successful: </span><span style="color:#f4dbd6">$FILENAME</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Database dump and compression failed&#34;</span>
</span></span><span style="display:flex;"><span>    send_email <span style="color:#a6da95">&#34;Failed&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Move the compressed file to the remote backup directory</span>
</span></span><span style="display:flex;"><span>rclone --config <span style="color:#f4dbd6">$RCLONE_CONFIG</span> move <span style="color:#f4dbd6">$FILENAME</span> <span style="color:#f4dbd6">$BACKUPDIR</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#f4dbd6">$?</span> -eq <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Successfully moved backup to </span><span style="color:#f4dbd6">$BACKUPDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>    log <span style="color:#a6da95">&#34;Failed to move backup to </span><span style="color:#f4dbd6">$BACKUPDIR</span><span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span>    send_email <span style="color:#a6da95">&#34;Failed&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>log <span style="color:#a6da95">&#34;Backup script completed successfully.&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Send email notification</span>
</span></span><span style="display:flex;"><span>send_email <span style="color:#a6da95">&#34;Completed Successfully&#34;</span></span></span></code></pre></div>
<p>If you run the script, you should receive an email notification after completing the backup. The email will contain the log details of the backup process. You can customize the email content and format as needed. The image below shows an example email notification:
<p>

    <figure>
        <img src="images/email.jpg" alt="Email notification"/>
        <figcaption>Email notification</figcaption>
    </figure>

</p></p>
<h3 id="step-44---scheduling-the-backup-script">Step 4.4 - Scheduling the Backup Script</h3>
<p>You can schedule the script to run regularly using a <code>cron</code> job to automate the backup process. You can create a <code>cron</code> job to run the script daily, weekly, or at any interval you prefer. Here&rsquo;s an example of a cron job to run the script daily:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#f5a97f">0</span> <span style="color:#f5a97f">0</span> * * * /home/amit/backup.sh</span></span></code></pre></div>
<p>This cron job runs the <code>backup.sh</code> script every day at midnight. You can customize the schedule based on your requirements. To add the cron job, open the crontab file using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>crontab -e</span></span></code></pre></div>
<p>Add the cron job to the file and save it. The cron job will now run the backup script at the specified interval.</p>
<p>I have the script running at 5 AM, 1 PM, and 9 PM daily. In addition, I log the output of the cron job to a file (<code>backup_cron.log</code>) so I can review it later if there is an issue.  Here are the steps to set this up:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#f5a97f">0</span> <span style="color:#f5a97f">5</span> * * * /home/amit/backup.sh &gt;&gt; /home/amit/backup_cron.log 2&gt;&amp;<span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#f5a97f">0</span> <span style="color:#f5a97f">13</span> * * * /home/amit/backup.sh &gt;&gt; /home/amit/backup_cron.log 2&gt;&amp;<span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span><span style="color:#f5a97f">0</span> <span style="color:#f5a97f">21</span> * * * /home/amit/backup.sh &gt;&gt; /home/amit/backup_cron.log 2&gt;&amp;<span style="color:#f5a97f">1</span></span></span></code></pre></div>
<p>Be sure to replace <code>/home/amit/backup.sh</code> with the actual path to your backup script. The <code>&gt;&gt; /home/amit/backup_cron.log 2&gt;&amp;1</code> part of the cron job redirects the script output to the specified log file. This allows you to capture the script output and any errors during execution.</p>
<p>Finally, you can check the cron job status using the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>crontab -l</span></span></code></pre></div>
<p>This will list the cron jobs that are currently scheduled. You can also check the cron job logs to verify that the script is running as expected.</p>
<p>Here is a snippet of the <code>backup_cron.log</code> file from my system:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>2024-07-05 13:00:01 - Starting backup script.
</span></span><span style="display:flex;"><span>2024-07-05 13:00:01 - Changed directory to /home/amit/teslamate
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">time</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;2024-07-05T13:00:01-07:00&#34;</span> <span style="color:#f4dbd6">level</span><span style="color:#91d7e3;font-weight:bold">=</span>warning <span style="color:#f4dbd6">msg</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;/home/amit/teslamate/docker-compose.yml: `version` is obsolete&#34;</span>
</span></span><span style="display:flex;"><span>2024-07-05 13:00:30 - Database dump successful: teslamate-2024-07-05-130001.bak
</span></span><span style="display:flex;"><span>2024-07-05 13:00:55 - Successfully compressed teslamate-2024-07-05-130001.bak.gz
</span></span><span style="display:flex;"><span>2024-07-05 13:00:57 - Successfully moved backup to /home/amit/onedrive
</span></span><span style="display:flex;"><span>2024-07-05 13:00:57 - Backup script completed successfully.
</span></span><span style="display:flex;"><span>2024-07-05 13:00:59 - Email <span style="color:#91d7e3">command</span> <span style="color:#91d7e3">exit</span> code: <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>2024-07-05 13:00:59 - Email sent successfully</span></span></code></pre></div>
<h3 id="step-45---timezone-configuration">Step 4.5 - Timezone Configuration</h3>
<p>Note in most cloud-based VMs, the timezone is set to UTC; if you specify the time in your local timezone, you might want to set the timezone to your local timezone. You can do this by running the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo timedatectl set-timezone your-timezone</span></span></code></pre></div>
<p>Replace <code>your-timezone</code> with the appropriate timezone (e.g., <code>America/Los_Angeles</code>).</p>
<h2 id="conclusion">Conclusion</h2>
<p>And that&rsquo;s it! You now have a backup script that backs up your Teslamate data to OneDrive and sends email notifications about the backup status. The script runs automatically at the scheduled intervals, ensuring your data is backed up regularly. You can customize the script further based on your requirements and preferences. 🫰</p>
]]></content:encoded>
    </item>
    <item>
      <title>SLMs - Running Phi-3 on an iphone and locally</title>
      <link>/post/2024/05/running-phi3-on-device/</link>
      <pubDate>Wed, 08 May 2024 00:00:00 +0000</pubDate>
      <guid>/post/2024/05/running-phi3-on-device/</guid>
      <description>A simple demo on how to run Phi-3 on a phone and locally.</description>
      <content:encoded><![CDATA[<p>We released Phi-3 recently, which builds on Phi-2 (<a
	
		href = "https://blog.desigeek.com/post/2024/03/running-phi2-locally/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		read more on that here
	</span>
</a>) and it is a great model to use for various tasks. In this post, we will show how to run Phi-3 locally including a demo of it running on a phone.</p>
<video class="video-shortcode" preload="auto" controls>
    <source src="https://1drv.ms/v/s!Alx12BenmZ2yx6tK2CRER3Q-cuY9iw?e=b0bqTh" type="video/webm">
    There should have been a video here but your browser does not seem
    to support it.
</video>

<h2 id="1-what-are-small-language-models-slms">1. What are Small Language Models (SLMs)?</h2>
<p>Before diving into running Phi-2 locally, let&rsquo;s take a moment to understand the concept of small language models (SLMs) and their significance in natural language processing (NLP). A SLM is a type of AI model that has been trained on a massive dataset of text but is limited in terms of its size and capabilities compared to a Large Language Model (LLM). SLMs are designed to be more lightweight and efficient, making them suitable for various applications, including chatbots, language translation, and content generation. SLMs are much smaller than LLMs, with fewer parameters and a smaller dataset, so they have a lower computational cost, making them more suitable for edge or resource-constraint devices.</p>
<h2 id="2-what-is-phi-2">2. What is Phi-2?</h2>
<p><a
	
		href = "https://www.microsoft.com/en-us/research/blog/phi-2-the-surprising-power-of-small-language-models/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Phi-2
	</span>
</a> is the latest model in the Phi series of small language models (SLMs) that aim to break the conventional scaling laws of language models. Unlike large language models (LLMs) that require massive amounts of data and compute resources, Phi models are trained on a mixture of web-crawled and synthetic &ldquo;textbook-quality&rdquo; data, following the idea of <a
	
		href = "https://arxiv.org/abs/2306.11644"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Textbooks Are All You Need
	</span>
</a>. Phi models also leverage innovations in model architecture, optimization, and data augmentation to achieve remarkable performance on various benchmarks. &#x1f600;</p>
<p>Phi-2 is twice as large as its predecessor Phi-1.5, and was trained for two weeks on a cluster of 96 A100 GPUs. It demonstrates outstanding reasoning and language understanding capabilities, showcasing state-of-the-art performance (for LLMs &lt;13 billion parameters).</p>
<p>As shown in the figure below on complex benchmarks, Phi-2 matches or outperforms models up to 25x larger, such as Llama-2 and Gemini Nano. Phi-2 is also an ideal playground for researchers, as it allows for exploration around mechanistic interpretability, safety improvements, or fine-tuning experimentation on various tasks. Phi-2 is available in the <a
	
		href = "https://learn.microsoft.com/en-us/azure/ai-studio/what-is-ai-studio?tabs=home"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure AI Studio
	</span>
</a> (via the <a
	
		href = "https://ai.azure.com/explore/models/microsoft-phi-2/version/4/registry/azureml-msr"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		model catalog
	</span>
</a>) and on Hugging Face.</p>
<p><p>

    <figure>
        <img src="images/phi2.png" alt="Phi-2 Evaluation"/>
        <figcaption>Phi-2 Evaluation</figcaption>
    </figure>

</p></p>
<h3 id="21-phi-2s-capabilities">2.1 Phi-2&rsquo;s Capabilities</h3>
<p>What better way to show this other than Some prompts that Phi-2 can handle:</p>
<ol>
<li>
<p><strong>Creative Writing</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Write a short story about a time-traveling computer geek who discovers a secret algorithm book with the power to change computing history.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;In the dimly lit archives of the ancient library, Amelia stumbled upon a dusty tome unlike any other. Its pages whispered secrets of forgotten civilizations and whispered promises of altered destinies. As she traced her fingers over the delicate script, the room seemed to blur, and suddenly, she was standing in the heart of Renaissance Florence. The air smelled of ink and ambition. The book had worked its magic, and Amelia knew she held the fate of empires in her hands.&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Philosophical Reflection</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Discuss the concept of free will versus determinism.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Free will and determinism have long been at odds in philosophical discourse. Some argue that our choices are predetermined by external factors—genetics, upbringing, societal norms—while others champion the idea that we possess true agency. Perhaps the truth lies in the delicate dance between the two. We navigate life&rsquo;s labyrinth, sometimes guided by fate, other times forging our own path.&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Educational Explanation</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Explain the process of photosynthesis in simple terms.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Photosynthesis, the magical dance of plants and sunlight, is how they create their own food. Imagine tiny chlorophyll factories in leaves, capturing sunlight like solar panels. They convert that energy into chemical fuel—glucose—while releasing oxygen as a byproduct. So next time you breathe in that fresh air, thank a leaf for its photosynthetic prowess!&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Humorous Scenario</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Describe a day in the life of a mischievous squirrel.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Meet Nutty, the squirrel extraordinaire. His mornings begin with acorn heists, followed by elaborate treehouse construction. He&rsquo;s the neighborhood&rsquo;s unofficial alarm clock, chattering loudly at dawn. But his pièce de résistance? Prank-calling the crows, convincing them they&rsquo;ve won a lifetime supply of shiny objects. Nutty&rsquo;s motto: &lsquo;Life&rsquo;s too short not to be a little nuts.&rsquo;&rdquo;</li>
</ul>
</li>
</ol>
<h2 id="3-small-language-models-slms-vs-large-language-models-llms">3. Small Language Models (SLMs) vs Large Language Models (LLMs)</h2>
<p>Large Language Models (LLMs) are a type of AI model that is much larger and more powerful than SLMs. LLMs have hundreds of billions of parameters and are trained on massive text datasets. This gives LLMs the ability to handle complex tasks, such as language generation, translation, and question answering, with high accuracy and fluency. However, LLMs also have some disadvantages. They are larger, making them more expensive and slower to train. They also have a higher computational cost, meaning they may require access to specialized hardware.</p>
<p>On the other hand, SLMs, as we called out, are smaller and more lightweight than LLMs, making them more efficient and cost-effective in training computing resources and inference. While it might seem that SLMs are also more suitable for edge or resource-constrained devices, such as mobile phones or IoT devices, they are small compared to LLMs but still require significant computational resources to run. Phi-2, for example, still has 2.7B parameters, and while it can make inferences on a CPU, it is very slow and impractical for real-time applications. One would need a GPU or a cloud-based service for any realistic use case.</p>
<h3 id="31-when-to-use-slm-vs-llm">3.1 When to use SLM vs LLM?</h3>
<p>Firstly, neither model is inherently better - the choice between an SLM and an LLM depends on the specific application and requirements. SLMs are a good choice when size, cost, and speed are important considerations. LLMs are a better choice when high performance and complex capabilities are required. If a task at hand is quite narrow and in one of the supported languages, then SLMs might be good. However, for a given task, an SLM may be sufficient, but an LLM may be necessary for more complex tasks or tasks requiring high accuracy and fluency.</p>
<p>Furthermore, it is key to understand that it is not necessarily about the number of languages understood but rather the depth and nuance with which each model can understand and generate language. SLMs are designed to be efficient and effective within their scope, which may include a wide range of languages. LLMs like GPT-4, due to their size and complexity, often can understand and generate text in a larger number of languages and with greater nuance.</p>
<p>The choice between an SLM and an LLM would again depend on the specific requirements of the task, including the languages involved and the level of language understanding and generation needed. Using a combination of SLMs and LLMs is common to achieve the best results for a given application.</p>
<h2 id="4-running-phi-2-locally">4. Running Phi-2 locally</h2>
<p>On one hand, running this is simple if you just don&rsquo;t want to program anything and only want to use the model. The easiest option in this case is to use [LM <a
	
		href = "https://lmstudio.ai/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Studio
	</span>
</a>, a web-based platform for running language models. You can use the Hugging Face API to download and run the model.</p>
<p>We use a simple console chat example that runs locally. We use the Hugging Face Transformers library to generate text based on user input. The user can generate a story, a haiku, or a joke on a topic of their choice. Here is how to run it locally on a Windows machine - the same should apply to a Mac or Linux machine.</p>
<p>The full code is below, but here are the key aspects to grok when running Phi-2 locally.</p>
<ul>
<li>The key is to use the <code>AutoModelForCausalLM</code> and <code>AutoTokenizer</code> classes from the <code>transformers</code> library to load the Phi-2 model and tokenizer.</li>
<li>We then use the <code>generate</code> method to generate text based on a user prompt. The <code>generate</code> method takes the user prompt as input and returns the generated text</li>
<li>We use the <code>from_pretrained</code> method to load the model and tokenizer from the Hugging Face model hub.</li>
<li>We also use the <code>save_pretrained</code> method to save the model and tokenizer to a local directory. This allows us to load the model and tokenizer from the local directory if they are already saved, which can help save time and resources.</li>
</ul>
<p>The following code snippet is what loads the model and the tokenizer:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>,
</span></span><span style="display:flex;"><span>                                            torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>,
</span></span><span style="display:flex;"><span>                                            trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>,
</span></span><span style="display:flex;"><span>                                         trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)</span></span></code></pre></div>
<p>And the following is where we encode the user input and call the generation. First, we create tokens of the user prompt; the resulting tokens are returned as PyTorch tensors. Then, the model generates text based on the tokenized input. We cap the tokens to a maximum of 500 tokens, and the end-of-sequence token is used for padding if necessary. Finally, the generated tokens are decoded back into human-readable text.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt,
</span></span><span style="display:flex;"><span>                   return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                   return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                   add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs,
</span></span><span style="display:flex;"><span>                         max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">500</span>,
</span></span><span style="display:flex;"><span>                         pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]</span></span></code></pre></div>
<p>The full code is below. The code is a simple console chat example that runs locally. The user can generate a story, a haiku, or a joke on a topic of their choice.</p>
<p>Some examples of what Phi-2 can generate using the above code are shown below. The first is a story about pandas and dogs.
<p>

    <figure>
        <img src="images/phi2-1.png" alt="Story about Pandas and Dogs"/>
        <figcaption>Story about Pandas 🐼 and Dogs 🐶</figcaption>
    </figure>

</p></p>
<p>Here is another example of a Haiku and a Joke generated by Phi-2 on Pandas.
<p>

    <figure>
        <img src="images/phi2-2.png" alt="Story about Pandas and Dogs"/>
        <figcaption>Haiku and Joke about Pandas 🐼</figcaption>
    </figure>

</p></p>
<p>Switching gears, let&rsquo;s look at how we can implement the RAG using Phi-2.</p>
<h3 id="41-running-phi-2-locally---full-code">4.1 Running Phi-2 Locally - Full Code</h3>
<p>The following code is the complete code that executes the examples we showed before for running Phi-2 locally. This can work on a CPU, but it is very slow, and a good GPU is strongly suggested.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64">64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65">65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66">66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67">67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68">68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69">69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70">70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71">71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72">72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73">73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74">74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75">75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76">76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77">77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78">78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79">79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80">80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81">81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82">82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83">83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84">84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85">85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86">86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87">87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88">88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89">89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90">90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91">91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92">92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93">93</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">warnings</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">logging</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoModelForCausalLM, AutoTokenizer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DEBUG <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Suppress warnings and set the logging level to ERROR</span>
</span></span><span style="display:flex;"><span>warnings<span style="color:#91d7e3;font-weight:bold">.</span>filterwarnings(<span style="color:#a6da95">&#34;ignore&#34;</span>)
</span></span><span style="display:flex;"><span>logging<span style="color:#91d7e3;font-weight:bold">.</span>getLogger(<span style="color:#a6da95">&#34;transformers&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>setLevel(logging<span style="color:#91d7e3;font-weight:bold">.</span>ERROR)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Define the directory where you want to save the model and tokenizer</span>
</span></span><span style="display:flex;"><span>MODEL_PATH <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./local_model&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Check if the model and tokenizer are already saved locally</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(MODEL_PATH):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Loading model and tokenizer from local directory: </span><span style="color:#a6da95">{</span>MODEL_PATH<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the model and tokenizer from the local directory</span>
</span></span><span style="display:flex;"><span>    model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Downloading model and tokenizer from Hugging Face&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>    model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>, torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Saving model and tokenizer to local directory: </span><span style="color:#a6da95">{</span>MODEL_PATH<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Save the model and tokenizer locally</span>
</span></span><span style="display:flex;"><span>    model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Model device: </span><span style="color:#a6da95">{</span>model<span style="color:#91d7e3;font-weight:bold">.</span>device<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;CUDA available: </span><span style="color:#a6da95">{</span>torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available()<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Set the default device to CUDA if available, otherwise use CPU</span>
</span></span><span style="display:flex;"><span>device <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>device(<span style="color:#a6da95">&#34;cuda&#34;</span> <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;cpu&#34;</span>)
</span></span><span style="display:flex;"><span>model<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">handle_prompt</span>(user_input, type_of_text)<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Instruct: Write a </span><span style="color:#a6da95">{</span>type_of_text<span style="color:#a6da95">}</span><span style="color:#a6da95"> about </span><span style="color:#a6da95">{</span>user_input<span style="color:#a6da95">}</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Output:&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> {name: tensor<span style="color:#91d7e3;font-weight:bold">.</span>to(model<span style="color:#91d7e3;font-weight:bold">.</span>device) <span style="color:#c6a0f6">for</span> name, tensor <span style="color:#91d7e3;font-weight:bold">in</span> inputs<span style="color:#91d7e3;font-weight:bold">.</span>items()}
</span></span><span style="display:flex;"><span>        outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">500</span>, pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove the prompt from the output text</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(prompt, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(<span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Answer:</span><span style="color:#a6da95">{</span>text<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#f4dbd6">__name__</span><span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#a6da95">&#34;__main__&#34;</span>: 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;First What would you like to write today?&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;1. Story 📝&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;2. Haiku ✍️&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;3. Joke 😆&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;4. Quit 👋&#34;</span>)
</span></span><span style="display:flex;"><span>        user_choice <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Choose an option:&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;4&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        user_prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;And on which topic:&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> user_prompt<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Input cannot be empty or consist only of spaces.&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">continue</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;1&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;story&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;2&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;haiku&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;3&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;joke&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Invalid choice. Please choose a valid option.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;_&#34;</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">100</span>)</span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="5-implementing-retrieval-augmented-generation-rag-with-phi-2">5. Implementing Retrieval-Augmented Generation (RAG) with Phi-2</h2>
<p>RAG is a powerful technique that combines the strengths of retrieval-based and generation-based approaches to natural language processing. RAG is one of the ways one can get proprietary information and knowledge to the model and use it as part of the prompt.  It leverages a retriever to find relevant context passages and a generator to produce fluent and coherent responses. The retriever identifies relevant context passages, and the generator uses these passages to generate a response.</p>
<p>This approach allows RAG to produce high-quality, informative, and contextually relevant responses. In-context learning is a key feature of RAG, as it allows the model to learn from the context of the conversation and generate more accurate and relevant responses. This is particularly useful in scenarios where the model needs to understand and respond to complex queries or provide detailed information on a specific topic.</p>
<p>At a high level, the process of implementing RAG  involves the following steps:</p>
<ol>
<li><strong>Generate Embeddings with Phi-2</strong>:
<ul>
<li>Use Phi-2 to encode your context passages (documents) and extract their embeddings.</li>
<li>These embeddings will represent the semantic content of each passage.</li>
</ul>
</li>
<li><strong>Create a Vector Index</strong>:
<ul>
<li>Choose a vector index library or framework (such as <strong>Faiss</strong>, <strong>Annoy</strong>, or <strong>HNSW</strong>).</li>
<li>Initialize an index structure to store the embeddings efficiently.</li>
<li>Add the generated embeddings to the index.</li>
</ul>
</li>
<li><strong>Save Embeddings to a Local Vector Database</strong>:
<ul>
<li>Create a local database to store the embeddings.</li>
<li>For each context passage, save its corresponding embedding in the database.</li>
<li>You can use the passage ID or a unique identifier as the key for retrieval.</li>
</ul>
</li>
<li><strong>Perform Similarity Search</strong>:
<ul>
<li>When you receive a new context (query), encode it using Phi-2 to obtain its embedding.</li>
<li>Use the vector index to perform a similarity search against the saved embeddings.</li>
<li>Retrieve the most similar context passages based on cosine similarity or another distance metric.</li>
<li>Return the relevant passages as results.</li>
</ul>
</li>
</ol>
<p>In our example, we will use the FAISS library to create a vector index and perform a similarity search. We will also save the embeddings to a local database for efficient retrieval. <a
	
		href = "https://faiss.ai/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		FAISS (Facebook AI Similarity Search)
	</span>
</a> is a library developed by Facebook for efficient similarity search and clustering of high-dimensional vectors. It allows for a quick nearest-neighbor search over large datasets and supports CPU and GPU-based computations. FAISS is widely used in information retrieval, recommendation systems, and other applications that require similarity search.</p>
<h3 id="51-loading-data-for-rag-and-phi-2">5.1 Loading data for RAG and Phi-2</h3>
<p>To implement RAG, we use the script from the Oppenheimer movie - which is quite new in that it is not in the Phi-2 training set and is available as a PDF. We will extract the script from this PDF, creating embeddings, which will then save the embeddings to a local database and perform a similarity search to retrieve relevant context passages based on a user query. We will use the FAISS library to create a vector index and perform a similarity search. We will also save the embeddings to a local database for efficient retrieval.</p>
<p>We use the <code>PyPDF2</code> library to parse PDFs, a pure Python library for reading and writing PDF files. It can extract text, merge and split documents, and more. We will use it to extract the PDF text from the Oppenheimer movie script. The following code function shows how to read the PDF and extract the text. This is efficient for our use case, but it is not the most efficient way to extract text from a PDF when thinking about production scale, especially if the PDF has a lot of images and tables.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">read_pdf</span>(file_path):
</span></span><span style="display:flex;"><span>    pdf_file_obj <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;rb&#39;</span>)
</span></span><span style="display:flex;"><span>    pdf_reader <span style="color:#91d7e3;font-weight:bold">=</span> PyPDF2<span style="color:#91d7e3;font-weight:bold">.</span>PdfFileReader(pdf_file_obj)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    num_pages <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>numPages
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> page_num <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(num_pages):
</span></span><span style="display:flex;"><span>        page_obj <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>getPage(page_num)
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> page_obj<span style="color:#91d7e3;font-weight:bold">.</span>extractText()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">yield</span> text
</span></span><span style="display:flex;"><span>    pdf_file_obj<span style="color:#91d7e3;font-weight:bold">.</span>close()</span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="52-generate-embeddings-using-phi-2">5.2 Generate embeddings using Phi-2</h3>
<p>Now that we have the text, the following functions show how to create the embeddings using Phi-2. We read the text as a list of context passages and then use Phi-2 to encode each passage and extract its embedding using the <code>encode</code> method.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_embeddings</span>(file_path, tokenizer, model, device):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith(<span style="color:#a6da95">&#39;.pdf&#39;</span>):
</span></span><span style="display:flex;"><span>        context_passages <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">list</span>(read_pdf(file_path))
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> file:
</span></span><span style="display:flex;"><span>            context_passages <span style="color:#91d7e3;font-weight:bold">=</span> file<span style="color:#91d7e3;font-weight:bold">.</span>readlines()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    embeddings <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> passage <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(context_passages):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> passage<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Skip the passage</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">pass</span>
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(passage, 
</span></span><span style="display:flex;"><span>                                    return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                                    add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                                    return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>            output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>            logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>            embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>            embeddings<span style="color:#91d7e3;font-weight:bold">.</span>append(embedding)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> embeddings, context_passages</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Here are a few things that are going on:</p>
<ul>
<li>Given that we are using this for inference and not training, we use a <code>torch.</code>no_grad()`, which tells PyTorch not to track, calculate, or modify gradients while executing code within this block. This helps us save the amount of memory needed.</li>
<li>Inside this block, the input_ids are fed into the model, and the output is stored in the output variable. The logits, which are the raw, unnormalized scores outputted by the last layer of the model, are then extracted from the model&rsquo;s output.</li>
<li>The logits are then processed to generate the embedding for the passage. The .mean(dim=1) method calculates the mean of the logits along dimension 1, which typically represents the sequence length in a language model.</li>
<li>The .detach() method detaches the result from the computation graph so that no gradients will be backpropagated along this variable.</li>
<li>The .cpu() method moves the tensor to the CPU if it&rsquo;s not already there. Finally, the tensor is converted to a numpy array using the .numpy() method.</li>
<li>The resulting embedding is then appended to the embeddings list, which contains the embeddings for all the passages.</li>
</ul>
<h3 id="53-creating-vector-index">5.3 Creating Vector Index</h3>
<p>The following function shows how to create a vector index using the FAISS library and perform a similarity search to retrieve relevant context passages based on a user query. The create_index function initializes a flat index structure to store the embeddings and adds the embeddings to the index. The search_query function encodes the user query using Phi-2 to obtain its embedding and performs a similarity search against the saved embeddings to retrieve the most similar context passages.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_index</span>(query_embedding):
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> faiss<span style="color:#91d7e3;font-weight:bold">.</span>IndexFlatL2(query_embedding[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">.</span>shape[<span style="color:#f5a97f">1</span>])  <span style="color:#6e738d;font-style:italic"># Euclidean distance</span>
</span></span><span style="display:flex;"><span>    faiss<span style="color:#91d7e3;font-weight:bold">.</span>normalize_L2(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add embeddings to the index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> i, item <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(<span style="color:#91d7e3">enumerate</span>(query_embedding), total<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">len</span>(query_embedding)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> item<span style="color:#91d7e3;font-weight:bold">.</span>ndim <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">1</span>:
</span></span><span style="display:flex;"><span>            item <span style="color:#91d7e3;font-weight:bold">=</span> item<span style="color:#91d7e3;font-weight:bold">.</span>reshape(<span style="color:#f5a97f">1</span>, <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)  <span style="color:#6e738d;font-style:italic"># Reshape 1D array to 2D</span>
</span></span><span style="display:flex;"><span>        index<span style="color:#91d7e3;font-weight:bold">.</span>add(item)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> index</span></span></code></pre></div>
<p>The <code>normalize_L2()</code> function normalizes the vectors and is a crucial step when using Euclidean distance in high-dimensional spaces to ensure that the distance is not dominated by the dimensionality of the vectors. As we iterate through the embeddings, we check if the item is a 1D array and reshape it to a 2D array if necessary. This is important because FAISS expects the input to be a 2D array, and we need to reshape the 1D array to a 2D array before adding it to the index.</p>
<p>The function finally returns the created index. This index can then be used to perform efficient similarity searches.</p>
<h3 id="54-perform-similarity-search">5.4 Perform Similarity Search</h3>
<p>The following function shows how to perform a similarity search using the vector index to retrieve relevant context passages based on a user query. As noted earlier, the most similar context passages are then retrieved based on cosine similarity or another distance metric.</p>
<p>The function starts by encoding the input query using a tokenizer and performs a similarity search on the FAISS index using the query embedding. It retrieves the indices of the top 3 most similar passages to the input query and then retrieves the corresponding context passages from the context_passages list. The similar context passages are then concatenated into a single string and passed to the Phi-2 model to generate a response.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">search_query</span>(input_query, inputTokenizer, model, device, index, context_passages):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Given a new query context, encode it and perform similarity search</span>
</span></span><span style="display:flex;"><span>    input_ids <span style="color:#91d7e3;font-weight:bold">=</span> inputTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(input_query,
</span></span><span style="display:flex;"><span>                                    return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                                    return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                                    add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> input_ids<span style="color:#91d7e3;font-weight:bold">.</span>long()
</span></span><span style="display:flex;"><span>        output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>        logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>        query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Perform similarity search - top 3 similar passages</span>
</span></span><span style="display:flex;"><span>    _, similar_indices <span style="color:#91d7e3;font-weight:bold">=</span> index<span style="color:#91d7e3;font-weight:bold">.</span>search(query_embedding, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">3</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Retrieve context passages based on similar_indices</span>
</span></span><span style="display:flex;"><span>    similar_contexts <span style="color:#91d7e3;font-weight:bold">=</span> [context_passages[i] <span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> similar_indices[<span style="color:#f5a97f">0</span>]]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Concatenate the similar contexts into a single string</span>
</span></span><span style="display:flex;"><span>    context <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39; &#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(similar_contexts)</span></span></code></pre></div>
<p>Let us run this and see how it works, as we discussed before. We will use the Oppenheimer movie script as the context passages and perform a similarity search to retrieve relevant context passages based on a user query. The next few figures show the output of us asking questions about the movie, where those pieces of information are not in the model but passed using the semantic search.</p>
<p><p>

    <figure>
        <img src="images/phi2-3.png" alt="Example 1 - Phi-2 and RAG implementation"/>
        <figcaption>Example 1 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/phi2-4.png" alt="Example 2 - Phi-2 and RAG implementation"/>
        <figcaption>Example 2 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/phi2-5.png" alt="Example 3 - Phi-2 and RAG implementation"/>
        <figcaption>Example 3 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p>Now that we have seen the different elements, the code below brings everything together as a console app that one can run. The Oppenheimer script (pdf file) you need can be <a
	
		href = "data/oppenheimer-2023.pdf"
	

	

	>
	
	<span>
		downloaded from here
	</span>
</a>.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="137"><a style="outline:none;text-decoration:none;color:inherit" href="#137">137</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="138"><a style="outline:none;text-decoration:none;color:inherit" href="#138">138</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="139"><a style="outline:none;text-decoration:none;color:inherit" href="#139">139</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="140"><a style="outline:none;text-decoration:none;color:inherit" href="#140">140</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="141"><a style="outline:none;text-decoration:none;color:inherit" href="#141">141</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="142"><a style="outline:none;text-decoration:none;color:inherit" href="#142">142</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="143"><a style="outline:none;text-decoration:none;color:inherit" href="#143">143</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="144"><a style="outline:none;text-decoration:none;color:inherit" href="#144">144</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="145"><a style="outline:none;text-decoration:none;color:inherit" href="#145">145</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="146"><a style="outline:none;text-decoration:none;color:inherit" href="#146">146</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="147"><a style="outline:none;text-decoration:none;color:inherit" href="#147">147</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="148"><a style="outline:none;text-decoration:none;color:inherit" href="#148">148</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="149"><a style="outline:none;text-decoration:none;color:inherit" href="#149">149</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="150"><a style="outline:none;text-decoration:none;color:inherit" href="#150">150</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="151"><a style="outline:none;text-decoration:none;color:inherit" href="#151">151</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="152"><a style="outline:none;text-decoration:none;color:inherit" href="#152">152</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="153"><a style="outline:none;text-decoration:none;color:inherit" href="#153">153</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="154"><a style="outline:none;text-decoration:none;color:inherit" href="#154">154</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="155"><a style="outline:none;text-decoration:none;color:inherit" href="#155">155</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="156"><a style="outline:none;text-decoration:none;color:inherit" href="#156">156</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="157"><a style="outline:none;text-decoration:none;color:inherit" href="#157">157</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="158"><a style="outline:none;text-decoration:none;color:inherit" href="#158">158</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="159"><a style="outline:none;text-decoration:none;color:inherit" href="#159">159</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="160"><a style="outline:none;text-decoration:none;color:inherit" href="#160">160</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="161"><a style="outline:none;text-decoration:none;color:inherit" href="#161">161</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="162"><a style="outline:none;text-decoration:none;color:inherit" href="#162">162</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="163"><a style="outline:none;text-decoration:none;color:inherit" href="#163">163</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="164"><a style="outline:none;text-decoration:none;color:inherit" href="#164">164</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="165"><a style="outline:none;text-decoration:none;color:inherit" href="#165">165</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="166"><a style="outline:none;text-decoration:none;color:inherit" href="#166">166</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="167"><a style="outline:none;text-decoration:none;color:inherit" href="#167">167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="168"><a style="outline:none;text-decoration:none;color:inherit" href="#168">168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="169"><a style="outline:none;text-decoration:none;color:inherit" href="#169">169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="170"><a style="outline:none;text-decoration:none;color:inherit" href="#170">170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="171"><a style="outline:none;text-decoration:none;color:inherit" href="#171">171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="172"><a style="outline:none;text-decoration:none;color:inherit" href="#172">172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="173"><a style="outline:none;text-decoration:none;color:inherit" href="#173">173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="174"><a style="outline:none;text-decoration:none;color:inherit" href="#174">174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="175"><a style="outline:none;text-decoration:none;color:inherit" href="#175">175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="176"><a style="outline:none;text-decoration:none;color:inherit" href="#176">176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="177"><a style="outline:none;text-decoration:none;color:inherit" href="#177">177</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="178"><a style="outline:none;text-decoration:none;color:inherit" href="#178">178</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="179"><a style="outline:none;text-decoration:none;color:inherit" href="#179">179</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="180"><a style="outline:none;text-decoration:none;color:inherit" href="#180">180</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="181"><a style="outline:none;text-decoration:none;color:inherit" href="#181">181</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="182"><a style="outline:none;text-decoration:none;color:inherit" href="#182">182</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="183"><a style="outline:none;text-decoration:none;color:inherit" href="#183">183</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="184"><a style="outline:none;text-decoration:none;color:inherit" href="#184">184</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="185"><a style="outline:none;text-decoration:none;color:inherit" href="#185">185</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="186"><a style="outline:none;text-decoration:none;color:inherit" href="#186">186</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="187"><a style="outline:none;text-decoration:none;color:inherit" href="#187">187</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="188"><a style="outline:none;text-decoration:none;color:inherit" href="#188">188</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="189"><a style="outline:none;text-decoration:none;color:inherit" href="#189">189</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">warnings</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">logging</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoModelForCausalLM, AutoTokenizer
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">numpy</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">np</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">faiss</span> 
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">tqdm</span> <span style="color:#8bd5ca">import</span> tqdm
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">pickle</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">re</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">PyPDF2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DEBUG <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>warnings<span style="color:#91d7e3;font-weight:bold">.</span>filterwarnings(<span style="color:#a6da95">&#34;ignore&#34;</span>)
</span></span><span style="display:flex;"><span>logging<span style="color:#91d7e3;font-weight:bold">.</span>getLogger(<span style="color:#a6da95">&#34;transformers&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>setLevel(logging<span style="color:#91d7e3;font-weight:bold">.</span>ERROR)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Define the directory where you want to save the model and tokenizer</span>
</span></span><span style="display:flex;"><span>MODEL_PATH <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./local_model&#34;</span>
</span></span><span style="display:flex;"><span>MODEL_NAME <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>
</span></span><span style="display:flex;"><span>BATCH_SIZE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Oppenheimer movie</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#DATA_FILE = &#34;./oppenheimer-2023.txt&#34;</span>
</span></span><span style="display:flex;"><span>DATA_FILE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./oppenheimer-2023.pdf&#34;</span>
</span></span><span style="display:flex;"><span>EMBEDDINGS_FILE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;./embeddings_movie.pkl&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">load_model</span>(model_path, model_name, debug<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check if the model and tokenizer are already saved locally</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(model_path):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Loading model and tokenizer from local directory: </span><span style="color:#a6da95">{</span>model_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load the model and tokenizer from the local directory</span>
</span></span><span style="display:flex;"><span>        model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_path)
</span></span><span style="display:flex;"><span>        tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_path)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Downloading model and tokenizer from Hugging Face&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>        model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name, torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Saving model and tokenizer to local directory: </span><span style="color:#a6da95">{</span>model_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Save the model and tokenizer locally</span>
</span></span><span style="display:flex;"><span>        model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(model_path)
</span></span><span style="display:flex;"><span>        tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(model_path)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Model device: </span><span style="color:#a6da95">{</span>model<span style="color:#91d7e3;font-weight:bold">.</span>device<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;CUDA available: </span><span style="color:#a6da95">{</span>torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available()<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the default device to CUDA if available, otherwise use CPU</span>
</span></span><span style="display:flex;"><span>    device <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>device(<span style="color:#a6da95">&#34;cuda&#34;</span> <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;cpu&#34;</span>)
</span></span><span style="display:flex;"><span>    model<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> model, tokenizer, device
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">save_embeddings</span>(embeddings, passages, file_name):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_name, <span style="color:#a6da95">&#39;wb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            pickle<span style="color:#91d7e3;font-weight:bold">.</span>dump((<span style="color:#91d7e3">list</span>(embeddings), <span style="color:#91d7e3">list</span>(passages)), f)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">IOError</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Error writing to file </span><span style="color:#a6da95">{</span>file_name<span style="color:#a6da95">}</span><span style="color:#a6da95">.&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>PicklingError:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Error pickling embeddings and passages.&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">load_embeddings</span>(file):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>load(f)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">FileNotFoundError</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;File </span><span style="color:#a6da95">{</span>file<span style="color:#a6da95">}</span><span style="color:#a6da95"> not found.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>UnpicklingError:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Error unpickling file </span><span style="color:#a6da95">{</span>file<span style="color:#a6da95">}</span><span style="color:#a6da95">.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">read_pdf</span>(file_path):
</span></span><span style="display:flex;"><span>    pdf_file_obj <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;rb&#39;</span>)
</span></span><span style="display:flex;"><span>    pdf_reader <span style="color:#91d7e3;font-weight:bold">=</span> PyPDF2<span style="color:#91d7e3;font-weight:bold">.</span>PdfFileReader(pdf_file_obj)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    num_pages <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>numPages
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> page_num <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(num_pages):
</span></span><span style="display:flex;"><span>        page_obj <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>getPage(page_num)
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> page_obj<span style="color:#91d7e3;font-weight:bold">.</span>extractText()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">yield</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Finished reading file. Number pages: </span><span style="color:#a6da95">{</span>num_pages<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    pdf_file_obj<span style="color:#91d7e3;font-weight:bold">.</span>close()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_embeddings</span>(file_path, tokenizer, model, device):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(EMBEDDINGS_FILE):
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load the embeddings and passages from disk</span>
</span></span><span style="display:flex;"><span>        embeddings, context_passages <span style="color:#91d7e3;font-weight:bold">=</span> load_embeddings(EMBEDDINGS_FILE)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith(<span style="color:#a6da95">&#39;.pdf&#39;</span>):
</span></span><span style="display:flex;"><span>            context_passages <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">list</span>(read_pdf(file_path))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> file:
</span></span><span style="display:flex;"><span>                context_passages <span style="color:#91d7e3;font-weight:bold">=</span> file<span style="color:#91d7e3;font-weight:bold">.</span>readlines()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        embeddings <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> passage <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(context_passages):
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> passage<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Skip the passage</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">pass</span>
</span></span><span style="display:flex;"><span>            input_ids <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(passage, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>                output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>                logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>                embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>                embeddings<span style="color:#91d7e3;font-weight:bold">.</span>append(embedding)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Save the embeddings and passages to disk</span>
</span></span><span style="display:flex;"><span>        save_embeddings(embeddings, context_passages, EMBEDDINGS_FILE)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> embeddings, context_passages
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">handle_prompt</span>(user_input, context)<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Instruct: You are a helpful bot who only answers using the given context ONLY. If you cannot find the answer in the context reply &#39;Sorry don&#39;t have that detail&#39;. Given the context &#39;</span><span style="color:#a6da95">{</span>context<span style="color:#a6da95">}</span><span style="color:#a6da95">&#39;, answer this:</span><span style="color:#a6da95">{</span>user_input<span style="color:#a6da95">}</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Output:&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> {name: tensor<span style="color:#91d7e3;font-weight:bold">.</span>to(model<span style="color:#91d7e3;font-weight:bold">.</span>device) <span style="color:#c6a0f6">for</span> name, tensor <span style="color:#91d7e3;font-weight:bold">in</span> inputs<span style="color:#91d7e3;font-weight:bold">.</span>items()}
</span></span><span style="display:flex;"><span>        outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2000</span>, pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove the prompt from the output text</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(prompt, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(<span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_index</span>(query_embedding):
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> faiss<span style="color:#91d7e3;font-weight:bold">.</span>IndexFlatL2(query_embedding[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">.</span>shape[<span style="color:#f5a97f">1</span>])  <span style="color:#6e738d;font-style:italic"># Euclidean distance</span>
</span></span><span style="display:flex;"><span>    faiss<span style="color:#91d7e3;font-weight:bold">.</span>normalize_L2(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add embeddings to the index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> i, item <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(<span style="color:#91d7e3">enumerate</span>(query_embedding), total<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">len</span>(query_embedding)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> item<span style="color:#91d7e3;font-weight:bold">.</span>ndim <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">1</span>:
</span></span><span style="display:flex;"><span>            item <span style="color:#91d7e3;font-weight:bold">=</span> item<span style="color:#91d7e3;font-weight:bold">.</span>reshape(<span style="color:#f5a97f">1</span>, <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)  <span style="color:#6e738d;font-style:italic"># Reshape 1D array to 2D</span>
</span></span><span style="display:flex;"><span>        index<span style="color:#91d7e3;font-weight:bold">.</span>add(item)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> index
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">search_query</span>(input_query, inputTokenizer, model, device, index, context_passages):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Given a new query context, encode it and perform similarity search</span>
</span></span><span style="display:flex;"><span>    input_ids <span style="color:#91d7e3;font-weight:bold">=</span> inputTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(input_query, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> input_ids<span style="color:#91d7e3;font-weight:bold">.</span>long()
</span></span><span style="display:flex;"><span>        output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>        logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>        query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Perform similarity search - top 3 similar passages</span>
</span></span><span style="display:flex;"><span>    _, similar_indices <span style="color:#91d7e3;font-weight:bold">=</span> index<span style="color:#91d7e3;font-weight:bold">.</span>search(query_embedding, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">3</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;DEBUG - Number of similar indices: </span><span style="color:#a6da95">{</span>similar_indices<span style="color:#91d7e3;font-weight:bold">.</span>size<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)    
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Retrieve context passages based on similar_indices</span>
</span></span><span style="display:flex;"><span>    similar_contexts <span style="color:#91d7e3;font-weight:bold">=</span> [context_passages[i] <span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> similar_indices[<span style="color:#f5a97f">0</span>]]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Concatenate the similar contexts into a single string</span>
</span></span><span style="display:flex;"><span>    context <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39; &#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(similar_contexts)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Pass the concatenated context and query to the Phi-2 model</span>
</span></span><span style="display:flex;"><span>    answer <span style="color:#91d7e3;font-weight:bold">=</span> handle_prompt(input_query, context)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Answer:&#34;</span>, answer)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#f4dbd6">__name__</span><span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#a6da95">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    model, tokenizer, device <span style="color:#91d7e3;font-weight:bold">=</span> load_model(MODEL_PATH, MODEL_NAME, DEBUG)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#6e738d;font-style:italic"># Create embeddings and add to index before entering the loop</span>
</span></span><span style="display:flex;"><span>    query_embedding, context_passages <span style="color:#91d7e3;font-weight:bold">=</span> create_embeddings(DATA_FILE, tokenizer, model, device)
</span></span><span style="display:flex;"><span>    query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> np<span style="color:#91d7e3;font-weight:bold">.</span>array(query_embedding)
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> create_index(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    exit_commands <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;exit&#39;</span>, <span style="color:#a6da95">&#39;quit&#39;</span>, <span style="color:#a6da95">&#39;q&#39;</span>, <span style="color:#a6da95">&#39;e&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        query <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Enter your query or &#39;exit&#39; to quit: &#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> query <span style="color:#91d7e3;font-weight:bold">in</span> exit_commands:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>        search_query(query, tokenizer, model, device, index, context_passages)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;-&#34;</span><span style="color:#91d7e3;font-weight:bold">*</span><span style="color:#f5a97f">50</span>)</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Now let us switch gears and try something that pushes the ability of Phi-2.</p>
<h2 id="6-code-generation-example-using-phi-2">6. Code generation example using Phi-2</h2>
<p>If we want to push the boundaries of what Phi-2 can do, we can use it to generate code. Below is an example of using Phi-2 to generate code for a simple C function. 🤓</p>
<p><strong>🗒️Prompt:</strong> &ldquo;Write a program in C that implements a BPE-based tokenizer; it should implement both encoding and decoding functions. Think through this step by step.&rdquo;</p>
<p>The code we see below is what was generated. At face value, it looks like a good start but is incomplete. It is a good starting point for a developer to continue from and shows the power of SLMs like Phi-2.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span><span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;stdio.h&gt; </span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span><span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;string.h&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#define MAX_LEN 100
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Encodes the input text into an array of integers
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#ed8796">int</span><span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#8aadf4">encode</span>(<span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>text, <span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Allocate memory for the tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>str <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">char</span>)<span style="color:#91d7e3;font-weight:bold">*</span>MAX_LEN<span style="color:#91d7e3;font-weight:bold">+</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#ed8796">int</span> i, j<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;</span><span style="color:#8aadf4">strlen</span>(text) <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> text[i] <span style="color:#91d7e3;font-weight:bold">!=</span> <span style="color:#a6da95">&#39;\0&#39;</span>; <span style="color:#91d7e3;font-weight:bold">++</span>i){
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Check if the current character is whitespace or not alphanumeric
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span>(<span style="color:#8aadf4">isspace</span>(text[i]) <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#91d7e3;font-weight:bold">!</span><span style="color:#8aadf4">isalpha</span>(text[i])){
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic">// Add whitespace to the tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>      <span style="color:#8aadf4">strcat</span>(str,<span style="color:#91d7e3;font-weight:bold">&amp;</span>text[i]); 
</span></span><span style="display:flex;"><span>    } <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">while</span> (k<span style="color:#91d7e3;font-weight:bold">&lt;</span>j <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#8aadf4">strcmp</span>(text<span style="color:#91d7e3;font-weight:bold">+</span>i, str <span style="color:#91d7e3;font-weight:bold">+</span> k)<span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#f5a97f">0</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3;font-weight:bold">++</span>k; 
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic">// Check if the token is of length atleast 1
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>      <span style="color:#c6a0f6">if</span>(k<span style="color:#91d7e3;font-weight:bold">-</span>j <span style="color:#91d7e3;font-weight:bold">&gt;=</span> <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">// If yes, concatenate it to tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        <span style="color:#8aadf4">strcat</span>(str,<span style="color:#91d7e3;font-weight:bold">&amp;</span>text[i]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Output the tokenized string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#8aadf4">printf</span>(<span style="color:#a6da95">&#34;Tokenized String: %s</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>,str);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Allocate memory for tokenized string size array
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">int</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">int</span>)<span style="color:#91d7e3;font-weight:bold">*</span>(j<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;</span>j; <span style="color:#91d7e3;font-weight:bold">++</span>i){
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Convert tokens to integer values
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    (<span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize)[i] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#8aadf4">strtol</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>str[i], <span style="color:#91d7e3">NULL</span>, <span style="color:#f5a97f">16</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Decodes the input array of integers into a string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#8aadf4">decode</span>(<span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokensize, <span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Declare memory for decoded string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> str[MAX_LEN] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;*</span>tokensize; <span style="color:#91d7e3;font-weight:bold">++</span>i) {
</span></span><span style="display:flex;"><span>    <span style="color:#8aadf4">strcat</span>(str, <span style="color:#a6da95">&#34;%x&#34;</span>, tokensize[i]);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Output the tokenized string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#8aadf4">printf</span>(<span style="color:#a6da95">&#34;Tokenized String: %s</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>,str);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Declare memory for decoded string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>decodedStr <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">char</span>)<span style="color:#91d7e3;font-weight:bold">*</span>(<span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize)<span style="color:#91d7e3;font-weight:bold">+</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;*</span>tokensize; <span style="color:#91d7e3;font-weight:bold">++</span>i) {
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Convert integer values to hexadecimal strings
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#8aadf4">sprintf</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>decodedStr[i], <span style="color:#a6da95">&#34;%x&#34;</span>, tokensize[i]);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> decodedStr;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>⚠️ Note: It has been a while since I wrote C, but at a high level, these are some of the issues I can see with this; these issues are off the top of my head and are not exhaustive. Finally, it is not meant to test my coding capabilities. 😬</p>
<ol>
<li>Memory Allocation: The <code>malloc</code> function is used without checking for successful allocation; if it returns <code>NULL</code>, which is not checked, we will get hurt.</li>
<li>Tokenization Logic: The logic in the <code>encode</code> function does not reflect the BPE algorithm, which involves merging the most frequent pairs of characters or bytes.</li>
<li>String Concatenation: The <code>strcat</code> function is used incorrectly; instead of a null-terminated string (as part of the second argument), we get a pointer to a single character</li>
<li>Decoding Logic: The <code>decode</code> function attempts to use <code>strcat</code> with a format string (<code>&quot;%x&quot;</code>), which is invalid. The <code>sprintf</code> function should be used for formatted strings.</li>
</ol>
<p>Hopefully, this gives you a good understanding of SLMs, specifically Phi-2, and how to use them locally. 😍</p>
]]></content:encoded>
    </item>
    <item>
      <title>SLMs - How to run Phi-2 Locally, and implement RAG</title>
      <link>/post/2024/03/running-phi2-locally/</link>
      <pubDate>Wed, 13 Mar 2024 00:00:00 +0000</pubDate>
      <guid>/post/2024/03/running-phi2-locally/</guid>
      <description>A primer on small language model (SLM) with big performance</description>
      <content:encoded><![CDATA[<h2 id="1-what-are-small-language-models-slms">1. What are Small Language Models (SLMs)?</h2>
<p>Before diving into running Phi-2 locally, let&rsquo;s take a moment to understand the concept of small language models (SLMs) and their significance in natural language processing (NLP). A SLM is a type of AI model that has been trained on a massive dataset of text but is limited in terms of its size and capabilities compared to a Large Language Model (LLM). SLMs are designed to be more lightweight and efficient, making them suitable for various applications, including chatbots, language translation, and content generation. SLMs are much smaller than LLMs, with fewer parameters and a smaller dataset, so they have a lower computational cost, making them more suitable for edge or resource-constraint devices.</p>
<h2 id="2-what-is-phi-2">2. What is Phi-2?</h2>
<p><a
	
		href = "https://www.microsoft.com/en-us/research/blog/phi-2-the-surprising-power-of-small-language-models/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Phi-2
	</span>
</a> is the latest model in the Phi series of small language models (SLMs) that aim to break the conventional scaling laws of language models. Unlike large language models (LLMs) that require massive amounts of data and compute resources, Phi models are trained on a mixture of web-crawled and synthetic &ldquo;textbook-quality&rdquo; data, following the idea of <a
	
		href = "https://arxiv.org/abs/2306.11644"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Textbooks Are All You Need
	</span>
</a>. Phi models also leverage innovations in model architecture, optimization, and data augmentation to achieve remarkable performance on various benchmarks. &#x1f600;</p>
<p>Phi-2 is twice as large as its predecessor Phi-1.5, and was trained for two weeks on a cluster of 96 A100 GPUs. It demonstrates outstanding reasoning and language understanding capabilities, showcasing state-of-the-art performance (for LLMs &lt;13 billion parameters).</p>
<p>As shown in the figure below on complex benchmarks, Phi-2 matches or outperforms models up to 25x larger, such as Llama-2 and Gemini Nano. Phi-2 is also an ideal playground for researchers, as it allows for exploration around mechanistic interpretability, safety improvements, or fine-tuning experimentation on various tasks. Phi-2 is available in the <a
	
		href = "https://learn.microsoft.com/en-us/azure/ai-studio/what-is-ai-studio?tabs=home"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure AI Studio
	</span>
</a> (via the <a
	
		href = "https://ai.azure.com/explore/models/microsoft-phi-2/version/4/registry/azureml-msr"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		model catalog
	</span>
</a>) and on Hugging Face.</p>
<p><p>

    <figure>
        <img src="images/phi2.png" alt="Phi-2 Evaluation"/>
        <figcaption>Phi-2 Evaluation</figcaption>
    </figure>

</p></p>
<h3 id="21-phi-2s-capabilities">2.1 Phi-2&rsquo;s Capabilities</h3>
<p>What better way to show this other than Some prompts that Phi-2 can handle:</p>
<ol>
<li>
<p><strong>Creative Writing</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Write a short story about a time-traveling computer geek who discovers a secret algorithm book with the power to change computing history.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;In the dimly lit archives of the ancient library, Amelia stumbled upon a dusty tome unlike any other. Its pages whispered secrets of forgotten civilizations and whispered promises of altered destinies. As she traced her fingers over the delicate script, the room seemed to blur, and suddenly, she was standing in the heart of Renaissance Florence. The air smelled of ink and ambition. The book had worked its magic, and Amelia knew she held the fate of empires in her hands.&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Philosophical Reflection</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Discuss the concept of free will versus determinism.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Free will and determinism have long been at odds in philosophical discourse. Some argue that our choices are predetermined by external factors—genetics, upbringing, societal norms—while others champion the idea that we possess true agency. Perhaps the truth lies in the delicate dance between the two. We navigate life&rsquo;s labyrinth, sometimes guided by fate, other times forging our own path.&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Educational Explanation</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Explain the process of photosynthesis in simple terms.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Photosynthesis, the magical dance of plants and sunlight, is how they create their own food. Imagine tiny chlorophyll factories in leaves, capturing sunlight like solar panels. They convert that energy into chemical fuel—glucose—while releasing oxygen as a byproduct. So next time you breathe in that fresh air, thank a leaf for its photosynthetic prowess!&rdquo;</li>
</ul>
</li>
<li>
<p><strong>Humorous Scenario</strong>:</p>
<ul>
<li><strong>🗒️Prompt:</strong> &ldquo;Describe a day in the life of a mischievous squirrel.&rdquo;</li>
<li><strong>🤖 Phi2-Generated output:</strong> &ldquo;Meet Nutty, the squirrel extraordinaire. His mornings begin with acorn heists, followed by elaborate treehouse construction. He&rsquo;s the neighborhood&rsquo;s unofficial alarm clock, chattering loudly at dawn. But his pièce de résistance? Prank-calling the crows, convincing them they&rsquo;ve won a lifetime supply of shiny objects. Nutty&rsquo;s motto: &lsquo;Life&rsquo;s too short not to be a little nuts.&rsquo;&rdquo;</li>
</ul>
</li>
</ol>
<h2 id="3-small-language-models-slms-vs-large-language-models-llms">3. Small Language Models (SLMs) vs Large Language Models (LLMs)</h2>
<p>Large Language Models (LLMs) are a type of AI model that is much larger and more powerful than SLMs. LLMs have hundreds of billions of parameters and are trained on massive text datasets. This gives LLMs the ability to handle complex tasks, such as language generation, translation, and question answering, with high accuracy and fluency. However, LLMs also have some disadvantages. They are larger, making them more expensive and slower to train. They also have a higher computational cost, meaning they may require access to specialized hardware.</p>
<p>On the other hand, SLMs, as we called out, are smaller and more lightweight than LLMs, making them more efficient and cost-effective in training computing resources and inference. While it might seem that SLMs are also more suitable for edge or resource-constrained devices, such as mobile phones or IoT devices, they are small compared to LLMs but still require significant computational resources to run. Phi-2, for example, still has 2.7B parameters, and while it can make inferences on a CPU, it is very slow and impractical for real-time applications. One would need a GPU or a cloud-based service for any realistic use case.</p>
<h3 id="31-when-to-use-slm-vs-llm">3.1 When to use SLM vs LLM?</h3>
<p>Firstly, neither model is inherently better - the choice between an SLM and an LLM depends on the specific application and requirements. SLMs are a good choice when size, cost, and speed are important considerations. LLMs are a better choice when high performance and complex capabilities are required. If a task at hand is quite narrow and in one of the supported languages, then SLMs might be good. However, for a given task, an SLM may be sufficient, but an LLM may be necessary for more complex tasks or tasks requiring high accuracy and fluency.</p>
<p>Furthermore, it is key to understand that it is not necessarily about the number of languages understood but rather the depth and nuance with which each model can understand and generate language. SLMs are designed to be efficient and effective within their scope, which may include a wide range of languages. LLMs like GPT-4, due to their size and complexity, often can understand and generate text in a larger number of languages and with greater nuance.</p>
<p>The choice between an SLM and an LLM would again depend on the specific requirements of the task, including the languages involved and the level of language understanding and generation needed. Using a combination of SLMs and LLMs is common to achieve the best results for a given application.</p>
<h2 id="4-running-phi-2-locally">4. Running Phi-2 locally</h2>
<p>On one hand, running this is simple if you just don&rsquo;t want to program anything and only want to use the model. The easiest option in this case is to use [LM <a
	
		href = "https://lmstudio.ai/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Studio
	</span>
</a>, a web-based platform for running language models. You can use the Hugging Face API to download and run the model.</p>
<p>We use a simple console chat example that runs locally. We use the Hugging Face Transformers library to generate text based on user input. The user can generate a story, a haiku, or a joke on a topic of their choice. Here is how to run it locally on a Windows machine - the same should apply to a Mac or Linux machine.</p>
<p>The full code is below, but here are the key aspects to grok when running Phi-2 locally.</p>
<ul>
<li>The key is to use the <code>AutoModelForCausalLM</code> and <code>AutoTokenizer</code> classes from the <code>transformers</code> library to load the Phi-2 model and tokenizer.</li>
<li>We then use the <code>generate</code> method to generate text based on a user prompt. The <code>generate</code> method takes the user prompt as input and returns the generated text</li>
<li>We use the <code>from_pretrained</code> method to load the model and tokenizer from the Hugging Face model hub.</li>
<li>We also use the <code>save_pretrained</code> method to save the model and tokenizer to a local directory. This allows us to load the model and tokenizer from the local directory if they are already saved, which can help save time and resources.</li>
</ul>
<p>The following code snippet is what loads the model and the tokenizer:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>,
</span></span><span style="display:flex;"><span>                                            torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>,
</span></span><span style="display:flex;"><span>                                            trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>,
</span></span><span style="display:flex;"><span>                                         trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)</span></span></code></pre></div>
<p>And the following is where we encode the user input and call the generation. First, we create tokens of the user prompt; the resulting tokens are returned as PyTorch tensors. Then, the model generates text based on the tokenized input. We cap the tokens to a maximum of 500 tokens, and the end-of-sequence token is used for padding if necessary. Finally, the generated tokens are decoded back into human-readable text.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt,
</span></span><span style="display:flex;"><span>                   return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                   return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                   add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs,
</span></span><span style="display:flex;"><span>                         max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">500</span>,
</span></span><span style="display:flex;"><span>                         pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]</span></span></code></pre></div>
<p>The full code is below. The code is a simple console chat example that runs locally. The user can generate a story, a haiku, or a joke on a topic of their choice.</p>
<p>Some examples of what Phi-2 can generate using the above code are shown below. The first is a story about pandas and dogs.
<p>

    <figure>
        <img src="images/phi2-1.png" alt="Story about Pandas and Dogs"/>
        <figcaption>Story about Pandas 🐼 and Dogs 🐶</figcaption>
    </figure>

</p></p>
<p>Here is another example of a Haiku and a Joke generated by Phi-2 on Pandas.
<p>

    <figure>
        <img src="images/phi2-2.png" alt="Story about Pandas and Dogs"/>
        <figcaption>Haiku and Joke about Pandas 🐼</figcaption>
    </figure>

</p></p>
<p>Switching gears, let&rsquo;s look at how we can implement the RAG using Phi-2.</p>
<h3 id="41-running-phi-2-locally---full-code">4.1 Running Phi-2 Locally - Full Code</h3>
<p>The following code is the complete code that executes the examples we showed before for running Phi-2 locally. This can work on a CPU, but it is very slow, and a good GPU is strongly suggested.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">warnings</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">logging</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoModelForCausalLM, AutoTokenizer
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DEBUG <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Suppress warnings and set the logging level to ERROR</span>
</span></span><span style="display:flex;"><span>warnings<span style="color:#91d7e3;font-weight:bold">.</span>filterwarnings(<span style="color:#a6da95">&#34;ignore&#34;</span>)
</span></span><span style="display:flex;"><span>logging<span style="color:#91d7e3;font-weight:bold">.</span>getLogger(<span style="color:#a6da95">&#34;transformers&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>setLevel(logging<span style="color:#91d7e3;font-weight:bold">.</span>ERROR)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Define the directory where you want to save the model and tokenizer</span>
</span></span><span style="display:flex;"><span>MODEL_PATH <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./local_model&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Check if the model and tokenizer are already saved locally</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(MODEL_PATH):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Loading model and tokenizer from local directory: </span><span style="color:#a6da95">{</span>MODEL_PATH<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the model and tokenizer from the local directory</span>
</span></span><span style="display:flex;"><span>    model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Downloading model and tokenizer from Hugging Face&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>    model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>, torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>    tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(<span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Saving model and tokenizer to local directory: </span><span style="color:#a6da95">{</span>MODEL_PATH<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Save the model and tokenizer locally</span>
</span></span><span style="display:flex;"><span>    model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>    tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(MODEL_PATH)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Model device: </span><span style="color:#a6da95">{</span>model<span style="color:#91d7e3;font-weight:bold">.</span>device<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;CUDA available: </span><span style="color:#a6da95">{</span>torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available()<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Set the default device to CUDA if available, otherwise use CPU</span>
</span></span><span style="display:flex;"><span>device <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>device(<span style="color:#a6da95">&#34;cuda&#34;</span> <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;cpu&#34;</span>)
</span></span><span style="display:flex;"><span>model<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">handle_prompt</span>(user_input, type_of_text)<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Instruct: Write a </span><span style="color:#a6da95">{</span>type_of_text<span style="color:#a6da95">}</span><span style="color:#a6da95"> about </span><span style="color:#a6da95">{</span>user_input<span style="color:#a6da95">}</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Output:&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> {name: tensor<span style="color:#91d7e3;font-weight:bold">.</span>to(model<span style="color:#91d7e3;font-weight:bold">.</span>device) <span style="color:#c6a0f6">for</span> name, tensor <span style="color:#91d7e3;font-weight:bold">in</span> inputs<span style="color:#91d7e3;font-weight:bold">.</span>items()}
</span></span><span style="display:flex;"><span>        outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">500</span>, pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove the prompt from the output text</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(prompt, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(<span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Answer:</span><span style="color:#a6da95">{</span>text<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#f4dbd6">__name__</span><span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#a6da95">&#34;__main__&#34;</span>: 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;First What would you like to write today?&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;1. Story 📝&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;2. Haiku ✍️&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;3. Joke 😆&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;4. Quit 👋&#34;</span>)
</span></span><span style="display:flex;"><span>        user_choice <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Choose an option:&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;4&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        user_prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;And on which topic:&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> user_prompt<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Input cannot be empty or consist only of spaces.&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">continue</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;1&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;story&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;2&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;haiku&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">elif</span> user_choice <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#39;3&#39;</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(handle_prompt(user_prompt, <span style="color:#a6da95">&#39;joke&#39;</span>))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Invalid choice. Please choose a valid option.&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;_&#34;</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">100</span>)</span></span></code></pre></div>
<h2 id="5-implementing-retrieval-augmented-generation-rag-with-phi-2">5. Implementing Retrieval-Augmented Generation (RAG) with Phi-2</h2>
<p>RAG is a powerful technique that combines the strengths of retrieval-based and generation-based approaches to natural language processing. RAG is one of the ways one can get proprietary information and knowledge to the model and use it as part of the prompt.  It leverages a retriever to find relevant context passages and a generator to produce fluent and coherent responses. The retriever identifies relevant context passages, and the generator uses these passages to generate a response.</p>
<p>This approach allows RAG to produce high-quality, informative, and contextually relevant responses. In-context learning is a key feature of RAG, as it allows the model to learn from the context of the conversation and generate more accurate and relevant responses. This is particularly useful in scenarios where the model needs to understand and respond to complex queries or provide detailed information on a specific topic.</p>
<p>At a high level, the process of implementing RAG  involves the following steps:</p>
<ol>
<li><strong>Generate Embeddings with Phi-2</strong>:
<ul>
<li>Use Phi-2 to encode your context passages (documents) and extract their embeddings.</li>
<li>These embeddings will represent the semantic content of each passage.</li>
</ul>
</li>
<li><strong>Create a Vector Index</strong>:
<ul>
<li>Choose a vector index library or framework (such as <strong>Faiss</strong>, <strong>Annoy</strong>, or <strong>HNSW</strong>).</li>
<li>Initialize an index structure to store the embeddings efficiently.</li>
<li>Add the generated embeddings to the index.</li>
</ul>
</li>
<li><strong>Save Embeddings to a Local Vector Database</strong>:
<ul>
<li>Create a local database to store the embeddings.</li>
<li>For each context passage, save its corresponding embedding in the database.</li>
<li>You can use the passage ID or a unique identifier as the key for retrieval.</li>
</ul>
</li>
<li><strong>Perform Similarity Search</strong>:
<ul>
<li>When you receive a new context (query), encode it using Phi-2 to obtain its embedding.</li>
<li>Use the vector index to perform a similarity search against the saved embeddings.</li>
<li>Retrieve the most similar context passages based on cosine similarity or another distance metric.</li>
<li>Return the relevant passages as results.</li>
</ul>
</li>
</ol>
<p>In our example, we will use the FAISS library to create a vector index and perform a similarity search. We will also save the embeddings to a local database for efficient retrieval. <a
	
		href = "https://faiss.ai/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		FAISS (Facebook AI Similarity Search)
	</span>
</a> is a library developed by Facebook for efficient similarity search and clustering of high-dimensional vectors. It allows for a quick nearest-neighbor search over large datasets and supports CPU and GPU-based computations. FAISS is widely used in information retrieval, recommendation systems, and other applications that require similarity search.</p>
<h3 id="51-loading-data-for-rag-and-phi-2">5.1 Loading data for RAG and Phi-2</h3>
<p>To implement RAG, we use the script from the Oppenheimer movie - which is quite new in that it is not in the Phi-2 training set and is available as a PDF. We will extract the script from this PDF, creating embeddings, which will then save the embeddings to a local database and perform a similarity search to retrieve relevant context passages based on a user query. We will use the FAISS library to create a vector index and perform a similarity search. We will also save the embeddings to a local database for efficient retrieval.</p>
<p>We use the <code>PyPDF2</code> library to parse PDFs, a pure Python library for reading and writing PDF files. It can extract text, merge and split documents, and more. We will use it to extract the PDF text from the Oppenheimer movie script. The following code function shows how to read the PDF and extract the text. This is efficient for our use case, but it is not the most efficient way to extract text from a PDF when thinking about production scale, especially if the PDF has a lot of images and tables.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">read_pdf</span>(file_path):
</span></span><span style="display:flex;"><span>    pdf_file_obj <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;rb&#39;</span>)
</span></span><span style="display:flex;"><span>    pdf_reader <span style="color:#91d7e3;font-weight:bold">=</span> PyPDF2<span style="color:#91d7e3;font-weight:bold">.</span>PdfFileReader(pdf_file_obj)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    num_pages <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>numPages
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> page_num <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(num_pages):
</span></span><span style="display:flex;"><span>        page_obj <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>getPage(page_num)
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> page_obj<span style="color:#91d7e3;font-weight:bold">.</span>extractText()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">yield</span> text
</span></span><span style="display:flex;"><span>    pdf_file_obj<span style="color:#91d7e3;font-weight:bold">.</span>close()</span></span></code></pre></div>
<h3 id="52-generate-embeddings-using-phi-2">5.2 Generate embeddings using Phi-2</h3>
<p>Now that we have the text, the following functions show how to create the embeddings using Phi-2. We read the text as a list of context passages and then use Phi-2 to encode each passage and extract its embedding using the <code>encode</code> method.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_embeddings</span>(file_path, tokenizer, model, device):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith(<span style="color:#a6da95">&#39;.pdf&#39;</span>):
</span></span><span style="display:flex;"><span>        context_passages <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">list</span>(read_pdf(file_path))
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> file:
</span></span><span style="display:flex;"><span>            context_passages <span style="color:#91d7e3;font-weight:bold">=</span> file<span style="color:#91d7e3;font-weight:bold">.</span>readlines()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    embeddings <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> passage <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(context_passages):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> passage<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Skip the passage</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">pass</span>
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(passage, 
</span></span><span style="display:flex;"><span>                                    return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                                    add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                                    return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>            output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>            logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>            embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>            embeddings<span style="color:#91d7e3;font-weight:bold">.</span>append(embedding)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> embeddings, context_passages</span></span></code></pre></div>
<p>Here are a few things that are going on:</p>
<ul>
<li>Given that we are using this for inference and not training, we use a <code>torch.</code>no_grad()`, which tells PyTorch not to track, calculate, or modify gradients while executing code within this block. This helps us save the amount of memory needed.</li>
<li>Inside this block, the input_ids are fed into the model, and the output is stored in the output variable. The logits, which are the raw, unnormalized scores outputted by the last layer of the model, are then extracted from the model&rsquo;s output.</li>
<li>The logits are then processed to generate the embedding for the passage. The .mean(dim=1) method calculates the mean of the logits along dimension 1, which typically represents the sequence length in a language model.</li>
<li>The .detach() method detaches the result from the computation graph so that no gradients will be backpropagated along this variable.</li>
<li>The .cpu() method moves the tensor to the CPU if it&rsquo;s not already there. Finally, the tensor is converted to a numpy array using the .numpy() method.</li>
<li>The resulting embedding is then appended to the embeddings list, which contains the embeddings for all the passages.</li>
</ul>
<h3 id="53-creating-vector-index">5.3 Creating Vector Index</h3>
<p>The following function shows how to create a vector index using the FAISS library and perform a similarity search to retrieve relevant context passages based on a user query. The create_index function initializes a flat index structure to store the embeddings and adds the embeddings to the index. The search_query function encodes the user query using Phi-2 to obtain its embedding and performs a similarity search against the saved embeddings to retrieve the most similar context passages.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_index</span>(query_embedding):
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> faiss<span style="color:#91d7e3;font-weight:bold">.</span>IndexFlatL2(query_embedding[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">.</span>shape[<span style="color:#f5a97f">1</span>])  <span style="color:#6e738d;font-style:italic"># Euclidean distance</span>
</span></span><span style="display:flex;"><span>    faiss<span style="color:#91d7e3;font-weight:bold">.</span>normalize_L2(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add embeddings to the index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> i, item <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(<span style="color:#91d7e3">enumerate</span>(query_embedding), total<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">len</span>(query_embedding)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> item<span style="color:#91d7e3;font-weight:bold">.</span>ndim <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">1</span>:
</span></span><span style="display:flex;"><span>            item <span style="color:#91d7e3;font-weight:bold">=</span> item<span style="color:#91d7e3;font-weight:bold">.</span>reshape(<span style="color:#f5a97f">1</span>, <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)  <span style="color:#6e738d;font-style:italic"># Reshape 1D array to 2D</span>
</span></span><span style="display:flex;"><span>        index<span style="color:#91d7e3;font-weight:bold">.</span>add(item)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> index</span></span></code></pre></div>
<p>The <code>normalize_L2()</code> function normalizes the vectors and is a crucial step when using Euclidean distance in high-dimensional spaces to ensure that the distance is not dominated by the dimensionality of the vectors. As we iterate through the embeddings, we check if the item is a 1D array and reshape it to a 2D array if necessary. This is important because FAISS expects the input to be a 2D array, and we need to reshape the 1D array to a 2D array before adding it to the index.</p>
<p>The function finally returns the created index. This index can then be used to perform efficient similarity searches.</p>
<h3 id="54-perform-similarity-search">5.4 Perform Similarity Search</h3>
<p>The following function shows how to perform a similarity search using the vector index to retrieve relevant context passages based on a user query. As noted earlier, the most similar context passages are then retrieved based on cosine similarity or another distance metric.</p>
<p>The function starts by encoding the input query using a tokenizer and performs a similarity search on the FAISS index using the query embedding. It retrieves the indices of the top 3 most similar passages to the input query and then retrieves the corresponding context passages from the context_passages list. The similar context passages are then concatenated into a single string and passed to the Phi-2 model to generate a response.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">search_query</span>(input_query, inputTokenizer, model, device, index, context_passages):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Given a new query context, encode it and perform similarity search</span>
</span></span><span style="display:flex;"><span>    input_ids <span style="color:#91d7e3;font-weight:bold">=</span> inputTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(input_query,
</span></span><span style="display:flex;"><span>                                    return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>,
</span></span><span style="display:flex;"><span>                                    return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>,
</span></span><span style="display:flex;"><span>                                    add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> input_ids<span style="color:#91d7e3;font-weight:bold">.</span>long()
</span></span><span style="display:flex;"><span>        output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>        logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>        query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Perform similarity search - top 3 similar passages</span>
</span></span><span style="display:flex;"><span>    _, similar_indices <span style="color:#91d7e3;font-weight:bold">=</span> index<span style="color:#91d7e3;font-weight:bold">.</span>search(query_embedding, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">3</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Retrieve context passages based on similar_indices</span>
</span></span><span style="display:flex;"><span>    similar_contexts <span style="color:#91d7e3;font-weight:bold">=</span> [context_passages[i] <span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> similar_indices[<span style="color:#f5a97f">0</span>]]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Concatenate the similar contexts into a single string</span>
</span></span><span style="display:flex;"><span>    context <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39; &#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(similar_contexts)</span></span></code></pre></div>
<p>Let us run this and see how it works, as we discussed before. We will use the Oppenheimer movie script as the context passages and perform a similarity search to retrieve relevant context passages based on a user query. The next few figures show the output of us asking questions about the movie, where those pieces of information are not in the model but passed using the semantic search.</p>
<p><p>

    <figure>
        <img src="images/phi2-3.png" alt="Example 1 - Phi-2 and RAG implementation"/>
        <figcaption>Example 1 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/phi2-4.png" alt="Example 2 - Phi-2 and RAG implementation"/>
        <figcaption>Example 2 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/phi2-5.png" alt="Example 3 - Phi-2 and RAG implementation"/>
        <figcaption>Example 3 - Phi-2 and RAG implementation</figcaption>
    </figure>

</p></p>
<p>Now that we have seen the different elements, the code below brings everything together as a console app that one can run. The Oppenheimer script (pdf file) you need can be <a
	
		href = "data/oppenheimer-2023.pdf"
	

	

	>
	
	<span>
		downloaded from here
	</span>
</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">warnings</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">logging</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">torch</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">transformers</span> <span style="color:#8bd5ca">import</span> AutoModelForCausalLM, AutoTokenizer
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">numpy</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">np</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">faiss</span> 
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">tqdm</span> <span style="color:#8bd5ca">import</span> tqdm
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">pickle</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">re</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">PyPDF2</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>DEBUG <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">False</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>warnings<span style="color:#91d7e3;font-weight:bold">.</span>filterwarnings(<span style="color:#a6da95">&#34;ignore&#34;</span>)
</span></span><span style="display:flex;"><span>logging<span style="color:#91d7e3;font-weight:bold">.</span>getLogger(<span style="color:#a6da95">&#34;transformers&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>setLevel(logging<span style="color:#91d7e3;font-weight:bold">.</span>ERROR)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Define the directory where you want to save the model and tokenizer</span>
</span></span><span style="display:flex;"><span>MODEL_PATH <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./local_model&#34;</span>
</span></span><span style="display:flex;"><span>MODEL_NAME <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;microsoft/phi-2&#34;</span>
</span></span><span style="display:flex;"><span>BATCH_SIZE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Oppenheimer movie</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#DATA_FILE = &#34;./oppenheimer-2023.txt&#34;</span>
</span></span><span style="display:flex;"><span>DATA_FILE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./oppenheimer-2023.pdf&#34;</span>
</span></span><span style="display:flex;"><span>EMBEDDINGS_FILE <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;./embeddings_movie.pkl&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">load_model</span>(model_path, model_name, debug<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Check if the model and tokenizer are already saved locally</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(model_path):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Loading model and tokenizer from local directory: </span><span style="color:#a6da95">{</span>model_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load the model and tokenizer from the local directory</span>
</span></span><span style="display:flex;"><span>        model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_path)
</span></span><span style="display:flex;"><span>        tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_path)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Downloading model and tokenizer from Hugging Face&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Download the model and tokenizer from Hugging Face</span>
</span></span><span style="display:flex;"><span>        model <span style="color:#91d7e3;font-weight:bold">=</span> AutoModelForCausalLM<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name, torch_dtype<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;auto&#34;</span>, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>        tokenizer <span style="color:#91d7e3;font-weight:bold">=</span> AutoTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>from_pretrained(model_name, trust_remote_code<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Saving model and tokenizer to local directory: </span><span style="color:#a6da95">{</span>model_path<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Save the model and tokenizer locally</span>
</span></span><span style="display:flex;"><span>        model<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(model_path)
</span></span><span style="display:flex;"><span>        tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>save_pretrained(model_path)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> debug:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Model device: </span><span style="color:#a6da95">{</span>model<span style="color:#91d7e3;font-weight:bold">.</span>device<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;CUDA available: </span><span style="color:#a6da95">{</span>torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available()<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the default device to CUDA if available, otherwise use CPU</span>
</span></span><span style="display:flex;"><span>    device <span style="color:#91d7e3;font-weight:bold">=</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>device(<span style="color:#a6da95">&#34;cuda&#34;</span> <span style="color:#c6a0f6">if</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>cuda<span style="color:#91d7e3;font-weight:bold">.</span>is_available() <span style="color:#c6a0f6">else</span> <span style="color:#a6da95">&#34;cpu&#34;</span>)
</span></span><span style="display:flex;"><span>    model<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> model, tokenizer, device
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">save_embeddings</span>(embeddings, passages, file_name):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_name, <span style="color:#a6da95">&#39;wb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            pickle<span style="color:#91d7e3;font-weight:bold">.</span>dump((<span style="color:#91d7e3">list</span>(embeddings), <span style="color:#91d7e3">list</span>(passages)), f)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">IOError</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Error writing to file </span><span style="color:#a6da95">{</span>file_name<span style="color:#a6da95">}</span><span style="color:#a6da95">.&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>PicklingError:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Error pickling embeddings and passages.&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">load_embeddings</span>(file):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>load(f)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">FileNotFoundError</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;File </span><span style="color:#a6da95">{</span>file<span style="color:#a6da95">}</span><span style="color:#a6da95"> not found.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span> pickle<span style="color:#91d7e3;font-weight:bold">.</span>UnpicklingError:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Error unpickling file </span><span style="color:#a6da95">{</span>file<span style="color:#a6da95">}</span><span style="color:#a6da95">.&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">None</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">read_pdf</span>(file_path):
</span></span><span style="display:flex;"><span>    pdf_file_obj <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;rb&#39;</span>)
</span></span><span style="display:flex;"><span>    pdf_reader <span style="color:#91d7e3;font-weight:bold">=</span> PyPDF2<span style="color:#91d7e3;font-weight:bold">.</span>PdfFileReader(pdf_file_obj)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    num_pages <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>numPages
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> page_num <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(num_pages):
</span></span><span style="display:flex;"><span>        page_obj <span style="color:#91d7e3;font-weight:bold">=</span> pdf_reader<span style="color:#91d7e3;font-weight:bold">.</span>getPage(page_num)
</span></span><span style="display:flex;"><span>        text <span style="color:#91d7e3;font-weight:bold">=</span> page_obj<span style="color:#91d7e3;font-weight:bold">.</span>extractText()
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">yield</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Finished reading file. Number pages: </span><span style="color:#a6da95">{</span>num_pages<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    pdf_file_obj<span style="color:#91d7e3;font-weight:bold">.</span>close()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_embeddings</span>(file_path, tokenizer, model, device):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> os<span style="color:#91d7e3;font-weight:bold">.</span>path<span style="color:#91d7e3;font-weight:bold">.</span>exists(EMBEDDINGS_FILE):
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Load the embeddings and passages from disk</span>
</span></span><span style="display:flex;"><span>        embeddings, context_passages <span style="color:#91d7e3;font-weight:bold">=</span> load_embeddings(EMBEDDINGS_FILE)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> file_path<span style="color:#91d7e3;font-weight:bold">.</span>endswith(<span style="color:#a6da95">&#39;.pdf&#39;</span>):
</span></span><span style="display:flex;"><span>            context_passages <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">list</span>(read_pdf(file_path))
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(file_path, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;utf-8&#39;</span>) <span style="color:#c6a0f6">as</span> file:
</span></span><span style="display:flex;"><span>                context_passages <span style="color:#91d7e3;font-weight:bold">=</span> file<span style="color:#91d7e3;font-weight:bold">.</span>readlines()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        embeddings <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> passage <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(context_passages):
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> passage<span style="color:#91d7e3;font-weight:bold">.</span>strip():
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Skip the passage</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">pass</span>
</span></span><span style="display:flex;"><span>            input_ids <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(passage, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>                output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>                logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>                embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>                embeddings<span style="color:#91d7e3;font-weight:bold">.</span>append(embedding)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Save the embeddings and passages to disk</span>
</span></span><span style="display:flex;"><span>        save_embeddings(embeddings, context_passages, EMBEDDINGS_FILE)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> embeddings, context_passages
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">handle_prompt</span>(user_input, context)<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#91d7e3">str</span>:
</span></span><span style="display:flex;"><span>    prompt <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;Instruct: You are a helpful bot who only answers using the given context ONLY. If you cannot find the answer in the context reply &#39;Sorry don&#39;t have that detail&#39;. Given the context &#39;</span><span style="color:#a6da95">{</span>context<span style="color:#a6da95">}</span><span style="color:#a6da95">&#39;, answer this:</span><span style="color:#a6da95">{</span>user_input<span style="color:#a6da95">}</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Output:&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer(prompt, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)
</span></span><span style="display:flex;"><span>        inputs <span style="color:#91d7e3;font-weight:bold">=</span> {name: tensor<span style="color:#91d7e3;font-weight:bold">.</span>to(model<span style="color:#91d7e3;font-weight:bold">.</span>device) <span style="color:#c6a0f6">for</span> name, tensor <span style="color:#91d7e3;font-weight:bold">in</span> inputs<span style="color:#91d7e3;font-weight:bold">.</span>items()}
</span></span><span style="display:flex;"><span>        outputs <span style="color:#91d7e3;font-weight:bold">=</span> model<span style="color:#91d7e3;font-weight:bold">.</span>generate(<span style="color:#91d7e3;font-weight:bold">**</span>inputs, max_length<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2000</span>, pad_token_id<span style="color:#91d7e3;font-weight:bold">=</span>tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>eos_token_id)
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> tokenizer<span style="color:#91d7e3;font-weight:bold">.</span>batch_decode(outputs)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Remove the prompt from the output text</span>
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(prompt, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    text <span style="color:#91d7e3;font-weight:bold">=</span> text<span style="color:#91d7e3;font-weight:bold">.</span>replace(<span style="color:#a6da95">&#34;&lt;|endoftext|&gt;&#34;</span>, <span style="color:#a6da95">&#39;&#39;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>strip()
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">create_index</span>(query_embedding):
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> faiss<span style="color:#91d7e3;font-weight:bold">.</span>IndexFlatL2(query_embedding[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">.</span>shape[<span style="color:#f5a97f">1</span>])  <span style="color:#6e738d;font-style:italic"># Euclidean distance</span>
</span></span><span style="display:flex;"><span>    faiss<span style="color:#91d7e3;font-weight:bold">.</span>normalize_L2(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add embeddings to the index</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> i, item <span style="color:#91d7e3;font-weight:bold">in</span> tqdm(<span style="color:#91d7e3">enumerate</span>(query_embedding), total<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#91d7e3">len</span>(query_embedding)):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> item<span style="color:#91d7e3;font-weight:bold">.</span>ndim <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">1</span>:
</span></span><span style="display:flex;"><span>            item <span style="color:#91d7e3;font-weight:bold">=</span> item<span style="color:#91d7e3;font-weight:bold">.</span>reshape(<span style="color:#f5a97f">1</span>, <span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>)  <span style="color:#6e738d;font-style:italic"># Reshape 1D array to 2D</span>
</span></span><span style="display:flex;"><span>        index<span style="color:#91d7e3;font-weight:bold">.</span>add(item)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> index
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">search_query</span>(input_query, inputTokenizer, model, device, index, context_passages):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Given a new query context, encode it and perform similarity search</span>
</span></span><span style="display:flex;"><span>    input_ids <span style="color:#91d7e3;font-weight:bold">=</span> inputTokenizer<span style="color:#91d7e3;font-weight:bold">.</span>encode(input_query, return_tensors<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;pt&#34;</span>, return_attention_mask<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>, add_special_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">False</span>)<span style="color:#91d7e3;font-weight:bold">.</span>to(device)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> torch<span style="color:#91d7e3;font-weight:bold">.</span>no_grad():
</span></span><span style="display:flex;"><span>        input_ids <span style="color:#91d7e3;font-weight:bold">=</span> input_ids<span style="color:#91d7e3;font-weight:bold">.</span>long()
</span></span><span style="display:flex;"><span>        output <span style="color:#91d7e3;font-weight:bold">=</span> model(input_ids)
</span></span><span style="display:flex;"><span>        logits <span style="color:#91d7e3;font-weight:bold">=</span> output<span style="color:#91d7e3;font-weight:bold">.</span>logits
</span></span><span style="display:flex;"><span>        query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> logits<span style="color:#91d7e3;font-weight:bold">.</span>mean(dim<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>)<span style="color:#91d7e3;font-weight:bold">.</span>detach()<span style="color:#91d7e3;font-weight:bold">.</span>cpu()<span style="color:#91d7e3;font-weight:bold">.</span>numpy()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Perform similarity search - top 3 similar passages</span>
</span></span><span style="display:flex;"><span>    _, similar_indices <span style="color:#91d7e3;font-weight:bold">=</span> index<span style="color:#91d7e3;font-weight:bold">.</span>search(query_embedding, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">3</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> DEBUG:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;DEBUG - Number of similar indices: </span><span style="color:#a6da95">{</span>similar_indices<span style="color:#91d7e3;font-weight:bold">.</span>size<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)    
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Retrieve context passages based on similar_indices</span>
</span></span><span style="display:flex;"><span>    similar_contexts <span style="color:#91d7e3;font-weight:bold">=</span> [context_passages[i] <span style="color:#c6a0f6">for</span> i <span style="color:#91d7e3;font-weight:bold">in</span> similar_indices[<span style="color:#f5a97f">0</span>]]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Concatenate the similar contexts into a single string</span>
</span></span><span style="display:flex;"><span>    context <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39; &#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>join(similar_contexts)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Pass the concatenated context and query to the Phi-2 model</span>
</span></span><span style="display:flex;"><span>    answer <span style="color:#91d7e3;font-weight:bold">=</span> handle_prompt(input_query, context)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Answer:&#34;</span>, answer)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#f4dbd6">__name__</span><span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#a6da95">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    model, tokenizer, device <span style="color:#91d7e3;font-weight:bold">=</span> load_model(MODEL_PATH, MODEL_NAME, DEBUG)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>     <span style="color:#6e738d;font-style:italic"># Create embeddings and add to index before entering the loop</span>
</span></span><span style="display:flex;"><span>    query_embedding, context_passages <span style="color:#91d7e3;font-weight:bold">=</span> create_embeddings(DATA_FILE, tokenizer, model, device)
</span></span><span style="display:flex;"><span>    query_embedding <span style="color:#91d7e3;font-weight:bold">=</span> np<span style="color:#91d7e3;font-weight:bold">.</span>array(query_embedding)
</span></span><span style="display:flex;"><span>    index <span style="color:#91d7e3;font-weight:bold">=</span> create_index(query_embedding)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    exit_commands <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#39;exit&#39;</span>, <span style="color:#a6da95">&#39;quit&#39;</span>, <span style="color:#a6da95">&#39;q&#39;</span>, <span style="color:#a6da95">&#39;e&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span> <span style="color:#f5a97f">True</span>:
</span></span><span style="display:flex;"><span>        query <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Enter your query or &#39;exit&#39; to quit: &#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> query <span style="color:#91d7e3;font-weight:bold">in</span> exit_commands:
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">break</span>
</span></span><span style="display:flex;"><span>        search_query(query, tokenizer, model, device, index, context_passages)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;-&#34;</span><span style="color:#91d7e3;font-weight:bold">*</span><span style="color:#f5a97f">50</span>)</span></span></code></pre></div>
<p>Now let us switch gears and try something that pushes the ability of Phi-2.</p>
<h2 id="6-code-generation-example-using-phi-2">6. Code generation example using Phi-2</h2>
<p>If we want to push the boundaries of what Phi-2 can do, we can use it to generate code. Below is an example of using Phi-2 to generate code for a simple C function. 🤓</p>
<p><strong>🗒️Prompt:</strong> &ldquo;Write a program in C that implements a BPE-based tokenizer; it should implement both encoding and decoding functions. Think through this step by step.&rdquo;</p>
<p>The code we see below is what was generated. At face value, it looks like a good start but is incomplete. It is a good starting point for a developer to continue from and shows the power of SLMs like Phi-2.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span><span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;stdio.h&gt; </span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span><span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;string.h&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#define MAX_LEN 100
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Encodes the input text into an array of integers
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#ed8796">int</span><span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#8aadf4">encode</span>(<span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>text, <span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Allocate memory for the tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>str <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">char</span>)<span style="color:#91d7e3;font-weight:bold">*</span>MAX_LEN<span style="color:#91d7e3;font-weight:bold">+</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#ed8796">int</span> i, j<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>, k<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;</span><span style="color:#8aadf4">strlen</span>(text) <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> text[i] <span style="color:#91d7e3;font-weight:bold">!=</span> <span style="color:#a6da95">&#39;\0&#39;</span>; <span style="color:#91d7e3;font-weight:bold">++</span>i){
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Check if the current character is whitespace or not alphanumeric
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">if</span>(<span style="color:#8aadf4">isspace</span>(text[i]) <span style="color:#91d7e3;font-weight:bold">||</span> <span style="color:#91d7e3;font-weight:bold">!</span><span style="color:#8aadf4">isalpha</span>(text[i])){
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic">// Add whitespace to the tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>      <span style="color:#8aadf4">strcat</span>(str,<span style="color:#91d7e3;font-weight:bold">&amp;</span>text[i]); 
</span></span><span style="display:flex;"><span>    } <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">while</span> (k<span style="color:#91d7e3;font-weight:bold">&lt;</span>j <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#8aadf4">strcmp</span>(text<span style="color:#91d7e3;font-weight:bold">+</span>i, str <span style="color:#91d7e3;font-weight:bold">+</span> k)<span style="color:#91d7e3;font-weight:bold">==</span><span style="color:#f5a97f">0</span>){
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3;font-weight:bold">++</span>k; 
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic">// Check if the token is of length atleast 1
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>      <span style="color:#c6a0f6">if</span>(k<span style="color:#91d7e3;font-weight:bold">-</span>j <span style="color:#91d7e3;font-weight:bold">&gt;=</span> <span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">// If yes, concatenate it to tokens list
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        <span style="color:#8aadf4">strcat</span>(str,<span style="color:#91d7e3;font-weight:bold">&amp;</span>text[i]);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Output the tokenized string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#8aadf4">printf</span>(<span style="color:#a6da95">&#34;Tokenized String: %s</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>,str);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Allocate memory for tokenized string size array
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">int</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">int</span>)<span style="color:#91d7e3;font-weight:bold">*</span>(j<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">1</span>));
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;</span>j; <span style="color:#91d7e3;font-weight:bold">++</span>i){
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Convert tokens to integer values
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    (<span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize)[i] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#8aadf4">strtol</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>str[i], <span style="color:#91d7e3">NULL</span>, <span style="color:#f5a97f">16</span>);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// Decodes the input array of integers into a string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#8aadf4">decode</span>(<span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokensize, <span style="color:#ed8796">int</span> <span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize){
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Declare memory for decoded string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> str[MAX_LEN] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;*</span>tokensize; <span style="color:#91d7e3;font-weight:bold">++</span>i) {
</span></span><span style="display:flex;"><span>    <span style="color:#8aadf4">strcat</span>(str, <span style="color:#a6da95">&#34;%x&#34;</span>, tokensize[i]);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Output the tokenized string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#8aadf4">printf</span>(<span style="color:#a6da95">&#34;Tokenized String: %s</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>,str);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">// Declare memory for decoded string
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>  <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>decodedStr <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span>)<span style="color:#8aadf4">malloc</span>(<span style="color:#c6a0f6">sizeof</span>(<span style="color:#ed8796">char</span>)<span style="color:#91d7e3;font-weight:bold">*</span>(<span style="color:#91d7e3;font-weight:bold">*</span>tokenizedStringsSize)<span style="color:#91d7e3;font-weight:bold">+</span><span style="color:#f5a97f">1</span>);
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> (i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&lt;*</span>tokensize; <span style="color:#91d7e3;font-weight:bold">++</span>i) {
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Convert integer values to hexadecimal strings
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#8aadf4">sprintf</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>decodedStr[i], <span style="color:#a6da95">&#34;%x&#34;</span>, tokensize[i]);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> decodedStr;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<p>⚠️ Note: It has been a while since I wrote C, but at a high level, these are some of the issues I can see with this; these issues are off the top of my head and are not exhaustive. Finally, it is not meant to test my coding capabilities. 😬</p>
<ol>
<li>Memory Allocation: The <code>malloc</code> function is used without checking for successful allocation; if it returns <code>NULL</code>, which is not checked, we will get hurt.</li>
<li>Tokenization Logic: The logic in the <code>encode</code> function does not reflect the BPE algorithm, which involves merging the most frequent pairs of characters or bytes.</li>
<li>String Concatenation: The <code>strcat</code> function is used incorrectly; instead of a null-terminated string (as part of the second argument), we get a pointer to a single character</li>
<li>Decoding Logic: The <code>decode</code> function attempts to use <code>strcat</code> with a format string (<code>&quot;%x&quot;</code>), which is invalid. The <code>sprintf</code> function should be used for formatted strings.</li>
</ol>
<p>Hopefully, this gives you a good understanding of SLMs, specifically Phi-2, and how to use them locally. 😍</p>
]]></content:encoded>
    </item>
    <item>
      <title>📚 My new book &#34;Generative AI in Action&#34;</title>
      <link>/post/2023/11/announcing-gen-ai-book/</link>
      <pubDate>Tue, 14 Nov 2023 00:00:00 +0000</pubDate>
      <guid>/post/2023/11/announcing-gen-ai-book/</guid>
      <description>🚀 Early access to my new book covering real-world practical guidance for enterprises now available  on MEAP 📚</description>
      <content:encoded><![CDATA[<p>🌐 As software continues to revolutionize the world, the advent of Generative AI is transforming the very fabric of software itself. My latest book, <strong>Generative AI in Action</strong> delves into this transformative journey.</p>
<p>I am thrilled to announce the early release of my latest book, <a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Generative AI in Action
	</span>
</a> now available through <a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Manning Early Access Program (MEAP)
	</span>
</a>. This publication is a deep dive into the cutting-edge world of #GenerativeAI, #LLMs, #OpenAI, and #Azure #OpenAI, tailored specifically for enterprises. 🤘</p>
<p>This practical, in-action, hands-on book allows one to explore the cutting-edge world of Generative AI, including LLMs, and covers both OpenAI and Azure OpenAI, allowing companies to understand basic concepts and scale to production.📘</p>
<p>I have the privilege of having a front-row seat as we build this tech out and work with key Fortune 500 customers who are incorporating this. A lot of this is learnings captured from this. 😇</p>
<p>📖 Inside the Book</p>
<ul>
<li>A comprehensive introduction to Generative AI, including foundational models like GPT, Codex, DALLE, and ChatGPT.</li>
<li>Insightful discussions on Large Language Models (LLMs) and their applications in various sectors.</li>
<li>Practical guides on generating text through APIs, focusing on OpenAI and Azure OpenAI.</li>
<li>Exploration of image generation techniques, including Stable Diffusion and DALLE.</li>
<li>Deep dive into Prompt Engineering, RAG, Bring your own Data, and model adaptation techniques.</li>
<li>And finally, best practices to allow enterprises to scale to production.</li>
</ul>
<p>🎯 Why This Book is a Must-Read?
It is specially crafted for businesses leveraging AI for innovation and competitive advantage.
Combining technical depth with practical applications makes it a valuable asset for decision-makers, architects, data scientists, developers, and AI enthusiasts.</p>
<p>🌟 Highlights:</p>
<ul>
<li>Real-world case studies and applications.</li>
<li>Expert guidance on model adaptation and the art of prompt engineering.</li>
<li>Comprehensive insights into the challenges and best practices for deploying AI in enterprise settings.</li>
<li>An essential discussion on AI&rsquo;s ethical dimensions, safety, and security concerns.</li>
<li>Generative AI in Action&quot; is more than just a book; it&rsquo;s a roadmap for harnessing the potential of AI to redefine the business landscape.</li>
</ul>
<p>Ready to get hands-on and deeper into Generative AI? You can use the code “<strong>mlbahree</strong>” to get 45% off – valid until Nov 28th! 😍</p>
<p>🔗 You can get the book and the discount from <a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		here
	</span>
</a> &ndash;&gt; <a
	
		href = "http://mng.bz/D9By"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		http://mng.bz/D9By
	</span>
</a> &#x1f43c;</p>
<p>💽 The GitHub repo with the code from the book can be found at &ndash;&gt; <a
	
		href = "https://bit.ly/GenAIBook"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://bit.ly/GenAIBook
	</span>
</a>. 💾</p>
<p><p>

    <figure>
        <img src="images/bahree_genai_in_action_meap.jpg" alt="Generative AI in Action - MEAP"/>
        <figcaption>Generative AI in Action - MEAP</figcaption>
    </figure>

</p></p>
<p>Thank you in advance, and I would love to hear your feedback.</p>
<p>With gratitude, 💚</p>
<p>Amit.</p>
<p>#GenerativeAI #LLMs #Azure #OpenAI #Microsoft #LearnwithManning #ManningBooks</p>
]]></content:encoded>
    </item>
    <item>
      <title>AI working with humans</title>
      <link>/post/2023/07/ai-working-with-humans/</link>
      <pubDate>Sat, 08 Jul 2023 00:00:00 +0000</pubDate>
      <guid>/post/2023/07/ai-working-with-humans/</guid>
      <description>AI-generated memes that outline their experience working with humans</description>
      <content:encoded><![CDATA[<h4 id="what-does-ai-think-of-humans-and-our-humor">What does AI think of humans and our humor?</h4>
<p>I asked #AI - #CodeInterpreter plugin for #ChatGPT to express its experience as an AI working with humans as a meme. It wrote this code to create the meme below.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">matplotlib.pyplot</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">plt</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">numpy</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">np</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Create a new figure with a white background</span>
</span></span><span style="display:flex;"><span>fig, ax <span style="color:#91d7e3;font-weight:bold">=</span> plt<span style="color:#91d7e3;font-weight:bold">.</span>subplots(figsize<span style="color:#91d7e3;font-weight:bold">=</span>(<span style="color:#f5a97f">6</span>, <span style="color:#f5a97f">6</span>), dpi<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">80</span>)
</span></span><span style="display:flex;"><span>fig<span style="color:#91d7e3;font-weight:bold">.</span>patch<span style="color:#91d7e3;font-weight:bold">.</span>set_facecolor(<span style="color:#a6da95">&#39;white&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Set up the text and meme caption</span>
</span></span><span style="display:flex;"><span>meme_caption <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;When I, an AI, try to understand human humor:&#34;</span>
</span></span><span style="display:flex;"><span>meme_text <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;Error 404: Humor not found&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Add the meme caption and text to the figure</span>
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>text(<span style="color:#f5a97f">0.5</span>, <span style="color:#f5a97f">0.7</span>, meme_caption, horizontalalignment<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, verticalalignment<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, transform<span style="color:#91d7e3;font-weight:bold">=</span>ax<span style="color:#91d7e3;font-weight:bold">.</span>transAxes, color<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, fontsize<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">14</span>, fontweight<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;bold&#39;</span>)
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>text(<span style="color:#f5a97f">0.5</span>, <span style="color:#f5a97f">0.3</span>, meme_text, horizontalalignment<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, verticalalignment<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, transform<span style="color:#91d7e3;font-weight:bold">=</span>ax<span style="color:#91d7e3;font-weight:bold">.</span>transAxes, color<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, fontsize<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">16</span>, fontweight<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;bold&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Remove the axes for a clean meme look</span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>axis(<span style="color:#a6da95">&#39;off&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Show the meme</span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>show()</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/humans.png" alt="AI&rsquo;s experience working with humans"/>
        <figcaption>AI&#39;s experience working with humans</figcaption>
    </figure>

</p></p>
<p>OK, that is funny.  I asked it to create another meme about its own experience as an AI. It wrote this code to create the meme below.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Let&#39;s use matplotlib to create a simple meme. </span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># We&#39;ll use a cartoon robot image (created from basic shapes) and add a text that relates to my AI experience.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">matplotlib.pyplot</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">plt</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">matplotlib.patches</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">patches</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Create a new figure with a white background</span>
</span></span><span style="display:flex;"><span>fig, ax <span style="color:#91d7e3;font-weight:bold">=</span> plt<span style="color:#91d7e3;font-weight:bold">.</span>subplots(figsize<span style="color:#91d7e3;font-weight:bold">=</span>(<span style="color:#f5a97f">8</span>, <span style="color:#f5a97f">8</span>), dpi<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">100</span>)
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>axis(<span style="color:#a6da95">&#39;off&#39;</span>)  <span style="color:#6e738d;font-style:italic"># Turn off the axes</span>
</span></span><span style="display:flex;"><span>fig<span style="color:#91d7e3;font-weight:bold">.</span>patch<span style="color:#91d7e3;font-weight:bold">.</span>set_facecolor(<span style="color:#a6da95">&#39;white&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Draw a robot face</span>
</span></span><span style="display:flex;"><span>face <span style="color:#91d7e3;font-weight:bold">=</span> patches<span style="color:#91d7e3;font-weight:bold">.</span>Circle((<span style="color:#f5a97f">0.5</span>, <span style="color:#f5a97f">0.6</span>), radius<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.3</span>, fc<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;gray&#39;</span>, ec<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, lw<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>)
</span></span><span style="display:flex;"><span>eye1 <span style="color:#91d7e3;font-weight:bold">=</span> patches<span style="color:#91d7e3;font-weight:bold">.</span>Circle((<span style="color:#f5a97f">0.4</span>, <span style="color:#f5a97f">0.7</span>), radius<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.05</span>, fc<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;white&#39;</span>, ec<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, lw<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>)
</span></span><span style="display:flex;"><span>eye2 <span style="color:#91d7e3;font-weight:bold">=</span> patches<span style="color:#91d7e3;font-weight:bold">.</span>Circle((<span style="color:#f5a97f">0.6</span>, <span style="color:#f5a97f">0.7</span>), radius<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.05</span>, fc<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;white&#39;</span>, ec<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, lw<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>)
</span></span><span style="display:flex;"><span>mouth <span style="color:#91d7e3;font-weight:bold">=</span> patches<span style="color:#91d7e3;font-weight:bold">.</span>Rectangle((<span style="color:#f5a97f">0.4</span>, <span style="color:#f5a97f">0.55</span>), <span style="color:#f5a97f">0.2</span>, <span style="color:#f5a97f">0.02</span>, fc<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;white&#39;</span>, ec<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;black&#39;</span>, lw<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">2</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>add_patch(face)
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>add_patch(eye1)
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>add_patch(eye2)
</span></span><span style="display:flex;"><span>ax<span style="color:#91d7e3;font-weight:bold">.</span>add_patch(mouth)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Add meme text</span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>text(<span style="color:#f5a97f">0.5</span>, <span style="color:#f5a97f">0.85</span>, <span style="color:#a6da95">&#39;AI life:&#39;</span>, ha<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, va<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, fontsize<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">20</span>, weight<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;bold&#39;</span>)
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>text(<span style="color:#f5a97f">0.5</span>, <span style="color:#f5a97f">0.1</span>, <span style="color:#a6da95">&#34;When humans ask me to </span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">understand a joke&#34;</span>, 
</span></span><span style="display:flex;"><span>         ha<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, va<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;center&#39;</span>, fontsize<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">20</span>, wrap<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>show()</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Top text: &ldquo;AI life:&rdquo;
Bottom text: &ldquo;When humans ask me to understand a joke&rdquo;</p>
<p><p>

    <figure>
        <img src="images/ai-life.png" alt="AI Life"/>
        <figcaption>AI&#39; Life</figcaption>
    </figure>

</p></p>
<p>The joke here is that as an AI system, I don&rsquo;t have emotions or personal experiences, and humor often relies on these aspects, which can make understanding and generating spirit challenging for AI. I hope you find it amusing!</p>
<p><em>AI seeks to find,
Humor in human design,
Error, not aligned.</em></p>
]]></content:encoded>
    </item>
    <item>
      <title>OpenAI&#39;s Whisper speech model - an overview</title>
      <link>/post/2023/02/openai-whisper-overview/</link>
      <pubDate>Tue, 28 Feb 2023 00:00:00 +0000</pubDate>
      <guid>/post/2023/02/openai-whisper-overview/</guid>
      <description>A lap around OpenAI&amp;#39;s Whisper speech model and examples on how to use it for transcription.</description>
      <content:encoded><![CDATA[<h2 id="what-is-whisper-from-openai">What is Whisper from OpenAI?</h2>
<p>Whisper is a speech recognition model (ASR &ndash; automatic speech recognition) from OpenAI. The model itself is multi-task model and as a result in addition to speech recognition, can also do language identification and speech translation across a number of languages. The model is open sourced and it comes in 5 sizes. Of these, 4 have a english-only variant which seem to perform better if one only needs english. The model is also robust to noise, accents, background noise and technical language. <a
	
		href = "https://openai.com/research/whisper"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Whisper
	</span>
</a> achieves near SOTA performance with zero-shot translation from multiple-languages to English.</p>
<h4 id="model-characteristics">Model Characteristics</h4>
<p>The model was trained on a large corpus of data and was trained using weak supervision using large scale noise data. Of this large data corpus ~680K hours of audio and corresponding transcripts; ~438K hours (65%) of this data is english only (both audio and transcripts); ~126K hours (18%) is non-english audio and english transcripts; and finally ~117K hours (17%) is non-english audio and non-english transcripts and cover 98 languages.</p>
<p>The model is available in multiple sizes as called out and the table below outlines these model characteristics.</p>
<table>
  <thead>
      <tr>
          <th>Size</th>
          <th>Parameters</th>
          <th>English-only model</th>
          <th>Multilingual-model</th>
          <th>VRAM needed</th>
          <th>Speed (Relative)</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>base</td>
          <td>74 m</td>
          <td><code>base.en</code></td>
          <td><code>base</code></td>
          <td>~ 1gb</td>
          <td>16x</td>
      </tr>
      <tr>
          <td>tiny</td>
          <td>39 m</td>
          <td><code>tiny.en</code></td>
          <td><code>tiny</code></td>
          <td>~ 1gb</td>
          <td>32x</td>
      </tr>
      <tr>
          <td>small</td>
          <td>244 m</td>
          <td><code>small.en</code></td>
          <td><code>small</code></td>
          <td>~ 2gb</td>
          <td>6x</td>
      </tr>
      <tr>
          <td>medium</td>
          <td>769 m</td>
          <td><code>medium.en</code></td>
          <td><code>medium</code></td>
          <td>~ 5gb</td>
          <td>2x</td>
      </tr>
      <tr>
          <td>large</td>
          <td>1.55 b</td>
          <td>n/a</td>
          <td><code>large</code></td>
          <td>~ 10gb</td>
          <td>1x</td>
      </tr>
  </tbody>
</table>
<p>Whisper does support transcription and translation across 98 language; it performs best when sticking with English. One needs to be careful when using the non-english models as the transcripts are not in the same language as the audio and can lead to hallucinations. The large model has a word error rate (WER) of 0.12 for English, 0.18 for Spanish, 0.23 for French, 0.25 for German and 0.28 for Mandarin2. However, some lower covered languages have much higher WER - e.g. Arabic (0.79), Hindi (0.86) and Swahili (1.00).</p>
<h3 id="whisper-asr-architecture">Whisper ASR Architecture</h3>
<p>As it is typical for language based models, Whisper uses a <a
	
		href = "https://en.wikipedia.org/wiki/Seq2seq"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		seq-to-seq (transformer encoder-decoder)
	</span>
</a> architecture, where the input is a sequence of audio frames (30 sec segment pairs) and the output is a sequence of text. Whisper is best used to transcribe &ldquo;audio to text&rdquo; use cases. It is not well suited for &ldquo;text to audio&rdquo; (i.e., TTS &ndash; text to speech) cases as it is not trained for this task. Whisper is also not trained for speech synthesis, but can be used to generate text from audio. And finally, Whisper cannot be used for real-time speech applications and is best used for batch processing.</p>
<p>The figure below shows the Whisper ASR architecture (image credit: OpenAI); the transformer model is training on many different speech-related tasks including speech recognition, language identification, and voice activity detection - these collectively represent the sequence of tokens for the decoder to predict and greatly simplifies things by allowing one model to replace many tradition speech processing pipelines.</p>
<p><p>

    <figure>
        <img src="images/whisper-1.svg" alt="Whisper ASR Architecture"/>
        <figcaption>Whisper ASR Architecture</figcaption>
    </figure>

</p></p>
<p>What I think is really interesting about the Whisper model is that it is trained using <strong>weak supervision</strong>. OpenAI took a different approach for speech recognition and not use the typical self-supervision or self-training techniques that have been a mainstay of recent large-scale speech recognition work. I believe this is what makes the model so robust and able to handle noise, accents, background noise and technical language. OpenAI trained Whisper to predict raw text of transcripts, using the expressiveness provided by the seq-2-seq implementation to learn the mapping between utterances and their transcripts. All of this allows a simpler pipeline.</p>
<p>More details on the Speech Recognition model can be found in the OpenAI Whisper paper <a
	
		href = "https://arxiv.org/abs/2212.04356"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Robust Speech Recognition via Large-Scale Weak Supervision
	</span>
</a>.</p>
<h3 id="what-is-weak-supervision">What is Weak Supervision?</h3>
<p>As a side node, I am quite excited to see how OpenAI is using weak supervision to scale and getting better results. The following quote from their paper speaks for itself.</p>
<blockquote>
<blockquote>
<blockquote>
<p><em>Our work suggests that simple scaling of weakly supervised pre-training has been underappreciated so far for speech recognition. We achieve these results without the need for the self-supervision or self-training techniques that have been a mainstay of recent large-scale speech recognition work.</em></p></blockquote></blockquote></blockquote>
<p>All is well and good, but what is Weak Supervision?</p>
<p>As I called out in my book <a
	
		href = "https://www.amazon.com/Weakly-Supervised-Learning-Doing-More/dp/1492077062/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Practical Weak Supervision: Doing More with Less Data
	</span>
</a> 📖 : Weak supervision is a broad collection of techniques in machine learning where models are trained using sources of information that are easier to provide than hand-labeled data, where this information is incomplete, inexact, or otherwise less accurate. Instead of hand-labeling high-quality data, all of which is very cost-prohibitive, we can use other techniques that combine diverse sources of data, creating an approximation of labels. Using weak supervision, we can reconcile these labels to a single label.</p>
<p>Weak supervision enables these noisy, weakly sourced labels to be combined programmatically to form the training data that can be used to train a model. Labels are considered “weak” because they are noisy—i.e., the data measurements that the labels represent are inaccurate and have a margin of error.</p>
<p>More details here:
<blockquote class="twitter-tweet" data-width="550" data-height="600">
  <p lang="en" dir="ltr">
    <a href="https://twitter.com/bahree/status/1450322692817571840?ref_src=twsrc%5Etfw">
      Loading tweet from @bahree...
    </a>
  </p>
  <a href="https://twitter.com/bahree/status/1450322692817571840?ref_src=twsrc%5Etfw" class="twitter-tweet-link">
    View on Twitter
  </a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</p>
<h2 id="transcription-with-whisper">Transcription with Whisper</h2>
<p>I figured, one of the best ways to try out the Whisper model and run it through its paces is to try a bunch of transcription - and that too on something fairly technical , where the language isn&rsquo;t typical in the broader sense of spoken english. And what better way to test AI is to use something that talks about AI. To that end, I used <a
	
		href = "https://twitter.com/samcharrington"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Sam Charrington
	</span>
</a>&rsquo;s popular <a
	
		href = "https://twimlai.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		TwimlAI podcast
	</span>
</a> as the guinea pig. 😄</p>
<p>Now on one hand, it seems pretty easy to install Whisper (it is a pip install) and run it on a single audio file.
<blockquote class="twitter-tweet" data-width="550" data-height="600">
  <p lang="en" dir="ltr">
    <a href="https://twitter.com/bahree/status/1580695321960906752?ref_src=twsrc%5Etfw">
      Loading tweet from @bahree...
    </a>
  </p>
  <a href="https://twitter.com/bahree/status/1580695321960906752?ref_src=twsrc%5Etfw" class="twitter-tweet-link">
    View on Twitter
  </a>
</blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</p>
<p>The reality is that there are a lot of dependencies and it is not as easy as it seems.
<p>

    <figure>
        <img src="images/whisper-2.png" alt="Whisper runtime issues"/>
        <figcaption>Whisper runtime issues</figcaption>
    </figure>

</p></p>
<p>However, I did manage to resolve everything and get it working - and the results were pretty good.👍
<p>

    <figure>
        <img src="images/whisper-3.png" alt="Whisper transcription"/>
        <figcaption>Whisper transcription</figcaption>
    </figure>

</p></p>
<p>But I wanted to see how well it would work on a large corpus of audio files. So I wrote a simple script that would download all the episodes of TwimlAI from YouTube as mp3, and then transcribe them using the Whisper model. As of writing this, there are 547 episodes of TwimlAI and all of those transcriber to my github repo <a
	
		href = "https://github.com/bahree/whisper"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		here.
	</span>
</a>. Each episode has three resulting files when transcribed:</p>
<ol>
<li>txt file - there is a text file which contains the transcript</li>
<li>srt file - this is the subrip subtitle file which can be used to add subtitles to the audio file</li>
<li>vtt file - this is WebVTT file (web video test to track file) and contains the transcript and the time codes that sync the captions.</li>
</ol>
<p>You can get all the transcripts which can either be downloaded as the zip file <code>twiml-episodes-whisper-transcribed.zip</code> 💾 or they are also in the folder <code>twiml-episodes-whisper-transcribed</code> 📁 in the github repo <a
	
		href = "https://github.com/bahree/whisper"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		here.
	</span>
</a></p>
<p>I also transcribed one file using both the To show the difference the base and the large model. You can find both versions in the folder <code>model-comparison</code> 📁 of one specific episode - <em>#544 - #AI Trends 2023 - AI Trends 2023: Natural Language Proc – ChatGPT, GPT-4 and Cutting Edge Research with Sameer Singh</em>. Not to get into all the details, but the transcription using the large model was approx 120 lines longer. The image below shows you an eagle view of the difference between the two transcriptions - there are a lot of differences in the text, and the quality is much better on the large model.
<p>

    <figure>
        <img src="images/whisper-4.jpg" alt="Transcription delta between base and large models"/>
        <figcaption>Transcription delta between base and large models</figcaption>
    </figure>

</p></p>
<h2 id="steps-to-run-this-locally">Steps to run this locally</h2>
<p>If you want to run this locally, start by cloning the <a
	
		href = "https://github.com/bahree/whisper"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		repo
	</span>
</a>. It is best to use <a
	
		href = "https://docs.conda.io/en/latest/index.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		conda
	</span>
</a> to get the dependencies managed. I prefer <a
	
		href = "https://docs.conda.io/en/latest/miniconda.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Miniconda
	</span>
</a>, but you can use any conda installation. The next set of steps assumes that you have conda installed; see the <a
	
		href = "https://docs.conda.io/projects/conda/en/latest/user-guide/install/download.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		docs
	</span>
</a> if you need help installing.</p>
<h3 id="step-1-create-a-conda-environment">Step 1: Create a conda environment</h3>
<p>I am running this on Ubuntu 22.0.01 LTS (Jammy Jellyfish) and am using an NVidia RTX 3090 GPU. I am also running Python 3.8.5.</p>
<p>Create a conda environment and install the dependencies. I have included the <code>environment.yml</code> file in the repo, which you can use to create the environment. The name of the environment can be changed to anything; I use <code>whisper</code> in my case.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#c6a0f6">name</span>: whisper
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">channels</span>:
</span></span><span style="display:flex;"><span>- pytorch
</span></span><span style="display:flex;"><span>- defaults
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">dependencies</span>:
</span></span><span style="display:flex;"><span>- cudatoolkit=11.3
</span></span><span style="display:flex;"><span>- git
</span></span><span style="display:flex;"><span>- numpy=1.22.3
</span></span><span style="display:flex;"><span>- pip=20.3
</span></span><span style="display:flex;"><span>- python=3.8.5
</span></span><span style="display:flex;"><span>- pytorch=1.11.0
</span></span><span style="display:flex;"><span>- scikit-image=0.19.2
</span></span><span style="display:flex;"><span>- torchvision=0.12.0
</span></span><span style="display:flex;"><span>- <span style="color:#c6a0f6">pip</span>:
</span></span><span style="display:flex;"><span>  - -r requirements.txt</span></span></code></pre></div>
<p>Once conda is installed, you run the following command to create the environment:
<code>conda env create -f environment.yaml</code></p>
<p>And you would sees something like this as the output:
<p>

    <figure>
        <img src="images/whisper-5.png" alt="Conda environment creation"/>
        <figcaption>Conda environment creation</figcaption>
    </figure>

</p></p>
<h3 id="step-2-activate-the-environment">Step 2: Activate the environment</h3>
<p>If the <code>whisper</code> environment if not already active, can be activate it by running the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>conda activate whisper</span></span></code></pre></div>
<h3 id="step-3-install-the-whisper-model">Step 3: Install the Whisper model</h3>
<p>The next step is to install the Whisper model in the environment. This is a <a
	
		href = "https://github.com/openai/whisper"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		pip install
	</span>
</a>, and you can run the following command to install it:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>pip install git+https://github.com/openai/whisper.git </span></span></code></pre></div>
<p>The output should look something like this - note the exact details most likely will be different.
<p>

    <figure>
        <img src="images/whisper-6.png" alt="Whisper model installation"/>
        <figcaption>Whisper model installation</figcaption>
    </figure>

</p></p>
<h3 id="step-4-download-the-twimlai-episodes">Step 4: Download the TwimlAI episodes</h3>
<p>The next step is to download the TwimlAI episodes. I have written a python program to do this. This downloads the episodes from YouTube and saves them as mp3 files. You can download all the episodes, or a single one. I also had to update this to use a local file to get around some issues that PyTube was having. You can find the program in the <code>download_episodes.py</code> file in the repo. You can run the program by running the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>python ./download_episodes.py</span></span></code></pre></div>
<p>You will see the following output:
<p>

    <figure>
        <img src="images/whisper-7.png" alt="Menu"/>
        <figcaption>Menu</figcaption>
    </figure>

</p></p>
<p>I would suggest using Option 4 - Using a local playlist. The file <code>twiml-episodes.txt</code> 🗒️already contains the list of all the episodes. By default the episodes will be downloaded as mp3&rsquo;s into a folder called <code>twiml-episodes</code> 📁. You can change the folder name by editing the <code>download_episodes.py</code> file.</p>
<p>Here is the code snippet that downloads the episodes:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Download all the videos from the local playlist text and save it as a mp3 file</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">downloadVideoFromLocalPlaylist</span>(playlist_name, mp3_path):
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(playlist_name, <span style="color:#a6da95">&#39;r&#39;</span>, encoding<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;utf8&#34;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>        reader <span style="color:#91d7e3;font-weight:bold">=</span> csv<span style="color:#91d7e3;font-weight:bold">.</span>reader(f, delimiter<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;|&#39;</span>)
</span></span><span style="display:flex;"><span>        index <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>        fileSaved <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">for</span> row <span style="color:#91d7e3;font-weight:bold">in</span> reader:
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># following is used to skip over episodes that have already been downloaded</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># if index &lt; 526:</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">#     print(&#34;Skiping ... # &#34; + str(index))</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">#     index += 1</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">#     continue</span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># print(&#34;Episode: &#34; + row[0], &#34;Title:&#34; + row[1], &#34;URL:&#34; + row[2])</span>
</span></span><span style="display:flex;"><span>            episode <span style="color:#91d7e3;font-weight:bold">=</span> row[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>            title <span style="color:#91d7e3;font-weight:bold">=</span> row[<span style="color:#f5a97f">1</span>]
</span></span><span style="display:flex;"><span>            url <span style="color:#91d7e3;font-weight:bold">=</span> row[<span style="color:#f5a97f">2</span>]
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Downloading Episode #&#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> episode <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; ... &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> title)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>               tempFileName <span style="color:#91d7e3;font-weight:bold">=</span> validFilename(<span style="color:#91d7e3">str</span>(index) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#39;_&#39;</span> <span style="color:#91d7e3;font-weight:bold">+</span> title <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34;.mp3&#34;</span>)
</span></span><span style="display:flex;"><span>               downloadVideo(url, mp3_path, tempFileName)
</span></span><span style="display:flex;"><span>               fileSaved <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>        
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">except</span> <span style="color:#f5a97f">IOError</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#ed8796">f</span><span style="color:#a6da95">&#34;</span><span style="color:#a6da95">{</span>textColors<span style="color:#91d7e3;font-weight:bold">.</span>FAIL<span style="color:#a6da95">}</span><span style="color:#a6da95">Error: can</span><span style="color:#8aadf4">\&#39;</span><span style="color:#a6da95">t save the following file. Most likely it has an invalid character in the name.</span><span style="color:#a6da95">{</span>textColors<span style="color:#91d7e3;font-weight:bold">.</span>RESET<span style="color:#a6da95">}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;File: &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> tempFileName)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">except</span> VideoUnavailable:
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#a6da95">{textColors.FAIL}</span><span style="color:#a6da95">Video: &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> tempFileName <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; is unavailable, skipping.</span><span style="color:#a6da95">{textColors.RESET}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">except</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#a6da95">{textColors.FAIL}</span><span style="color:#a6da95">Unexpected error: &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> sys<span style="color:#91d7e3;font-weight:bold">.</span>exc_info()[<span style="color:#f5a97f">0</span>] <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34;</span><span style="color:#a6da95">{textColors.RESET}</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;Download complete. Number of episodes saved: &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">str</span>(fileSaved))
</span></span><span style="display:flex;"><span>            index <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># download mp3 from youtube</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">downloadVideo</span>(video_url, mp3_location, filenametoSave):
</span></span><span style="display:flex;"><span>    yt <span style="color:#91d7e3;font-weight:bold">=</span> YouTube(video_url)
</span></span><span style="display:flex;"><span>    yt<span style="color:#91d7e3;font-weight:bold">.</span>register_on_progress_callback(fancy_progress_bar)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    yt<span style="color:#91d7e3;font-weight:bold">.</span>streams<span style="color:#91d7e3;font-weight:bold">.</span>filter(only_audio<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">True</span>)<span style="color:#91d7e3;font-weight:bold">.</span>first()<span style="color:#91d7e3;font-weight:bold">.</span>download(output_path<span style="color:#91d7e3;font-weight:bold">=</span>mp3_location,filename<span style="color:#91d7e3;font-weight:bold">=</span>filenametoSave)</span></span></code></pre></div>
<p>The reason the options fail randomly is because of a change that YouTube made. At the time of this post, the way they render the page breaks things and one cannot get the title. You will see an error related to the video title not being found.
<p>

    <figure>
        <img src="images/whisper-8.png" alt="Title not found error"/>
        <figcaption>Title not found error</figcaption>
    </figure>

</p></p>
<h3 id="step-5-running-transcription">Step 5: Running transcription</h3>
<p>Finally you can transcribe the episodes. The <code>transcribe.sh</code> file in the repo contains the code to do this and it simply loops over the <code>twiml-episodes</code> folder and one-by-one processes the mp3 files. The output is saved in the <code>out</code> folder 📁. The shell script is as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#c6a0f6">for</span> f in twiml-episodes/*.mp3 ; <span style="color:#c6a0f6">do</span> whisper --language en --model base -o out -- <span style="color:#f4dbd6">$f</span>; <span style="color:#c6a0f6">done</span></span></span></code></pre></div>
<p>Here is what the transcription looks like when it is running:
<p>

    <figure>
        <img src="images/whisper-9.png" alt="Whisper transcription"/>
        <figcaption>Whisper transcription</figcaption>
    </figure>

</p></p>
<p>You can also run this only on a subset or one file to transcribe - as shown below. Also if the model isn&rsquo;t already downloaded, it will download it first.
<p>

    <figure>
        <img src="images/whisper-10.png" alt="Whisper model download"/>
        <figcaption>Whisper model download</figcaption>
    </figure>

</p></p>
<p>Of course you can update the folders, etc to match what you needs are.</p>
<h3 id="gpu-profile">GPU Profile</h3>
<p>I also wanted to show the GPU profile when inferencing between the <code>base</code> and the <code>large</code> models. The image below shows the GPU profile when running the <code>base</code> model. You can see that the GPU is being used at 100% and the memory is being used at ~4gb and ~200W of power. The time to transcribe each episode isn&rsquo;t too long as well - around 2 minutes.</p>
<p><p>

    <figure>
        <img src="images/whisper-11.png" alt="Whisper base model"/>
        <figcaption>Whisper base model</figcaption>
    </figure>

</p></p>
<p>The image below shows the GPU profile when running the <code>large</code> model. You can see that the GPU is being used at 100% and the memory is being used at ~14gb and ~320W of power. The time to transcribe each episode is much longer - around 10 minutes.</p>
<p><p>

    <figure>
        <img src="images/whisper-12.png" alt="Whisper large model"/>
        <figcaption>Whisper large model</figcaption>
    </figure>

</p></p>
<p>In conclusion, this was a fun little thing to work on; I had done this a few months ago but not had the time until now to blog it. I also transcribed the episodes using our <a
	
		href = "https://azure.microsoft.com/en-us/products/cognitive-services/speech-to-text/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure Speech service
	</span>
</a> which I think is more robust and scalable in many ways (but then I am a little biased 💜). I will blog about that in the future and we can compare.</p>
]]></content:encoded>
    </item>
    <item>
      <title>PFOaaS - Polite Fork Off As A Service</title>
      <link>/post/2023/01/pfoaas-polite-fork-off-as-a-service/</link>
      <pubDate>Mon, 02 Jan 2023 00:00:00 +0000</pubDate>
      <guid>/post/2023/01/pfoaas-polite-fork-off-as-a-service/</guid>
      <description>PFOaaS (Polite Fork Off As A Service) provides a modern, RESTful, scalable solution to the common problem of telling people to Fork off.</description>
      <content:encoded><![CDATA[<h3 id="api-introduction">API Introduction</h3>
<p>Polite Fork Off As A Service (or PFOaaS) - <a
	
		href = "https://pfoaas.desigeek.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://pfoaas.desigeek.com/
	</span>
</a> is a modern REST API that solves the problem of one telling people to politely fork off. &#x1f607;</p>
<p>There are days when we all need such a service for various reasons, and I think it is a great way to release some pent-up frustration. 🖤 It is also a great way to get some laughs too. This is of course meant for hard code engineers, writing RPC free code &#x1f61c;</p>
<p><p>

    <figure>
        <img src="images/pfoaas1.png" alt="Polite Fork Off As A Service"/>
        <figcaption>Polite Fork Off As A Service - API</figcaption>
    </figure>

</p></p>
<h3 id="api-usage">API Usage</h3>
<p>The API is simple to use; you make a GET request to the API endpoint and the relevant parameters. You can also use the API from the command line too. There are a few options to choose for the response format - plain text, JSON, XML, and HTTP. If you don&rsquo;t select an option the default is HTML. The JSON response also supports a callback.</p>
<p>All the operations are idempotent, so you can make the same request multiple times and get the same response.</p>
<p>Here is a sample usage using the <code>/row/:from</code> operation which requires one parameter - the name of the person wishing this.  &#x1f480; The output shown below is in plain text.</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -H <span style="color:#a6da95">&#39;Accept: text/plain&#39;</span> https://pfoaas.desigeek.com/row/Amit</span></span></code></pre></div>
<p><p>

    <figure>
        <img src="images/pfoaas2.png" alt="PFOaaS API - sample usage"/>
        <figcaption>PFOaaS API - sample usage</figcaption>
    </figure>

</p></p>
<p>The same operation in JSON format:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -H <span style="color:#a6da95">&#39;Accept: application/json&#39;</span> https://pfoaas.desigeek.com/row/Amit</span></span></code></pre></div>
<p><p>

    <figure>
        <img src="images/pfoaas3.png" alt="PFOaaS API - JSON sample usage"/>
        <figcaption>PFOaaS API - JSON sample usage</figcaption>
    </figure>

</p></p>
<p>Some of the other operations require more than one parameter such as the <code>/shakespeare/:name/:from</code> that can be invoked as follows:</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -H <span style="color:#a6da95">&#39;Accept: text/plain&#39;</span> https://pfoaas.desigeek.com/shakespeare/Putin/Amit</span></span></code></pre></div>
<p><p>

    <figure>
        <img src="images/pfoaas4.png" alt="PFOaaS - Shakespeare sample usage"/>
        <figcaption>PFOaaS API - Shakespeare sample usage</figcaption>
    </figure>

</p></p>
<p>There is a notion of filters, which are output modifiers. These are added to the URL as a query parameter and can be chained together. For example, the same operation as above but with the <code>i18n</code> filter showing the output in Russia (Русский).</p>
<div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -H <span style="color:#a6da95">&#39;Accept: text/plain&#39;</span> https://pfoaas.desigeek.com/row/Amit?i18n<span style="color:#91d7e3;font-weight:bold">=</span>ru</span></span></code></pre></div>
<p><p>

    <figure>
        <img src="images/pfoaas5.png" alt="PFOaaS - Shakespeare sample usage in Russian"/>
        <figcaption>Shakespeare sample usage output in Russian</figcaption>
    </figure>

</p></p>
<p>The <code>i18n</code> is used to translate the output to another language via the ISO 639-1 language code, and supports the following languages:</p>
<table>
  <thead>
      <tr>
          <th>Language</th>
          <th>ISO 639-1</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>English</td>
          <td>en</td>
      </tr>
      <tr>
          <td>Italian / Italiano</td>
          <td>it</td>
      </tr>
      <tr>
          <td>Spanish / Español</td>
          <td>es</td>
      </tr>
      <tr>
          <td>French / Français</td>
          <td>fr</td>
      </tr>
      <tr>
          <td>Dutch / Deutsch</td>
          <td>de</td>
      </tr>
      <tr>
          <td>Norwegian / Nederlands</td>
          <td>nl</td>
      </tr>
      <tr>
          <td>Swedish / Svenska</td>
          <td>sv</td>
      </tr>
      <tr>
          <td>Portuguese / Português</td>
          <td>pt</td>
      </tr>
      <tr>
          <td>Russia / Русский</td>
          <td>ru</td>
      </tr>
      <tr>
          <td>Japanese / 日本語</td>
          <td>ja</td>
      </tr>
      <tr>
          <td>Chinese / 汉语</td>
          <td>zh</td>
      </tr>
      <tr>
          <td>Korean / 한국어</td>
          <td>ko</td>
      </tr>
      <tr>
          <td>Turkish / Türkçe</td>
          <td>tr</td>
      </tr>
  </tbody>
</table>
<h3 id="api-details-and-code">API Details and Code</h3>
<p>You can see all the API usage and examples at <a
	
		href = "https://pfoaas.desigeek.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://pfoaas.desigeek.com/
	</span>
</a>. I won&rsquo;t go into the details here, but you can see the code on 
 <i class="fa-brands fa-github"></i> 
 <a
	
		href = "https://github.com/bahree/pfoaas"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		GitHub here
	</span>
</a>.</p>
<p>You can see a simple theme in the code, and easy to follow along. This is a simple 
 <i class="fa-brands fa-node-js"></i> 
 NodeJs app that is hosted on Azure. If you want to run it locally, you can clone the repo and run <code>npm install</code>, and then <code>npm start</code>. The app will be available at <code>http://localhost:5000</code>.</p>
<p>You can also run this as a docker container, and build the container yourself; the Dockerfile is included in the repo.</p>
<p>I would welcome any contributions and am open to adding more operations to the API. If you prefer to do this, adding a new PFOaaS operation is as follows:</p>
<ol>
<li>Fork the repo 
 <i class="fa-solid fa-code-fork"></i> 
</li>
<li>Branch into a feature branch (e.g. <code>feature/operation-name</code>) 
 <i class="fa-solid fa-code-branch"></i> 

<ul>
<li>See the operations in the <code>/lib/operations</code> folder for examples. 
 <i class="fa-solid fa-folder-open"></i> 
</li>
</ul>
</li>
<li>Add specs using the <code>spec/operations</code> as examples. Note, the specs are needed for the operation to be added to the API. 
 <i class="fa-solid fa-file-code"></i> 
</li>
<li>Push the changes to your fork and merge 
 <i class="fa-solid fa-code-merge"></i> 
</li>
<li>Submit a PR. 
 <i class="fa-solid fa-code-pull-request"></i> 
👍</li>
</ol>
<p>I look forward to seeing your contributions and hope this helps you de-stress and get you to smile. &#x1f604; &#x1f49c;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Using CoPilot beyond code</title>
      <link>/post/2022/12/using-copilot-beyond-code/</link>
      <pubDate>Sat, 10 Dec 2022 00:00:00 +0000</pubDate>
      <guid>/post/2022/12/using-copilot-beyond-code/</guid>
      <description>Trying to see if Github CoPilot can be more general purpose than just code</description>
      <content:encoded><![CDATA[<p>In the last week or so, all the range online has been #OpenAI&rsquo;s new chatbot called #ChatGPT (you can read more details on <a
	
		href = "/post/2022/12/hello-chatgpt/"
	

	

	>
	
	<span>
		ChatGPT here
	</span>
</a>). This also got me thinking, about how can we use #CoPilot more than just code. <a
	
		href = "https://blog.desigeek.com/post/2021/10/ai-writing-ai-code/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		GitHub CoPilot
	</span>
</a> as you might recall is your #AI powered pair-programmer.</p>
<p>And as we can see below, it indeed is possible to use Codex as sort of a more general purpose usage. I start with the prompts on how one might use CoPilot &ndash; a function to read a file and return its contents as a string, just to show there isn&rsquo;t anything different I am doing in using this. And then for general-purpose usage, I used the prompts in VSCode.</p>
<p><p>

    <figure>
        <img src="images/codeview-copilot.png" alt="CoPilot general purpose usage"/>
        <figcaption>CoPilot general purpose usage</figcaption>
    </figure>

</p></p>
<p>I missed the first &lsquo;Q&rsquo; in the first question, but that didn&rsquo;t throw it off. Also, there were typos in the other questions - for example in the third question on the most dangerous volcano.</p>
<p>All this is possible because Codex (which is the model that Copilot uses) is derived from GPT. This of course doesn&rsquo;t mean that Copilot is replacing the chatbot - it doesn&rsquo;t have the context in the dialogue turns, and hence the &lsquo;memory&rsquo; of the conversation. It does mean that we can use Copilot for more general-purpose usage.</p>
<p>I also did a reverse engineering of sorts - asked Copilot to explain the code it wrote in the first place. Here is the explanation of the function:</p>
<p><p>

    <figure>
        <img src="images/codeview-copilot-explanation.png" alt="CoPilot code explanation"/>
        <figcaption>CoPilot code explanation</figcaption>
    </figure>

</p></p>
<p>And if you want to see the complete interaction (minus the code explanation), here is the video:
<p>

    <figure>
        <img src="images/copilot-code.gif" alt="CoPilot Usage"/>
        <figcaption>CoPilot Usage in VS Code</figcaption>
    </figure>

</p></p>
<p>And of course, Woot means &ldquo;woot&rdquo; in woot :)))</p>
]]></content:encoded>
    </item>
    <item>
      <title>Nuget packages not found after installing Visual Studio 2022</title>
      <link>/post/2022/09/nuget-packages-not-found-after-installing-visual-studio-2022/</link>
      <pubDate>Sat, 03 Sep 2022 00:00:00 +0000</pubDate>
      <guid>/post/2022/09/nuget-packages-not-found-after-installing-visual-studio-2022/</guid>
      <description>&lt;p&gt;I recently needed to install Visual Studio 2022 on one my existing machines to debug a new zeroshot model that has a dependency on our Speech SDK. The &lt;a
	
		href = &#34;https://docs.microsoft.com/en-us/dotnet/api/microsoft.cognitiveservices.speech?view=azure-dotnet&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Speech SDK
	&lt;/span&gt;
&lt;/a&gt; is one of our key #AI services in Cognitive Services (as part of #AzureAI). I already had VSCode running, but in this case I need the bigger brother.&lt;/p&gt;
&lt;p&gt;After installing Visual Studio, I could not get any nuget packages to install; I could not even fetch anything and didn&amp;rsquo;t matter what I used - the package manager console in Visual Studio, PowerShell, etc.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I recently needed to install Visual Studio 2022 on one my existing machines to debug a new zeroshot model that has a dependency on our Speech SDK. The <a
	
		href = "https://docs.microsoft.com/en-us/dotnet/api/microsoft.cognitiveservices.speech?view=azure-dotnet"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Speech SDK
	</span>
</a> is one of our key #AI services in Cognitive Services (as part of #AzureAI). I already had VSCode running, but in this case I need the bigger brother.</p>
<p>After installing Visual Studio, I could not get any nuget packages to install; I could not even fetch anything and didn&rsquo;t matter what I used - the package manager console in Visual Studio, PowerShell, etc.</p>
<p>Nuget would only point to the local offline package store, which in my case is available at: <code>C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\</code>. I kept getting the error: <code>Package X is not found on source 'C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\'</code></p>
<p>I could not understand the behavior, and not seen this before. Turns out it is a known issue, albeit not very common. Seems like if either PowerShell&rsquo;s <code>Install-Module</code>, or Chocolatey&rsquo;s <code>coco</code> command was used <strong>before</strong> using nuget or Visual Studio for the <strong>first time</strong>, this will happen.</p>
<p>The solution is to add nuget.org as a package source, and the URL it should point to is: <code>https://api.nuget.org/v3/index.json</code>. Below is the screenshot on what this looks in Visual Studio for me:</p>
<p><p>

    <figure>
        <img src="images/image.png" alt="Screenshot showing updated nutget package sources in Visual Studio 2022"/>
        <figcaption>Updated nuget package sources</figcaption>
    </figure>

</p></p>
<p>If one is setting up a brand new dev box, the odds of you running into this is low; in my case given I was adding Visual Studio 2022 much later is what caused this. There is <a
	
		href = "https://github.com/NuGet/Home/wiki/nuget.org-not-used-on-new-machine"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		more details on this here too
	</span>
</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>How to run TeslaMate on Azure</title>
      <link>/post/2021/12/how-to-run-teslamate-on-azure/</link>
      <pubDate>Thu, 30 Dec 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/12/how-to-run-teslamate-on-azure/</guid>
      <description>&lt;p&gt;If you have a Tesla, then you should absolutely check out &lt;a
	
		href = &#34;https://github.com/adriankumpf/teslamate&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		TeslaMate
	&lt;/span&gt;
&lt;/a&gt; which is data logger for your car(s) that one self-hosts. This uses the car&amp;rsquo;s API and gets all different kinds of telemetry of your drives, charging, batter conditions, acceleration, braking, parking, etc. I personally prefer this, over other online services (of which there are a few) - as it is giving away the keys to the kingdom - &lt;em&gt;literally&lt;/em&gt; in this case (the Tokens used to authenticate and login).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you have a Tesla, then you should absolutely check out <a
	
		href = "https://github.com/adriankumpf/teslamate"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		TeslaMate
	</span>
</a> which is data logger for your car(s) that one self-hosts. This uses the car&rsquo;s API and gets all different kinds of telemetry of your drives, charging, batter conditions, acceleration, braking, parking, etc. I personally prefer this, over other online services (of which there are a few) - as it is giving away the keys to the kingdom - <em>literally</em> in this case (the Tokens used to authenticate and login).</p>
<p>I have been running TeslaMate at home on a couple of machines for a while and figured a cloud version would work out better. I had network issues on one of the machines, where no car telemetry was downloaded. It was a few days before I realized that the machine wasn&rsquo;t online due to a separate DNS issue and those few days&rsquo; worth of car telemetry (drives and other data of course) wasn&rsquo;t recorded.</p>
<p>In our example, we will deploy TeslaMate in a docker container running on Ubuntu - which is hosted on Azure. To help with isolation and managing this, I would recommend we use a resource group (RG) only for running TeslaMate. Of course, we need an Azure subscription, which I would assume you already have.</p>
<p>If you are not familiar with TeslaMate, before we get started here, I would highly recommend <a
	
		href = "https://github.com/adriankumpf/teslamate"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		checking out the features
	</span>
</a>, including <a
	
		href = "https://docs.teslamate.org/docs/screenshots/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		some screenshots
	</span>
</a> and the <a
	
		href = "https://docs.teslamate.org/docs/installation/docker"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		installation documentation
	</span>
</a> to get an idea.</p>
<p><p>

    <figure>
        <img src="images/web_interface.png" alt="Web Interface"/>
        <figcaption>TeslaMate Overview</figcaption>
    </figure>

</p>
(Credit: <a
	
		href = "https://github.com/adriankumpf/teslamate"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		TeslaMate GitHub repro
	</span>
</a>)</p>
<h3 id="step-1---creating-new-rg">Step 1 - Creating new RG</h3>
<p>We start by logging into the <a
	
		href = "https://portal.azure.com"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure portal
	</span>
</a> and create a new resource group (RG) for TeslaMate; if you are not sure how to do this, the <a
	
		href = "https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal#:~:text=Create%20resource%20groups%201%20Sign%20in%20to%20the,newly%20created%20resource%20group%20to%20open%20it.%20"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		documentation here
	</span>
</a> outlines the steps needed. Once you have an empty RG, it would look something like the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/1-New-RG-1024x587.jpg" alt=""/>
        <figcaption>New Azure Resource Group</figcaption>
    </figure>

</p></p>
<h3 id="step-2---creating-new-ubuntu-vm">Step 2 - Creating new Ubuntu VM</h3>
<p>Now that we have a new RG, we need to create a new Ubuntu virtual machine (VM) in that. We will choose the option to create resources as shown in the middle of the screen (see previous screenshot).</p>
<p>Clicking on &ldquo;Create resources&rdquo;, we see various menu options; the options you see might be a little different than the one shown below.</p>
<p><p>

    <figure>
        <img src="images/2-New-Resource-1024x677.jpg" alt=""/>
        <figcaption>Create a resource - Azure</figcaption>
    </figure>

</p></p>
<p>We need to create a Virtual Machine - the first choice under &ldquo;Popular Azure services&rdquo; and will click the &ldquo;Create&rdquo; link. This starts a wizard that allows you to go through the various settings and options.</p>
<p>The first step when creating a VM is to start with the basic details for machine we are creating - instance details, subscription details, admin user details, etc. I outline the steps and show screenshots to help those who are not comfortable with this level of tech, or new to Azure. If you are a more advanced user, a more efficient way would be via the <a
	
		href = "https://docs.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-cli"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure CLI
	</span>
</a>. You can read up more details on <a
	
		href = "https://docs.microsoft.com/en-us/azure/virtual-machines/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		VM&rsquo;s on Azure here
	</span>
</a>.</p>
<h3 id="step-3---vm-basics">Step 3 - VM Basics</h3>
<ul>
<li>Subscription and resource group - Make sure you have the correct subscription and RG selected. If you haven&rsquo;t created a new RG yet, you can do so using the &ldquo;Create new&rdquo; link under the RG option (see the screenshot below).</li>
<li>VM Name - You can give the VM any name - this is more for you to remember and manage.</li>
<li>Region - In terms of a region, in most cases it would make sense to pick a region that is physically close to the same area where you are based (and the car too of course).</li>
<li>Image - I use the latest Ubuntu LTS image which as of this writing is v20.04 Gen 2.</li>
</ul>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-1024x583.jpg" alt=""/>
        <figcaption>Create a new VM in Azure</figcaption>
    </figure>

</p></p>
<ul>
<li>Size - In terms of picking the size for the VM - we don&rsquo;t need a very beefy machine, and needless to say - the bigger the machine, the higher the monthly costs. I keep the standard Size. This is not my main instance as I already have that running - this new instance is being setup as a demo that I will be deleting later.</li>
<li>Username - This is obvious and should be something you know and can remember.</li>
<li>Password - I choose password as the auth type, more so as this is for demo purposes for this post; ideally ssh keys are more secure and you would want to use that. If you do go down a password path, I cannot stress enough not to reuse passwords and create a strong password; it is always a good idea to use a password manager (e.g., I use LastPass).</li>
</ul>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-2-1024x926.jpg" alt=""/>
        <figcaption>Basic details required when setting up a new VM</figcaption>
    </figure>

</p></p>
<p>I chose the simple SSD option; we don&rsquo;t need a lot of advanced things.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-disks-1024x742.jpg" alt=""/>
        <figcaption>Disk details when setting up a new VM</figcaption>
    </figure>

</p></p>
<p>For the network options, you do want a public IP and, in most cases, just leaving the default would work. And I don&rsquo;t show it in the screenshot, but we don&rsquo;t need a load balancer and leave the default option of &ldquo;None&rdquo;. And we do want the ability to ssh into the machine to deploy and manage TeslaMate.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-network-1024x863.jpg" alt=""/>
        <figcaption>Networking details when setting up a new VM</figcaption>
    </figure>

</p></p>
<p>For the Inbound port rules, by default only port 22 is enabled for SSH; to allow us to access the web server we also need to both ports 80 (http) and 443 (https) are enabled as shown in the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/image-20-1024x387.png" alt=""/>
        <figcaption>Inbound port rules - when setting up a new VM</figcaption>
    </figure>

</p></p>
<p>For the next set of Tabs (Management, Advanced, and Tags) I didn&rsquo;t change anything and went with the defaults. Once the validations are passed, and the final review shows the cost and other details you choose.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-Review1-1024x951.jpg" alt=""/>
        <figcaption>VM Creation - summary</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-Review2.jpg" alt=""/>
        <figcaption>VM Creation - summary</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-Review3.jpg" alt=""/>
        <figcaption>VM Creation - summary</figcaption>
    </figure>

</p></p>
<p>And once you are happy with everything click the <strong>Create</strong> button on the bottom left corner.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Basics-Review4-830x1024.jpg" alt=""/>
        <figcaption>VM Creation confirmation</figcaption>
    </figure>

</p></p>
<p>Once the deployment of the VM starts, it can take a few minutes and you will see a similar progress as shown below.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Deployment-1024x422.jpg" alt=""/>
        <figcaption>VM deployment status screen</figcaption>
    </figure>

</p></p>
<p>Once the VM is created, deployed and wired up (which can take a few minutes) - we will see the confirmation as shown below.</p>
<p><p>

    <figure>
        <img src="images/3-VM-Deployment-complete-1024x423.jpg" alt=""/>
        <figcaption>VM deployment confirmation</figcaption>
    </figure>

</p></p>
<p>From the confirmation screen, clicking on &ldquo;Go to resource&rdquo; takes us to a screen where we see the different details of the VM. One of the details we are interested in at this point is the IP address and the ability to give the machine a DNS name. We need these to be able to connect to the VM over SSH (see screenshot below).</p>
<p><p>

    <figure>
        <img src="images/3-VM-details-1024x554.jpg" alt=""/>
        <figcaption>VM essential details</figcaption>
    </figure>

</p></p>
<p>It might be worthwhile to also setup a DNS name that one can use in addition to the IP. This DNS name would be the fully qualified domain name (FQDN) that would be needed later when configuring the docker container. The DNS name allows us to connect to the machine using something like <code>&quot;https://my-car-details.cloudapp.azure.com&quot;</code>&quot; (or similar). You can read more details on FQDN in the <a
	
		href = "https://docs.microsoft.com/en-us/azure/virtual-machines/create-fqdn"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Azure docs here
	</span>
</a>. If you are interested in using your own DNS server, you can read details on <a
	
		href = "https://docs.microsoft.com/en-us/azure/virtual-machines/linux/azure-dns#name-resolution-using-your-own-dns-server"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		how to go about that here
	</span>
</a>.</p>
<p>Click on the &ldquo;Not Configured&rdquo; for the DNS name (as shown in the image below) and you can set a unique name that is something memorable.</p>
<p><p>

    <figure>
        <img src="images/3-VM-DNS-Name-1-1024x327.jpg" alt=""/>
        <figcaption>VM DNS name</figcaption>
    </figure>

</p></p>
<p>The DNS name is tied to the region you have, and it must be unique.</p>
<p><p>

    <figure>
        <img src="images/3-VM-DNS-Name-2-1-1024x319.jpg" alt=""/>
        <figcaption>VM DNS name label creating</figcaption>
    </figure>

</p></p>
<p>And once this is setup, you can see the FQDN in your VM details as shown below.</p>
<p><p>

    <figure>
        <img src="images/3-VM-DNS-Name-3-1024x207.jpg" alt=""/>
        <figcaption>VM DNS name</figcaption>
    </figure>

</p></p>
<p>If for some reason you didn&rsquo;t open ports 80 and 443 earlier, you can always configure them now. To do so, in the Azure portal, when you have the Ubuntu VM resource selected, click on <strong>Networking</strong> on the left, and you can update the Inbound port rules.</p>
<p><p>

    <figure>
        <img src="images/3-VM-network-1024x396.jpg" alt=""/>
        <figcaption>VM networking setting</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/image-21-719x1024.png" alt=""/>
        <figcaption>VM adding inbound network rules</figcaption>
    </figure>

</p></p>
<p>When you add both ports (you would need to give them unique names and priority orders), and the final results would look something like the screenshot shown below.</p>
<p><p>

    <figure>
        <img src="images/image-22-1024x285.png" alt=""/>
        <figcaption>VM network inbound port rules</figcaption>
    </figure>

</p></p>
<p>And finally, we can <code>ssh</code> into that machine using the credentials and the IP we configured earlier. This can be done using ssh (e.g. <code>ssh user-name@IP-address</code> of the machine).</p>
<p><p>

    <figure>
        <img src="images/3-VM-ssh-1000x1024.jpg" alt=""/>
        <figcaption>SSH Screen shot</figcaption>
    </figure>

</p></p>
<h3 id="step-4---install-docker">Step 4 - Install Docker</h3>
<p>The first thing we need to do once we ssh into the machine is to update the various packages installed. The first time you run this, it will take a few minutes. You do this by running the following commands.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># I prefer to run these separately - to get a handle on what is getting updated.</span>
</span></span><span style="display:flex;"><span>sudo apt update
</span></span><span style="display:flex;"><span>sudo apt upgrade
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># You can of course run them together if that is what you prefer</span>
</span></span><span style="display:flex;"><span>sudo apt-get update</span></span></code></pre></td></tr></table>
</div>
</div>
<p>This is pretty standard and should not cause any issues; below is the screenshot showing the output - there are too many packages being updated for me to show everything.</p>
<p><p>

    <figure>
        <img src="images/4-OS-Update-1024x749.jpg" alt=""/>
        <figcaption>OS Updating screenshot</figcaption>
    </figure>

</p></p>
<p>Installing docker on our Ubuntu VM isn&rsquo;t terribly complex - the <a
	
		href = "https://docs.docker.com/engine/install/ubuntu/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		docker docs outline all the steps
	</span>
</a> and the details. We will want to install from the repro and follow the steps outlined and be mindful of specific versions and drivers.</p>
<p>We setup the repository, and for that need to install the following prerequisites.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    ca-certificates <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    curl <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    gnupg <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>    lsb-release</span></span></code></pre></td></tr></table>
</div>
</div>
<p>In my case, with the latest Ubuntu image in Azure, we already had these:</p>
<p><p>

    <img src="images/image.png" alt=""/>

</p></p>
<p>Next we add docker&rsquo;s GPG key using the following command:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The output isn&rsquo;t dramatic in case you were wondering.</p>
<p><p>

    <figure>
        <img src="images/image-1-1024x75.png" alt=""/>
        <figcaption>GPG key addition</figcaption>
    </figure>

</p></p>
<p>Next we add docker&rsquo;s repro to Ubuntu - this will allow us to find and install the packages.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#91d7e3">echo</span> <span style="color:#8aadf4">\
</span></span></span><span style="display:flex;"><span><span style="color:#8aadf4"></span>  <span style="color:#a6da95">&#34;deb [arch=</span><span style="color:#c6a0f6">$(</span>dpkg --print-architecture<span style="color:#c6a0f6">)</span><span style="color:#a6da95"> signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">  </span><span style="color:#c6a0f6">$(</span>lsb_release -cs<span style="color:#c6a0f6">)</span><span style="color:#a6da95"> stable&#34;</span> | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-2-1024x80.png" alt=""/>
        <figcaption>Adding docker repro</figcaption>
    </figure>

</p></p>
<p>At this time, we should run <code>apt-get</code> update command to update the newly added repository. We should check to ensure that docker is going to be installed from the docker repository, and not Ubuntu&rsquo;s default. To do this we run the following command.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>apt-cache policy docker-ce</span></span></code></pre></td></tr></table>
</div>
</div>
<p>This shows us that docker isn&rsquo;t installed, but the candidate for installation is from docker.com and is for &ldquo;focal&rdquo; - which is the release name of Ubuntu v20.04. The list we see is long because it outlines the different versions of docker.</p>
<p><p>

    <figure>
        <img src="images/image-3-992x1024.png" alt=""/>
        <figcaption>Check if docker is installed</figcaption>
    </figure>

</p></p>
<p>Now, we are finally ready to install docker using the following command and also choosing Yes on the prompts that confirm the installation.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo apt-get install docker-ce docker-ce-cli containerd.io</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Once complete, you will see something like the output shown below.</p>
<p><p>

    <figure>
        <img src="images/image-4-1024x721.png" alt=""/>
        <figcaption>Docker installation</figcaption>
    </figure>

</p></p>
<p>At this time, docker should be installed running. We can also check its daemon is configured to run on booting up.</p>
<p><p>

    <figure>
        <img src="images/image-5-1024x407.png" alt=""/>
        <figcaption>Docker daemon confirmation</figcaption>
    </figure>

</p></p>
<p>Whilst not needed, it is good practice to add the current username to the docker group created - this will ensure we don&rsquo;t need to use &ldquo;<code>sudo</code>&rdquo; for every docker command. And using the groups command we can validate our current username (&quot;<code>amit</code>&quot; in my case) is in the docker group.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo usermod -aG docker <span style="color:#a6da95">${</span><span style="color:#f4dbd6">USER</span><span style="color:#a6da95">}</span>
</span></span><span style="display:flex;"><span>su - <span style="color:#a6da95">${</span><span style="color:#f4dbd6">USER</span><span style="color:#a6da95">}</span> <span style="color:#6e738d;font-style:italic"># this allows us to add the user without logging out</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-7-1024x184.png" alt=""/>
        <figcaption>Adding user to docker group</figcaption>
    </figure>

</p></p>
<p>Woot! We have docker running. &#x1f60e;</p>
<p><p>

    <figure>
        <img src="images/image-8.png" alt=""/>
        <figcaption>Docker running</figcaption>
    </figure>

</p></p>
<p>The first thing one should do with any new docker installation is to run its equivalent of Hello World. This is done using the following command - which downloads a test image and runs it in a container, prints a message, and then exists the container - so a full life cycle.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker run hello-world</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-10-1024x846.png" alt=""/>
        <figcaption>Testing docker</figcaption>
    </figure>

</p></p>
<p>And yay, we validated that docker is up and running on our VM! Congratulations.</p>
<p>Before we get to configuring TeslaMate, we also need to install <code>docker-compose</code>, which is a tool that allows us to run multi-container docker applications (such as TeslaMate). We will install docker-compose using the following command with the result of that command shown after that.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>sudo apt install docker-compose</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-17-1024x737.png" alt=""/>
        <figcaption>Install docker-compose</figcaption>
    </figure>

</p></p>
<h3 id="step-5---configure-teslamate">Step 5 - Configure TeslaMate</h3>
<p>Given we will be exposing TeslaMate to the internet directly we should not use the default TeslaMate docker installation, but the <a
	
		href = "https://docs.teslamate.org/docs/guides/traefik"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		advanced version
	</span>
</a> which uses <a
	
		href = "https://doc.traefik.io/traefik/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Traefik as a proxy server
	</span>
</a> and helps us secure the web server better and only expose the (Grafana) dashboards behind an authentication mechanism.</p>
<p>For this we will create a new folder for TeslaMate which will contain not only the docker compose file needed but other relevant configuration details. I like to keep this in a folder, to help manage - in this case it resides in <code>~/docker/teslamate</code></p>
<p><p>

    <figure>
        <img src="images/image-11.png" alt=""/>
        <figcaption>Creating folder for TeslaMate</figcaption>
    </figure>

</p></p>
<p>It is in this folder we will create the docker-compose yaml file that is needed; you would want to start with the one outlined in the <a
	
		href = "https://docs.teslamate.org/docs/guides/traefik"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		TeslaMate
	</span>
</a> instructions and tweak it for your needs.</p>
<p>This file needs to be called <code>docker-compose.yml</code> and my example is shared below. It is a good idea to always get the <a
	
		href = "https://docs.teslamate.org/docs/guides/traefik#docker-composeyml"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		latest yaml file from TeslaMate&rsquo;s docs
	</span>
</a> - over time we would expect things will evolve and the file below might not be accurate down the road.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#c6a0f6">version</span>: <span style="color:#a6da95">&#34;3&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">teslamate</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: teslamate/teslamate:latest
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">restart</span>: always
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">depends_on</span>:
</span></span><span style="display:flex;"><span>      - database
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">environment</span>:
</span></span><span style="display:flex;"><span>      - DATABASE_USER=${TM_DB_USER}
</span></span><span style="display:flex;"><span>      - DATABASE_PASS=${TM_DB_PASS}
</span></span><span style="display:flex;"><span>      - DATABASE_NAME=${TM_DB_NAME}
</span></span><span style="display:flex;"><span>      - DATABASE_HOST=database
</span></span><span style="display:flex;"><span>      - MQTT_HOST=mosquitto
</span></span><span style="display:flex;"><span>      - VIRTUAL_HOST=${FQDN_TM}
</span></span><span style="display:flex;"><span>      - CHECK_ORIGIN=true
</span></span><span style="display:flex;"><span>      - TZ=${TM_TZ}
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>      - ./import:/opt/app/import
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">labels</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.enable=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.port=4000&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.middlewares.redirect.redirectscheme.scheme=https&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.middlewares.teslamate-auth.basicauth.realm=teslamate&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.middlewares.teslamate-auth.basicauth.usersfile=/auth/.htpasswd&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate-insecure.rule=Host(`${FQDN_TM}`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate-insecure.middlewares=redirect&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate-ws.rule=Host(`${FQDN_TM}`) &amp;&amp; Path(`/live/websocket`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate-ws.entrypoints=websecure&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate-ws.tls&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate.rule=Host(`${FQDN_TM}`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate.middlewares=teslamate-auth&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate.entrypoints=websecure&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.teslamate.tls.certresolver=tmhttpchallenge&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">cap_drop</span>:
</span></span><span style="display:flex;"><span>      - all
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">database</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: postgres:13
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">restart</span>: always
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">environment</span>:
</span></span><span style="display:flex;"><span>      - POSTGRES_USER=${TM_DB_USER}
</span></span><span style="display:flex;"><span>      - POSTGRES_PASSWORD=${TM_DB_PASS}
</span></span><span style="display:flex;"><span>      - POSTGRES_DB=${TM_DB_NAME}
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>      - teslamate-db:/var/lib/postgresql/data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">grafana</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: teslamate/grafana:latest
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">restart</span>: always
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">environment</span>:
</span></span><span style="display:flex;"><span>      - DATABASE_USER=${TM_DB_USER}
</span></span><span style="display:flex;"><span>      - DATABASE_PASS=${TM_DB_PASS}
</span></span><span style="display:flex;"><span>      - DATABASE_NAME=${TM_DB_NAME}
</span></span><span style="display:flex;"><span>      - DATABASE_HOST=database
</span></span><span style="display:flex;"><span>      - GRAFANA_PASSWD=${GRAFANA_PW}
</span></span><span style="display:flex;"><span>      - GF_SECURITY_ADMIN_USER=${GRAFANA_USER}
</span></span><span style="display:flex;"><span>      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PW}
</span></span><span style="display:flex;"><span>      - GF_AUTH_ANONYMOUS_ENABLED=false
</span></span><span style="display:flex;"><span>      - GF_SERVER_DOMAIN=${FQDN_TM}
</span></span><span style="display:flex;"><span>      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
</span></span><span style="display:flex;"><span>      - GF_SERVER_SERVE_FROM_SUB_PATH=true
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>      - teslamate-grafana-data:/var/lib/grafana
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">labels</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.enable=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.port=3000&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.middlewares.redirect.redirectscheme.scheme=https&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.grafana-insecure.rule=Host(`${FQDN_TM}`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.grafana-insecure.middlewares=redirect&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.grafana.rule=Path(`/grafana`) || PathPrefix(`/grafana/`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.grafana.entrypoints=websecure&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;traefik.http.routers.grafana.tls.certresolver=tmhttpchallenge&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">mosquitto</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: eclipse-mosquitto:2
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">restart</span>: always
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">command</span>: mosquitto -c /mosquitto-no-auth.conf
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f5a97f">127.0.0.1</span>:<span style="color:#f5a97f">1883</span>:<span style="color:#f5a97f">1883</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>      - mosquitto-conf:/mosquitto/config
</span></span><span style="display:flex;"><span>      - mosquitto-data:/mosquitto/data
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">proxy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: traefik:v2.4
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">restart</span>: always
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">command</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--global.sendAnonymousUsage=false&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--providers.docker&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--providers.docker.exposedByDefault=false&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--entrypoints.web.address=:80&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--entrypoints.websecure.address=:443&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--certificatesresolvers.tmhttpchallenge.acme.httpchallenge=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--certificatesresolvers.tmhttpchallenge.acme.httpchallenge.entrypoint=web&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--certificatesresolvers.tmhttpchallenge.acme.email=${LETSENCRYPT_EMAIL}&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#a6da95">&#34;--certificatesresolvers.tmhttpchallenge.acme.storage=/etc/acme/acme.json&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f5a97f">80</span>:<span style="color:#f5a97f">80</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f5a97f">443</span>:<span style="color:#f5a97f">443</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>      - ./.htpasswd:/auth/.htpasswd
</span></span><span style="display:flex;"><span>      - ./acme/:/etc/acme/
</span></span><span style="display:flex;"><span>      - /var/run/docker.sock:/var/run/docker.sock:ro
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">volumes</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">teslamate-db</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">teslamate-grafana-data</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">mosquitto-conf</span>:
</span></span><span style="display:flex;"><span>  mosquitto-data:</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-12-1024x867.png" alt=""/>
        <figcaption>Screenshot showing the docker-compose.yml file</figcaption>
    </figure>

</p></p>
<p>Next we need to create a <strong><code>.env</code> file</strong>. The environmental secrets (i.e. passwords) are not saved in the yaml file but are stored are stored in this <code>.env</code> file which we will create next.</p>
<p>We will enter the DNS name as the FQDN that you setup earlier for the VM; update the TM_TZ for the time-zone you are based out of. This is the TZ database name, and if you aren&rsquo;t sure what it should be for your time-zone, check out the <a
	
		href = "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		details here
	</span>
</a>.</p>
<p>Like the yaml file, you should get the <a
	
		href = "https://docs.teslamate.org/docs/guides/traefik#env"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		latest .env file template from TeslaMate
	</span>
</a>, as the one shown below might change over time.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>TM_DB_USER=teslamate
</span></span><span style="display:flex;"><span>TM_DB_PASS=secret
</span></span><span style="display:flex;"><span>TM_DB_NAME=teslamate
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>GRAFANA_USER=admin
</span></span><span style="display:flex;"><span>GRAFANA_PW=admin
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>FQDN_TM=teslamate.example.com
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>TM_TZ=Europe/Berlin
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>LETSENCRYPT_EMAIL=yourperson@example.com</span></span></code></pre></td></tr></table>
</div>
</div>
<p>If you are not sure on how to create a new file in Ubuntu (or any other Linux distro for that matter) - you can use <code>nano</code> editor as shown below. You need to make sure this is in the same folder as where the <code>docker-compose.yml</code> file is (which is ~/docker/teslamate in our example).</p>
<p><p>

    <figure>
        <img src="images/image-13.png" alt=""/>
        <figcaption>Console screenshot showing how to create .env file</figcaption>
    </figure>

</p></p>
<p>And finally, we need to create a <strong><code>.htpasswd</code></strong> file which is used to authenticate the website (see TeslaMate&rsquo;s <a
	
		href = "https://docs.teslamate.org/docs/guides/traefik#htpasswd"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		documentation
	</span>
</a> for more details). I chose to create this locally after installing Apache tools, but you can also do it online.</p>
<p><strong>Note:</strong> this is *not* your Tesla login credentials but are the credentials you will use to access the site we are setting up now.</p>
<p><p>

    <figure>
        <img src="images/image-14.png" alt=""/>
        <figcaption>Console screenshot showing installation of Apache Utils</figcaption>
    </figure>

</p></p>
<p>We can create a new file password as shown below. Given we are in the TeslaMate folder, we don&rsquo;t have to provide a full path for the file.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>htpasswd -c .htpasswd amit</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-15.png" alt=""/>
        <figcaption>Screenshot showing htpasswd usage</figcaption>
    </figure>

</p></p>
<p>So, in the end we should have the following three files in the same folder:</p>
<p><p>

    <figure>
        <img src="images/image-16.png" alt=""/>
        <figcaption>Screenshot showing directory listing</figcaption>
    </figure>

</p></p>
<h3 id="step-6---starting-teslamate">Step 6 - Starting TeslaMate</h3>
<p>Now we are finally ready to start the docker container for TeslaMate. When this launches, go to the URL for the DNS name you setup, and login using your Tesla credentials. For the first time, I would recommend running the container attached to the console, so if there are any errors or issues you can see them. Normally you would want to run this detached (which is using the &ldquo;<code>-d</code>&rdquo;) option.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># don&#39;t forget the sudo command</span>
</span></span><span style="display:flex;"><span>sudo docker-compose up</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The first time you run this, it will take a few minutes to pull all the images, and wire things up. During the process you will see the progress for each image in the various container app.</p>
<p><p>

    <img src="images/image-18-1024x717.png" alt=""/>

</p> &ldquo;Console showing docker-compose progress&rdquo;)</p>
<p>And finally, if everything is setup properly you should see the container running with the logs in the console of your terminal. This is a running log, and the process is active. You will see something like the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/5-Docker-Compose-Up-1024x717.jpg" alt=""/>
        <figcaption>Console showing docker-compose logs</figcaption>
    </figure>

</p></p>
<p>If you didn&rsquo;t setup a DNS name earlier and thought you can try and use the IP name - that unfortunately will fail with the Traefik proxy server and in the logs, you will see an error to that effect. Let&rsquo;s Encrypt doesn&rsquo;t issue certificates for IP addresses <a
	
		href = "https://community.letsencrypt.org/t/ssl-certificate-for-ip-address/52054"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		as a policy
	</span>
</a>.</p>
<p>Now if we browse the URL (also known as the FQDN) you had setup earlier, you will see an authentication challenge. This is great and shows that the proxy server is setup properly and working as expected.</p>
<p><p>

    <figure>
        <img src="images/5-Server-Login-1024x449.jpg" alt=""/>
        <figcaption>Traefik http authentication</figcaption>
    </figure>

</p></p>
<p>Once you enter the credentials you setup in the .<code>htpasswd</code> file earlier you will be able to login and see the TeslaMate&rsquo;s Tesla login!</p>
<p><p>

    <figure>
        <img src="images/5-TM-Login-1024x916.jpg" alt=""/>
        <figcaption>TeslaMate Tesla authentication screen</figcaption>
    </figure>

</p></p>
<p>Congratulations! You have TeslaMate running on Azure. ✌️</p>
<h3 id="step-7---finishing-up-teslamate-configuration">Step 7 - Finishing up TeslaMate configuration</h3>
<p>Now that you have TeslaMate running, you need to login to Tesla. The best way to do this these days is using existing tokens . There are a few ways to do this, and one of the easiest is using <a
	
		href = "https://tesla-info.com/tesla-token.php"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		this tool - Tesla API Token
	</span>
</a>.</p>
<p>Once you login, go to <code>Settings</code> and change the Dashboards URL - which would be your FQDN with &ldquo;<code>/grafana</code>&rdquo; appended. Remember the credentials you use for the dashboards (i.e. Grafana) are the ones you set in the <code>.env</code> file.</p>
<p><p>

    <figure>
        <img src="images/5-TM-Dashboard-URL.jpg" alt=""/>
        <figcaption>TeslaMate URL configuration</figcaption>
    </figure>

</p></p>
<p>Now that everything is up and running, we can kill the docker-compose process, which is attached to the console, and re-run it detached from the console. To do this, go back to the <code>ssh</code> session we have connected to the Ubuntu VM and press CTRL + C. This will stop that container and you will see a similar output as shown below.</p>
<p><p>

    <figure>
        <img src="images/image-23-1024x796.png" alt=""/>
        <figcaption>Console output</figcaption>
    </figure>

</p></p>
<p>And now you can restart the container with the &ldquo;-d&rdquo; option, which is for detached.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo docker-compose up -d</span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-26.png" alt=""/>
        <figcaption>docker-compose output</figcaption>
    </figure>

</p></p>
<p>Congratulations, you have TeslaMate running on an Azure host Ubuntu VM via docker. &#x270c;&#xfe0f;</p>
<p>Below is a screenshot of my instance that has been running for some time.
<p>

    <figure>
        <img src="images/image-27.png" alt=""/>
        <figcaption>TeslaMate Overview Dashboard</figcaption>
    </figure>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>AI writing AI code🤐</title>
      <link>/post/2021/10/ai-writing-ai-code/</link>
      <pubDate>Sun, 10 Oct 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/10/ai-writing-ai-code/</guid>
      <description>&lt;p&gt;It is 2021. And we have #AI writing #AI code. 🤪 It is quite interesting, but also can be quite boring once you get beyond the initial technology, and just think of it as one of the tools in your arsenal. And getting to that point is a good think.&lt;/p&gt;
&lt;p&gt;As part of a think at work I recently started playing with &lt;a
	
		href = &#34;https://copilot.github.com/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		GitHub Copilot
	&lt;/span&gt;
&lt;/a&gt;, which is using GPT3 to be your pair programmer &amp;ndash; helping write code. GPT3 has &lt;a
	
		href = &#34;https://www.desigeek.com/blog/amit/2021/06/21/gpt-3-vs-other-ai-powered-assistants/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		multiple models
	&lt;/span&gt;
&lt;/a&gt; (called engines), and Copilot uses one of these family of engines called Codex. Codex is a derivative of the base GPT3 engine that is trained on billions of lines of code.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>It is 2021. And we have #AI writing #AI code. 🤪 It is quite interesting, but also can be quite boring once you get beyond the initial technology, and just think of it as one of the tools in your arsenal. And getting to that point is a good think.</p>
<p>As part of a think at work I recently started playing with <a
	
		href = "https://copilot.github.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		GitHub Copilot
	</span>
</a>, which is using GPT3 to be your pair programmer &ndash; helping write code. GPT3 has <a
	
		href = "https://www.desigeek.com/blog/amit/2021/06/21/gpt-3-vs-other-ai-powered-assistants/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		multiple models
	</span>
</a> (called engines), and Copilot uses one of these family of engines called Codex. Codex is a derivative of the base GPT3 engine that is trained on billions of lines of code.</p>
<p>Using Copilot is quite simple; you install the <a
	
		href = "https://marketplace.visualstudio.com/items?itemName=GitHub.copilot"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Github Copilot extension,
	</span>
</a> and it shows up in your IDE (VSCode in my example). We need to make sure we decompose the problem we are trying to solve - we should not think of this as helping write the complete program or all parts; but as it can help with different functions and pieces of code. To do this, we need to tell it what we are trying to do - these are done via prompts (code comments). For GPT models, prompt engineering is quite critical, and would be worth getting to details and understanding.</p>
<p>Starting simple, I create an empty python file and entered a prompt that outlines what I want to try and do. In this case as you can see in the screenshot below - I want to load an image from a file, and using our <a
	
		href = "https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/#overview"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Vision Cognitive Services
	</span>
</a>, run an image analysis, and auto-generate a caption for that image.</p>
<p><p>

    <img src="images/image-1024x538.png" alt=""/>

</p></p>
<p>I started typing the definition of a function, and Copilot (via the add-in) understands the prompt I outlined, and the context of the code on what I am doing. Remember Codex builds on the base GPT3 and does have all that NLU capability.</p>
<p>Taking all of this in, it suggests completing the function for me. In terms of using this as an end-user (i.e. the developer) - the suggested code shows up as auto-complete and you can see it in the grey color. If I like that suggestion, I press tab and have it added to the file.</p>
<p>In this case you can see how it is reading the file from disk, calling a function called <code>get_caption()</code> and printing the caption to the stdout (console in this example).</p>
<p>There is also an option to cycle through different suggestions and then pick another one as shown in the screenshot below.</p>
<p><p>

    <img src="images/image-1-1024x551.png" alt=""/>

</p></p>
<p>This variant of the suggested code is creating a function called <code>image_caption()</code> which takes the path to the image file to load. This also expects other required things for the Vision cognitive service to work - such as the subscription key to authenticate, the API end-point details to call, etc.</p>
<p>Typically, Copilot can synthesize up to 10 code options (Copilot calls these as Solutions), that one can cycle through and see if there is a better variant for the task at hand. The screenshot below shows this experience in VS Code.</p>
<p><p>

    <img src="images/image-2-1024x829.png" alt=""/>

</p></p>
<p>The ask whilst simple, still involves a bit of code which needs to be written - reading from file, setting up the subscription details, and wiring that up to call this etc. And it is in these cases really where Copilot shines - it is your copilot picking up the &lsquo;gunk work&rsquo; - freeing up your bandwidth, and cognition capacity on the more interesting and higher order bit of code and value to your business.</p>
<p>To get a flavor of our AI writing AI code, below is the full set of the nine suggestions the Copilot came back for what I was trying to do.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="137"><a style="outline:none;text-decoration:none;color:inherit" href="#137">137</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="138"><a style="outline:none;text-decoration:none;color:inherit" href="#138">138</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="139"><a style="outline:none;text-decoration:none;color:inherit" href="#139">139</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="140"><a style="outline:none;text-decoration:none;color:inherit" href="#140">140</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="141"><a style="outline:none;text-decoration:none;color:inherit" href="#141">141</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="142"><a style="outline:none;text-decoration:none;color:inherit" href="#142">142</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="143"><a style="outline:none;text-decoration:none;color:inherit" href="#143">143</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="144"><a style="outline:none;text-decoration:none;color:inherit" href="#144">144</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="145"><a style="outline:none;text-decoration:none;color:inherit" href="#145">145</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="146"><a style="outline:none;text-decoration:none;color:inherit" href="#146">146</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="147"><a style="outline:none;text-decoration:none;color:inherit" href="#147">147</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="148"><a style="outline:none;text-decoration:none;color:inherit" href="#148">148</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="149"><a style="outline:none;text-decoration:none;color:inherit" href="#149">149</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="150"><a style="outline:none;text-decoration:none;color:inherit" href="#150">150</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="151"><a style="outline:none;text-decoration:none;color:inherit" href="#151">151</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="152"><a style="outline:none;text-decoration:none;color:inherit" href="#152">152</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="153"><a style="outline:none;text-decoration:none;color:inherit" href="#153">153</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="154"><a style="outline:none;text-decoration:none;color:inherit" href="#154">154</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="155"><a style="outline:none;text-decoration:none;color:inherit" href="#155">155</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="156"><a style="outline:none;text-decoration:none;color:inherit" href="#156">156</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="157"><a style="outline:none;text-decoration:none;color:inherit" href="#157">157</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="158"><a style="outline:none;text-decoration:none;color:inherit" href="#158">158</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="159"><a style="outline:none;text-decoration:none;color:inherit" href="#159">159</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="160"><a style="outline:none;text-decoration:none;color:inherit" href="#160">160</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="161"><a style="outline:none;text-decoration:none;color:inherit" href="#161">161</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="162"><a style="outline:none;text-decoration:none;color:inherit" href="#162">162</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="163"><a style="outline:none;text-decoration:none;color:inherit" href="#163">163</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="164"><a style="outline:none;text-decoration:none;color:inherit" href="#164">164</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="165"><a style="outline:none;text-decoration:none;color:inherit" href="#165">165</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="166"><a style="outline:none;text-decoration:none;color:inherit" href="#166">166</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="167"><a style="outline:none;text-decoration:none;color:inherit" href="#167">167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="168"><a style="outline:none;text-decoration:none;color:inherit" href="#168">168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="169"><a style="outline:none;text-decoration:none;color:inherit" href="#169">169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="170"><a style="outline:none;text-decoration:none;color:inherit" href="#170">170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="171"><a style="outline:none;text-decoration:none;color:inherit" href="#171">171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="172"><a style="outline:none;text-decoration:none;color:inherit" href="#172">172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="173"><a style="outline:none;text-decoration:none;color:inherit" href="#173">173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="174"><a style="outline:none;text-decoration:none;color:inherit" href="#174">174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="175"><a style="outline:none;text-decoration:none;color:inherit" href="#175">175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="176"><a style="outline:none;text-decoration:none;color:inherit" href="#176">176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="177"><a style="outline:none;text-decoration:none;color:inherit" href="#177">177</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="178"><a style="outline:none;text-decoration:none;color:inherit" href="#178">178</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="179"><a style="outline:none;text-decoration:none;color:inherit" href="#179">179</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="180"><a style="outline:none;text-decoration:none;color:inherit" href="#180">180</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="181"><a style="outline:none;text-decoration:none;color:inherit" href="#181">181</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="182"><a style="outline:none;text-decoration:none;color:inherit" href="#182">182</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="183"><a style="outline:none;text-decoration:none;color:inherit" href="#183">183</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="184"><a style="outline:none;text-decoration:none;color:inherit" href="#184">184</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="185"><a style="outline:none;text-decoration:none;color:inherit" href="#185">185</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="186"><a style="outline:none;text-decoration:none;color:inherit" href="#186">186</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="187"><a style="outline:none;text-decoration:none;color:inherit" href="#187">187</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="188"><a style="outline:none;text-decoration:none;color:inherit" href="#188">188</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="189"><a style="outline:none;text-decoration:none;color:inherit" href="#189">189</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="190"><a style="outline:none;text-decoration:none;color:inherit" href="#190">190</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="191"><a style="outline:none;text-decoration:none;color:inherit" href="#191">191</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="192"><a style="outline:none;text-decoration:none;color:inherit" href="#192">192</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="193"><a style="outline:none;text-decoration:none;color:inherit" href="#193">193</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="194"><a style="outline:none;text-decoration:none;color:inherit" href="#194">194</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="195"><a style="outline:none;text-decoration:none;color:inherit" href="#195">195</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="196"><a style="outline:none;text-decoration:none;color:inherit" href="#196">196</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="197"><a style="outline:none;text-decoration:none;color:inherit" href="#197">197</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="198"><a style="outline:none;text-decoration:none;color:inherit" href="#198">198</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="199"><a style="outline:none;text-decoration:none;color:inherit" href="#199">199</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="200"><a style="outline:none;text-decoration:none;color:inherit" href="#200">200</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="201"><a style="outline:none;text-decoration:none;color:inherit" href="#201">201</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="202"><a style="outline:none;text-decoration:none;color:inherit" href="#202">202</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="203"><a style="outline:none;text-decoration:none;color:inherit" href="#203">203</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="204"><a style="outline:none;text-decoration:none;color:inherit" href="#204">204</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="205"><a style="outline:none;text-decoration:none;color:inherit" href="#205">205</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="206"><a style="outline:none;text-decoration:none;color:inherit" href="#206">206</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="207"><a style="outline:none;text-decoration:none;color:inherit" href="#207">207</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="208"><a style="outline:none;text-decoration:none;color:inherit" href="#208">208</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="209"><a style="outline:none;text-decoration:none;color:inherit" href="#209">209</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="210"><a style="outline:none;text-decoration:none;color:inherit" href="#210">210</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="211"><a style="outline:none;text-decoration:none;color:inherit" href="#211">211</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="212"><a style="outline:none;text-decoration:none;color:inherit" href="#212">212</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="213"><a style="outline:none;text-decoration:none;color:inherit" href="#213">213</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="214"><a style="outline:none;text-decoration:none;color:inherit" href="#214">214</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="215"><a style="outline:none;text-decoration:none;color:inherit" href="#215">215</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="216"><a style="outline:none;text-decoration:none;color:inherit" href="#216">216</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="217"><a style="outline:none;text-decoration:none;color:inherit" href="#217">217</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="218"><a style="outline:none;text-decoration:none;color:inherit" href="#218">218</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="219"><a style="outline:none;text-decoration:none;color:inherit" href="#219">219</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="220"><a style="outline:none;text-decoration:none;color:inherit" href="#220">220</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="221"><a style="outline:none;text-decoration:none;color:inherit" href="#221">221</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="222"><a style="outline:none;text-decoration:none;color:inherit" href="#222">222</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="223"><a style="outline:none;text-decoration:none;color:inherit" href="#223">223</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="224"><a style="outline:none;text-decoration:none;color:inherit" href="#224">224</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="225"><a style="outline:none;text-decoration:none;color:inherit" href="#225">225</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="226"><a style="outline:none;text-decoration:none;color:inherit" href="#226">226</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="227"><a style="outline:none;text-decoration:none;color:inherit" href="#227">227</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="228"><a style="outline:none;text-decoration:none;color:inherit" href="#228">228</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="229"><a style="outline:none;text-decoration:none;color:inherit" href="#229">229</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="230"><a style="outline:none;text-decoration:none;color:inherit" href="#230">230</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="231"><a style="outline:none;text-decoration:none;color:inherit" href="#231">231</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="232"><a style="outline:none;text-decoration:none;color:inherit" href="#232">232</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="233"><a style="outline:none;text-decoration:none;color:inherit" href="#233">233</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="234"><a style="outline:none;text-decoration:none;color:inherit" href="#234">234</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="235"><a style="outline:none;text-decoration:none;color:inherit" href="#235">235</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="236"><a style="outline:none;text-decoration:none;color:inherit" href="#236">236</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="237"><a style="outline:none;text-decoration:none;color:inherit" href="#237">237</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="238"><a style="outline:none;text-decoration:none;color:inherit" href="#238">238</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="239"><a style="outline:none;text-decoration:none;color:inherit" href="#239">239</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="240"><a style="outline:none;text-decoration:none;color:inherit" href="#240">240</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="241"><a style="outline:none;text-decoration:none;color:inherit" href="#241">241</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="242"><a style="outline:none;text-decoration:none;color:inherit" href="#242">242</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="243"><a style="outline:none;text-decoration:none;color:inherit" href="#243">243</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="244"><a style="outline:none;text-decoration:none;color:inherit" href="#244">244</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="245"><a style="outline:none;text-decoration:none;color:inherit" href="#245">245</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="246"><a style="outline:none;text-decoration:none;color:inherit" href="#246">246</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="247"><a style="outline:none;text-decoration:none;color:inherit" href="#247">247</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="248"><a style="outline:none;text-decoration:none;color:inherit" href="#248">248</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="249"><a style="outline:none;text-decoration:none;color:inherit" href="#249">249</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="250"><a style="outline:none;text-decoration:none;color:inherit" href="#250">250</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="251"><a style="outline:none;text-decoration:none;color:inherit" href="#251">251</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="252"><a style="outline:none;text-decoration:none;color:inherit" href="#252">252</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="253"><a style="outline:none;text-decoration:none;color:inherit" href="#253">253</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="254"><a style="outline:none;text-decoration:none;color:inherit" href="#254">254</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="255"><a style="outline:none;text-decoration:none;color:inherit" href="#255">255</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="256"><a style="outline:none;text-decoration:none;color:inherit" href="#256">256</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="257"><a style="outline:none;text-decoration:none;color:inherit" href="#257">257</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="258"><a style="outline:none;text-decoration:none;color:inherit" href="#258">258</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="259"><a style="outline:none;text-decoration:none;color:inherit" href="#259">259</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="260"><a style="outline:none;text-decoration:none;color:inherit" href="#260">260</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="261"><a style="outline:none;text-decoration:none;color:inherit" href="#261">261</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="262"><a style="outline:none;text-decoration:none;color:inherit" href="#262">262</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="263"><a style="outline:none;text-decoration:none;color:inherit" href="#263">263</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="264"><a style="outline:none;text-decoration:none;color:inherit" href="#264">264</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="265"><a style="outline:none;text-decoration:none;color:inherit" href="#265">265</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="266"><a style="outline:none;text-decoration:none;color:inherit" href="#266">266</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="267"><a style="outline:none;text-decoration:none;color:inherit" href="#267">267</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="268"><a style="outline:none;text-decoration:none;color:inherit" href="#268">268</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="269"><a style="outline:none;text-decoration:none;color:inherit" href="#269">269</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="270"><a style="outline:none;text-decoration:none;color:inherit" href="#270">270</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="271"><a style="outline:none;text-decoration:none;color:inherit" href="#271">271</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="272"><a style="outline:none;text-decoration:none;color:inherit" href="#272">272</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="273"><a style="outline:none;text-decoration:none;color:inherit" href="#273">273</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="274"><a style="outline:none;text-decoration:none;color:inherit" href="#274">274</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="275"><a style="outline:none;text-decoration:none;color:inherit" href="#275">275</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="276"><a style="outline:none;text-decoration:none;color:inherit" href="#276">276</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="277"><a style="outline:none;text-decoration:none;color:inherit" href="#277">277</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="278"><a style="outline:none;text-decoration:none;color:inherit" href="#278">278</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="279"><a style="outline:none;text-decoration:none;color:inherit" href="#279">279</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="280"><a style="outline:none;text-decoration:none;color:inherit" href="#280">280</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="281"><a style="outline:none;text-decoration:none;color:inherit" href="#281">281</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="282"><a style="outline:none;text-decoration:none;color:inherit" href="#282">282</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="283"><a style="outline:none;text-decoration:none;color:inherit" href="#283">283</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="284"><a style="outline:none;text-decoration:none;color:inherit" href="#284">284</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="285"><a style="outline:none;text-decoration:none;color:inherit" href="#285">285</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="286"><a style="outline:none;text-decoration:none;color:inherit" href="#286">286</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="287"><a style="outline:none;text-decoration:none;color:inherit" href="#287">287</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="288"><a style="outline:none;text-decoration:none;color:inherit" href="#288">288</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="289"><a style="outline:none;text-decoration:none;color:inherit" href="#289">289</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="290"><a style="outline:none;text-decoration:none;color:inherit" href="#290">290</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="291"><a style="outline:none;text-decoration:none;color:inherit" href="#291">291</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="292"><a style="outline:none;text-decoration:none;color:inherit" href="#292">292</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="293"><a style="outline:none;text-decoration:none;color:inherit" href="#293">293</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="294"><a style="outline:none;text-decoration:none;color:inherit" href="#294">294</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="295"><a style="outline:none;text-decoration:none;color:inherit" href="#295">295</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="296"><a style="outline:none;text-decoration:none;color:inherit" href="#296">296</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="297"><a style="outline:none;text-decoration:none;color:inherit" href="#297">297</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="298"><a style="outline:none;text-decoration:none;color:inherit" href="#298">298</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="299"><a style="outline:none;text-decoration:none;color:inherit" href="#299">299</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="300"><a style="outline:none;text-decoration:none;color:inherit" href="#300">300</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="301"><a style="outline:none;text-decoration:none;color:inherit" href="#301">301</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="302"><a style="outline:none;text-decoration:none;color:inherit" href="#302">302</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="303"><a style="outline:none;text-decoration:none;color:inherit" href="#303">303</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="304"><a style="outline:none;text-decoration:none;color:inherit" href="#304">304</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="305"><a style="outline:none;text-decoration:none;color:inherit" href="#305">305</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="306"><a style="outline:none;text-decoration:none;color:inherit" href="#306">306</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="307"><a style="outline:none;text-decoration:none;color:inherit" href="#307">307</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="308"><a style="outline:none;text-decoration:none;color:inherit" href="#308">308</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="309"><a style="outline:none;text-decoration:none;color:inherit" href="#309">309</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="310"><a style="outline:none;text-decoration:none;color:inherit" href="#310">310</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="311"><a style="outline:none;text-decoration:none;color:inherit" href="#311">311</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="312"><a style="outline:none;text-decoration:none;color:inherit" href="#312">312</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="313"><a style="outline:none;text-decoration:none;color:inherit" href="#313">313</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="314"><a style="outline:none;text-decoration:none;color:inherit" href="#314">314</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="315"><a style="outline:none;text-decoration:none;color:inherit" href="#315">315</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="316"><a style="outline:none;text-decoration:none;color:inherit" href="#316">316</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="317"><a style="outline:none;text-decoration:none;color:inherit" href="#317">317</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="318"><a style="outline:none;text-decoration:none;color:inherit" href="#318">318</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="319"><a style="outline:none;text-decoration:none;color:inherit" href="#319">319</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="320"><a style="outline:none;text-decoration:none;color:inherit" href="#320">320</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="321"><a style="outline:none;text-decoration:none;color:inherit" href="#321">321</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="322"><a style="outline:none;text-decoration:none;color:inherit" href="#322">322</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="323"><a style="outline:none;text-decoration:none;color:inherit" href="#323">323</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="324"><a style="outline:none;text-decoration:none;color:inherit" href="#324">324</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="325"><a style="outline:none;text-decoration:none;color:inherit" href="#325">325</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="326"><a style="outline:none;text-decoration:none;color:inherit" href="#326">326</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="327"><a style="outline:none;text-decoration:none;color:inherit" href="#327">327</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="328"><a style="outline:none;text-decoration:none;color:inherit" href="#328">328</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="329"><a style="outline:none;text-decoration:none;color:inherit" href="#329">329</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="330"><a style="outline:none;text-decoration:none;color:inherit" href="#330">330</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="331"><a style="outline:none;text-decoration:none;color:inherit" href="#331">331</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="332"><a style="outline:none;text-decoration:none;color:inherit" href="#332">332</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="333"><a style="outline:none;text-decoration:none;color:inherit" href="#333">333</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="334"><a style="outline:none;text-decoration:none;color:inherit" href="#334">334</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="335"><a style="outline:none;text-decoration:none;color:inherit" href="#335">335</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="336"><a style="outline:none;text-decoration:none;color:inherit" href="#336">336</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="337"><a style="outline:none;text-decoration:none;color:inherit" href="#337">337</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="338"><a style="outline:none;text-decoration:none;color:inherit" href="#338">338</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="339"><a style="outline:none;text-decoration:none;color:inherit" href="#339">339</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="340"><a style="outline:none;text-decoration:none;color:inherit" href="#340">340</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="341"><a style="outline:none;text-decoration:none;color:inherit" href="#341">341</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="342"><a style="outline:none;text-decoration:none;color:inherit" href="#342">342</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="343"><a style="outline:none;text-decoration:none;color:inherit" href="#343">343</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="344"><a style="outline:none;text-decoration:none;color:inherit" href="#344">344</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="345"><a style="outline:none;text-decoration:none;color:inherit" href="#345">345</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="346"><a style="outline:none;text-decoration:none;color:inherit" href="#346">346</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="347"><a style="outline:none;text-decoration:none;color:inherit" href="#347">347</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="348"><a style="outline:none;text-decoration:none;color:inherit" href="#348">348</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="349"><a style="outline:none;text-decoration:none;color:inherit" href="#349">349</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="350"><a style="outline:none;text-decoration:none;color:inherit" href="#350">350</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="351"><a style="outline:none;text-decoration:none;color:inherit" href="#351">351</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="352"><a style="outline:none;text-decoration:none;color:inherit" href="#352">352</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="353"><a style="outline:none;text-decoration:none;color:inherit" href="#353">353</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="354"><a style="outline:none;text-decoration:none;color:inherit" href="#354">354</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="355"><a style="outline:none;text-decoration:none;color:inherit" href="#355">355</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="356"><a style="outline:none;text-decoration:none;color:inherit" href="#356">356</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>Synthesizing <span style="color:#f5a97f">9</span><span style="color:#91d7e3;font-weight:bold">/</span><span style="color:#f5a97f">10</span> solutions
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision</span> <span style="color:#8bd5ca">import</span> ComputerVisionClient
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.authentication</span> <span style="color:#8bd5ca">import</span> CognitiveServicesCredentials
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> TextOperationStatusCodes
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> TextRecognitionMode
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> VisualFeatureTypes
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.exceptions</span> <span style="color:#8bd5ca">import</span> CognitiveServicesError
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os.path</span> <span style="color:#8bd5ca">import</span> exists
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os</span> <span style="color:#8bd5ca">import</span> remove
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os</span> <span style="color:#8bd5ca">import</span> makedirs
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os</span> <span style="color:#8bd5ca">import</span> listdir
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os.path</span> <span style="color:#8bd5ca">import</span> isfile, join
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">time</span> <span style="color:#8bd5ca">import</span> sleep
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">sys</span> <span style="color:#8bd5ca">import</span> exit
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">random</span> <span style="color:#8bd5ca">import</span> randint
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">datetime</span> <span style="color:#8bd5ca">import</span> datetime
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># set up the connection to the cognitive services account</span>
</span></span><span style="display:flex;"><span>    subscription_key <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;YOUR KEY HERE&#34;</span>
</span></span><span style="display:flex;"><span>    endpoint <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;https://YOUR ENDPOINT HERE&#34;</span>
</span></span><span style="display:flex;"><span>    group_id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;YOUR GROUP ID HERE&#34;</span>
</span></span><span style="display:flex;"><span>    client <span style="color:#91d7e3;font-weight:bold">=</span> ComputerVisionClient(endpoint, CognitiveServicesCredentials(subscription_key))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># set up the directory where the images will be downloaded</span>
</span></span><span style="display:flex;"><span>    download_dir <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./downloads/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> exists(download_dir):
</span></span><span style="display:flex;"><span>        makedirs(download_dir)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># set up the directory where the images will be saved</span>
</span></span><span style="display:flex;"><span>    save_dir <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./images/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> exists(save_dir):
</span></span><span style="display:flex;"><span>        makedirs(save_dir)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># set up the directory where the images will be saved</span>
</span></span><span style="display:flex;"><span>    caption_dir <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;./captions/&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> exists(caption_dir):
</span></span><span style="display:flex;"><span>        makedirs(caption_dir)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># get a list of the files in the download directory</span>
</span></span><span style="display:flex;"><span>    files <span style="color:#91d7e3;font-weight:bold">=</span> [f <span style="color:#c6a0f6">for</span> f <span style="color:#91d7e3;font-weight:bold">in</span> listdir(download_dir) <span style="color:#c6a0f6">if</span> isfile(join(download_dir, f))]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3">len</span>(files) <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#f5a97f">0</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;No files found in the download directory&#34;</span>)
</span></span><span style="display:flex;"><span>        exit(<span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># get the first file in the list</span>
</span></span><span style="display:flex;"><span>    file_name <span style="color:#91d7e3;font-weight:bold">=</span> files[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>    file
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os</span> <span style="color:#8bd5ca">import</span> environ
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision</span> <span style="color:#8bd5ca">import</span> ComputerVisionClient
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.authentication</span> <span style="color:#8bd5ca">import</span> CognitiveServicesCredentials
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> TextOperationStatusCodes
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> TextRecognitionMode
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision.models</span> <span style="color:#8bd5ca">import</span> VisualFeatureTypes
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.exceptions</span> <span style="color:#8bd5ca">import</span> CognitiveServicesError
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add your Computer Vision subscription key to your environment variables.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#39;COMPUTER_VISION_SUBSCRIPTION_KEY&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> environ:
</span></span><span style="display:flex;"><span>        subscription_key <span style="color:#91d7e3;font-weight:bold">=</span> environ[<span style="color:#a6da95">&#39;COMPUTER_VISION_SUBSCRIPTION_KEY&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Set the COMPUTER_VISION_SUBSCRIPTION_KEY environment variable.</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">**Restart your shell or IDE for changes to take effect.**&#34;</span>)
</span></span><span style="display:flex;"><span>        sys<span style="color:#91d7e3;font-weight:bold">.</span>exit()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add your Computer Vision endpoint to your environment variables.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#39;COMPUTER_VISION_ENDPOINT&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> environ:
</span></span><span style="display:flex;"><span>        endpoint <span style="color:#91d7e3;font-weight:bold">=</span> environ[<span style="color:#a6da95">&#39;COMPUTER_VISION_ENDPOINT&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Set the COMPUTER_VISION_ENDPOINT environment variable.</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">**Restart your shell or IDE for changes to take effect.**&#34;</span>)
</span></span><span style="display:flex;"><span>        sys<span style="color:#91d7e3;font-weight:bold">.</span>exit()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create an authenticated Computer Vision client.</span>
</span></span><span style="display:flex;"><span>    client <span style="color:#91d7e3;font-weight:bold">=</span> ComputerVisionClient(endpoint, CognitiveServicesCredentials(subscription_key))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Open the local image file.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> image_file:
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Read the image into a byte array.</span>
</span></span><span style="display:flex;"><span>        image_data <span style="color:#91d7e3;font-weight:bold">=</span> image_file<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the features to return.</span>
</span></span><span style="display:flex;"><span>    features <span style="color:#91d7e3;font-weight:bold">=</span> [VisualFeatureTypes<span style="color:#91d7e3;font-weight:bold">.</span>categories, VisualFeatureTypes<span style="color:#91d7e3;font-weight:bold">.</span>description, VisualFeatureTypes<span style="color:#91d7e3;font-weight:bold">.</span>tags]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Analyze the local image.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        result <span style="color:#91d7e3;font-weight:bold">=</span> client<span style="color:#91d7e3;font-weight:bold">.</span>analyze_image(image<span style="color:#91d7e3;font-weight:bold">=</span>image_data, visual_features<span style="color:#91d7e3;font-weight:bold">=</span>features)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_file, subscription_key):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision</span> <span style="color:#8bd5ca">import</span> ComputerVisionClient
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.authentication</span> <span style="color:#8bd5ca">import</span> CognitiveServicesCredentials
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">PIL</span> <span style="color:#8bd5ca">import</span> Image
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">io</span> <span style="color:#8bd5ca">import</span> BytesIO
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    client <span style="color:#91d7e3;font-weight:bold">=</span> ComputerVisionClient(ComputerVisionClient<span style="color:#91d7e3;font-weight:bold">.</span>SUBSCRIPTION_KEY_ENDPOINT,
</span></span><span style="display:flex;"><span>                                  CognitiveServicesCredentials(subscription_key))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    image_stream <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(image_file, <span style="color:#a6da95">&#39;rb&#39;</span>)
</span></span><span style="display:flex;"><span>    image <span style="color:#91d7e3;font-weight:bold">=</span> Image<span style="color:#91d7e3;font-weight:bold">.</span>open(image_stream)
</span></span><span style="display:flex;"><span>    image_bytes <span style="color:#91d7e3;font-weight:bold">=</span> BytesIO(image)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    analysis <span style="color:#91d7e3;font-weight:bold">=</span> client<span style="color:#91d7e3;font-weight:bold">.</span>analyze_image(image_bytes, ComputerVisionClient<span style="color:#91d7e3;font-weight:bold">.</span>DESCRIPTION)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> analysis<span style="color:#91d7e3;font-weight:bold">.</span>description<span style="color:#91d7e3;font-weight:bold">.</span>captions[<span style="color:#f5a97f">0</span>]<span style="color:#91d7e3;font-weight:bold">.</span>text
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_filename):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">json</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">azure.cognitiveservices.vision.computervision</span> <span style="color:#8bd5ca">import</span> ComputerVisionClient
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">msrest.authentication</span> <span style="color:#8bd5ca">import</span> CognitiveServicesCredentials
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">PIL</span> <span style="color:#8bd5ca">import</span> Image
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">io</span> <span style="color:#8bd5ca">import</span> BytesIO
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">sys</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">requests</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the environment variables for your subscription key and region</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># For example, for US West</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># &#34;COMPUTER_VISION_SUBSCRIPTION_KEY=&lt;subscription key&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># &#34;COMPUTER_VISION_ENDPOINT=https://westus2.api.cognitive.microsoft.com/vision/v2.0&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># For example, for China East</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># &#34;COMPUTER_VISION_SUBSCRIPTION_KEY=&lt;subscription key&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># &#34;COMPUTER_VISION_ENDPOINT=https://eastasia.api.cognitive.microsoft.com/vision/v2.0&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set your subscription key and region</span>
</span></span><span style="display:flex;"><span>    subscription_key <span style="color:#91d7e3;font-weight:bold">=</span> os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;COMPUTER_VISION_SUBSCRIPTION_KEY&#39;</span>]
</span></span><span style="display:flex;"><span>    endpoint <span style="color:#91d7e3;font-weight:bold">=</span> os<span style="color:#91d7e3;font-weight:bold">.</span>environ[<span style="color:#a6da95">&#39;COMPUTER_VISION_ENDPOINT&#39;</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set your image path</span>
</span></span><span style="display:flex;"><span>    image_path <span style="color:#91d7e3;font-weight:bold">=</span> image_filename
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set your region</span>
</span></span><span style="display:flex;"><span>    region <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;eastus&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create an instance of the Computer Vision client</span>
</span></span><span style="display:flex;"><span>    computer_vision_client <span style="color:#91d7e3;font-weight:bold">=</span> ComputerVisionClient(endpoint, CognitiveServicesCredentials(subscription_key))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the image from disk</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> image_file:
</span></span><span style="display:flex;"><span>        image_content <span style="color:#91d7e3;font-weight:bold">=</span> image_file<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Add the image to the list of images to analyze</span>
</span></span><span style="display:flex;"><span>    image_list <span style="color:#91d7e3;font-weight:bold">=</span> [image_content]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Make the analysis asynchronous</span>
</span></span><span style="display:flex;"><span>    analyze_result <span style="color:#91d7e3;font-weight:bold">=</span> computer_vision_client<span style="color:#91d7e3;font-weight:bold">.</span>analyze_image_in_stream(image_list, visual_features<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#39;Categories&#39;</span>, <span style="color:#a6da95">&#39;Description&#39;</span>, <span style="color:#a6da95">&#39;Color&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Get the analysis result</span>
</span></span><span style="display:flex;"><span>    analysis <span style="color:#91d7e3;font-weight:bold">=</span> analyze_result<span style="color:#91d7e3;font-weight:bold">.</span>result()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Display the image captions</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">Image captions:&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> caption <span style="color:#91d7e3;font-weight:bold">in</span> analysis<span style="color:#91d7e3;font-weight:bold">.</span>description<span style="color:#91d7e3;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># load the image</span>
</span></span><span style="display:flex;"><span>    image <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#34;rb&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># encode the image into base64</span>
</span></span><span style="display:flex;"><span>    image <span style="color:#91d7e3;font-weight:bold">=</span> base64<span style="color:#91d7e3;font-weight:bold">.</span>b64encode(image)<span style="color:#91d7e3;font-weight:bold">.</span>decode(<span style="color:#a6da95">&#34;utf-8&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># create a dict with the image</span>
</span></span><span style="display:flex;"><span>    body <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#34;image&#34;</span>: image}
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># create a dict with the subscription key</span>
</span></span><span style="display:flex;"><span>    headers <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#34;Ocp-Apim-Subscription-Key&#34;</span>: <span style="color:#a6da95">&#34;0d5a5f7f1e534d05a8e8d6f9e6e9c6d3&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># create a url for the api</span>
</span></span><span style="display:flex;"><span>    url <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;https://westcentralus.api.cognitive.microsoft.com/vision/v1.0/analyze?visualFeatures=Description,Tags&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># send the request</span>
</span></span><span style="display:flex;"><span>    response <span style="color:#91d7e3;font-weight:bold">=</span> requests<span style="color:#91d7e3;font-weight:bold">.</span>post(url, headers<span style="color:#91d7e3;font-weight:bold">=</span>headers, json<span style="color:#91d7e3;font-weight:bold">=</span>body)
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># get the response</span>
</span></span><span style="display:flex;"><span>    response<span style="color:#91d7e3;font-weight:bold">.</span>raise_for_status()
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># get the caption</span>
</span></span><span style="display:flex;"><span>    caption <span style="color:#91d7e3;font-weight:bold">=</span> response<span style="color:#91d7e3;font-weight:bold">.</span>json()[<span style="color:#a6da95">&#34;description&#34;</span>][<span style="color:#a6da95">&#34;captions&#34;</span>][<span style="color:#f5a97f">0</span>][<span style="color:#a6da95">&#34;text&#34;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># return the caption</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> caption
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os</span> <span style="color:#8bd5ca">import</span> environ
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os.path</span> <span style="color:#8bd5ca">import</span> join
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">urllib.parse</span> <span style="color:#8bd5ca">import</span> urlparse
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">urllib.parse</span> <span style="color:#8bd5ca">import</span> parse_qs
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">http.client</span> <span style="color:#8bd5ca">import</span> HTTPSConnection
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">json</span> <span style="color:#8bd5ca">import</span> loads
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">base64</span> <span style="color:#8bd5ca">import</span> b64encode
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the image into a byte array</span>
</span></span><span style="display:flex;"><span>    image_data <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#34;rb&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>    image_data <span style="color:#91d7e3;font-weight:bold">=</span> b64encode(image_data)<span style="color:#91d7e3;font-weight:bold">.</span>decode(<span style="color:#a6da95">&#39;utf-8&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Read the subscription key from the environment variables</span>
</span></span><span style="display:flex;"><span>    subscription_key <span style="color:#91d7e3;font-weight:bold">=</span> environ[<span style="color:#a6da95">&#34;AZURE_SUBSCRIPTION_KEY&#34;</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Define the vision API endpoint</span>
</span></span><span style="display:flex;"><span>    vision_base_url <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;https://westus2.api.cognitive.microsoft.com/vision/v2.0/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the vision API endpoint to analyze the image</span>
</span></span><span style="display:flex;"><span>    vision_analyze_url <span style="color:#91d7e3;font-weight:bold">=</span> vision_base_url <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34;analyze&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Set the parameters</span>
</span></span><span style="display:flex;"><span>    parameters <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;visualFeatures&#39;</span>: <span style="color:#a6da95">&#39;Categories,Description,Color&#39;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Prepare the request</span>
</span></span><span style="display:flex;"><span>    headers <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;Ocp-Apim-Subscription-Key&#39;</span>: subscription_key,
</span></span><span style="display:flex;"><span>               <span style="color:#a6da95">&#39;Content-Type&#39;</span>: <span style="color:#a6da95">&#39;application/octet-stream&#39;</span>}
</span></span><span style="display:flex;"><span>    params <span style="color:#91d7e3;font-weight:bold">=</span> parse_qs(urlparse(vision_analyze_url)<span style="color:#91d7e3;font-weight:bold">.</span>query)
</span></span><span style="display:flex;"><span>    params[<span style="color:#a6da95">&#39;visualFeatures&#39;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;Categories,Description,Color&#39;</span>
</span></span><span style="display:flex;"><span>    params[<span style="color:#a6da95">&#39;language&#39;</span>] <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;en&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Send the request</span>
</span></span><span style="display:flex;"><span>    conn <span style="color:#91d7e3;font-weight:bold">=</span> HTTPSConnection(<span style="color:#a6da95">&#39;westus2.api.cognitive.microsoft.com&#39;</span>)
</span></span><span style="display:flex;"><span>    conn<span style="color:#91d7e3;font-weight:bold">.</span>request(<span style="color:#a6da95">&#34;POST&#34;</span>, vision_analyze_url, image_data, headers)
</span></span><span style="display:flex;"><span>    response <span style="color:#91d7e3;font-weight:bold">=</span> conn<span style="color:#91d7e3;font-weight:bold">.</span>getresponse()
</span></span><span style="display:flex;"><span>    data <span style="color:#91d7e3;font-weight:bold">=</span> response<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the JSON response into a Python dictionary</span>
</span></span><span style="display:flex;"><span>    parsed <span style="color:#91d7e3;font-weight:bold">=</span> loads(data)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> parsed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#a6da95">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    Generate a caption for an image.
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    :param image_path: path to an image
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    :return: the caption
</span></span></span><span style="display:flex;"><span><span style="color:#a6da95">    &#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the image</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">os.path</span> <span style="color:#8bd5ca">import</span> isfile
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">PIL</span> <span style="color:#8bd5ca">import</span> Image
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">requests</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">not</span> isfile(image_path):
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">raise</span> <span style="color:#f5a97f">FileNotFoundError</span>(image_path)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    image <span style="color:#91d7e3;font-weight:bold">=</span> Image<span style="color:#91d7e3;font-weight:bold">.</span>open(image_path)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Encode the image</span>
</span></span><span style="display:flex;"><span>    image_data <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#34;rb&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Computer Vision parameters</span>
</span></span><span style="display:flex;"><span>    params <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;visualFeatures&#39;</span>: <span style="color:#a6da95">&#39;Categories,Description,Color&#39;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Computer Vision API URL</span>
</span></span><span style="display:flex;"><span>    url <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;https://westus.api.cognitive.microsoft.com/vision/v1.0/analyze&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Connect to Computer Vision</span>
</span></span><span style="display:flex;"><span>    headers <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;Ocp-Apim-Subscription-Key&#39;</span>: <span style="color:#a6da95">&#39;d3f3e1b8e7e14d0b9d7d3f8c8f7e0b6f&#39;</span>, <span style="color:#a6da95">&#39;Content-Type&#39;</span>: <span style="color:#a6da95">&#39;application/octet-stream&#39;</span>}
</span></span><span style="display:flex;"><span>    response <span style="color:#91d7e3;font-weight:bold">=</span> requests<span style="color:#91d7e3;font-weight:bold">.</span>post(url, headers<span style="color:#91d7e3;font-weight:bold">=</span>headers, params<span style="color:#91d7e3;font-weight:bold">=</span>params, data<span style="color:#91d7e3;font-weight:bold">=</span>image_data)
</span></span><span style="display:flex;"><span>    response<span style="color:#91d7e3;font-weight:bold">.</span>raise_for_status()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Load the response as JSON</span>
</span></span><span style="display:flex;"><span>    analysis <span style="color:#91d7e3;font-weight:bold">=</span> response<span style="color:#91d7e3;font-weight:bold">.</span>json()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Extract the word tags</span>
</span></span><span style="display:flex;"><span>    categories <span style="color:#91d7e3;font-weight:bold">=</span> [(category[<span style="color:#a6da95">&#39;name&#39;</span>], category[<span style="color:#a6da95">&#39;score&#39;</span>])
</span></span><span style="display:flex;"><span>                  <span style="color:#c6a0f6">for</span> category <span style="color:#91d7e3;font-weight:bold">in</span> analysis[<span style="color:#a6da95">&#39;categories&#39;</span>]]
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;Tags:&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> category <span style="color:#91d7e3;font-weight:bold">in</span> categories:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(category[<span style="color:#f5a97f">0</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Extract the description</span>
</span></span><span style="display:flex;"><span>    descriptions <span style="color:#91d7e3;font-weight:bold">=</span> [(description[<span style="color:#a6da95">&#39;text&#39;</span>], description[<span style="color:#a6da95">&#39;score&#39;</span>])
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">for</span> description <span style="color:#91d7e3;font-weight:bold">in</span> analysis[<span style="color:#a6da95">&#39;description&#39;</span>][<span style="color:#a6da95">&#39;captions&#39;</span>]]
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;Descriptions:&#39;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> description <span style="color:#91d7e3;font-weight:bold">in</span> descriptions:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(description[<span style="color:#f5a97f">0</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Display the color</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span>:
</span></span><span style="display:flex;"><span>        dominant_color <span style="color:#91d7e3;font-weight:bold">=</span> analysis[<span style="color:#a6da95">&#39;color&#39;</span>][<span style="color:#a6da95">&#39;dominantColorBackground&#39;</span>]
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">except</span>:
</span></span><span style="display:flex;"><span>        dominant_color <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;N/A&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;Dominant color: </span><span style="color:#a6da95">{}</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>format(dominant_color))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Display the caption</span>
</span></span><span style="display:flex;"><span>    caption <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;</span><span style="color:#a6da95">{}</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>format(
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(filename):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">sys</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">json</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">requests</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">cognitive_services_keys</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">cognitive_services_key_names</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># create the header for the request</span>
</span></span><span style="display:flex;"><span>    headers <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;Ocp-Apim-Subscription-Key&#39;</span>: cognitive_services_keys<span style="color:#91d7e3;font-weight:bold">.</span>get_key(cognitive_services_key_names<span style="color:#91d7e3;font-weight:bold">.</span>BING_KEY)}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># the URL for the bing endpoint</span>
</span></span><span style="display:flex;"><span>    bing_url <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#39;https://api.cognitive.microsoft.com/bing/v5.0/images/search&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># the parameters for the request</span>
</span></span><span style="display:flex;"><span>    params <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;q&#39;</span>: <span style="color:#a6da95">&#39;image&#39;</span>, <span style="color:#a6da95">&#39;count&#39;</span>: <span style="color:#f5a97f">1</span>, <span style="color:#a6da95">&#39;offset&#39;</span>: <span style="color:#f5a97f">0</span>, <span style="color:#a6da95">&#39;mkt&#39;</span>: <span style="color:#a6da95">&#39;en-us&#39;</span>, <span style="color:#a6da95">&#39;safeSearch&#39;</span>: <span style="color:#a6da95">&#39;Moderate&#39;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># read the contents of the file</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">with</span> <span style="color:#91d7e3">open</span>(filename, <span style="color:#a6da95">&#39;rb&#39;</span>) <span style="color:#c6a0f6">as</span> f:
</span></span><span style="display:flex;"><span>        data <span style="color:#91d7e3;font-weight:bold">=</span> f<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># build the request</span>
</span></span><span style="display:flex;"><span>    request <span style="color:#91d7e3;font-weight:bold">=</span> requests<span style="color:#91d7e3;font-weight:bold">.</span>post(bing_url, headers<span style="color:#91d7e3;font-weight:bold">=</span>headers, params<span style="color:#91d7e3;font-weight:bold">=</span>params, data<span style="color:#91d7e3;font-weight:bold">=</span>data)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># check for errors</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> request<span style="color:#91d7e3;font-weight:bold">.</span>status_code <span style="color:#91d7e3;font-weight:bold">!=</span> <span style="color:#f5a97f">200</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;Error code: </span><span style="color:#a6da95">{}</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>format(request<span style="color:#91d7e3;font-weight:bold">.</span>status_code))
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(request<span style="color:#91d7e3;font-weight:bold">.</span>text)
</span></span><span style="display:flex;"><span>        sys<span style="color:#91d7e3;font-weight:bold">.</span>exit(<span style="color:#f5a97f">1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># process the response</span>
</span></span><span style="display:flex;"><span>    response <span style="color:#91d7e3;font-weight:bold">=</span> json<span style="color:#91d7e3;font-weight:bold">.</span>loads(request<span style="color:#91d7e3;font-weight:bold">.</span>text)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> <span style="color:#a6da95">&#39;value&#39;</span> <span style="color:#91d7e3;font-weight:bold">in</span> response:
</span></span><span style="display:flex;"><span>        caption <span style="color:#91d7e3;font-weight:bold">=</span> response[<span style="color:#a6da95">&#39;value&#39;</span>][<span style="color:#f5a97f">0</span>][<span style="color:#a6da95">&#39;name&#39;</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;The caption is: </span><span style="color:#a6da95">{}</span><span style="color:#a6da95">&#39;</span><span style="color:#91d7e3;font-weight:bold">.</span>format(caption))
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">print</span>(<span style="color:#a6da95">&#39;No caption found.&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">=======</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">generate_caption</span>(image_path):
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">json</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">requests</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    subscription_key <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;e9e9c8a8d1d9400e8d0b7f2d8f8e7c6e&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">assert</span> subscription_key
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    vision_base_url <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;https://westcentralus.api.cognitive.microsoft.com/vision/v1.0/&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    analyze_url <span style="color:#91d7e3;font-weight:bold">=</span> vision_base_url <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34;analyze&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    image_data <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">open</span>(image_path, <span style="color:#a6da95">&#34;rb&#34;</span>)<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    headers    <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;Ocp-Apim-Subscription-Key&#39;</span>: subscription_key,
</span></span><span style="display:flex;"><span>                  <span style="color:#a6da95">&#39;Content-Type&#39;</span>: <span style="color:#a6da95">&#39;application/octet-stream&#39;</span>}
</span></span><span style="display:flex;"><span>    params     <span style="color:#91d7e3;font-weight:bold">=</span> {<span style="color:#a6da95">&#39;visualFeatures&#39;</span>: <span style="color:#a6da95">&#39;Categories,Description,Color&#39;</span>}
</span></span><span style="display:flex;"><span>    response <span style="color:#91d7e3;font-weight:bold">=</span> requests<span style="color:#91d7e3;font-weight:bold">.</span>post(
</span></span><span style="display:flex;"><span>        analyze_url, headers<span style="color:#91d7e3;font-weight:bold">=</span>headers, params<span style="color:#91d7e3;font-weight:bold">=</span>params, data<span style="color:#91d7e3;font-weight:bold">=</span>image_data)
</span></span><span style="display:flex;"><span>    response<span style="color:#91d7e3;font-weight:bold">.</span>raise_for_status()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    analysis <span style="color:#91d7e3;font-weight:bold">=</span> response<span style="color:#91d7e3;font-weight:bold">.</span>json()
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">print</span>(analysis)
</span></span><span style="display:flex;"><span>    image_caption <span style="color:#91d7e3;font-weight:bold">=</span> analysis[<span style="color:#a6da95">&#34;description&#34;</span>][<span style="color:#a6da95">&#34;captions&#34;</span>][<span style="color:#f5a97f">0</span>][<span style="color:#a6da95">&#34;text&#34;</span>]<span style="color:#91d7e3;font-weight:bold">.</span>capitalize()
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> image_caption</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>GPT-3 vs other AI powered assistants</title>
      <link>/post/2021/06/gpt-3-vs-other-ai-powered-assistants/</link>
      <pubDate>Mon, 21 Jun 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/06/gpt-3-vs-other-ai-powered-assistants/</guid>
      <description>&lt;p&gt;I have been kicking the tires with Open AI&amp;rsquo;s &lt;a
	
		href = &#34;#GPT3&#34;
	

	

	&gt;
	
	&lt;span&gt;
		#GPT-3
	&lt;/span&gt;
&lt;/a&gt;. Based on the screenshot below, it might be easy to think &amp;ldquo;oh boy does the model think highly of itself&amp;rdquo;, but as with most things in life - the devil is in the details.😃 The screenshot below was a forked version of &lt;a
	
		href = &#34;https://beta.openai.com/docs/engines&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		davinci engine
	&lt;/span&gt;
&lt;/a&gt; and follows the Q&amp;amp;A structure.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have been kicking the tires with Open AI&rsquo;s <a
	
		href = "#GPT3"
	

	

	>
	
	<span>
		#GPT-3
	</span>
</a>. Based on the screenshot below, it might be easy to think &ldquo;oh boy does the model think highly of itself&rdquo;, but as with most things in life - the devil is in the details.😃 The screenshot below was a forked version of <a
	
		href = "https://beta.openai.com/docs/engines"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		davinci engine
	</span>
</a> and follows the Q&amp;A structure.</p>
<p><p>

    <figure>
        <img src="images/openAI-vs-others.png" alt="OpenAI&rsquo;s GPT3 answering questions when compared to other AI-powered assistants."/>
        <figcaption>GPT-3 vs other AI assistants</figcaption>
    </figure>

</p></p>
<p>Using OpenAI&rsquo;s API is quite simple; perhaps too simple! It is quite easy to unleash the beast as the code snippet shown below. If you are new to using GPT3, I would highly recommend you start with the <a
	
		href = "https://beta.openai.com/docs/use-case-guidelines/use-case-requirements-library"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		use case model guidelines
	</span>
</a> first.</p>
<p>In the context of a toy example, to get to a simple Q&amp;A chatbot as the screenshot earlier shown is quite simple. The API is powerful, and simple to use, and getting started is easy as the code below shows.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">os</span>
</span></span><span style="display:flex;"><span> <span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">openai</span>
</span></span><span style="display:flex;"><span> openai<span style="color:#91d7e3;font-weight:bold">.</span>api_key <span style="color:#91d7e3;font-weight:bold">=</span> os<span style="color:#91d7e3;font-weight:bold">.</span>getenv(<span style="color:#a6da95">&#34;OPENAI_API_KEY&#34;</span>)
</span></span><span style="display:flex;"><span> response <span style="color:#91d7e3;font-weight:bold">=</span> openai<span style="color:#91d7e3;font-weight:bold">.</span>Completion<span style="color:#91d7e3;font-weight:bold">.</span>create(
</span></span><span style="display:flex;"><span>   engine<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;davinci&#34;</span>,
</span></span><span style="display:flex;"><span>   prompt<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;I am a highly intelligent question answering bot. If you ask me a question that is rooted in truth, I will give you the answer. If you ask me a question that is nonsense, trickery, or has no clear answer, I will respond with </span><span style="color:#8aadf4">\&#34;</span><span style="color:#a6da95">Unknown</span><span style="color:#8aadf4">\&#34;</span><span style="color:#a6da95">.</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">Q: What is human life expectancy in the United States?</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">A: Human life expectancy in the United States is 78 years.</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">Q: Who was president of the United States in 1955?</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">A: Dwight D. Eisenhower was president of the United States in 1955.</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">Q: Which party did he belong to?</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">A: He belonged to the Republican Party.</span><span style="color:#8aadf4">\n\n</span><span style="color:#a6da95">Q: What is the square root of banana?</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">A: Unknown</span><span style="color:#8aadf4">\n\&#34;</span><span style="color:#a6da95">,</span>
</span></span><span style="display:flex;"><span>   temperature<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>,
</span></span><span style="display:flex;"><span>   max_tokens<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">100</span>,
</span></span><span style="display:flex;"><span>   top_p<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span>,
</span></span><span style="display:flex;"><span>   frequency_penalty<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.0</span>,
</span></span><span style="display:flex;"><span>   presence_penalty<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0.0</span>,
</span></span><span style="display:flex;"><span>   stop<span style="color:#91d7e3;font-weight:bold">=</span>[<span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span>]
</span></span><span style="display:flex;"><span> )</span></span></code></pre></td></tr></table>
</div>
</div>
<p>There are three core concepts when using GPT-3: Prompt, Completion, and Tokens.</p>
<p>To start using the API, we need to start giving it some <strong>prompts</strong> - this provide some context to the engine on what is expecting. Without the surface area is too broad and we get into nonsensical situations. This is part of the task-specific fine-tuning required.</p>
<p>Think of when giving examples as part of the prompt, we are essentially <em>&ldquo;programming&rdquo;</em> the model and providing guidance and providing some hints to context and pattern matching. Note the training data cut off in late 2019, so the model in production today doesn&rsquo;t have access to data and events post that (e.g., Covid).</p>
<p><strong>Completion</strong> is the output that GPT3 generates based on the prompt. To be clear, this is not the full text but is the predicted completions; think of it as &ldquo;autocomplete&rdquo; in Word, or Outlook or a search engine. The API has flexibility to return more than one predicted completion along with the probabilities of alternative tokens at each position (to me it seems just like the wave function when thinking of Quantum mechanics 🐼).</p>
<p>Finally, think of <strong>Token</strong> are the smaller Lego blocks that combine to make words. The API, which is nothing but wrappers around GPT-3 breaks up the text into tokens before processing it. The GPT-3 model understands the statistical relationships between these tokens and uses this to produce the next token in a sequence of tokens.</p>
<p>For example, if we are curious about Tokens, we can see in the screenshot below how the API &ldquo;tokenizes&rdquo; this paragraph and get the details of the tokens. This paragraph contains 207 characters and 43 tokens.</p>
<p><p>

    <figure>
        <img src="images/gpt3-text-tokens.png" alt="Token text that GPT-3 API converts to before using."/>
        <figcaption>GPT-3 Tokens - Text</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/gpt3-text-token-IDs.png" alt="Token ID&rsquo;s that GPT-3 API converts to before using"/>
        <figcaption>GPT-3 Token - IDs</figcaption>
    </figure>

</p></p>
<p>At a high level, think of one token == ~4 characters of text, which is ¾ of a word; so, 100 tokens ~= 75 words.</p>
<p>This is just dipping our toes in the beast that is GPT-3; the APIs which wrap up and expose the engines (more on that in another post) make it simple to use and without getting too much in the weeds of 175 billion parameters. &#x1f604;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Compiling</title>
      <link>/post/2021/01/compiling/</link>
      <pubDate>Thu, 21 Jan 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/01/compiling/</guid>
      <description>&lt;p&gt;#GeekyJokes&lt;/p&gt;
&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;images/compiling.png&#34; alt=&#34;Compiling&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>#GeekyJokes</p>
<p><p>

    <img src="images/compiling.png" alt="Compiling"/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>Auto-update PowerShell and nag-free</title>
      <link>/post/2021/01/auto-update-powershell-and-nag-free/</link>
      <pubDate>Mon, 11 Jan 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/01/auto-update-powershell-and-nag-free/</guid>
      <description>&lt;p&gt;If you are like me and get annoyed with the big PowerShell upgrade &amp;rsquo;nag&amp;rsquo; &amp;lsquo;reminder&amp;rsquo; (see screenshot below); instead of trying to figure out what to download and install the update, there is a simpler way to get the latest update and address the nag. :)&lt;/p&gt;
&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;images/image-1024x329.png&#34; alt=&#34;&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;You can just run the code below in an elevated prompt to get the latest release of PowerShell - it is easy-peasy. :)&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you are like me and get annoyed with the big PowerShell upgrade &rsquo;nag&rsquo; &lsquo;reminder&rsquo; (see screenshot below); instead of trying to figure out what to download and install the update, there is a simpler way to get the latest update and address the nag. :)</p>
<p><p>

    <img src="images/image-1024x329.png" alt=""/>

</p></p>
<p>You can just run the code below in an elevated prompt to get the latest release of PowerShell - it is easy-peasy. :)</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#91d7e3">iex </span><span style="color:#a6da95">&#34;&amp; { </span>$(<span style="color:#91d7e3">irm </span>https<span style="color:#ed8796">:</span>//aka.ms/<span style="color:#91d7e3">install-powershell</span>.ps1)<span style="color:#a6da95"> } -UseMSI&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Changing Window Terminal&#39;s default directory</title>
      <link>/post/2021/01/changing-window-terminals-default-directory/</link>
      <pubDate>Fri, 08 Jan 2021 00:00:00 +0000</pubDate>
      <guid>/post/2021/01/changing-window-terminals-default-directory/</guid>
      <description>&lt;p&gt;If you are like me, and don&amp;rsquo;t really have your work saved in the &amp;ldquo;%USERPROFILE%&amp;rdquo; it gets annoying after a time, to keep changing the directory.&lt;/p&gt;
&lt;p&gt;If there is one specific folder that you prefer, it is an easy configuration change in the profile setting - add a setting called &amp;ldquo;startingDirectory&amp;rdquo; and point it to the path you want.&lt;/p&gt;
&lt;p&gt;For example, I have a root folder called &amp;ldquo;src&amp;rdquo; where most of the code I am working on sits, and that&amp;rsquo;s where I wanted to default the terminal to.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you are like me, and don&rsquo;t really have your work saved in the &ldquo;%USERPROFILE%&rdquo; it gets annoying after a time, to keep changing the directory.</p>
<p>If there is one specific folder that you prefer, it is an easy configuration change in the profile setting - add a setting called &ldquo;startingDirectory&rdquo; and point it to the path you want.</p>
<p>For example, I have a root folder called &ldquo;src&rdquo; where most of the code I am working on sits, and that&rsquo;s where I wanted to default the terminal to.</p>
<p>To get to the profile, you can either use the shortcut <strong>CTRL+,</strong> or from the dropdown in the title bar, click settings (see below). This will open the settings.json in your default editor.</p>
<p><p>

    <figure>
        <img src="images/image.png" alt=""/>
        <figcaption>Terminal setting</figcaption>
    </figure>

</p></p>
<p>In my case, I wanted the starting directory for all the shells, so I put it under &ldquo;defaults&rdquo; - you can choose different options for different shells, and then would have this in the appropriate shell&rsquo;s settings and not the default block of course.</p>
<p>Below is what this looks like for me pointing this to &ldquo;c:\src&rdquo;. Also note, the escape characters need to be formatted properly to parse.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#a6da95">&#34;defaults&#34;</span><span style="color:#ed8796">:</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Put settings here that you want to apply to all profiles.
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">&#34;fontFace&#34;</span>:  <span style="color:#a6da95">&#34;CaskaydiaCove NF&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;startingDirectory&#34;</span>: <span style="color:#a6da95">&#34;c:\\src&#34;</span>,
</span></span><span style="display:flex;"><span>}<span style="color:#ed8796">,</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><p>

    <figure>
        <img src="images/image-1.png" alt=""/>
        <figcaption>setting.json screenshot</figcaption>
    </figure>

</p></p>
<p>Once you save the file, it should automatically reload the terminal. And if the json didn&rsquo;t parse - because of a typo or a syntax error then you would see an error similar to the one shown below.</p>
<p><p>

    <figure>
        <img src="images/image-2-1024x441.png" alt=""/>
        <figcaption>Parsing error</figcaption>
    </figure>

</p></p>
<p>In this example, I set the starting folder as <code>&quot;c:\\src&quot;</code>; instead of <code>&quot;c:\\\\src&quot;</code>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Getting list of users from Microsoft Teams</title>
      <link>/post/2020/06/getting-list-of-users-from-microsoft-teams/</link>
      <pubDate>Fri, 05 Jun 2020 00:00:00 +0000</pubDate>
      <guid>/post/2020/06/getting-list-of-users-from-microsoft-teams/</guid>
      <description>&lt;p&gt;I recently needed to get a list of users that belong to a specific Microsoft Teams team - and there isnt anything out of the box to get this using the Teams app. AFAIK, the only way to do this is using the Microsoft graph API - for which there are a few options.&lt;/p&gt;
&lt;p&gt;For something quick (e.g. getting a list of users in a team), using the &lt;a
	
		href = &#34;https://developer.microsoft.com/en-us/graph/graph-explorer&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Graph explorer
	&lt;/span&gt;
&lt;/a&gt; could be easy enough. On the other hand, if you need something more robust, you should program against the (REST) API.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I recently needed to get a list of users that belong to a specific Microsoft Teams team - and there isnt anything out of the box to get this using the Teams app. AFAIK, the only way to do this is using the Microsoft graph API - for which there are a few options.</p>
<p>For something quick (e.g. getting a list of users in a team), using the <a
	
		href = "https://developer.microsoft.com/en-us/graph/graph-explorer"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Graph explorer
	</span>
</a> could be easy enough. On the other hand, if you need something more robust, you should program against the (REST) API.</p>
<h3 id="graph-explorer">Graph Explorer</h3>
<p>Navigate to <a
	
		href = "https://developer.microsoft.com/en-us/graph/graph-explorer"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Graph explorer
	</span>
</a>, sign in and authenticate yourself against the specific O365 tenant you are interested in - most folks would only have one.</p>
<p><p>

    <figure>
        <img src="https://i1.wp.com/www.desigeek.com/blog/amit/wp-content/uploads/2020/05/image-1.png?fit=660%2C349&amp;ssl=1" alt=""/>
        <figcaption>Microsoft Graph Explorer</figcaption>
    </figure>

</p></p>
<p>Once authenticated, on the panel on the left you see several sample queries and scroll down until you see the <strong>Teams</strong>.</p>
<p><p>

    <figure>
        <img src="images/image.png" alt=""/>
        <figcaption>Teams sample queries</figcaption>
    </figure>

</p></p>
<p>To get members of a specific team, you need to get the team ID for that Team. This is unique ID (GUID) and doesn&rsquo;t change over the lifetime of the team. If you have this, then go ahead to the next section - Getting team members.</p>
<h3 id="getting-a-list-of-teams-and-team-id">Getting a list of Teams and Team ID</h3>
<p>On the query panel in Graph explorer, select the &ldquo;<strong>my joined teams</strong>&rdquo; and run the query. You will get a JSON back that contains the list of teams that you are a member of. The &ldquo;<em>id</em>&rdquo; element represents the Team ID which you would need for any team related API calls. For example, I am interested in this specific #AI team: &ldquo;#Reinforcement Learning and Decision AI&rdquo;.</p>
<p><p>

    <figure>
        <img src="images/team-explorer-1024x500.jpg" alt=""/>
        <figcaption>Get team details</figcaption>
    </figure>

</p></p>
<h3 id="getting-team-members">Getting team members</h3>
<p>Once you have the Team ID (the unique GUID that each identifies each team), you can get the members of the team using that option on the left. As shown on the screenshot below, you do need to pass in the team ID to the REST API and this would be something like this (and don&rsquo;t worry what I am showing below is a fictious GUID):</p>
<p><a
	
		href = "https://graph.microsoft.com/v1.0/groups/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://graph.microsoft.com/v1.0/groups/
	</span>
</a><strong>f3f9ad1f-beea-4026-9b86-dd3788404999</strong>/members</p>
<p><p>

    <figure>
        <img src="https://i2.wp.com/www.desigeek.com/blog/amit/wp-content/uploads/2020/05/team-user.jpg?fit=660%2C320&amp;ssl=1" alt=""/>
        <figcaption>Member details for a specific Microsoft Team team</figcaption>
    </figure>

</p></p>
<h3 id="programmatically-getting-microsoft-team-details">Programmatically getting Microsoft Team details</h3>
<p>If you want something more robust and repeatable, then using the API (via code) or PowerShell might be better. If you are programming, you will need to register an app - which <a
	
		href = "https://docs.microsoft.com/en-us/graph/auth/auth-concepts"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		can authenticate
	</span>
</a> using the <a
	
		href = "https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Identify platform
	</span>
</a>. This of course is quite powerful, but at times for simple things might be a bit too much.</p>
<p>In my simple task to get users from Teams, I prefer the PowerShell option. To get this going first you need to install the MicrosoftTeam module. This can be done using the command below.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#91d7e3">Install-Module</span> -Name MicrosoftTeams</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Depending on your configuration you might get a warning as shown below.</p>
<p><p>

    <figure>
        <img src="https://i1.wp.com/www.desigeek.com/blog/amit/wp-content/uploads/2020/05/image-2.png?fit=660%2C146&amp;ssl=1" alt=""/>
        <figcaption>PowerShell module installation</figcaption>
    </figure>

</p></p>
<p>Once the Teams PowerShell module is installed, you can run PowerShell scripts against Teams and achieve the same result. I have two scripts below showing the same steps as with the Graph Explorer above. One of these gets details of the teams that a user is a member of. And the second script is to get members of a selected team.</p>
<h3 id="using-powershell-to-get-team-details">Using PowerShell to get Team Details</h3>
<p>The PowerShell script below to get a Team details is below; you can also get it <a
	
		href = "https://github.com/bahree/teams"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		from GitHub
	</span>
</a>. Before you run this, there are two variables that need to be set.</p>
<ul>
<li>One, the path where you want the team details to be exported (this is a csv file).</li>
<li>Two, set the email that you will use. This needs to be the same one that you authenticated against.</li>
</ul>
<p>You will be prompted to sign in to authentic and this should be an experience that most folks would be familiar with. Note, each time you run the script, you need to authenticate - and this is irrespective of say if you are already logged into Teams of Office 365.</p>
<p><p>

    <figure>
        <img src="images/login-759x1024.png" alt=""/>
        <figcaption>Authenticating user against Office 365</figcaption>
    </figure>

</p></p>
<p>Assuming you have authenticated successfully, you should see an output like the one shown below; and a csv file in the path you configured will be created. This file will always be overwritten - without any prompts (of course this is assuming no other process is open that has a lock on that file).</p>
<p><p>

    <img src="https://i1.wp.com/www.desigeek.com/blog/amit/wp-content/uploads/2020/06/team-details.png?fit=660%2C401&amp;ssl=1" alt=""/>

</p></p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64">64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65">65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66">66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67">67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68">68</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Set these variables, to what makes sense in your situation. The email here is the one that is the one connected to your teams account.</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$exportLocation</span> = <span style="color:#a6da95">&#34;C:\temp\team-details.csv&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$emailAddress</span> = <span style="color:#a6da95">&#34;your-email@shouldbeputhere.com&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Authenticate against teams</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Connect-MicrosoftTeams</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Patience</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Successfully connected to Teams&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Getting all team details for user: </span>$(<span style="color:#f4dbd6">$emailAddress</span>)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Please be patient, if there are a lot of teams, this can take a while...&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Get all of the team Groups IDs</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># $GetUsersTeams = (Get-Team).GroupID</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$GetUsersTeams</span> = <span style="color:#91d7e3">Get-Team</span> -User <span style="color:#f4dbd6">$emailAddress</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$Report</span> = <span style="color:#f4dbd6">@</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Will hold a basic count of user types and teams</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$unavailableTeamCount</span> = <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Loop through all teams that the user belongs to</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$currentIndex</span> = <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">ForEach</span>(<span style="color:#f4dbd6">$thisTeam</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$GetUsersTeams</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#6e738d;font-style:italic"># Show some output to the user</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-Progress</span> -Id <span style="color:#f5a97f">0</span> -Activity <span style="color:#a6da95">&#34;Building report from Microsoft Teams&#34;</span> -Status <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$currentIndex</span><span style="color:#a6da95"> of </span>$(<span style="color:#f4dbd6">$GetUsersTeams</span>.Count)<span style="color:#a6da95">&#34;</span> -PercentComplete ((<span style="color:#f4dbd6">$currentIndex</span> / <span style="color:#f4dbd6">$GetUsersTeams</span>.Count) * <span style="color:#f5a97f">100</span>)
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Attempt to get team details, throw error message if no access</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Get team members</span>
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">#$users = Get-TeamUser -GroupId $thisTeam.groupID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#6e738d;font-style:italic"># Create an object to hold all values</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$teamReportObject</span> = <span style="color:#91d7e3">New-Object</span> PSObject -Property <span style="color:#f4dbd6">@</span>{
</span></span><span style="display:flex;"><span>                GroupID = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">GroupID</span>
</span></span><span style="display:flex;"><span>				TeamName = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">DisplayName</span>
</span></span><span style="display:flex;"><span>                Description = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">Description</span>
</span></span><span style="display:flex;"><span>                Archived = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">Archived</span>
</span></span><span style="display:flex;"><span>                Visibility = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">Visibility</span>
</span></span><span style="display:flex;"><span>				eMail = <span style="color:#f4dbd6">$thisTeam</span>.<span style="color:#f5a97f">MailNickName</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Add to the report</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$Report</span> += <span style="color:#f4dbd6">$teamReportObject</span>
</span></span><span style="display:flex;"><span>       
</span></span><span style="display:flex;"><span>    } <span style="color:#c6a0f6">catch</span> [<span style="color:#eed49f">Microsoft.TeamsCmdlets.PowerShell.Custom.ErrorHandling.ApiException</span>] {
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-Host</span> -ForegroundColor Yellow <span style="color:#a6da95">&#34;No access to </span>$(<span style="color:#f4dbd6">$team</span>.DisplayName)<span style="color:#a6da95"> team, cannot generate report&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$unavailableTeamCount</span>++
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>	<span style="color:#f4dbd6">$currentIndex</span>++
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Progress</span> -Id <span style="color:#f5a97f">0</span> -Activity <span style="color:#a6da95">&#34; &#34;</span> -Status <span style="color:#a6da95">&#34; &#34;</span> -Completed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Disconnect from teams</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Disconnect-MicrosoftTeams</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Provide some nice output</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;============================================================&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;                Microsoft Teams User Report                 &#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;  Count of All Teams - </span>$(<span style="color:#f4dbd6">$GetUsersTeams</span>.Count)<span style="color:#a6da95">                &#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;  Count of Inaccesible Teams - </span>$(<span style="color:#f4dbd6">$unavailableTeamCount</span>)<span style="color:#a6da95">         &#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$Report</span> | <span style="color:#91d7e3">Export-CSV</span> <span style="color:#f4dbd6">$exportLocation</span> -NoTypeInformation -Force
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Exported report to </span>$(<span style="color:#f4dbd6">$exportLocation</span>)<span style="color:#a6da95">&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="getting-team-members-using-powershell">Getting Team members using PowerShell</h3>
<p>Now that you have the Team ID you are interested, you can run the other PowerShell script (<a
	
		href = "https://github.com/bahree/teams"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		also available on GitHub
	</span>
</a>) to get a list of all the users in a specific team. Like the previous script, you would need set a couple of variables in the script:</p>
<ul>
<li>The Team ID for the team you are interested in.</li>
<li>Path for the csv file with details to be saved.</li>
</ul>
<p>Once you have authenticated and ran the script, the output will look like the one shown below. You get a summary of the team details, and details of the Teams users and their type (owner, member, or guest). And just like earlier, the file will be overwritten without a prompt, assuming no locks on it.</p>
<p><p>

    <figure>
        <img src="https://i0.wp.com/www.desigeek.com/blog/amit/wp-content/uploads/2020/06/team-users.png?fit=660%2C587&amp;ssl=1" alt=""/>
        <figcaption>Members of a Microsoft Team</figcaption>
    </figure>

</p></p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64">64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65">65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66">66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67">67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68">68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69">69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70">70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71">71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72">72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73">73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74">74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75">75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76">76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77">77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78">78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79">79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80">80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81">81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82">82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83">83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84">84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85">85</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Global variables to set:</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#path of the file where to export</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#specific ID of the team that you want the users for. </span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$exportLocation</span> = <span style="color:#a6da95">&#34;C:\temp\RL-decision-AI-export.csv&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$TEAM_ID</span> = <span style="color:#a6da95">&#34;f3f9ad1f-beea-4026-9b86-dd3788404999&#34;</span>
</span></span><span style="display:flex;"><span>            
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$Report</span> = <span style="color:#f4dbd6">@</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># counters</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$ownerCount</span> = <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$memberCount</span> = <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$guestCount</span> = <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#connect to teams</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Connect-MicrosoftTeams</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$team</span> = <span style="color:#91d7e3">Get-Team</span> -GroupId <span style="color:#f4dbd6">$TEAM_ID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Patience, supposed to be a virtue</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Successfully connected to Team: </span>$(<span style="color:#f4dbd6">$team</span>.DisplayName)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Getting all users in the team&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Please be patient, if there are a lot of users, this can take a while...&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Attempt to get team users, throw error message if no access</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#6e738d;font-style:italic"># Get team members</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f4dbd6">$users</span> = <span style="color:#91d7e3">Get-TeamUser</span> -GroupId <span style="color:#f4dbd6">$team</span>.<span style="color:#f5a97f">groupID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#6e738d;font-style:italic"># Loop through and get all the users</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f4dbd6">$currentIndex</span> = <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>	<span style="color:#6e738d;font-style:italic"># foreach user create a line in the report</span>
</span></span><span style="display:flex;"><span>	<span style="color:#c6a0f6">ForEach</span>(<span style="color:#f4dbd6">$user</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$users</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#6e738d;font-style:italic"># Show some output to the user</span>
</span></span><span style="display:flex;"><span>		<span style="color:#91d7e3">Write-Progress</span> -Id <span style="color:#f5a97f">0</span> -Activity <span style="color:#a6da95">&#34;Generating user report from Teams&#34;</span> -Status <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$currentIndex</span><span style="color:#a6da95"> of </span>$(<span style="color:#f4dbd6">$users</span>.Count)<span style="color:#a6da95">&#34;</span> -PercentComplete ((<span style="color:#f4dbd6">$currentIndex</span> / <span style="color:#f4dbd6">$users</span>.Count) * <span style="color:#f5a97f">100</span>)
</span></span><span style="display:flex;"><span>	
</span></span><span style="display:flex;"><span>		<span style="color:#6e738d;font-style:italic"># Maintain a count of user types</span>
</span></span><span style="display:flex;"><span>		<span style="color:#c6a0f6">switch</span>(<span style="color:#f4dbd6">$user</span>.Role) {
</span></span><span style="display:flex;"><span>			<span style="color:#a6da95">&#34;owner&#34;</span> { <span style="color:#f4dbd6">$ownerCount</span>++ }
</span></span><span style="display:flex;"><span>			<span style="color:#a6da95">&#34;member&#34;</span> { <span style="color:#f4dbd6">$memberCount</span>++ }
</span></span><span style="display:flex;"><span>			<span style="color:#a6da95">&#34;guest&#34;</span> { <span style="color:#f4dbd6">$guestCount</span>++ }
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#6e738d;font-style:italic"># Create an object to hold all values</span>
</span></span><span style="display:flex;"><span>		<span style="color:#f4dbd6">$ReportObject</span> = <span style="color:#91d7e3">New-Object</span> PSObject -Property <span style="color:#f4dbd6">@</span>{
</span></span><span style="display:flex;"><span>			User = <span style="color:#f4dbd6">$user</span>.<span style="color:#f5a97f">Name</span>
</span></span><span style="display:flex;"><span>			Email = <span style="color:#f4dbd6">$user</span>.<span style="color:#f5a97f">User</span>
</span></span><span style="display:flex;"><span>			Role = <span style="color:#f4dbd6">$user</span>.<span style="color:#f5a97f">Role</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#6e738d;font-style:italic"># Add to the report</span>
</span></span><span style="display:flex;"><span>		<span style="color:#f4dbd6">$Report</span> += <span style="color:#f4dbd6">$ReportObject</span>
</span></span><span style="display:flex;"><span>		
</span></span><span style="display:flex;"><span>		<span style="color:#f4dbd6">$currentIndex</span>++
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>} 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">catch</span> [<span style="color:#eed49f">Microsoft.TeamsCmdlets.PowerShell.Custom.ErrorHandling.ApiException</span>] {
</span></span><span style="display:flex;"><span>	<span style="color:#91d7e3">Write-Host</span> -ForegroundColor Yellow <span style="color:#a6da95">&#34;No access to </span>$(<span style="color:#f4dbd6">$team</span>.DisplayName)<span style="color:#a6da95"> team, cannot generate report&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Complete progress</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Progress</span> -Id <span style="color:#f5a97f">0</span> -Activity <span style="color:#a6da95">&#34; &#34;</span> -Status <span style="color:#a6da95">&#34; &#34;</span> -Completed
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Disconnect from teams</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Disconnect-MicrosoftTeams</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Write out details for the user</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;============================================================&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;                Microsoft Teams User Report                 &#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Team Details:&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Name: </span>$(<span style="color:#f4dbd6">$team</span>.DisplayName)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Description: </span>$(<span style="color:#f4dbd6">$team</span>.Description)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Mail Nickname: </span>$(<span style="color:#f4dbd6">$team</span>.MailNickName)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Archived: </span>$(<span style="color:#f4dbd6">$team</span>.Archived)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Visiblity: </span>$(<span style="color:#f4dbd6">$team</span>.Visibility)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Team User Details:&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Owners - </span>$(<span style="color:#f4dbd6">$ownerCount</span>)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Members - </span>$(<span style="color:#f4dbd6">$memberCount</span>)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;Guests - </span>$(<span style="color:#f4dbd6">$guestCount</span>)<span style="color:#a6da95">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Green <span style="color:#a6da95">&#34;============================================================&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$Report</span> | <span style="color:#91d7e3">Export-CSV</span> <span style="color:#f4dbd6">$exportLocation</span> -NoTypeInformation -Force
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-Host</span> -ForegroundColor Blue <span style="color:#a6da95">&#34;Exported report to </span>$(<span style="color:#f4dbd6">$exportLocation</span>)<span style="color:#a6da95">&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Of course, programming against the API is always more powerful, but sometimes quick and easy is what is needed. :)</p>
]]></content:encoded>
    </item>
    <item>
      <title>Git and Code</title>
      <link>/post/2020/05/git-and-code/</link>
      <pubDate>Sun, 10 May 2020 00:00:00 +0000</pubDate>
      <guid>/post/2020/05/git-and-code/</guid>
      <description>&lt;p&gt;I think this from &lt;a
	
		href = &#34;https://xkcd.com/1597/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		xkcd
	&lt;/span&gt;
&lt;/a&gt; sums up my afternoon quite nicely. Messed up a repo, and then was trying to &amp;lsquo;clean up&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;A huge thank you to Lily, on the team, for working with me to cleaning up my mess, and helping me show some of the ropes.&lt;/p&gt;
&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;images/git.png&#34; alt=&#34;&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;I know there are quite a few tutorials out there; a couple of these that I found including one from Lily.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I think this from <a
	
		href = "https://xkcd.com/1597/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		xkcd
	</span>
</a> sums up my afternoon quite nicely. Messed up a repo, and then was trying to &lsquo;clean up&rsquo;.</p>
<p>A huge thank you to Lily, on the team, for working with me to cleaning up my mess, and helping me show some of the ropes.</p>
<p><p>

    <img src="images/git.png" alt=""/>

</p></p>
<p>I know there are quite a few tutorials out there; a couple of these that I found including one from Lily.</p>
<ul>
<li>[Git syncing](http://git syncing) and a great <a
	
		href = "https://www.atlassian.com/git/tutorials/setting-up-a-repository"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		beginner&rsquo;s guide
	</span>
</a> if you need that.</li>
<li><a
	
		href = "https://learngitbranching.js.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Learning Git Branching
	</span>
</a></li>
</ul>
<p>So go ahead, and set up an experiment repo, and don&rsquo;t be afraid to play and break things.</p>
<p>Maybe <a
	
		href = "https://www.desigeek.com/blog/amit/2013/08/06/how-to-insult-a-developer/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		this needs
	</span>
</a> to be updated to reflect Git, from REST. &#x1f604;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Docker / Docker Compose on a Pi</title>
      <link>/post/2019/09/docker-docker-compose-on-a-pi/</link>
      <pubDate>Thu, 26 Sep 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/09/docker-docker-compose-on-a-pi/</guid>
      <description>&lt;p&gt;Been playing with a few things at home, and as part of that was trying to get &lt;a
	
		href = &#34;https://docs.docker.com/get-started/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Docker
	&lt;/span&gt;
&lt;/a&gt; and Docker Compose running on a Raspberry Pi. &lt;a
	
		href = &#34;https://docs.docker.com/compose/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Docker Compose
	&lt;/span&gt;
&lt;/a&gt; if you aren&amp;rsquo;t familiar with, allows one to run multi-container apps, and is very handy when building multi-tier layered applications - which are quite common.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Been playing with a few things at home, and as part of that was trying to get <a
	
		href = "https://docs.docker.com/get-started/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Docker
	</span>
</a> and Docker Compose running on a Raspberry Pi. <a
	
		href = "https://docs.docker.com/compose/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Docker Compose
	</span>
</a> if you aren&rsquo;t familiar with, allows one to run multi-container apps, and is very handy when building multi-tier layered applications - which are quite common.</p>
<p>I was running it docker on my (Synology) NAS, but a recent update from them broke docker - specifically environment variables. That in turn broke the ability to run Docker Compose, and of course a bunch of stuff; and the opportunity to experiment.</p>
<p>First, we need to install docker - which these days is quite simple. You need the ability to ssh into the pi (or if you are connected to a display, then via a terminal prompt). And in some cases if things fail then you might need to run them as root (via sudo). To install docker, run the following:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -sSL https://get.docker.com | sh</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And once you are done installing docker, then test it by running the classic <a
	
		href = "https://hub.docker.com/_/hello-world"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		hello world image
	</span>
</a>. To so that you run the following command - this will get the Hello World image, and once run will automatically remove it (which is because of the &ndash;rm option)</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker run --rm hello-world</span></span></code></pre></td></tr></table>
</div>
</div>
<p>If everything is installed OK, then you should see a output that looks something like this the shown below. And this is good - means everything is up and running as expected.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pi@pi-server2:~ $ docker run --rm hello-world
</span></span><span style="display:flex;"><span>Unable to find image <span style="color:#a6da95">&#39;hello-world:latest&#39;</span> locally
</span></span><span style="display:flex;"><span>latest: Pulling from library/hello-world
</span></span><span style="display:flex;"><span>c1eda109e4da: Pull <span style="color:#91d7e3">complete</span>
</span></span><span style="display:flex;"><span>Digest: sha256:b8ba256769a0ac28dd126d584e0a2011cd2877f3f76e093a7ae560f2a5301c00
</span></span><span style="display:flex;"><span>Status: Downloaded newer image <span style="color:#c6a0f6">for</span> hello-world:latest
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Hello from Docker!
</span></span><span style="display:flex;"><span>This message shows that your installation appears to be working correctly.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>To generate this message, Docker took the following steps:
</span></span><span style="display:flex;"><span> 1. The Docker client contacted the Docker daemon.
</span></span><span style="display:flex;"><span> 2. The Docker daemon pulled the <span style="color:#a6da95">&#34;hello-world&#34;</span> image from the Docker Hub.
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3;font-weight:bold">(</span>arm32v7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span> 3. The Docker daemon created a new container from that image which runs the
</span></span><span style="display:flex;"><span>    executable that produces the output you are currently reading.
</span></span><span style="display:flex;"><span> 4. The Docker daemon streamed that output to the Docker client, which sent it
</span></span><span style="display:flex;"><span>    to your terminal.
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>To try something more ambitious, you can run an Ubuntu container with:
</span></span><span style="display:flex;"><span> $ docker run -it ubuntu bash
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Share images, automate workflows, and more with a free Docker ID:
</span></span><span style="display:flex;"><span> https://hub.docker.com/
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>For more examples and ideas, visit:
</span></span><span style="display:flex;"><span> https://docs.docker.com/get-started/</span></span></code></pre></td></tr></table>
</div>
</div>
<p>To make like more simple, you should add the user you are logged in as to the &lsquo;docker&rsquo; group. In my case it is the default &lsquo;pi&rsquo; user, so that command would look like this. And for this to take effect, you would need to logout - I just reboot the machine - old habits. :)</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo usermod -aG docker pi</span></span></code></pre></td></tr></table>
</div>
</div>
<p>OK, now that docker is installed, lets get to docker-compose. For that we first install pip, and use that to install docker-compose. And don&rsquo;t forget the apt-get update in the end.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> sudo python3 get-pip.py
</span></span><span style="display:flex;"><span>sudo pip3 install docker-compose
</span></span><span style="display:flex;"><span>sudo apt-get update</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Now before anything else, lets try and make sure all dependencies are there. Create a file called &lsquo;docker-compose.yml&rsquo; with the following. You can put this file anywhere, but I like to create a separate folder and save it in that.</p>
<p>In this example I expose port <code>6666</code> to the host which is mapped to port <code>8000</code> internally on the image. If your port <code>6666</code> is taken you can choose another port - it doesn&rsquo;t matter. Spacing and indent, do matter in a yml file, so you would want to pay extra attention to that.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#c6a0f6">version</span>: <span style="color:#a6da95">&#39;3&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">webapp</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f5a97f">6666</span>:<span style="color:#f5a97f">8000</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">image</span>: python:3.7-alpine
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">command</span>: <span style="color:#a6da95">&#34;python -m http.server 8000&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Once the file is saved you run it with the following command. The image handles you would see are very likely going to be different and that is OK.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pi@pi-server2:~/docker/docker-test $ docker-compose up
</span></span><span style="display:flex;"><span>Creating network <span style="color:#a6da95">&#34;docker-test_default&#34;</span> with the default driver
</span></span><span style="display:flex;"><span>Pulling webapp <span style="color:#91d7e3;font-weight:bold">(</span>python:3.7-alpine<span style="color:#91d7e3;font-weight:bold">)</span>...
</span></span><span style="display:flex;"><span>3.7-alpine: Pulling from library/python
</span></span><span style="display:flex;"><span>33b18ff7f9b7: Pull <span style="color:#91d7e3">complete</span>
</span></span><span style="display:flex;"><span>0c1f90421c3a: Pull <span style="color:#91d7e3">complete</span>
</span></span><span style="display:flex;"><span>91543a0ba590: Pull <span style="color:#91d7e3">complete</span>
</span></span><span style="display:flex;"><span>913b1310b79e: Pull <span style="color:#91d7e3">complete</span>
</span></span><span style="display:flex;"><span>6b545e90ee55: Pull <span style="color:#91d7e3">complete</span>                                                                                             Digest: sha256:9363cb46e52894a22ba87ebec0845d30f4c27efd6b907705ba9a27192b45e797
</span></span><span style="display:flex;"><span>Status: Downloaded newer image <span style="color:#c6a0f6">for</span> python:3.7-alpine
</span></span><span style="display:flex;"><span>Creating docker-test_webapp_1 ... <span style="color:#c6a0f6">done</span>                                                                                  Attaching to docker-test_webapp_1</span></span></code></pre></td></tr></table>
</div>
</div>
<p>At this point, the image is running in attached mode and it seems like it is waiting, when in reality it is running. If you open another ssh terminal and type in the following command - change the port to whatever you used earlier in the yml file.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pi@pi-server2:~ $ curl -iv 0.0.0.0:6666</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And if everything is working then you will see a output something like this. And if you see towards the top you got a <code>HTTP 200</code> - that is all that mattes in this case.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>* Expire in <span style="color:#f5a97f">0</span> ms <span style="color:#c6a0f6">for</span> <span style="color:#f5a97f">6</span> <span style="color:#91d7e3;font-weight:bold">(</span>transfer 0x1b097c0<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>*   Trying 0.0.0.0...
</span></span><span style="display:flex;"><span>* TCP_NODELAY <span style="color:#91d7e3">set</span>
</span></span><span style="display:flex;"><span>* Expire in <span style="color:#f5a97f">200</span> ms <span style="color:#c6a0f6">for</span> <span style="color:#f5a97f">4</span> <span style="color:#91d7e3;font-weight:bold">(</span>transfer 0x1b097c0<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>* Connected to 0.0.0.0 <span style="color:#91d7e3;font-weight:bold">(</span>127.0.0.1<span style="color:#91d7e3;font-weight:bold">)</span> port <span style="color:#f5a97f">6666</span> <span style="color:#91d7e3;font-weight:bold">(</span><span style="color:#6e738d;font-style:italic">#0)</span>
</span></span><span style="display:flex;"><span>&gt; GET / HTTP/1.1
</span></span><span style="display:flex;"><span>&gt; Host: 0.0.0.0:6666
</span></span><span style="display:flex;"><span>&gt; User-Agent: curl/7.64.0
</span></span><span style="display:flex;"><span>&gt; Accept: */*
</span></span><span style="display:flex;"><span>&gt;
</span></span><span style="display:flex;"><span>* HTTP 1.0, assume close after body
</span></span><span style="display:flex;"><span>&lt; HTTP/1.0 <span style="color:#f5a97f">200</span> OK
</span></span><span style="display:flex;"><span>HTTP/1.0 <span style="color:#f5a97f">200</span> OK
</span></span><span style="display:flex;"><span>&lt; Server: SimpleHTTP/0.6 Python/3.7.4
</span></span><span style="display:flex;"><span>Server: SimpleHTTP/0.6 Python/3.7.4
</span></span><span style="display:flex;"><span>&lt; Date: Thu, <span style="color:#f5a97f">26</span> Sep <span style="color:#f5a97f">2019</span> 22:10:28 GMT
</span></span><span style="display:flex;"><span>Date: Thu, <span style="color:#f5a97f">26</span> Sep <span style="color:#f5a97f">2019</span> 22:10:28 GMT
</span></span><span style="display:flex;"><span>&lt; Content-type: text/html; <span style="color:#f4dbd6">charset</span><span style="color:#91d7e3;font-weight:bold">=</span>utf-8
</span></span><span style="display:flex;"><span>Content-type: text/html; <span style="color:#f4dbd6">charset</span><span style="color:#91d7e3;font-weight:bold">=</span>utf-8
</span></span><span style="display:flex;"><span>&lt; Content-Length: <span style="color:#f5a97f">915</span>
</span></span><span style="display:flex;"><span>Content-Length: <span style="color:#f5a97f">915</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;
</span></span><span style="display:flex;"><span>&lt;!DOCTYPE HTML PUBLIC <span style="color:#a6da95">&#34;-//W3C//DTD HTML 4.01//EN&#34;</span> <span style="color:#a6da95">&#34;http://www.w3.org/TR/html4/strict.dtd&#34;</span>&gt;
</span></span><span style="display:flex;"><span>&lt;html&gt;
</span></span><span style="display:flex;"><span>&lt;head&gt;
</span></span><span style="display:flex;"><span>&lt;meta http-equiv<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;Content-Type&#34;</span> <span style="color:#f4dbd6">content</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;text/html; charset=utf-8&#34;</span>&gt;
</span></span><span style="display:flex;"><span>&lt;title&gt;Directory listing <span style="color:#c6a0f6">for</span> /&lt;/title&gt;
</span></span><span style="display:flex;"><span>&lt;/head&gt;
</span></span><span style="display:flex;"><span>&lt;body&gt;
</span></span><span style="display:flex;"><span>&lt;h1&gt;Directory listing <span style="color:#c6a0f6">for</span> /&lt;/h1&gt;
</span></span><span style="display:flex;"><span>&lt;hr&gt;
</span></span><span style="display:flex;"><span>&lt;ul&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;.dockerenv&#34;</span>&gt;.dockerenv&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;bin/&#34;</span>&gt;bin/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;dev/&#34;</span>&gt;dev/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;etc/&#34;</span>&gt;etc/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;home/&#34;</span>&gt;home/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;lib/&#34;</span>&gt;lib/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;media/&#34;</span>&gt;media/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;mnt/&#34;</span>&gt;mnt/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;opt/&#34;</span>&gt;opt/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;proc/&#34;</span>&gt;proc/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;root/&#34;</span>&gt;root/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;run/&#34;</span>&gt;run/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;sbin/&#34;</span>&gt;sbin/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;srv/&#34;</span>&gt;srv/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;sys/&#34;</span>&gt;sys/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;tmp/&#34;</span>&gt;tmp/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;usr/&#34;</span>&gt;usr/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;li&gt;&lt;a <span style="color:#f4dbd6">href</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;var/&#34;</span>&gt;var/&lt;/a&gt;&lt;/li&gt;
</span></span><span style="display:flex;"><span>&lt;/ul&gt;
</span></span><span style="display:flex;"><span>&lt;hr&gt;
</span></span><span style="display:flex;"><span>&lt;/body&gt;
</span></span><span style="display:flex;"><span>&lt;/html&gt;
</span></span><span style="display:flex;"><span>* Closing connection <span style="color:#f5a97f">0</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>You can go back to the first ssh session and hit Ctrl + C to shutdown the image. Once you do that you will see something like:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>^CGracefully stopping... <span style="color:#91d7e3;font-weight:bold">(</span>press Ctrl+C again to force<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Stopping docker-test_webapp_1 ... <span style="color:#c6a0f6">done</span>                                                                                  pi@pi-server2:~/docker/docker-test $</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Now you know docker-compose and all the dependencies are installed. Next I would want docker to auto start whenever the pi boots up, and for that we will use the following two commands.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo systemctl <span style="color:#91d7e3">enable</span> docker
</span></span><span style="display:flex;"><span>sudo systemctl start docker</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And that should be it. If you are running low on space you might want to clean up the images we downloaded in testing this installation.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Tesla API v3.9.1</title>
      <link>/post/2019/08/tesla-api-v3-9-1/</link>
      <pubDate>Thu, 29 Aug 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/08/tesla-api-v3-9-1/</guid>
      <description>&lt;p&gt;Haven&amp;rsquo;t had time until now to explore on what is new as Tesla continues to push updates. The latest version as of this post is v3.9.1 which is what there I decompiled and when compared to the earlier version (&lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2019/04/20/tesla-rest-apis-v3-8-2/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		I had posted (v3.8.2)
	&lt;/span&gt;
&lt;/a&gt;, there three new REST API&amp;rsquo;s outlined below.&lt;/p&gt;
&lt;p&gt;Service data from the car - not sure what exactly does this will. Need to try it.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Haven&rsquo;t had time until now to explore on what is new as Tesla continues to push updates. The latest version as of this post is v3.9.1 which is what there I decompiled and when compared to the earlier version (<a
	
		href = "http://desigeek.com/blog/amit/2019/04/20/tesla-rest-apis-v3-8-2/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		I had posted (v3.8.2)
	</span>
</a>, there three new REST API&rsquo;s outlined below.</p>
<p>Service data from the car - not sure what exactly does this will. Need to try it.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>   &#34;VEHICLE_SERVICE_DATA&#34;: {
</span></span><span style="display:flex;"><span>    &#34;TYPE&#34;: &#34;GET&#34;,
</span></span><span style="display:flex;"><span>    &#34;URI&#34;: &#34;api/1/vehicles/{vehicle_id}/service_data&#34;,
</span></span><span style="display:flex;"><span>    &#34;AUTH&#34;: true
</span></span><span style="display:flex;"><span>  }</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Now, when I call that, I get a 200OK response (see below), so it is accepting the request, and that includes the bearer code in the header as expected. I don&rsquo;t see anything interesting back, but that could be because my car is not in service. Maybe someone who has their vehicle in the service center can try and validate this.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    &#34;response&#34;: {}
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The next new API is a POST, for reports; and calling this just sends a 200OK back, but I don&rsquo;t know what it is for. It seems very similar to the SEND_LOG method.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&#34;SEND_REPORT&#34;: {
</span></span><span style="display:flex;"><span>    &#34;TYPE&#34;: &#34;POST&#34;,
</span></span><span style="display:flex;"><span>    &#34;URI&#34;: &#34;api/1/reports&#34;,
</span></span><span style="display:flex;"><span>    &#34;AUTH&#34;: true
</span></span><span style="display:flex;"><span>  }</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The next two set of APIs seem quite interesting and related t AutoPilot upgrade. It might be that these could be in app purchases - checking the eligibility, and then allowing one to purchase.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&#34;UPGRADE_ELIGIBILITY&#34;: {
</span></span><span style="display:flex;"><span>    &#34;TYPE&#34;: &#34;GET&#34;,
</span></span><span style="display:flex;"><span>    &#34;URI&#34;: &#34;api/1/vehicles/{vehicle_id}/eligible_upgrades&#34;,
</span></span><span style="display:flex;"><span>    &#34;AUTH&#34;: true
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  &#34;AUTOPILOT_UPGRADE_URL&#34;: {
</span></span><span style="display:flex;"><span>    &#34;TYPE&#34;: &#34;GET&#34;,
</span></span><span style="display:flex;"><span>    &#34;URI&#34;: &#34;api/1/vehicles/{vehicle_id}/purchase_url&#34;,
</span></span><span style="display:flex;"><span>    &#34;AUTH&#34;: true
</span></span><span style="display:flex;"><span>  }</span></span></code></pre></td></tr></table>
</div>
</div>
<p>When I try and call the <code>Purchase_URL</code>, I get a <code>HTTP 400</code>, and seems like I am missing some parameters - other than the headers.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    &#34;error&#34;: &#34;bad_request&#34;,
</span></span><span style="display:flex;"><span>    &#34;error_description&#34;: &#34;The data given to this server does not meet our criteria.&#34;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And calling the <code>eligible_upgrades</code> I get a <code>'false'</code>. Now I already have AutoPilot, so this might make sense. And given this seems to be a key-value pair, I am guessing there will be other things that Tesla would add over time to up-sell.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    &#34;autopilot&#34;: false
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The final new API is related to energy sites, and something I of course don&rsquo;t have or have an interest, but sharing here if someone does care. :)</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&#34;CALENDAR_HISTORY_DATA&#34;: {
</span></span><span style="display:flex;"><span>    &#34;TYPE&#34;: &#34;GET&#34;,
</span></span><span style="display:flex;"><span>    &#34;URI&#34;: &#34;api/1/energy_sites/{site_id}/calendar_history&#34;,
</span></span><span style="display:flex;"><span>    &#34;AUTH&#34;: true
</span></span><span style="display:flex;"><span>  }</span></span></code></pre></td></tr></table>
</div>
</div>
<p>I am not publishing the full API here as there aren&rsquo;t significant changes. You of course can see the <a
	
		href = "http://desigeek.com/blog/amit/2019/04/20/tesla-rest-apis-v3-8-2/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		older post
	</span>
</a> which has the details.</p>
]]></content:encoded>
    </item>
    <item>
      <title>npm install blues - npm ERR! Error: Method Not Allowed</title>
      <link>/post/2019/08/npm-install-blues-npm-err-error-method-not-allowed/</link>
      <pubDate>Tue, 13 Aug 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/08/npm-install-blues-npm-err-error-method-not-allowed/</guid>
      <description>&lt;p&gt;This is a output of a few frustrating hours (spanning over a few days - as and when I can get time), and finally got it fixed and working. Hopefully it might help someone who is also dealing with npm blues.&lt;/p&gt;
&lt;p&gt;When NodeJS and npm works, its awesome. But when it borks, it is worst than my code or so it seems :).&lt;/p&gt;
&lt;p&gt;Been playing with a few things and wanting to get a dashboard going with &lt;a
	
		href = &#34;https://grafana.com/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Grafana
	&lt;/span&gt;
&lt;/a&gt; (and &lt;a
	
		href = &#34;https://www.influxdata.com/products/influxdb-overview/influxdb-2-0/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		InfluxBD
	&lt;/span&gt;
&lt;/a&gt; as a time-series DB). But some of the installation was failing and for the life of me, could not figure out why and how. Clean image install and downgrading to the previous stable version also didn&amp;rsquo;t help.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>This is a output of a few frustrating hours (spanning over a few days - as and when I can get time), and finally got it fixed and working. Hopefully it might help someone who is also dealing with npm blues.</p>
<p>When NodeJS and npm works, its awesome. But when it borks, it is worst than my code or so it seems :).</p>
<p>Been playing with a few things and wanting to get a dashboard going with <a
	
		href = "https://grafana.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Grafana
	</span>
</a> (and <a
	
		href = "https://www.influxdata.com/products/influxdb-overview/influxdb-2-0/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		InfluxBD
	</span>
</a> as a time-series DB). But some of the installation was failing and for the life of me, could not figure out why and how. Clean image install and downgrading to the previous stable version also didn&rsquo;t help.</p>
<p>One example of npm failing miserably was the <code>&quot;Error: Method not Allowed&quot;</code> which is not very helpful. Here is an example of what I was seeing:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>root@pi-server:/var/lib/grafana/plugins/grafana-trackmap-panel# npm install
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">(</span>node:4538<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#8aadf4">\[</span>DEP0022<span style="color:#8aadf4">\]</span> DeprecationWarning: os.tmpDir<span style="color:#91d7e3;font-weight:bold">()</span> is deprecated. Use os.tmpdir<span style="color:#91d7e3;font-weight:bold">()</span> instead.
</span></span><span style="display:flex;"><span>npm ERR! Error: Method Not Allowed
</span></span><span style="display:flex;"><span>npm ERR!     at errorResponse <span style="color:#91d7e3;font-weight:bold">(</span>/usr/share/npm/lib/cache/add-named.js:260:10<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>npm ERR!     at /usr/share/npm/lib/cache/add-named.js:203:12
</span></span><span style="display:flex;"><span>npm ERR!     at saved <span style="color:#91d7e3;font-weight:bold">(</span>/usr/share/npm/node<span style="color:#8aadf4">\_</span>modules/npm-registry-client/lib/get.js:167:7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>npm ERR!     at FSReqWrap.oncomplete <span style="color:#91d7e3;font-weight:bold">(</span>fs.js:135:15<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>npm ERR! If you need help, you may report this <span style="color:#8aadf4">\*</span>entire<span style="color:#8aadf4">\*</span> log,
</span></span><span style="display:flex;"><span>npm ERR! including the npm and node versions, at:
</span></span><span style="display:flex;"><span>npm ERR!     &lt;http://github.com/npm/npm/issues&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>npm ERR! System Linux 4.19.57-v7+
</span></span><span style="display:flex;"><span>npm ERR! <span style="color:#91d7e3">command</span> <span style="color:#a6da95">&#34;/usr/bin/node&#34;</span> <span style="color:#a6da95">&#34;/usr/bin/npm&#34;</span> <span style="color:#a6da95">&#34;install&#34;</span>
</span></span><span style="display:flex;"><span>npm ERR! cwd /var/lib/grafana/plugins/grafana-trackmap-panel
</span></span><span style="display:flex;"><span>npm ERR! node -v v8.11.1
</span></span><span style="display:flex;"><span>npm ERR! npm -v 1.4.21
</span></span><span style="display:flex;"><span>npm ERR! code E405
</span></span><span style="display:flex;"><span>npm ERR!
</span></span><span style="display:flex;"><span>npm ERR! Additional logging details can be found in:
</span></span><span style="display:flex;"><span>npm ERR!     /var/lib/grafana/plugins/grafana-trackmap-panel/npm-debug.log
</span></span><span style="display:flex;"><span>npm ERR! not ok code <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>root@pi-server:/var/lib/grafana/plugins/grafana-trackmap-panel#</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Again, like I said not very helpful. But I finally got to be able to fix it and move on. And here is what worked for me, and it seems like in the OS image, there was a corrupted files, at some level. In most cases you need root access.</p>
<p>Step 1: Remove and clean up NodeJS.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt-get remove nodejs nodejs-legacy nodered</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Step 2: Get the latest stable source.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -sL https://deb.nodesource.com/setup<span style="color:#8aadf4">\_</span><span style="color:#f4dbd6">$NODE</span><span style="color:#8aadf4">\_</span>STABLE<span style="color:#8aadf4">\_</span>BRANCH | sudo -E bash -
</span></span><span style="display:flex;"><span>sudo apt-get install -y nodejs
</span></span><span style="display:flex;"><span>npm install -g npm@latest<span style="color:#8aadf4">\\</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>I also noticed sometimes the commands above don&rsquo;t work. If that is the case then then try the following, to get the latest.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -sL https://deb.nodesource.com/setup<span style="color:#8aadf4">\_</span>9.x | sudo -E bash -
</span></span><span style="display:flex;"><span>sudo apt-get install -y nodejs
</span></span><span style="display:flex;"><span>npm install -g npm@latest</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And based on your dependencies, v9 might not work and you need v8 then you change the first line as following Or for the latest:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -sL https://deb.nodesource.com/setup<span style="color:#8aadf4">\_</span>8.x | sudo -E bash -
</span></span><span style="display:flex;"><span>sudo apt-get install -y nodejs
</span></span><span style="display:flex;"><span>npm install -g npm@latest</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And finally in the end install and start.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>npm install <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> npm start</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And if you do need to check for the update and get the latest, then try:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo npm install -g npm@latest</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Programming</title>
      <link>/post/2019/05/programming/</link>
      <pubDate>Thu, 02 May 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/05/programming/</guid>
      <description>&lt;p&gt;A key virtue of a programmer is laziness. As an example it is what inspires me to automate my home to the point where I don&amp;rsquo;t have to lift a finger to switch on the light. Removing friction from a system is a anesthetic joy. The drug of efficiency, feels really good.&lt;/p&gt;
&lt;p&gt;I still write code and people get surprised by that sometimes - maybe it&amp;rsquo;s the quality of the code 🤓.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>A key virtue of a programmer is laziness. As an example it is what inspires me to automate my home to the point where I don&rsquo;t have to lift a finger to switch on the light. Removing friction from a system is a anesthetic joy. The drug of efficiency, feels really good.</p>
<p>I still write code and people get surprised by that sometimes - maybe it&rsquo;s the quality of the code 🤓.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Getting DonkeyCar working on a Mac</title>
      <link>/post/2019/03/getting-donkeycar-working-on-a-mac/</link>
      <pubDate>Tue, 12 Mar 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/03/getting-donkeycar-working-on-a-mac/</guid>
      <description>&lt;p&gt;I have been &lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2018/05/30/my-self-driving-car/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		playing with a #selfdriving car for a while
	&lt;/span&gt;
&lt;/a&gt;, and that is super exciting. From a #AI and #ML perspective it is small scale but allows one to exploit all aspects of the tech stack and also appreciate the limitations of not only the software but also the hardware.&lt;/p&gt;
&lt;p&gt;With this, You run a NN on a raspberry pi that uses TensorFlow, and Keras and run inference on the edge. The pi doesn&amp;rsquo;t have enough power to train, so you need to do that on a beefier machine and then deploy the model back to run this.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have been <a
	
		href = "http://desigeek.com/blog/amit/2018/05/30/my-self-driving-car/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		playing with a #selfdriving car for a while
	</span>
</a>, and that is super exciting. From a #AI and #ML perspective it is small scale but allows one to exploit all aspects of the tech stack and also appreciate the limitations of not only the software but also the hardware.</p>
<p>With this, You run a NN on a raspberry pi that uses TensorFlow, and Keras and run inference on the edge. The pi doesn&rsquo;t have enough power to train, so you need to do that on a beefier machine and then deploy the model back to run this.</p>
<p>Now, I didn&rsquo;t have any issues in getting this running on Windows, but getting it on a Mac was a different story. The documentation is there that outlines all the steps, and even if you follow it to the T, it breaks right in the end.</p>
<p>When I tried to create a car, using a <strong><code>createcar</code></strong> command (this essentially creates the buckets, where you would save the training images, and the model, and the configuration of the car when you connect to it from your machine). The actual file paths would probably be different for you but, essentially it is the same thing.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">(</span>donkey<span style="color:#91d7e3;font-weight:bold">)</span> AMAC02XN1T9JGH5:donkeycar amit.bahree$ donkey createcar ~/mycar
</span></span><span style="display:flex;"><span>Traceback <span style="color:#91d7e3;font-weight:bold">(</span>most recent call last<span style="color:#91d7e3;font-weight:bold">)</span>:
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 660, in <span style="color:#8aadf4">\_</span>build<span style="color:#8aadf4">\_</span>master
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 968, in require
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 859, in resolve
</span></span><span style="display:flex;"><span>pkg<span style="color:#8aadf4">\_</span>resources.ContextualVersionConflict: <span style="color:#91d7e3;font-weight:bold">(</span>imageio 2.4.1 <span style="color:#91d7e3;font-weight:bold">(</span>/anaconda3/envs/donkey/lib/python3.6/site-packages<span style="color:#91d7e3;font-weight:bold">)</span>, Requirement.parse<span style="color:#91d7e3;font-weight:bold">(</span><span style="color:#a6da95">&#39;imageio&lt;3.0,&gt;=2.5&#39;</span><span style="color:#91d7e3;font-weight:bold">)</span>, <span style="color:#91d7e3;font-weight:bold">{</span><span style="color:#a6da95">&#39;moviepy&#39;</span><span style="color:#91d7e3;font-weight:bold">})</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>During handling of the above exception, another exception occurred:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Traceback <span style="color:#91d7e3;font-weight:bold">(</span>most recent call last<span style="color:#91d7e3;font-weight:bold">)</span>:
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/bin/donkey&#34;</span>, line 6, in &lt;module&gt;
</span></span><span style="display:flex;"><span>    from pkg<span style="color:#8aadf4">\_</span>resources import load<span style="color:#8aadf4">\_</span>entry<span style="color:#8aadf4">\_</span>point
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;&lt;frozen importlib.\_bootstrap&gt;&#34;</span>, line 961, in <span style="color:#8aadf4">\_</span>find<span style="color:#8aadf4">\_</span>and<span style="color:#8aadf4">\_</span>load
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;&lt;frozen importlib.\_bootstrap&gt;&#34;</span>, line 950, in <span style="color:#8aadf4">\_</span>find<span style="color:#8aadf4">\_</span>and<span style="color:#8aadf4">\_</span>load<span style="color:#8aadf4">\_</span>unlocked
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;&lt;frozen importlib.\_bootstrap&gt;&#34;</span>, line 646, in <span style="color:#8aadf4">\_</span>load<span style="color:#8aadf4">\_</span>unlocked
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;&lt;frozen importlib.\_bootstrap&gt;&#34;</span>, line 616, in <span style="color:#8aadf4">\_</span>load<span style="color:#8aadf4">\_</span>backward<span style="color:#8aadf4">\_</span>compatible
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 2985, in &lt;module&gt;
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 2971, in <span style="color:#8aadf4">\_</span>call<span style="color:#8aadf4">\_</span>aside
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 2998, in <span style="color:#8aadf4">\_</span>initialize<span style="color:#8aadf4">\_</span>master<span style="color:#8aadf4">\_</span>working<span style="color:#8aadf4">\_</span>set
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 662, in <span style="color:#8aadf4">\_</span>build<span style="color:#8aadf4">\_</span>master
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 675, in <span style="color:#8aadf4">\_</span>build<span style="color:#8aadf4">\_</span>from<span style="color:#8aadf4">\_</span>requirements
</span></span><span style="display:flex;"><span>  File <span style="color:#a6da95">&#34;/anaconda3/envs/donkey/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg/pkg\_resources/\_\_init\_\_.py&#34;</span>, line 854, in resolve
</span></span><span style="display:flex;"><span>pkg<span style="color:#8aadf4">\_</span>resources.DistributionNotFound: The <span style="color:#a6da95">&#39;imageio&lt;3.0,&gt;=2.5&#39;</span> distribution was not found and is required by moviepy</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The key here to focus is on the last lines on both of those blocks of code - the main thing causing the issue is MoviePy (see highlighted lines above).</p>
<p><a
	
		href = "https://zulko.github.io/moviepy/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		MoviePy
	</span>
</a> is a Python library for video editing: cutting, concatenations, title insertions, video compositing (a.k.a. non-linear editing), video processing, and creation of custom effects.</p>
<p>It seems like when you go through the steps - clone the repo, setup anaconda, install tensorflow and get the car configured - there is a mismatch in the MoviePy dependencies which it doesn&rsquo;t like. The way to fix the issue is outlined below.</p>
<h4 id="skip-moviepy">Skip MoviePy</h4>
<p>MoviePy is something you don&rsquo;t need to use right away but later when trying to make a movie (using the makemovie command - which allows you to create a movie file from the images in a Tub.); this is not essential. To do this, the easiest way is to remove (or my suggestion it to comment) out the moviepy dependency from the setup.py file.</p>
<p>This should be line 33 in the setup.py file that you will find in the same folder where you cloned the git repo. As an example the updated file is below, where the moviepy dependency is commented out (see highlighted). And once you save this and go about creating the car, it should work. Of course you cannot use the makemovie option later.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64">64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65">65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66">66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67">67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68">68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69">69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70">70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71">71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72">72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73">73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74">74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75">75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76">76</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span>from setuptools import setup, find\_packages
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>import os
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>with open(<span style="color:#a6da95">&#34;README.md&#34;</span>, <span style="color:#a6da95">&#34;r&#34;</span>) as fh:
</span></span><span style="display:flex;"><span>    long\_description <span style="color:#91d7e3;font-weight:bold">=</span> fh<span style="color:#91d7e3;font-weight:bold">.</span>read()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>setup(name<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;donkeycar&#39;</span>,
</span></span><span style="display:flex;"><span>      version<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;2.5.7&#39;</span>,
</span></span><span style="display:flex;"><span>      description<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Self driving library for python.&#39;</span>,
</span></span><span style="display:flex;"><span>      long\_description<span style="color:#91d7e3;font-weight:bold">=</span>long\_description,
</span></span><span style="display:flex;"><span>      long\_description\_content\_type<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;text/markdown&#34;</span>,
</span></span><span style="display:flex;"><span>      url<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;https://github.com/autorope/donkeycar&#39;</span>,
</span></span><span style="display:flex;"><span>      download\_url<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;https://github.com/autorope/donkeycar/archive/2.1.5.tar.gz&#39;</span>,
</span></span><span style="display:flex;"><span>      author<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;Will Roscoe&#39;</span>,
</span></span><span style="display:flex;"><span>      author\_email<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;wroscoe@gmail.com&#39;</span>,
</span></span><span style="display:flex;"><span>      license<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;MIT&#39;</span>,
</span></span><span style="display:flex;"><span>      entry\_points<span style="color:#91d7e3;font-weight:bold">=</span>{
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;console\_scripts&#39;</span>: \[
</span></span><span style="display:flex;"><span>              <span style="color:#a6da95">&#39;donkey=donkeycar.management.base:execute\_from\_command\_line&#39;</span>,
</span></span><span style="display:flex;"><span>          \],
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      install\_requires<span style="color:#91d7e3;font-weight:bold">=</span>\[<span style="color:#a6da95">&#39;numpy&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;pillow&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;docopt&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;tornado==4.5.3&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;requests&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;h5py&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;python-socketio&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;flask&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;eventlet&#39;</span>,
</span></span><span style="display:flex;"><span>                        <span style="color:#6e738d;font-style:italic">#&#39;moviepy&#39;,</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#a6da95">&#39;pandas&#39;</span>,
</span></span><span style="display:flex;"><span>                        \],
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      extras\_require<span style="color:#91d7e3;font-weight:bold">=</span>{
</span></span><span style="display:flex;"><span>                      <span style="color:#a6da95">&#39;tf&#39;</span>: \[<span style="color:#a6da95">&#39;tensorflow&gt;=1.9.0&#39;</span>\],
</span></span><span style="display:flex;"><span>                      <span style="color:#a6da95">&#39;tf\_gpu&#39;</span>: \[<span style="color:#a6da95">&#39;tensorflow-gpu&gt;=1.9.0&#39;</span>\],
</span></span><span style="display:flex;"><span>                      <span style="color:#a6da95">&#39;pi&#39;</span>: \[
</span></span><span style="display:flex;"><span>                          <span style="color:#a6da95">&#39;picamera&#39;</span>,
</span></span><span style="display:flex;"><span>                          <span style="color:#a6da95">&#39;Adafruit\_PCA9685&#39;</span>,
</span></span><span style="display:flex;"><span>                          \],
</span></span><span style="display:flex;"><span>                      <span style="color:#a6da95">&#39;dev&#39;</span>: \[
</span></span><span style="display:flex;"><span>                          <span style="color:#a6da95">&#39;pytest&#39;</span>,
</span></span><span style="display:flex;"><span>                          <span style="color:#a6da95">&#39;pytest-cov&#39;</span>,
</span></span><span style="display:flex;"><span>                          <span style="color:#a6da95">&#39;responses&#39;</span>
</span></span><span style="display:flex;"><span>                          \],
</span></span><span style="display:flex;"><span>                      <span style="color:#a6da95">&#39;ci&#39;</span>: \[<span style="color:#a6da95">&#39;codecov&#39;</span>\]
</span></span><span style="display:flex;"><span>                  },
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      include\_package\_data<span style="color:#91d7e3;font-weight:bold">=</span>True,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      classifiers<span style="color:#91d7e3;font-weight:bold">=</span>\[
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic"># How mature is this project? Common values are</span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic">#   3 - Alpha</span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic">#   4 - Beta</span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic">#   5 - Production/Stable</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;Development Status :: 3 - Alpha&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic"># Indicate who your project is intended for</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;Intended Audience :: Developers&#39;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;Topic :: Scientific/Engineering :: Artificial Intelligence&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic"># Pick your license as you wish (should match &#34;license&#34; above)</span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;License :: OSI Approved :: MIT License&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic"># Specify the Python versions you support here. In particular, ensure</span>
</span></span><span style="display:flex;"><span>          <span style="color:#6e738d;font-style:italic"># that you indicate whether you support Python 2, Python 3 or both.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;Programming Language :: Python :: 3.5&#39;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#a6da95">&#39;Programming Language :: Python :: 3.6&#39;</span>,
</span></span><span style="display:flex;"><span>      \],
</span></span><span style="display:flex;"><span>      keywords<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#39;selfdriving cars donkeycar diyrobocars&#39;</span>,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      packages<span style="color:#91d7e3;font-weight:bold">=</span>find\_packages(exclude<span style="color:#91d7e3;font-weight:bold">=</span>(\[<span style="color:#a6da95">&#39;tests&#39;</span>, <span style="color:#a6da95">&#39;docs&#39;</span>, <span style="color:#a6da95">&#39;site&#39;</span>, <span style="color:#a6da95">&#39;env&#39;</span>\])),
</span></span><span style="display:flex;"><span>      )</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Once you have saved the setup.py file, you need to run the installation again with the following command and then run the create car command. Both of these are outlined below.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>pip install -e .
</span></span><span style="display:flex;"><span>donkey createcar ~/mycar</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Once you run these, then you should see the successful installation as shown by the output below. Note - your output might be a little different depending on the conda state of packages</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">(</span>donkey<span style="color:#91d7e3;font-weight:bold">)</span> AMAC02XN1T9JGH5:donkeycar amit.bahree$ pip install -e .
</span></span><span style="display:flex;"><span>Obtaining file:///Users/amit.bahree/CloudStation/Documents/Code/donkeycar
</span></span><span style="display:flex;"><span>Requirement already satisfied: numpy in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>1.14.5<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: pillow in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>4.2.1<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: docopt in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>0.6.2<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Collecting <span style="color:#f4dbd6">tornado</span><span style="color:#91d7e3;font-weight:bold">==</span>4.5.3 <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: requests in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>2.18.4<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: h5py in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>2.7.1<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Collecting python-socketio <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/a1/71/118e4b7fb453d7095d6863f4b783dbaa57109af4bc2380300649c8942d61/python<span style="color:#8aadf4">\_</span>socketio-4.0.0-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting flask <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting eventlet <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/86/7e/96e1412f96eeb2f2eca9342dcc4d5bc9305880a448b603b0a8e54439b71c/eventlet-0.24.1-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting pandas <span style="color:#91d7e3;font-weight:bold">(</span>from <span style="color:#f4dbd6">donkeycar</span><span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/99/12/bf4c58eea94cea4f91ff931f284146337814fb8546e6eb0b52584446fd52/pandas-0.24.1-cp36-cp36m-macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>6<span style="color:#8aadf4">\_</span>intel.macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>9<span style="color:#8aadf4">\_</span>intel.macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>9<span style="color:#8aadf4">\_</span>x86<span style="color:#8aadf4">\_</span>64.macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>intel.macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>x86<span style="color:#8aadf4">\_</span>64.whl
</span></span><span style="display:flex;"><span>Requirement already satisfied: olefile in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from pillow-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>0.44<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: chardet&lt;3.1.0,&gt;<span style="color:#91d7e3;font-weight:bold">=</span>3.0.2 in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from requests-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>3.0.4<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: certifi&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2017.4.17 in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from requests-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>2017.7.27.1<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: idna&lt;2.7,&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2.5 in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from requests-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>2.6<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: urllib3&lt;1.23,&gt;<span style="color:#91d7e3;font-weight:bold">=</span>1.21.1 in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from requests-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>1.22<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Requirement already satisfied: six in /anaconda3/envs/donkey/lib/python3.6/site-packages <span style="color:#91d7e3;font-weight:bold">(</span>from h5py-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#91d7e3;font-weight:bold">(</span>1.10.0<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Collecting python-engineio&gt;<span style="color:#91d7e3;font-weight:bold">=</span>3.2.0 <span style="color:#91d7e3;font-weight:bold">(</span>from python-socketio-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/95/91/d083bd7b5d408af53633377dfbf87bf181236c8916d36213388b12eaa999/python<span style="color:#8aadf4">\_</span>engineio-3.4.3-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting click&gt;<span style="color:#91d7e3;font-weight:bold">=</span>5.1 <span style="color:#91d7e3;font-weight:bold">(</span>from flask-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting itsdangerous&gt;<span style="color:#91d7e3;font-weight:bold">=</span>0.24 <span style="color:#91d7e3;font-weight:bold">(</span>from flask-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting Werkzeug&gt;<span style="color:#91d7e3;font-weight:bold">=</span>0.14 <span style="color:#91d7e3;font-weight:bold">(</span>from flask-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting Jinja2&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2.10 <span style="color:#91d7e3;font-weight:bold">(</span>from flask-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting monotonic&gt;<span style="color:#91d7e3;font-weight:bold">=</span>1.4 <span style="color:#91d7e3;font-weight:bold">(</span>from eventlet-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/ac/aa/063eca6a416f397bd99552c534c6d11d57f58f2e94c14780f3bbf818c4cf/monotonic-1.5-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting greenlet&gt;<span style="color:#91d7e3;font-weight:bold">=</span>0.3 <span style="color:#91d7e3;font-weight:bold">(</span>from eventlet-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>Collecting dnspython&gt;<span style="color:#91d7e3;font-weight:bold">=</span>1.15.0 <span style="color:#91d7e3;font-weight:bold">(</span>from eventlet-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/ec/d3/3aa0e7213ef72b8585747aa0e271a9523e713813b9a20177ebe1e939deb0/dnspython-1.16.0-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting pytz&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2011k <span style="color:#91d7e3;font-weight:bold">(</span>from pandas-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/61/28/1d3920e4d1d50b19bc5d24398a7cd85cc7b9a75a490570d5a30c57622d34/pytz-2018.9-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting python-dateutil&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2.5.0 <span style="color:#91d7e3;font-weight:bold">(</span>from pandas-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python<span style="color:#8aadf4">\_</span>dateutil-2.8.0-py2.py3-none-any.whl
</span></span><span style="display:flex;"><span>Collecting MarkupSafe&gt;<span style="color:#91d7e3;font-weight:bold">=</span>0.23 <span style="color:#91d7e3;font-weight:bold">(</span>from Jinja2&gt;<span style="color:#91d7e3;font-weight:bold">=</span>2.10-&gt;flask-&gt;donkeycar<span style="color:#91d7e3;font-weight:bold">==</span>2.5.7<span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>  Using cached https://files.pythonhosted.org/packages/f0/00/a6aea33f5598b080b86d6b6d1214b51afe3ffa6100b902d5aa465080083f/MarkupSafe-1.1.1-cp36-cp36m-macosx<span style="color:#8aadf4">\_</span>10<span style="color:#8aadf4">\_</span>6<span style="color:#8aadf4">\_</span>intel.whl
</span></span><span style="display:flex;"><span>Installing collected packages: tornado, python-engineio, python-socketio, click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, flask, monotonic, greenlet, dnspython, eventlet, pytz, python-dateutil, pandas, donkeycar
</span></span><span style="display:flex;"><span>  Found existing installation: tornado 4.5.1
</span></span><span style="display:flex;"><span>    Uninstalling tornado-4.5.1:
</span></span><span style="display:flex;"><span>      Successfully uninstalled tornado-4.5.1
</span></span><span style="display:flex;"><span>  Found existing installation: Werkzeug 0.12.2
</span></span><span style="display:flex;"><span>    Uninstalling Werkzeug-0.12.2:
</span></span><span style="display:flex;"><span>      Successfully uninstalled Werkzeug-0.12.2
</span></span><span style="display:flex;"><span>  Running setup.py develop <span style="color:#c6a0f6">for</span> donkeycar
</span></span><span style="display:flex;"><span>Successfully installed Jinja2-2.10 MarkupSafe-1.1.1 Werkzeug-0.14.1 click-7.0 dnspython-1.16.0 donkeycar eventlet-0.24.1 flask-1.0.2 greenlet-0.4.15 itsdangerous-1.1.0 monotonic-1.5 pandas-0.24.1 python-dateutil-2.8.0 python-engineio-3.4.3 python-socketio-4.0.0 pytz-2018.9 tornado-4.5.3</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And when I run the createcar, you can see it worked as expected. In my case creating the &lsquo;mycar&rsquo; folder in my home directory. Of course you can choose this wherever you prefer.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">(</span>donkey<span style="color:#91d7e3;font-weight:bold">)</span> AMAC02XN1T9JGH5:donkeycar amit.bahree$ donkey createcar ~/mycar
</span></span><span style="display:flex;"><span>using donkey version: 2.5.7 ...
</span></span><span style="display:flex;"><span>Creating car folder: /Users/amit.bahree/mycar
</span></span><span style="display:flex;"><span>making dir  /Users/amit.bahree/mycar
</span></span><span style="display:flex;"><span>Creating data &amp; model folders.
</span></span><span style="display:flex;"><span>making dir  /Users/amit.bahree/mycar/models
</span></span><span style="display:flex;"><span>making dir  /Users/amit.bahree/mycar/data
</span></span><span style="display:flex;"><span>making dir  /Users/amit.bahree/mycar/logs
</span></span><span style="display:flex;"><span>Copying car application template: donkey2
</span></span><span style="display:flex;"><span>Copying car config defaults. Adjust these before starting your car.
</span></span><span style="display:flex;"><span>Donkey setup complete.</span></span></code></pre></td></tr></table>
</div>
</div>
<p>It is interesting to see this is more stable on Windows, than on a Mac. Also, one last thing to leave you with - when I first ran the installation, the hint that someone was wrong was in the output, but I didn&rsquo;t pay too much attention to it. See the red line highlighted in the output below.</p>
<p><p>

    <figure>
        <img src="images/donkeycar-mac-setup-issue.png" alt="moviepy failure - donkeycar installation"/>
        <figcaption>moviepy failure – donkeycar installation</figcaption>
    </figure>

</p></p>
<p>Don&rsquo;t know at this time on what the solution for moviepy is to get this sorted - luckily its not a big deal at the moment.</p>
]]></content:encoded>
    </item>
    <item>
      <title>VSCode &#43; Python on a mac</title>
      <link>/post/2019/01/vscode-python-on-a-mac/</link>
      <pubDate>Sat, 19 Jan 2019 00:00:00 +0000</pubDate>
      <guid>/post/2019/01/vscode-python-on-a-mac/</guid>
      <description>&lt;p&gt;As my experimentation continues, I wanted to get &lt;a
	
		href = &#34;https://code.visualstudio.com/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Visual Studio Code
	&lt;/span&gt;
&lt;/a&gt; installed on a mac, and wanted to use python as the language of choice - main reason for the mac is to understand and explore the #ML libraries, runtimes, and their support on a mac (both natively and in containers - docker).&lt;/p&gt;
&lt;p&gt;Now, Microsoft has a &lt;a
	
		href = &#34;https://code.visualstudio.com/docs/setup/mac&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		very nice tutorial
	&lt;/span&gt;
&lt;/a&gt; to get VSCode setup and running on a mac, including some basic configuration (e.g. touchbar support). But when it comes to &lt;a
	
		href = &#34;https://code.visualstudio.com/docs/python/python-tutorial&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		getting
	&lt;/span&gt;
&lt;/a&gt; &lt;a
	
		href = &#34;https://code.visualstudio.com/docs/python/debugging&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		python setup
	&lt;/span&gt;
&lt;/a&gt;, and running, that is a different matter. Whilst the tutorial is good, it doesn&amp;rsquo;t actually work and errors out.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>As my experimentation continues, I wanted to get <a
	
		href = "https://code.visualstudio.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Visual Studio Code
	</span>
</a> installed on a mac, and wanted to use python as the language of choice - main reason for the mac is to understand and explore the #ML libraries, runtimes, and their support on a mac (both natively and in containers - docker).</p>
<p>Now, Microsoft has a <a
	
		href = "https://code.visualstudio.com/docs/setup/mac"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		very nice tutorial
	</span>
</a> to get VSCode setup and running on a mac, including some basic configuration (e.g. touchbar support). But when it comes to <a
	
		href = "https://code.visualstudio.com/docs/python/python-tutorial"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		getting
	</span>
</a> <a
	
		href = "https://code.visualstudio.com/docs/python/debugging"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		python setup
	</span>
</a>, and running, that is a different matter. Whilst the tutorial is good, it doesn&rsquo;t actually work and errors out.</p>
<p>Below is the code that <a
	
		href = "https://code.visualstudio.com/docs/python/python-tutorial#_install-and-use-packages"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Microsoft outlines
	</span>
</a> in the tutorial for python. It essentially is the HelloWorld using packages and is quite simple; but this will fail and won&rsquo;t work.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">matplotlib.pyplot</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">plt</span>
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">numpy</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">np</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>x <span style="color:#91d7e3;font-weight:bold">=</span> np<span style="color:#91d7e3;font-weight:bold">.</span>linspace(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">20</span>, <span style="color:#f5a97f">100</span>)  <span style="color:#6e738d;font-style:italic"># Create a list of evenly-spaced numbers over the range</span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>plot(x, np<span style="color:#91d7e3;font-weight:bold">.</span>sin(x))       <span style="color:#6e738d;font-style:italic"># Plot the sine of each x point</span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>show()                   <span style="color:#6e738d;font-style:italic"># Display the plot</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>When you run this, you will see an error that is something like the one outlined below.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>2019-01-18 14:23:34.648 python<span style="color:#8aadf4">\[</span>38527:919087<span style="color:#8aadf4">\]</span> -<span style="color:#8aadf4">\[</span>NSApplication <span style="color:#8aadf4">\_</span>setup:<span style="color:#8aadf4">\]</span>: unrecognized selector sent to instance 0x7fbafa49bf10
</span></span><span style="display:flex;"><span>2019-01-18 14:23:34.654 python<span style="color:#8aadf4">\[</span>38527:919087<span style="color:#8aadf4">\]</span> <span style="color:#8aadf4">\*\*\*</span> Terminating app due to uncaught exception <span style="color:#a6da95">&#39;NSInvalidArgumentException&#39;</span>, reason: <span style="color:#a6da95">&#39;-\[NSApplication \_setup:\]: unrecognized selector sent to instance 0x7fbafa49bf10&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#8aadf4">\*\*\*</span> First throw call stack:
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">(</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">0</span>   CoreFoundation                      0x00007fff521a1ecd <span style="color:#8aadf4">\_\_</span>exceptionPreprocess + <span style="color:#f5a97f">256</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">1</span>   libobjc.A.dylib                     0x00007fff7e25d720 objc<span style="color:#8aadf4">\_</span>exception<span style="color:#8aadf4">\_</span>throw + <span style="color:#f5a97f">48</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">2</span>   CoreFoundation                      0x00007fff5221f275 -<span style="color:#8aadf4">\[</span>NSObject<span style="color:#91d7e3;font-weight:bold">(</span>NSObject<span style="color:#91d7e3;font-weight:bold">)</span> <span style="color:#8aadf4">\_\_</span>retain<span style="color:#8aadf4">\_</span>OA<span style="color:#8aadf4">\]</span> + <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">3</span>   CoreFoundation                      0x00007fff52143b40 <span style="color:#8aadf4">\_\_\_</span>forwarding<span style="color:#8aadf4">\_\_\_</span> + <span style="color:#f5a97f">1486</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">4</span>   CoreFoundation                      0x00007fff521434e8 <span style="color:#8aadf4">\_</span>CF<span style="color:#8aadf4">\_</span>forwarding<span style="color:#8aadf4">\_</span>prep<span style="color:#8aadf4">\_</span><span style="color:#f5a97f">0</span> + <span style="color:#f5a97f">120</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">5</span>   libtk8.6.dylib                      0x000000011523031d TkpInit + <span style="color:#f5a97f">413</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">6</span>   libtk8.6.dylib                      0x000000011518817e Initialize + <span style="color:#f5a97f">2622</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">7</span>   <span style="color:#8aadf4">\_</span>tkinter.cpython-37m-darwin.so      0x0000000114fb2a0f <span style="color:#8aadf4">\_</span>tkinter<span style="color:#8aadf4">\_</span>create + <span style="color:#f5a97f">1183</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">8</span>   python                              0x0000000101836ba6 <span style="color:#8aadf4">\_</span>PyMethodDef<span style="color:#8aadf4">\_</span>RawFastCallKeywords + <span style="color:#f5a97f">230</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">9</span>   python                              0x00000001019772b1 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">257</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">10</span>  python                              0x0000000101974daf <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45215</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">11</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">12</span>  python                              0x0000000101835867 <span style="color:#8aadf4">\_</span>PyFunction<span style="color:#8aadf4">\_</span>FastCallDict + <span style="color:#f5a97f">231</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">13</span>  python                              0x00000001018b9481 slot<span style="color:#8aadf4">\_</span>tp<span style="color:#8aadf4">\_</span>init + <span style="color:#f5a97f">193</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">14</span>  python                              0x00000001018c3441 type<span style="color:#8aadf4">\_</span>call + <span style="color:#f5a97f">241</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">15</span>  python                              0x0000000101836573 <span style="color:#8aadf4">\_</span>PyObject<span style="color:#8aadf4">\_</span>FastCallKeywords + <span style="color:#f5a97f">179</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">16</span>  python                              0x000000010197733f call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">399</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">17</span>  python                              0x0000000101975052 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45890</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">18</span>  python                              0x0000000101836368 <span style="color:#c6a0f6">function</span><span style="color:#8aadf4">\_</span>code<span style="color:#8aadf4">\_</span>fastcall + <span style="color:#f5a97f">120</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">19</span>  python                              0x0000000101977265 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">181</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">20</span>  python                              0x0000000101974daf <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45215</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">21</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">22</span>  python                              0x0000000101835867 <span style="color:#8aadf4">\_</span>PyFunction<span style="color:#8aadf4">\_</span>FastCallDict + <span style="color:#f5a97f">231</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">23</span>  python                              0x0000000101839782 method<span style="color:#8aadf4">\_</span>call + <span style="color:#f5a97f">130</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">24</span>  python                              0x00000001018371e2 PyObject<span style="color:#8aadf4">\_</span>Call + <span style="color:#f5a97f">130</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">25</span>  python                              0x00000001019751c6 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">46262</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">26</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">27</span>  python                              0x0000000101836a73 <span style="color:#8aadf4">\_</span>PyFunction<span style="color:#8aadf4">\_</span>FastCallKeywords + <span style="color:#f5a97f">195</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">28</span>  python                              0x0000000101977265 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">181</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">29</span>  python                              0x0000000101974f99 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45705</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">30</span>  python                              0x0000000101836368 <span style="color:#c6a0f6">function</span><span style="color:#8aadf4">\_</span>code<span style="color:#8aadf4">\_</span>fastcall + <span style="color:#f5a97f">120</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">31</span>  python                              0x0000000101977265 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">181</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">32</span>  python                              0x0000000101974f99 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45705</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">33</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">34</span>  python                              0x0000000101836a73 <span style="color:#8aadf4">\_</span>PyFunction<span style="color:#8aadf4">\_</span>FastCallKeywords + <span style="color:#f5a97f">195</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">35</span>  python                              0x0000000101977265 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">181</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">36</span>  python                              0x0000000101974f99 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45705</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">37</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">38</span>  python                              0x0000000101836a73 <span style="color:#8aadf4">\_</span>PyFunction<span style="color:#8aadf4">\_</span>FastCallKeywords + <span style="color:#f5a97f">195</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">39</span>  python                              0x0000000101977265 call<span style="color:#8aadf4">\_</span>function + <span style="color:#f5a97f">181</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">40</span>  python                              0x0000000101974daf <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalFrameDefault + <span style="color:#f5a97f">45215</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">41</span>  python                              0x0000000101968a42 <span style="color:#8aadf4">\_</span>PyEval<span style="color:#8aadf4">\_</span>EvalCodeWithName + <span style="color:#f5a97f">418</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">42</span>  python                              0x00000001019cc9a0 PyRun<span style="color:#8aadf4">\_</span>FileExFlags + <span style="color:#f5a97f">256</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">43</span>  python                              0x00000001019cc104 PyRun<span style="color:#8aadf4">\_</span>SimpleFileExFlags + <span style="color:#f5a97f">388</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">44</span>  python                              0x00000001019f7edc pymain<span style="color:#8aadf4">\_</span>main + <span style="color:#f5a97f">9148</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">45</span>  python                              0x0000000101808ece main + <span style="color:#f5a97f">142</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">46</span>  libdyld.dylib                       0x00007fff7f32bed9 start + <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>	<span style="color:#f5a97f">47</span>  ???                                 0x0000000000000003 0x0 + <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>libc++abi.dylib: terminating with uncaught exception of <span style="color:#91d7e3">type</span> NSException
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8aadf4">\[</span>Done<span style="color:#8aadf4">\]</span> exited with <span style="color:#f4dbd6">code</span><span style="color:#91d7e3;font-weight:bold">=</span>null in 1.017 seconds</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The main reason this fails is that one has to be a little more explicit with matplot (the library that we are trying to use). Matplot has this <a
	
		href = "https://matplotlib.org/faq/usage_faq.html#what-is-a-backend"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		concept of backends
	</span>
</a>, which essentially is the runtime dependencies needed to support various execution environments - including both interactive and non-interactive environments.</p>
<p>For matplot to work on a mac, the raster graphics c++ library that it uses is based on something called <a
	
		href = "http://antigrain.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Anti-Grain Geometry (AGG)
	</span>
</a>. And for the library to render, we need to be explicit on which agg to use (there are multiple raster libraries).</p>
<p>In addition on a mac OS X there is a limitation when rendering in OSX windows (presently lacks blocking show() behavior when matplotlib is in non-interactive mode).</p>
<p>To get around this, we explicitly tell matplot to use the specific agg (&ldquo;TkAgg in our case) and then it will all work. I have a updated code sample below, which adds more points, and also waits for the console input, so one can see what the output looks like.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">matplotlib</span>
</span></span><span style="display:flex;"><span>matplotlib<span style="color:#91d7e3;font-weight:bold">.</span>use(<span style="color:#a6da95">&#34;TkAgg&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">from</span> <span style="color:#f5a97f">matplotlib</span> <span style="color:#8bd5ca">import</span> pyplot <span style="color:#c6a0f6">as</span> plt
</span></span><span style="display:flex;"><span><span style="color:#8bd5ca">import</span> <span style="color:#f5a97f">numpy</span> <span style="color:#c6a0f6">as</span> <span style="color:#f5a97f">np</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">waitforuser</span>():
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">input</span>(<span style="color:#a6da95">&#34;Press enter to continue ...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>x <span style="color:#91d7e3;font-weight:bold">=</span> np<span style="color:#91d7e3;font-weight:bold">.</span>linspace(<span style="color:#f5a97f">0</span>, <span style="color:#f5a97f">50</span>, <span style="color:#f5a97f">200</span>)  <span style="color:#6e738d;font-style:italic"># Create a list of evenly-spaced numbers over the range</span>
</span></span><span style="display:flex;"><span>y <span style="color:#91d7e3;font-weight:bold">=</span> np<span style="color:#91d7e3;font-weight:bold">.</span>sin(x)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">print</span>(x)
</span></span><span style="display:flex;"><span>waitforuser()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">print</span>(y)
</span></span><span style="display:flex;"><span>waitforuser()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>plot(x,y)
</span></span><span style="display:flex;"><span>plt<span style="color:#91d7e3;font-weight:bold">.</span>show()</span></span></code></pre></td></tr></table>
</div>
</div>
<p>And incase you are wondering what it looks like, below are a few screenshots showing the output.</p>
<p><p>

    <img src="images/Screen-Shot-1.png" alt=""/>

</p></p>
<p><p>

    <img src="https://i2.wp.com/desigeek.com/blog/amit/wp-content/uploads/2019/01/Screen-Shot-2.png?fit=660%2C747" alt=""/>

</p></p>
<p><p>

    <img src="https://i2.wp.com/desigeek.com/blog/amit/wp-content/uploads/2019/01/Screen-Shot-2019-01-18-at-2.41.40-PM.png?fit=660%2C550" alt=""/>

</p></p>
<p>To get everything working, make sure you setup the <a
	
		href = "https://code.visualstudio.com/docs/python/linting"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Linting
	</span>
</a>, <a
	
		href = "https://code.visualstudio.com/docs/python/debugging"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		debugger
	</span>
</a>, and the <a
	
		href = "https://code.visualstudio.com/docs/python/environments"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		python environment
	</span>
</a> properly. And of course, you can go <a
	
		href = "https://code.visualstudio.com/docs/python/tutorial-deploy-containers"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		nuts with containers
	</span>
</a>!</p>
<p>Happy coding!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Update on Tesla .ssq files</title>
      <link>/post/2018/10/update-on-tesla-ssq-files/</link>
      <pubDate>Tue, 09 Oct 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/10/update-on-tesla-ssq-files/</guid>
      <description>&lt;p&gt;Sometime back, I &lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2018/09/13/tesla-ssq-file/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		noticed the car downloaded
	&lt;/span&gt;
&lt;/a&gt; a large file (5.1 GB) which was a .ssq file. I hadn&amp;rsquo;t heard of a ssq file, and was curious on what this was.&lt;/p&gt;
&lt;p&gt;I researched a little and as it turns out, a .ssq file is a compressed file system which is often used in an embedded Linux system, where storage size might be a area of concern. This file-system is called SquashFS, and is usually used on a read-only mode.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Sometime back, I <a
	
		href = "http://desigeek.com/blog/amit/2018/09/13/tesla-ssq-file/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		noticed the car downloaded
	</span>
</a> a large file (5.1 GB) which was a .ssq file. I hadn&rsquo;t heard of a ssq file, and was curious on what this was.</p>
<p>I researched a little and as it turns out, a .ssq file is a compressed file system which is often used in an embedded Linux system, where storage size might be a area of concern. This file-system is called SquashFS, and is usually used on a read-only mode.</p>
<p>SquashFS is interesting, as it lets one mount the file-system directly and is distributed as a kernel source patch - which makes it easy to daisy chain and use it other regular Linux tools.</p>
<p><a
	
		href = "http://www.tldp.org/HOWTO/SquashFS-HOWTO/mksqoverview.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SquashFS tools
	</span>
</a> are useful to mount and create a SquashFS file-system. As shown below, I can mount the downloaded file, using <em>unsquashfs</em>.</p>
<p><p>

    <figure>
        <img src="images/Capture2.jpg" alt=""/>
        <figcaption>unsquashfs to mount a SquashFS file-system</figcaption>
    </figure>

</p></p>
<p>I think it is <a
	
		href = "https://electrek.co/2017/07/03/tesla-map-navigation-open-source-platforms/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		known
	</span>
</a> that Tesla uses Valhalla for their maps and this file is the updated maps data. Valhalla, is a open source routing library which is using <a
	
		href = "https://www.openstreetmap.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		OpenStreetMap
	</span>
</a>. Valhalla, also incorporates the <a
	
		href = "https://en.wikipedia.org/wiki/Travelling_salesman_problem"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		traditional travelling salesman problem
	</span>
</a> which is a <a
	
		href = "http://artemis.cs.yale.edu/classes/cs460/Spring98/chap5/nondet.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		non-deterministic polynomial problem
	</span>
</a>.</p>
<p>When extracted and mounted, we see the following directory structure; each of these folders (and files therein) are in fact the tiles that make up the maps (next time in the car, when you zoom in or out or search of a non-cached location, notice carefully on how it is loading and you can just about make out the tiles - it is quick and easy to miss). And it is these tiles that is used for routing as part of the navigation. </p>
<p><p>

    <img src="images/Capture3.jpg" alt=""/>

</p></p>
<p>Tiled based routing is supposed to be beneficial - it uses less memory (the graph can be decomposed much easier, with a smaller set of it loaded in memory), cahce-able, easier to manage (update-able), etc. We can see a glimpse on how the routing and calculation happen on a tile basis below.</p>
<p><p>

    <img src="images/ezgif.com-optimize.gif" alt=""/>

</p></p>
<p>tiles based routing</p>
<p>When, extracted we see there are three levels of hierarchy (0, 1, and, 2). In the file-system these are shown as directories, but there is a method to the madness.</p>
<ul>
<li>Level 0 - these contain edges pertaining to roads that are considered highway / freeway / motorway roads. These are stored as 4 degree tiles.</li>
<li>Level 1 - contains roads that are at a arterial level and are saved in 1 degree tiles.</li>
<li>Level 2 - these are local roads and are saved as 0.25 degree tiles.</li>
</ul>
<p>For example, the world at Level 0 would look like what we are seeing in the image below. And Pennsylvania can be seen below that; Level 0 colored in light blue, Level 1 in light green, and finally Level 2 in light red (which might not be obvious with the translucency).</p>
<p><p>

    <figure>
        <img src="images/world_level0.jpg" alt=""/>
        <figcaption>World Level 0 tiles</figcaption>
    </figure>

</p></p>
<p><p>

    <figure>
        <img src="images/pennsylvania.png" alt=""/>
        <figcaption>Pennsylvania Level 0, 1, and 2 tiles</figcaption>
    </figure>

</p></p>
<p>So, to use this, one can use a few helper functions to get the exact tile to load and vice-versa. For example using the GPS coordinate of 41.413203, -73.623787 (which is just outside of <a
	
		href = "https://www.google.com/maps/place/41%C2%B024%2747.5%22N&#43;73%C2%B037%2725.6%22W/@41.3764196,-73.663217,12z/data=!4m5!3m4!1s0x0:0x0!8m2!3d41.413203!4d-73.623787"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Brewster, NY
	</span>
</a>), loading Level 2 (via the get_title_2 function) would give us the structure of <code>/2/000/756/425.gph</code> using which we know which tile to load.</p>
<p>Helper function (in python) that help obtain levels, tile ids, tile lists, lat/long coordinates, etc. from an intersecting box.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>valhalla_tiles <span style="color:#91d7e3;font-weight:bold">=</span> [{<span style="color:#a6da95">&#39;level&#39;</span>: <span style="color:#f5a97f">2</span>, <span style="color:#a6da95">&#39;size&#39;</span>: <span style="color:#f5a97f">0.25</span>}, {<span style="color:#a6da95">&#39;level&#39;</span>: <span style="color:#f5a97f">1</span>, <span style="color:#a6da95">&#39;size&#39;</span>: <span style="color:#f5a97f">1.0</span>}, {<span style="color:#a6da95">&#39;level&#39;</span>: <span style="color:#f5a97f">0</span>, <span style="color:#a6da95">&#39;size&#39;</span>: <span style="color:#f5a97f">4.0</span>}]
</span></span><span style="display:flex;"><span>LEVEL_BITS <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>TILE_INDEX_BITS <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">22</span>
</span></span><span style="display:flex;"><span>ID_INDEX_BITS <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">21</span>
</span></span><span style="display:flex;"><span>LEVEL_MASK <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#f5a97f">2</span><span style="color:#91d7e3;font-weight:bold">**</span>LEVEL_BITS) <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>TILE_INDEX_MASK <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#f5a97f">2</span><span style="color:#91d7e3;font-weight:bold">**</span>TILE_INDEX_BITS) <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>ID_INDEX_MASK <span style="color:#91d7e3;font-weight:bold">=</span> (<span style="color:#f5a97f">2</span><span style="color:#91d7e3;font-weight:bold">**</span>ID_INDEX_BITS) <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>INVALID_ID <span style="color:#91d7e3;font-weight:bold">=</span> (ID_INDEX_MASK <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> (TILE_INDEX_BITS <span style="color:#91d7e3;font-weight:bold">+</span> LEVEL_BITS)) <span style="color:#91d7e3;font-weight:bold">|</span> (TILE_INDEX_MASK <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> LEVEL_BITS) <span style="color:#91d7e3;font-weight:bold">|</span> LEVEL_MASK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_tile_level</span>(<span style="color:#91d7e3">id</span>):
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">id</span> <span style="color:#91d7e3;font-weight:bold">&amp;</span> LEVEL_MASK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_tile_index</span>(<span style="color:#91d7e3">id</span>):
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> (<span style="color:#91d7e3">id</span> <span style="color:#91d7e3;font-weight:bold">&gt;&gt;</span> LEVEL_BITS) <span style="color:#91d7e3;font-weight:bold">&amp;</span> TILE_INDEX_MASK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_index</span>(<span style="color:#91d7e3">id</span>):
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> (<span style="color:#91d7e3">id</span> <span style="color:#91d7e3;font-weight:bold">&gt;&gt;</span> (LEVEL_BITS <span style="color:#91d7e3;font-weight:bold">+</span> TILE_INDEX_BITS)) <span style="color:#91d7e3;font-weight:bold">&amp;</span> ID_INDEX_MASK
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">tiles_for_bounding_box</span>(left, bottom, right, top):
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">#if this is crossing the anti meridian split it up and combine</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">if</span> left <span style="color:#91d7e3;font-weight:bold">&gt;</span> right:
</span></span><span style="display:flex;"><span>    east <span style="color:#91d7e3;font-weight:bold">=</span> tiles_for_bounding_box(left, bottom, <span style="color:#f5a97f">180.0</span>, top)
</span></span><span style="display:flex;"><span>    west <span style="color:#91d7e3;font-weight:bold">=</span> tiles_for_bounding_box(<span style="color:#91d7e3;font-weight:bold">-</span><span style="color:#f5a97f">180.0</span>, bottom, right, top)
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> east <span style="color:#91d7e3;font-weight:bold">+</span> west
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">#move these so we can compute percentages</span>
</span></span><span style="display:flex;"><span>  left <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">180</span>
</span></span><span style="display:flex;"><span>  right <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">180</span>
</span></span><span style="display:flex;"><span>  bottom <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">90</span>
</span></span><span style="display:flex;"><span>  top <span style="color:#91d7e3;font-weight:bold">+=</span> <span style="color:#f5a97f">90</span>
</span></span><span style="display:flex;"><span>  tiles <span style="color:#91d7e3;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>  <span style="color:#6e738d;font-style:italic">#for each size of tile</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">for</span> tile_set <span style="color:#91d7e3;font-weight:bold">in</span> valhalla_tiles:
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">#for each column</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span> x <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(<span style="color:#91d7e3">int</span>(left<span style="color:#91d7e3;font-weight:bold">/</span>tile_set[<span style="color:#a6da95">&#39;size&#39;</span>]), <span style="color:#91d7e3">int</span>(right<span style="color:#91d7e3;font-weight:bold">/</span>tile_set[<span style="color:#a6da95">&#39;size&#39;</span>]) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>):
</span></span><span style="display:flex;"><span>      <span style="color:#6e738d;font-style:italic">#for each row</span>
</span></span><span style="display:flex;"><span>      <span style="color:#c6a0f6">for</span> y <span style="color:#91d7e3;font-weight:bold">in</span> <span style="color:#91d7e3">range</span>(<span style="color:#91d7e3">int</span>(bottom<span style="color:#91d7e3;font-weight:bold">/</span>tile_set[<span style="color:#a6da95">&#39;size&#39;</span>]), <span style="color:#91d7e3">int</span>(top<span style="color:#91d7e3;font-weight:bold">/</span>tile_set[<span style="color:#a6da95">&#39;size&#39;</span>]) <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">#give back the level and the tile index</span>
</span></span><span style="display:flex;"><span>        tiles<span style="color:#91d7e3;font-weight:bold">.</span>append((tile_set[<span style="color:#a6da95">&#39;level&#39;</span>], <span style="color:#91d7e3">int</span>(y <span style="color:#91d7e3;font-weight:bold">*</span> (<span style="color:#f5a97f">360.0</span><span style="color:#91d7e3;font-weight:bold">/</span>tile_set[<span style="color:#a6da95">&#39;size&#39;</span>]) <span style="color:#91d7e3;font-weight:bold">+</span> x)))
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> tiles
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_tile_id</span>(tile_level, lat, lon):
</span></span><span style="display:flex;"><span>  level <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">filter</span>(<span style="color:#c6a0f6">lambda</span> x: x[<span style="color:#a6da95">&#39;level&#39;</span>] <span style="color:#91d7e3;font-weight:bold">==</span> tile_level, valhalla_tiles)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>  width <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(<span style="color:#f5a97f">360</span> <span style="color:#91d7e3;font-weight:bold">/</span> level[<span style="color:#a6da95">&#39;size&#39;</span>])
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">int</span>((lat <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">90</span>) <span style="color:#91d7e3;font-weight:bold">/</span> level[<span style="color:#a6da95">&#39;size&#39;</span>]) <span style="color:#91d7e3;font-weight:bold">*</span> width <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">int</span>((lon <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">180</span> ) <span style="color:#91d7e3;font-weight:bold">/</span> level[<span style="color:#a6da95">&#39;size&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">def</span> <span style="color:#8aadf4">get_ll</span>(<span style="color:#91d7e3">id</span>):
</span></span><span style="display:flex;"><span>  tile_level <span style="color:#91d7e3;font-weight:bold">=</span> get_tile_level(<span style="color:#91d7e3">id</span>)
</span></span><span style="display:flex;"><span>  tile_index <span style="color:#91d7e3;font-weight:bold">=</span> get_tile_index(<span style="color:#91d7e3">id</span>)
</span></span><span style="display:flex;"><span>  level <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">filter</span>(<span style="color:#c6a0f6">lambda</span> x: x[<span style="color:#a6da95">&#39;level&#39;</span>] <span style="color:#91d7e3;font-weight:bold">==</span> tile_level, valhalla_tiles)[<span style="color:#f5a97f">0</span>]
</span></span><span style="display:flex;"><span>  width <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(<span style="color:#f5a97f">360</span> <span style="color:#91d7e3;font-weight:bold">/</span> level[<span style="color:#a6da95">&#39;size&#39;</span>])
</span></span><span style="display:flex;"><span>  height <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">int</span>(<span style="color:#f5a97f">180</span> <span style="color:#91d7e3;font-weight:bold">/</span> level[<span style="color:#a6da95">&#39;size&#39;</span>])
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">return</span> <span style="color:#91d7e3">int</span>(tile_index <span style="color:#91d7e3;font-weight:bold">/</span> width) <span style="color:#91d7e3;font-weight:bold">*</span> level[<span style="color:#a6da95">&#39;size&#39;</span>] <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">90</span>, (tile_index <span style="color:#91d7e3;font-weight:bold">%</span> width) <span style="color:#91d7e3;font-weight:bold">*</span> level[<span style="color:#a6da95">&#39;size&#39;</span>] <span style="color:#91d7e3;font-weight:bold">-</span> <span style="color:#f5a97f">180</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Tesla has actually open-sourced <a
	
		href = "https://github.com/teslamotors/valhalla"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		their implementation of Valhalla
	</span>
</a>, which is based on C++. This still seems like an active project, but parts of the code haven&rsquo;t been updated for a while.</p>
<p>Whilst I haven&rsquo;t tried to set this up myself, it seems quite simple. Below are the instructions to get this going on Ubuntu or Debian (I think Mac is also supported, but needs a little different dependency set).</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#below are the dependencies needed</span>
</span></span><span style="display:flex;"><span>sudo add-apt-repository -y ppa:valhalla-core/valhalla
</span></span><span style="display:flex;"><span>sudo apt-get update
</span></span><span style="display:flex;"><span>sudo apt-get install -y autoconf automake make libtool pkg-config g++ gcc jq lcov protobuf-compiler vim-common libboost-all-dev libboost-all-dev libcurl4-openssl-dev libprime-server0.6.3-dev libprotobuf-dev prime-server0.6.3-bin
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#if you plan to compile with data building support, see below for more info</span>
</span></span><span style="display:flex;"><span>sudo apt-get install -y libgeos-dev libgeos++-dev liblua5.2-dev libspatialite-dev libsqlite3-dev lua5.2
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[[</span> <span style="color:#c6a0f6">$(</span>grep -cF xenial /etc/lsb-release<span style="color:#c6a0f6">)</span> &gt; <span style="color:#f5a97f">0</span> <span style="color:#91d7e3;font-weight:bold">]]</span>; <span style="color:#c6a0f6">then</span> sudo apt-get install -y libsqlite3-mod-spatialite; <span style="color:#c6a0f6">fi</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#if you plan to compile with python bindings, see below for more info</span>
</span></span><span style="display:flex;"><span>sudo apt-get install -y python-all-dev
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#install with the following</span>
</span></span><span style="display:flex;"><span>git submodule update --init --recursive
</span></span><span style="display:flex;"><span>./autogen.sh
</span></span><span style="display:flex;"><span>./configure
</span></span><span style="display:flex;"><span>make <span style="color:#91d7e3">test</span> -j<span style="color:#c6a0f6">$(</span>nproc<span style="color:#c6a0f6">)</span>
</span></span><span style="display:flex;"><span>sudo make install</span></span></code></pre></td></tr></table>
</div>
</div>
<p>There you have it - we know now what the .ssq files are and how they are used. Just need more time to get it going and play with it - perhaps another project for another time. &#x1f604;</p>
]]></content:encoded>
    </item>
    <item>
      <title>Tesla v9 API endpoints</title>
      <link>/post/2018/10/tesla-v9-api-endpoints/</link>
      <pubDate>Tue, 02 Oct 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/10/tesla-v9-api-endpoints/</guid>
      <description>&lt;p&gt;In case you haven&amp;rsquo;t been following the news, Tesla is in the process of releasing the new firmware beta. I think many folks online are super interested in new autopilot upgrades.&lt;/p&gt;
&lt;p&gt;I reverse engineered the associated app and there are certainly a few new end points exposed, as outlined below. Need time to now figure out more details on this and what they entail. Also need time to see what changes in the existing code and json (data structure). &lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In case you haven&rsquo;t been following the news, Tesla is in the process of releasing the new firmware beta. I think many folks online are super interested in new autopilot upgrades.</p>
<p>I reverse engineered the associated app and there are certainly a few new end points exposed, as outlined below. Need time to now figure out more details on this and what they entail. Also need time to see what changes in the existing code and json (data structure). </p>
<p>Is it interesting to go noodle on this, and see the associated calls. This outlines all the products as of today</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="137"><a style="outline:none;text-decoration:none;color:inherit" href="#137">137</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="138"><a style="outline:none;text-decoration:none;color:inherit" href="#138">138</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="139"><a style="outline:none;text-decoration:none;color:inherit" href="#139">139</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="140"><a style="outline:none;text-decoration:none;color:inherit" href="#140">140</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="141"><a style="outline:none;text-decoration:none;color:inherit" href="#141">141</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="142"><a style="outline:none;text-decoration:none;color:inherit" href="#142">142</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="143"><a style="outline:none;text-decoration:none;color:inherit" href="#143">143</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="144"><a style="outline:none;text-decoration:none;color:inherit" href="#144">144</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="145"><a style="outline:none;text-decoration:none;color:inherit" href="#145">145</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="146"><a style="outline:none;text-decoration:none;color:inherit" href="#146">146</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="147"><a style="outline:none;text-decoration:none;color:inherit" href="#147">147</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="148"><a style="outline:none;text-decoration:none;color:inherit" href="#148">148</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="149"><a style="outline:none;text-decoration:none;color:inherit" href="#149">149</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="150"><a style="outline:none;text-decoration:none;color:inherit" href="#150">150</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="151"><a style="outline:none;text-decoration:none;color:inherit" href="#151">151</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="152"><a style="outline:none;text-decoration:none;color:inherit" href="#152">152</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="153"><a style="outline:none;text-decoration:none;color:inherit" href="#153">153</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="154"><a style="outline:none;text-decoration:none;color:inherit" href="#154">154</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="155"><a style="outline:none;text-decoration:none;color:inherit" href="#155">155</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="156"><a style="outline:none;text-decoration:none;color:inherit" href="#156">156</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="157"><a style="outline:none;text-decoration:none;color:inherit" href="#157">157</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="158"><a style="outline:none;text-decoration:none;color:inherit" href="#158">158</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="159"><a style="outline:none;text-decoration:none;color:inherit" href="#159">159</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="160"><a style="outline:none;text-decoration:none;color:inherit" href="#160">160</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="161"><a style="outline:none;text-decoration:none;color:inherit" href="#161">161</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="162"><a style="outline:none;text-decoration:none;color:inherit" href="#162">162</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="163"><a style="outline:none;text-decoration:none;color:inherit" href="#163">163</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="164"><a style="outline:none;text-decoration:none;color:inherit" href="#164">164</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="165"><a style="outline:none;text-decoration:none;color:inherit" href="#165">165</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="166"><a style="outline:none;text-decoration:none;color:inherit" href="#166">166</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="167"><a style="outline:none;text-decoration:none;color:inherit" href="#167">167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="168"><a style="outline:none;text-decoration:none;color:inherit" href="#168">168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="169"><a style="outline:none;text-decoration:none;color:inherit" href="#169">169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="170"><a style="outline:none;text-decoration:none;color:inherit" href="#170">170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="171"><a style="outline:none;text-decoration:none;color:inherit" href="#171">171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="172"><a style="outline:none;text-decoration:none;color:inherit" href="#172">172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="173"><a style="outline:none;text-decoration:none;color:inherit" href="#173">173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="174"><a style="outline:none;text-decoration:none;color:inherit" href="#174">174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="175"><a style="outline:none;text-decoration:none;color:inherit" href="#175">175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="176"><a style="outline:none;text-decoration:none;color:inherit" href="#176">176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="177"><a style="outline:none;text-decoration:none;color:inherit" href="#177">177</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="178"><a style="outline:none;text-decoration:none;color:inherit" href="#178">178</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="179"><a style="outline:none;text-decoration:none;color:inherit" href="#179">179</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="180"><a style="outline:none;text-decoration:none;color:inherit" href="#180">180</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="181"><a style="outline:none;text-decoration:none;color:inherit" href="#181">181</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="182"><a style="outline:none;text-decoration:none;color:inherit" href="#182">182</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="183"><a style="outline:none;text-decoration:none;color:inherit" href="#183">183</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="184"><a style="outline:none;text-decoration:none;color:inherit" href="#184">184</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="185"><a style="outline:none;text-decoration:none;color:inherit" href="#185">185</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="186"><a style="outline:none;text-decoration:none;color:inherit" href="#186">186</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="187"><a style="outline:none;text-decoration:none;color:inherit" href="#187">187</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="188"><a style="outline:none;text-decoration:none;color:inherit" href="#188">188</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="189"><a style="outline:none;text-decoration:none;color:inherit" href="#189">189</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="190"><a style="outline:none;text-decoration:none;color:inherit" href="#190">190</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="191"><a style="outline:none;text-decoration:none;color:inherit" href="#191">191</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="192"><a style="outline:none;text-decoration:none;color:inherit" href="#192">192</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="193"><a style="outline:none;text-decoration:none;color:inherit" href="#193">193</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="194"><a style="outline:none;text-decoration:none;color:inherit" href="#194">194</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="195"><a style="outline:none;text-decoration:none;color:inherit" href="#195">195</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="196"><a style="outline:none;text-decoration:none;color:inherit" href="#196">196</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="197"><a style="outline:none;text-decoration:none;color:inherit" href="#197">197</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="198"><a style="outline:none;text-decoration:none;color:inherit" href="#198">198</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="199"><a style="outline:none;text-decoration:none;color:inherit" href="#199">199</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="200"><a style="outline:none;text-decoration:none;color:inherit" href="#200">200</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="201"><a style="outline:none;text-decoration:none;color:inherit" href="#201">201</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="202"><a style="outline:none;text-decoration:none;color:inherit" href="#202">202</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="203"><a style="outline:none;text-decoration:none;color:inherit" href="#203">203</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="204"><a style="outline:none;text-decoration:none;color:inherit" href="#204">204</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="205"><a style="outline:none;text-decoration:none;color:inherit" href="#205">205</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="206"><a style="outline:none;text-decoration:none;color:inherit" href="#206">206</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="207"><a style="outline:none;text-decoration:none;color:inherit" href="#207">207</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="208"><a style="outline:none;text-decoration:none;color:inherit" href="#208">208</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="209"><a style="outline:none;text-decoration:none;color:inherit" href="#209">209</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="210"><a style="outline:none;text-decoration:none;color:inherit" href="#210">210</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="211"><a style="outline:none;text-decoration:none;color:inherit" href="#211">211</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="212"><a style="outline:none;text-decoration:none;color:inherit" href="#212">212</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="213"><a style="outline:none;text-decoration:none;color:inherit" href="#213">213</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="214"><a style="outline:none;text-decoration:none;color:inherit" href="#214">214</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="215"><a style="outline:none;text-decoration:none;color:inherit" href="#215">215</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="216"><a style="outline:none;text-decoration:none;color:inherit" href="#216">216</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="217"><a style="outline:none;text-decoration:none;color:inherit" href="#217">217</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="218"><a style="outline:none;text-decoration:none;color:inherit" href="#218">218</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="219"><a style="outline:none;text-decoration:none;color:inherit" href="#219">219</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="220"><a style="outline:none;text-decoration:none;color:inherit" href="#220">220</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="221"><a style="outline:none;text-decoration:none;color:inherit" href="#221">221</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="222"><a style="outline:none;text-decoration:none;color:inherit" href="#222">222</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="223"><a style="outline:none;text-decoration:none;color:inherit" href="#223">223</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="224"><a style="outline:none;text-decoration:none;color:inherit" href="#224">224</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="225"><a style="outline:none;text-decoration:none;color:inherit" href="#225">225</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="226"><a style="outline:none;text-decoration:none;color:inherit" href="#226">226</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="227"><a style="outline:none;text-decoration:none;color:inherit" href="#227">227</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="228"><a style="outline:none;text-decoration:none;color:inherit" href="#228">228</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="229"><a style="outline:none;text-decoration:none;color:inherit" href="#229">229</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="230"><a style="outline:none;text-decoration:none;color:inherit" href="#230">230</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="231"><a style="outline:none;text-decoration:none;color:inherit" href="#231">231</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="232"><a style="outline:none;text-decoration:none;color:inherit" href="#232">232</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="233"><a style="outline:none;text-decoration:none;color:inherit" href="#233">233</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="234"><a style="outline:none;text-decoration:none;color:inherit" href="#234">234</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="235"><a style="outline:none;text-decoration:none;color:inherit" href="#235">235</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="236"><a style="outline:none;text-decoration:none;color:inherit" href="#236">236</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="237"><a style="outline:none;text-decoration:none;color:inherit" href="#237">237</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="238"><a style="outline:none;text-decoration:none;color:inherit" href="#238">238</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="239"><a style="outline:none;text-decoration:none;color:inherit" href="#239">239</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="240"><a style="outline:none;text-decoration:none;color:inherit" href="#240">240</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="241"><a style="outline:none;text-decoration:none;color:inherit" href="#241">241</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="242"><a style="outline:none;text-decoration:none;color:inherit" href="#242">242</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="243"><a style="outline:none;text-decoration:none;color:inherit" href="#243">243</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="244"><a style="outline:none;text-decoration:none;color:inherit" href="#244">244</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="245"><a style="outline:none;text-decoration:none;color:inherit" href="#245">245</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="246"><a style="outline:none;text-decoration:none;color:inherit" href="#246">246</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="247"><a style="outline:none;text-decoration:none;color:inherit" href="#247">247</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="248"><a style="outline:none;text-decoration:none;color:inherit" href="#248">248</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="249"><a style="outline:none;text-decoration:none;color:inherit" href="#249">249</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="250"><a style="outline:none;text-decoration:none;color:inherit" href="#250">250</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="251"><a style="outline:none;text-decoration:none;color:inherit" href="#251">251</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="252"><a style="outline:none;text-decoration:none;color:inherit" href="#252">252</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="253"><a style="outline:none;text-decoration:none;color:inherit" href="#253">253</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="254"><a style="outline:none;text-decoration:none;color:inherit" href="#254">254</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="255"><a style="outline:none;text-decoration:none;color:inherit" href="#255">255</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="256"><a style="outline:none;text-decoration:none;color:inherit" href="#256">256</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="257"><a style="outline:none;text-decoration:none;color:inherit" href="#257">257</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="258"><a style="outline:none;text-decoration:none;color:inherit" href="#258">258</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="259"><a style="outline:none;text-decoration:none;color:inherit" href="#259">259</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="260"><a style="outline:none;text-decoration:none;color:inherit" href="#260">260</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="261"><a style="outline:none;text-decoration:none;color:inherit" href="#261">261</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="262"><a style="outline:none;text-decoration:none;color:inherit" href="#262">262</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="263"><a style="outline:none;text-decoration:none;color:inherit" href="#263">263</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="264"><a style="outline:none;text-decoration:none;color:inherit" href="#264">264</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="265"><a style="outline:none;text-decoration:none;color:inherit" href="#265">265</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="266"><a style="outline:none;text-decoration:none;color:inherit" href="#266">266</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="267"><a style="outline:none;text-decoration:none;color:inherit" href="#267">267</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="268"><a style="outline:none;text-decoration:none;color:inherit" href="#268">268</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="269"><a style="outline:none;text-decoration:none;color:inherit" href="#269">269</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="270"><a style="outline:none;text-decoration:none;color:inherit" href="#270">270</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="271"><a style="outline:none;text-decoration:none;color:inherit" href="#271">271</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="272"><a style="outline:none;text-decoration:none;color:inherit" href="#272">272</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="273"><a style="outline:none;text-decoration:none;color:inherit" href="#273">273</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="274"><a style="outline:none;text-decoration:none;color:inherit" href="#274">274</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="275"><a style="outline:none;text-decoration:none;color:inherit" href="#275">275</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="276"><a style="outline:none;text-decoration:none;color:inherit" href="#276">276</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="277"><a style="outline:none;text-decoration:none;color:inherit" href="#277">277</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="278"><a style="outline:none;text-decoration:none;color:inherit" href="#278">278</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="279"><a style="outline:none;text-decoration:none;color:inherit" href="#279">279</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="280"><a style="outline:none;text-decoration:none;color:inherit" href="#280">280</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="281"><a style="outline:none;text-decoration:none;color:inherit" href="#281">281</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="282"><a style="outline:none;text-decoration:none;color:inherit" href="#282">282</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="283"><a style="outline:none;text-decoration:none;color:inherit" href="#283">283</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="284"><a style="outline:none;text-decoration:none;color:inherit" href="#284">284</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="285"><a style="outline:none;text-decoration:none;color:inherit" href="#285">285</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="286"><a style="outline:none;text-decoration:none;color:inherit" href="#286">286</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="287"><a style="outline:none;text-decoration:none;color:inherit" href="#287">287</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="288"><a style="outline:none;text-decoration:none;color:inherit" href="#288">288</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="289"><a style="outline:none;text-decoration:none;color:inherit" href="#289">289</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="290"><a style="outline:none;text-decoration:none;color:inherit" href="#290">290</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="291"><a style="outline:none;text-decoration:none;color:inherit" href="#291">291</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="292"><a style="outline:none;text-decoration:none;color:inherit" href="#292">292</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="293"><a style="outline:none;text-decoration:none;color:inherit" href="#293">293</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="294"><a style="outline:none;text-decoration:none;color:inherit" href="#294">294</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="295"><a style="outline:none;text-decoration:none;color:inherit" href="#295">295</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="296"><a style="outline:none;text-decoration:none;color:inherit" href="#296">296</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="297"><a style="outline:none;text-decoration:none;color:inherit" href="#297">297</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="298"><a style="outline:none;text-decoration:none;color:inherit" href="#298">298</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="299"><a style="outline:none;text-decoration:none;color:inherit" href="#299">299</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="300"><a style="outline:none;text-decoration:none;color:inherit" href="#300">300</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="301"><a style="outline:none;text-decoration:none;color:inherit" href="#301">301</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="302"><a style="outline:none;text-decoration:none;color:inherit" href="#302">302</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="303"><a style="outline:none;text-decoration:none;color:inherit" href="#303">303</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="304"><a style="outline:none;text-decoration:none;color:inherit" href="#304">304</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="305"><a style="outline:none;text-decoration:none;color:inherit" href="#305">305</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="306"><a style="outline:none;text-decoration:none;color:inherit" href="#306">306</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="307"><a style="outline:none;text-decoration:none;color:inherit" href="#307">307</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="308"><a style="outline:none;text-decoration:none;color:inherit" href="#308">308</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="309"><a style="outline:none;text-decoration:none;color:inherit" href="#309">309</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="310"><a style="outline:none;text-decoration:none;color:inherit" href="#310">310</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="311"><a style="outline:none;text-decoration:none;color:inherit" href="#311">311</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="312"><a style="outline:none;text-decoration:none;color:inherit" href="#312">312</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="313"><a style="outline:none;text-decoration:none;color:inherit" href="#313">313</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="314"><a style="outline:none;text-decoration:none;color:inherit" href="#314">314</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="315"><a style="outline:none;text-decoration:none;color:inherit" href="#315">315</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="316"><a style="outline:none;text-decoration:none;color:inherit" href="#316">316</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="317"><a style="outline:none;text-decoration:none;color:inherit" href="#317">317</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="318"><a style="outline:none;text-decoration:none;color:inherit" href="#318">318</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="319"><a style="outline:none;text-decoration:none;color:inherit" href="#319">319</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="320"><a style="outline:none;text-decoration:none;color:inherit" href="#320">320</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="321"><a style="outline:none;text-decoration:none;color:inherit" href="#321">321</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="322"><a style="outline:none;text-decoration:none;color:inherit" href="#322">322</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="323"><a style="outline:none;text-decoration:none;color:inherit" href="#323">323</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="324"><a style="outline:none;text-decoration:none;color:inherit" href="#324">324</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="325"><a style="outline:none;text-decoration:none;color:inherit" href="#325">325</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="326"><a style="outline:none;text-decoration:none;color:inherit" href="#326">326</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="327"><a style="outline:none;text-decoration:none;color:inherit" href="#327">327</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="328"><a style="outline:none;text-decoration:none;color:inherit" href="#328">328</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="329"><a style="outline:none;text-decoration:none;color:inherit" href="#329">329</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="330"><a style="outline:none;text-decoration:none;color:inherit" href="#330">330</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="331"><a style="outline:none;text-decoration:none;color:inherit" href="#331">331</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="332"><a style="outline:none;text-decoration:none;color:inherit" href="#332">332</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="333"><a style="outline:none;text-decoration:none;color:inherit" href="#333">333</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="334"><a style="outline:none;text-decoration:none;color:inherit" href="#334">334</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="335"><a style="outline:none;text-decoration:none;color:inherit" href="#335">335</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="336"><a style="outline:none;text-decoration:none;color:inherit" href="#336">336</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="337"><a style="outline:none;text-decoration:none;color:inherit" href="#337">337</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="338"><a style="outline:none;text-decoration:none;color:inherit" href="#338">338</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="339"><a style="outline:none;text-decoration:none;color:inherit" href="#339">339</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="340"><a style="outline:none;text-decoration:none;color:inherit" href="#340">340</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="341"><a style="outline:none;text-decoration:none;color:inherit" href="#341">341</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="342"><a style="outline:none;text-decoration:none;color:inherit" href="#342">342</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="343"><a style="outline:none;text-decoration:none;color:inherit" href="#343">343</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="344"><a style="outline:none;text-decoration:none;color:inherit" href="#344">344</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="345"><a style="outline:none;text-decoration:none;color:inherit" href="#345">345</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="346"><a style="outline:none;text-decoration:none;color:inherit" href="#346">346</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="347"><a style="outline:none;text-decoration:none;color:inherit" href="#347">347</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="348"><a style="outline:none;text-decoration:none;color:inherit" href="#348">348</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="349"><a style="outline:none;text-decoration:none;color:inherit" href="#349">349</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="350"><a style="outline:none;text-decoration:none;color:inherit" href="#350">350</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="351"><a style="outline:none;text-decoration:none;color:inherit" href="#351">351</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="352"><a style="outline:none;text-decoration:none;color:inherit" href="#352">352</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="353"><a style="outline:none;text-decoration:none;color:inherit" href="#353">353</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="354"><a style="outline:none;text-decoration:none;color:inherit" href="#354">354</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="355"><a style="outline:none;text-decoration:none;color:inherit" href="#355">355</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="356"><a style="outline:none;text-decoration:none;color:inherit" href="#356">356</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="357"><a style="outline:none;text-decoration:none;color:inherit" href="#357">357</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="358"><a style="outline:none;text-decoration:none;color:inherit" href="#358">358</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="359"><a style="outline:none;text-decoration:none;color:inherit" href="#359">359</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="360"><a style="outline:none;text-decoration:none;color:inherit" href="#360">360</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="361"><a style="outline:none;text-decoration:none;color:inherit" href="#361">361</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="362"><a style="outline:none;text-decoration:none;color:inherit" href="#362">362</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="363"><a style="outline:none;text-decoration:none;color:inherit" href="#363">363</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="364"><a style="outline:none;text-decoration:none;color:inherit" href="#364">364</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="365"><a style="outline:none;text-decoration:none;color:inherit" href="#365">365</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="366"><a style="outline:none;text-decoration:none;color:inherit" href="#366">366</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="367"><a style="outline:none;text-decoration:none;color:inherit" href="#367">367</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="368"><a style="outline:none;text-decoration:none;color:inherit" href="#368">368</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="369"><a style="outline:none;text-decoration:none;color:inherit" href="#369">369</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="370"><a style="outline:none;text-decoration:none;color:inherit" href="#370">370</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="371"><a style="outline:none;text-decoration:none;color:inherit" href="#371">371</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="372"><a style="outline:none;text-decoration:none;color:inherit" href="#372">372</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="373"><a style="outline:none;text-decoration:none;color:inherit" href="#373">373</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="374"><a style="outline:none;text-decoration:none;color:inherit" href="#374">374</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="375"><a style="outline:none;text-decoration:none;color:inherit" href="#375">375</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="376"><a style="outline:none;text-decoration:none;color:inherit" href="#376">376</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="377"><a style="outline:none;text-decoration:none;color:inherit" href="#377">377</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="378"><a style="outline:none;text-decoration:none;color:inherit" href="#378">378</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="379"><a style="outline:none;text-decoration:none;color:inherit" href="#379">379</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="380"><a style="outline:none;text-decoration:none;color:inherit" href="#380">380</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="381"><a style="outline:none;text-decoration:none;color:inherit" href="#381">381</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="382"><a style="outline:none;text-decoration:none;color:inherit" href="#382">382</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="383"><a style="outline:none;text-decoration:none;color:inherit" href="#383">383</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="384"><a style="outline:none;text-decoration:none;color:inherit" href="#384">384</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="385"><a style="outline:none;text-decoration:none;color:inherit" href="#385">385</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="386"><a style="outline:none;text-decoration:none;color:inherit" href="#386">386</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="387"><a style="outline:none;text-decoration:none;color:inherit" href="#387">387</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="388"><a style="outline:none;text-decoration:none;color:inherit" href="#388">388</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="389"><a style="outline:none;text-decoration:none;color:inherit" href="#389">389</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="390"><a style="outline:none;text-decoration:none;color:inherit" href="#390">390</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="391"><a style="outline:none;text-decoration:none;color:inherit" href="#391">391</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;AUTHENTICATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;oauth/token&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">false</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;REVOKE_AUTH_TOKEN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;oauth/revoke&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;PRODUCT_LIST&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/products&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;VEHICLE_LIST&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;VEHICLE_SUMMARY&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;VEHICLE_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/data&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;WAKE_UP&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,     
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/wake_up&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;UNLOCK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/door_unlock&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;LOCK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/door_lock&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;HONK_HORN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/honk_horn&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;FLASH_LIGHTS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/flash_lights&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CLIMATE_ON&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/auto_conditioning_start&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CLIMATE_OFF&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/auto_conditioning_stop&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CHANGE_CLIMATE_TEMPERATURE_SETTING&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/set_temps&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CHANGE_CHARGE_LIMIT&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/set_charge_limit&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CHANGE_SUNROOF_STATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/sun_roof_control&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;ACTUATE_TRUNK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/actuate_trunk&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;REMOTE_START&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/remote_start_drive&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CHARGE_PORT_DOOR_OPEN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/charge_port_door_open&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CHARGE_PORT_DOOR_CLOSE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/charge_port_door_close&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;START_CHARGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/charge_start&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;STOP_CHARGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/charge_stop&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_TOGGLE_PLAYBACK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_toggle_playback&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_NEXT_TRACK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_next_track&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_PREVIOUS_TRACK&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_prev_track&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_NEXT_FAVORITE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_next_fav&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_PREVIOUS_FAVORITE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_prev_fav&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_VOLUME_UP&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_volume_up&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MEDIA_VOLUME_DOWN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/media_volume_down&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_LOG&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/logs&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;RETRIEVE_NOTIFICATION_PREFERENCES&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/notification_preferences&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_NOTIFICATION_PREFERENCES&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/notification_preferences&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;RETRIEVE_NOTIFICATION_SUBSCRIPTION_PREFERENCES&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicle_subscriptions&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_NOTIFICATION_SUBSCRIPTION_PREFERENCES&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicle_subscriptions&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;DEACTIVATE_DEVICE_TOKEN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/device/{device_token}/deactivate&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CALENDAR_SYNC&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/upcoming_calendar_entries&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SET_VALET_MODE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/set_valet_mode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;RESET_VALET_PIN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/reset_valet_pin&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SPEED_LIMIT_ACTIVATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/speed_limit_activate&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SPEED_LIMIT_DEACTIVATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/speed_limit_deactivate&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SPEED_LIMIT_SET_LIMIT&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/speed_limit_set_limit&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SPEED_LIMIT_CLEAR_PIN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/speed_limit_clear_pin&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SCHEDULE_SOFTWARE_UPDATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/schedule_software_update&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;CANCEL_SOFTWARE_UPDATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/cancel_software_update&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;POWERWALL_ORDER_SESSION_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/users/powerwall_order_entry_data&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;POWERWALL_ORDER_PAGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;powerwall_order_page&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;CONTENT&#34;</span>: <span style="color:#a6da95">&#34;HTML&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;ONBOARDING_EXPERIENCE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/users/onboarding_data&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;ONBOARDING_EXPERIENCE_PAGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;onboarding_page&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;CONTENT&#34;</span>: <span style="color:#a6da95">&#34;HTML&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;REFERRAL_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/users/referral_data&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;REFERRAL_PAGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;referral_page&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;CONTENT&#34;</span>: <span style="color:#a6da95">&#34;HTML&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MESSAGE_CENTER_MESSAGE_LIST&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/messages&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MESSAGE_CENTER_MESSAGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/messages/{message_id}&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MESSAGE_CENTER_COUNTS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/messages/count&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MESSAGE_CENTER_MESSAGE_ACTION_UPDATE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/messages/{message_id}/actions&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;MESSAGE_CENTER_CTA_PAGE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;messages_cta_page&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;CONTENT&#34;</span>: <span style="color:#a6da95">&#34;HTML&#34;</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;AUTH_COMMAND_TOKEN&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/users/command_token&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_DEVICE_KEY&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/users/keys&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;DIAGNOSTICS_ENTITLEMENTS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/diagnostics&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_DIAGNOSTICS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/diagnostics&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_SUMMARY&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/status&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_POWER_TIMESERIES_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/powerhistory&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_ENERGY_TIMESERIES_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/energyhistory&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_BACKUP_RESERVE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/backup&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_SITE_NAME&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/site_name&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BATTERY_OPERATION_MODE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/powerwalls/{battery_id}/operation&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SITE_SUMMARY&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/status&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SITE_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/live_status&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SITE_CONFIG&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/site_info&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;HISTORY_DATA&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;GET&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/history&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;BACKUP_RESERVE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/backup&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SITE_NAME&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/site_name&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;OPERATION_MODE&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/operation&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;TIME_OF_USE_SETTINGS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/time_of_use_settings&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;STORM_MODE_SETTINGS&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/energy_sites/{site_id}/storm_mode&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;SEND_NOTIFICATION_CONFIRMATION&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/notification_confirmations&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#c6a0f6">&#34;NAVIGATION_REQUEST&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;TYPE&#34;</span>: <span style="color:#a6da95">&#34;POST&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;URI&#34;</span>: <span style="color:#a6da95">&#34;api/1/vehicles/{vehicle_id}/command/navigation_request&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">&#34;AUTH&#34;</span>: <span style="color:#f5a97f">true</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Setting up your own Model 3 &#34;keyfob&#34; - using a IoT Button</title>
      <link>/post/2018/09/setting-up-your-own-model-3-keyfob-using-a-iot-button/</link>
      <pubDate>Sun, 16 Sep 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/09/setting-up-your-own-model-3-keyfob-using-a-iot-button/</guid>
      <description>&lt;p&gt;Some time ago, I talked about my &lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2018/08/06/my-tesla-model-3-keyfob/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Tesla Model 3 &amp;ldquo;keyfob&amp;rdquo;
	&lt;/span&gt;
&lt;/a&gt; which essentially uses a Amazon IoT button to call some of Tesla API&amp;rsquo;s and &amp;ldquo;talk&amp;rdquo; to the car. This for me, is cool as it allows my daughter to unlock, and lock the car at home. And of course it is a bit geeky, and allowing one to play with more things. :)&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Some time ago, I talked about my <a
	
		href = "http://desigeek.com/blog/amit/2018/08/06/my-tesla-model-3-keyfob/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Tesla Model 3 &ldquo;keyfob&rdquo;
	</span>
</a> which essentially uses a Amazon IoT button to call some of Tesla API&rsquo;s and &ldquo;talk&rdquo; to the car. This for me, is cool as it allows my daughter to unlock, and lock the car at home. And of course it is a bit geeky, and allowing one to play with more things. :)</p>
<p>Since publishing this, I was surprised how many of you ping me asking on details on how they can did this for themselves. Given the level of interest, I thought I will document this and outline the steps here. I do have to warn you, that this would be a little long - it entails getting a IoT Button configured, and then the code deployed. Before you get started, and if you aren&rsquo;t techy, I would recommend to go through the post completely, so you get a sense of what is needed.</p>
<p>At a high level, below are the steps that you need to go through to get this working. And this might seem cumbersome and a lot but it is not that difficult. Also if you prefer you can follow the official AWS documentation <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/iot-console-signin.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		online here
	</span>
</a>.</p>
<ol>
<li>Create a AWS Login (if you have a existing Amazon.com login, you can use the same one if you prefer)</li>
<li>Order a IoT Button</li>
<li>Register the IoT Button in the AWS Registry (this is done via the AWS console)</li>
<li>Create (and activate) a device certificate</li>
<li>Create a IoT security policy</li>
<li>Attach the IoT security policy (from the previous step) to the device certificate created earlier</li>
<li>Attach the IoT security policy (now with the associated certificate) to the IoT button</li>
<li>Configure the IoT button</li>
<li>Deploy some code - this is done via a server-less function (also called a Lambda function) - this is the code that gets executed</li>
<li>Test and Deploy</li>
<li>Enjoy the Fob! :)</li>
</ol>
<h3 id="step-1---get-the-iot-button">Step 1 - Get the IoT Button</h3>
<p>Of course you need to get a IoT Button; I got the <a
	
		href = "http://a.co/d/j4ipLkW"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		AWS IoT Button (2nd Generation
	</span>
</a>) which is what I would recommend.</p>
<h3 id="step-2---login-to-aws-iot-console">Step 2 - Login to AWS IoT Console</h3>
<p>Open <a
	
		href = "https://aws.amazon.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		AWS home page
	</span>
</a> and login with your amazon.com credentials. Of course if you don&rsquo;t have a Amazon.com account, then you want to click in sign up on the top right corner, to get this started.</p>
<p><p>

    <figure>
        <img src="images/1-Login-to-AWS.png" alt=""/>
        <figcaption>AWS Login</figcaption>
    </figure>

</p></p>
<p>After I login, I see something similar to the screenshot below. Your exact view might differ a little.</p>
<p><p>

    <figure>
        <img src="images/2-AWS-Services.png" alt=""/>
        <figcaption>AWS Console</figcaption>
    </figure>

</p></p>
<p>I recommend to change the region to one closer to you. To do this, click on the region on the top right corner and choose a region that is physically closest to you. In the longer run this would help with latency issues between you clicking the button and the car responding. For example in my case, Oregon makes most sense.</p>
<p><p>

    <figure>
        <img src="images/3-region.png" alt=""/>
        <figcaption>AWS Region Selection</figcaption>
    </figure>

</p></p>
<p>Once you have a AWS account setup, login to the AWS <a
	
		href = "https://console.aws.amazon.com/iot/home"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		IoT console
	</span>
</a> or on the AWS page in the previous step, scroll down to IoT Core as shown in the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/4-IoT-console.png" alt=""/>
        <figcaption>AWS Console</figcaption>
    </figure>

</p></p>
<h3 id="step-3---register-iot-button">Step 3 - Register IoT Button</h3>
<p>Next step would be to register your IoT button - which of course means you physically have the button with you. The best way to register is to <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/register-device.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		follow the instructions here
	</span>
</a>. I don&rsquo;t see much sense in trying to replicate that here.</p>
<p><strong>Note:</strong> If you are not very technical, or comfortable, it might be best to use either the &ldquo;AWS IoT Button Dev&rdquo; app which is available both on the <a
	
		href = "https://itunes.apple.com/us/app/aws-iot-button/id1178216626?mt=8"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Apple Store
	</span>
</a> (for iOS) and <a
	
		href = "https://play.google.com/store/apps/details?id=com.amazonaws.iotbutton"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Google play
	</span>
</a> (for Android).</p>
<p>Once you have registered a button (it doesn’t matter what you call it) - it will show up similar to the screenshot below. I only have one device listed.</p>
<p><p>

    <figure>
        <img src="images/5-Device-view.png" alt=""/>
        <figcaption>List of IoT things</figcaption>
    </figure>

</p></p>
<h3 id="step-4---create-a-device-certificate">Step 4 - Create a Device Certificate</h3>
<p>Next, we need to create and activate a certificate for the device. Without this, the button won&rsquo;t work. The certificate (which is a <a
	
		href = "https://en.wikipedia.org/wiki/X.509"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		X.509 certificate
	</span>
</a>) protects the communication between the button and AWS.</p>
<p>For most people, the one-click certification creation that AWS has, is probably the way to go. To get to this, on the AWS IoT console, click on Secure and then choose Certificates on the left if not already selected as shown below. I already have a certificate that you can see in the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/6-certs.png" alt=""/>
        <figcaption>Certificates</figcaption>
    </figure>

</p></p>
<p>If you need to create a certificate, click on the <strong>Create</strong> button on the top right corner, and choose one of the options shown in the image below. In most cases you would want to use the <strong>One-click certificate</strong> option.</p>
<p><p>

    <figure>
        <img src="images/6-certificate-creation.png" alt=""/>
        <figcaption>Certificate creation options</figcaption>
    </figure>

</p></p>
<p><strong>NOTE:</strong> Once you create a Certificate, you get three files (these are the keys) that you need to download and keep safe. The certificate itself can be downloaded anytime, but the private and the public keys <strong>CANNOT</strong> be retrieved again after you close this page. It is <strong>IMPORTANT</strong> that you download these and save them in a safe place.</p>
<p><p>

    <figure>
        <img src="images/7-certificate-keys.png" alt=""/>
        <figcaption>Certificate Keys</figcaption>
    </figure>

</p></p>
<p>Once you have these downloaded then click on Activate on the bottom. And you should see a different certificate number than what you are seeing here. And don&rsquo;t worry I have long deleted what you are seeing on this screen. :)</p>
<p>You can also see these in the <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/create-device-certificate.html%ef%bb%bf"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		developer guide
	</span>
</a> on AWS documentation.</p>
<h3 id="step-5---create-a-iot-security-policy">Step 5 - Create a IoT Security Policy</h3>
<p>Next step is go back to the <a
	
		href = "https://console.aws.amazon.com/iot/home"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		AWS IoT Console page
	</span>
</a> and click on Policies under Security. This is used to create a IoT policy that you will need to attach to the certificate. Once you have a policy created, then it will look something like the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/8-iot-policies.png" alt=""/>
        <figcaption>IoT Policies</figcaption>
    </figure>

</p></p>
<p>To create a policy, click on Create (or you might be prompted automatically if you don&rsquo;t have one). On the create screen, in the <strong>Name</strong> you can enter anything that you prefer. I would suggest naming this something that you can remember and differentiate if you will have more than one button. In my case I named it as the same thing as my device.</p>
<ul>
<li>In the policy statements for <strong>Action</strong> enter &ldquo;iot:Connect&rdquo; - without the quotes, but this is case sensitive so make sure you match is exactly.</li>
<li>For the <strong>Resource</strong> ARN enter &ldquo;*&rdquo; (again without the quotes) as shown below.</li>
<li>And finally for the effect, make sure &ldquo;<strong>Allow</strong>&rdquo; is checked.</li>
<li>And click on <strong>Create</strong> at the bottom.</li>
</ul>
<p><p>

    <figure>
        <img src="images/9-policy-creation.png" alt=""/>
        <figcaption>IoT Policy Creation</figcaption>
    </figure>

</p></p>
<p>After this is created this you will see the policies listed as shown below. You can see the new one we just created with &ldquo;<em>WhateverNameYouWillRecognize</em>&rdquo;. You can also see these and more details on the developer documentation - <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/create-iot-policy.html%ef%bb%bf"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Create a AWS IoT Policy
	</span>
</a>.</p>
<p><p>

    <figure>
        <img src="images/10-iot-policies-listed.png" alt=""/>
        <figcaption>IoT Policies</figcaption>
    </figure>

</p></p>
<h3 id="step-6---attach-a-iot-policy">Step 6 - Attach a IoT Policy</h3>
<p>Next step is to attach the policy that is just created to the certificate created earlier. To do that, click on Secure and Certificates on the left, and then click on the three dots (called ellipses) on the top right of the Certificate you created earlier. From the new menu that you get, choose &ldquo;<strong>Attach Policy</strong>&rdquo; as shown below.</p>
<p><p>

    <figure>
        <img src="images/11-attach-policy.png" alt=""/>
        <figcaption>Attach Policy to Certificate</figcaption>
    </figure>

</p></p>
<p>From the resulting menu, select the policy that you had created earlier and select <strong>Attach</strong>. Using a sensible name that you would recognize would be helpful. You can also see these details on the <a
	
		href = "http://%ef%bb%bfhttps://docs.aws.amazon.com/iot/latest/developerguide/attach-policy-to-certificate.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		developer documentation
	</span>
</a>.</p>
<p><p>

    <figure>
        <img src="images/12-attach-policy-to-cert.png" alt=""/>
        <figcaption>Attach Policy to Certificate</figcaption>
    </figure>

</p></p>
<h3 id="step-7---attach-certificate-to-iot-device">Step 7 - Attach Certificate to IoT Device</h3>
<p>Next step is to attach the certificate to the IoT device (or thing). A device must have a certificate, a private key and a root CA certificate to authenticate with AWS. Amazon also recommends to attach a device certificate to the device - this probably isn&rsquo;t helpful right now, but might be in the future if you start playing with this more.</p>
<p>To do this, select the certificate under Security on the left, and same as the previous step, by click on the three dots on the top right corner, select &ldquo;Attach thing&rdquo;.</p>
<p><p>

    <figure>
        <img src="images/13-attach-cert-menu.png" alt=""/>
        <figcaption>Attach Certificate</figcaption>
    </figure>

</p></p>
<p>And from the next screen select the IoT button that you registered earlier, and select &ldquo;Attach&rdquo;.</p>
<p><p>

    <figure>
        <img src="images/14-attach-cert-screen.png" alt=""/>
        <figcaption>Attach Certificate</figcaption>
    </figure>

</p></p>
<h3 id="step-8---configure-iot-button">Step 8 - Configure IoT Button</h3>
<p>To validate that everything is setup correctly - the certificate needs to be associated with a policy, and a thing (the IoT button in our case). So on the Certificates menu on the left, select your certificate by clicking on it (not the three dots this time - but rather the name). You will see a new screen that shows the details of the certificate as shown below.</p>
<p><p>

    <figure>
        <img src="images/15-cert-details.png" alt=""/>
        <figcaption>Certificate Details</figcaption>
    </figure>

</p></p>
<p>And on the new menu on the left, if you click on Policies you should see the policy you created, and the Things should have the IoT button you created earlier.</p>
<p>Once all of this is done the next step is to configure the device. You can see more detailed steps on this on the developer <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/configure-iot.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		guide here
	</span>
</a>.</p>
<ul>
<li><strong>KEY TIP</strong>: The documentation doesn&rsquo;t make it too obvious, but as part of configuring - the device (IoT Button) will become an access point that you will need to connect to and upload the certificates and key you created earlier. You cannot do this from a phone and it is best done from a desktop/laptop that has wifi network. Whilst these days all laptops will have a wifi network card, that isn&rsquo;t necessarily true for desktops. So use a machine which has a wifi that you can temporarily connect to the access point that the IoT device creates.</li>
<li>Note this is only needed for getting the device configured to authenticate for AWS, and get on your Wifi network; once that is done you don&rsquo;t need to do this.</li>
<li>Once you have configured the device as outlined (<a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/configure-iot.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		https://docs.aws.amazon.com/iot/latest/developerguide/configure-iot.html
	</span>
</a>) then continue to the next step.</li>
</ul>
<h3 id="step-9---deploy-some-code">Step 9 - Deploy some code</h3>
<p>At last we are starting to get the interesting part - a lot of what we were doing until now, was getting the button configured and ready.</p>
<p>Now that you have a IoT button configured and registered, the next step is to deploy some code. For this you need to setup a Lambda function using the <a
	
		href = "https://console.aws.amazon.com/lambda/home"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		AWS Lambda Console
	</span>
</a>.</p>
<p>When you login, click on Create Function. On the <strong>Create function</strong> screen, choose the <strong>Blueprints</strong> option as shown below. You can see some of these in the developer <a
	
		href = "https://docs.aws.amazon.com/iot/latest/developerguide/iot-button-lambda.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		documentation here
	</span>
</a>.</p>
<p><p>

    <figure>
        <img src="images/12-Create-function.png" alt=""/>
        <figcaption>Create Function screen</figcaption>
    </figure>

</p></p>
<h3 id="step-10---blueprint-search">Step 10 - Blueprint Search</h3>
<p>On the Blueprints search box (which says Filters by tags), type in &ldquo;<strong>button</strong>&rdquo; (without quotes) and press enter. You should see an option called &ldquo;<strong>iot-button-email</strong>&rdquo; as shown below, select that and click <strong>configure</strong> on the bottom right corner.</p>
<p><p>

    <figure>
        <img src="images/13-Create-function-iot-button.png" alt=""/>
        <figcaption>IoT Button filter</figcaption>
    </figure>

</p></p>
<h3 id="step-11---basic-information">Step 11 - Basic Information</h3>
<p>On the next screen that says &ldquo;Basic information&rdquo;, enter the details as shown below. The names should be meaningful for you to remember. Roles can be reused across other areas, for now you can use a simple name something like &ldquo;unlockCar&rdquo; or &ldquo;unlockCarSomeName&rdquo; if you have more than one vehicle. The policy template should already be populated and you shouldn’t need to do anything else.</p>
<p><p>

    <figure>
        <img src="images/14-function-basic-info.png" alt=""/>
        <figcaption>Function basic information</figcaption>
    </figure>

</p></p>
<p>For the 2nd half - AWS IoT Trigger, select the IoT type as &ldquo;IoT Button&rdquo; and enter your device serial number as outlined in the screenshot below.</p>
<p><p>

    <figure>
        <img src="images/15-IoT-trigger.png" alt=""/>
        <figcaption>IoT Trigger</figcaption>
    </figure>

</p></p>
<p>It won&rsquo;t hurt to download these certificate and keys in addition to the ones created separately and save them in different folders. And for the Lambda function code, it doesn’t matter on the template code as we will be deleting it all. At this point that will be read-only and you won&rsquo;t be able to modify anything - as shown in the screen shot below.</p>
<p><p>

    <figure>
        <img src="images/16-Lambda-function.png" alt=""/>
        <figcaption>Lambda function</figcaption>
    </figure>

</p></p>
<p>And finally scrolling down more, you will see the environment variables. Here is where you need to specify your Tesla credentials to it to be able to use create the token and call the Tesla API. For that you need the following two variables: <strong>TESLA_EMAIL</strong> and <strong>TESLA_PASS</strong>. These case sensitive so you need to enter them as is. And then finally click on Create function.</p>
<p><p>

    <figure>
        <img src="images/17-environment-variables.png" alt=""/>
        <figcaption>Environment Variables</figcaption>
    </figure>

</p></p>
<h3 id="step-12---code-upload">Step 12 - Code upload</h3>
<p>Once you create a function, you will see something like the screen below. In my case the function is called &ldquo;unlockSquirty&rdquo; which is what you are seeing. This is divided in to two parts - when on the Configuration page. The top part is the designer that visually shows you what inputs are the triggers that execute the function, and then what it outputs to on the right hand side.  And below the designer is the editor where one can edit the code inline or upload a zip file with the code.</p>
<p><p>

    <img src="images/18-unlock-squirty.png" alt=""/>

</p></p>
<p>In the function code section, on the first drop down in the left (Code entry type) select upload a .zip file.</p>
<p><p>

    <img src="images/19-function-code-source.png" alt=""/>

</p></p>
<p>And on the next screen upload the function package that you can <a
	
		href = "http://desigeek.com/blog/amit/download/iot-button-lambda-function-package-unlock-tesla"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		download from here.
	</span>
</a></p>
<ul>
<li>Make sure the Runtime is Node.js 8.10</li>
<li>Keep the Handler as the default.</li>
<li>Double check your Environment variable contain TESLA_EMAIL, and TESLA_PASS.</li>
</ul>
<p><p>

    <img src="images/20-function-code.png" alt=""/>

</p></p>
<p>And scroll down and in the Basic settings, change the <strong>timeout</strong> to <strong>1 minute</strong>. We run thus asynchronously and adding a little buffer would be better. You can leave all the other settings at their default. If your network might be iffy you can make this 2 mins.</p>
<p><p>

    <figure>
        <img src="images/21-env-setting.png" alt=""/>
        <figcaption>Environment Settings</figcaption>
    </figure>

</p></p>
<h3 id="step-13---code-publish">Step 13 - Code Publish</h3>
<p>Once you have entered all of this, click on <strong>Save</strong> on the top right corner and then <strong>publish</strong> new version. Finally once it is published you will be able to see the code show up as shown in the screenshot below.</p>
<p><p>

    <img src="images/22-code.png" alt=""/>

</p></p>
<p>Again, a single click will unlock the car, a double-click would lock it, and a long press (holding it for 2-3 seconds) would open the charge port door.</p>
<p>And here is the code:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span> <span style="color:#ed8796">var</span> tjs <span style="color:#91d7e3;font-weight:bold">=</span> require(<span style="color:#a6da95">&#39;teslajs&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#ed8796">var</span> username <span style="color:#91d7e3;font-weight:bold">=</span> process.env.TESLA_EMAIL;
</span></span><span style="display:flex;"><span> <span style="color:#ed8796">var</span> password <span style="color:#91d7e3;font-weight:bold">=</span> process.env.TESLA_PASS;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> exports.handler <span style="color:#91d7e3;font-weight:bold">=</span> (event, context, callback) =&gt; 
</span></span><span style="display:flex;"><span> {
</span></span><span style="display:flex;"><span>  tjs.loginAsync(username, password).done(<span style="color:#ed8796">function</span>(result) 
</span></span><span style="display:flex;"><span>  {
</span></span><span style="display:flex;"><span>   <span style="color:#ed8796">var</span> token <span style="color:#91d7e3;font-weight:bold">=</span> JSON.stringify(result.authToken);
</span></span><span style="display:flex;"><span>   <span style="color:#c6a0f6">if</span> (token)
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a6da95">&#34;Login Succesful!&#34;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   <span style="color:#ed8796">var</span> options <span style="color:#91d7e3;font-weight:bold">=</span> 
</span></span><span style="display:flex;"><span>   {
</span></span><span style="display:flex;"><span>    authToken<span style="color:#91d7e3;font-weight:bold">:</span> result.authToken
</span></span><span style="display:flex;"><span>   };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>   tjs.vehicleAsync(options).done(<span style="color:#ed8796">function</span>(vehicle) 
</span></span><span style="display:flex;"><span>   {
</span></span><span style="display:flex;"><span>    console.log(<span style="color:#a6da95">&#34;Vehicle &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> vehicle.vin <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#a6da95">&#34; is: &#34;</span> <span style="color:#91d7e3;font-weight:bold">+</span> vehicle.state);
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">var</span> options <span style="color:#91d7e3;font-weight:bold">=</span> 
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>     authToken<span style="color:#91d7e3;font-weight:bold">:</span> result.authToken,
</span></span><span style="display:flex;"><span>     vehicleID<span style="color:#91d7e3;font-weight:bold">:</span> vehicle.id_s
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span>(event.clickType <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;SINGLE&#34;</span>)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>     console.log(<span style="color:#a6da95">&#34;Single click, attempting to UNLOCK&#34;</span>);
</span></span><span style="display:flex;"><span>     tjs.doorUnlockAsync(options).done(<span style="color:#ed8796">function</span>(unlockResult) 
</span></span><span style="display:flex;"><span>     {
</span></span><span style="display:flex;"><span>      console.log(<span style="color:#a6da95">&#34;Doors are now UNLOCKED&#34;</span>);
</span></span><span style="display:flex;"><span>     });
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span> <span style="color:#c6a0f6">if</span>(event.clickType <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;DOUBLE&#34;</span>)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>     console.log(<span style="color:#a6da95">&#34;Double click, attempting to LOCK&#34;</span>);
</span></span><span style="display:flex;"><span>     tjs.doorLockAsync(options).done(<span style="color:#ed8796">function</span>(lockResults) {
</span></span><span style="display:flex;"><span>      console.log(<span style="color:#a6da95">&#34;Doors are now LOCKED&#34;</span>);
</span></span><span style="display:flex;"><span>     });              
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span> <span style="color:#c6a0f6">if</span>(event.clickType <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;LONG&#34;</span>)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>     console.log(<span style="color:#a6da95">&#34;Long click, attempting to CHARGE PORT&#34;</span>);
</span></span><span style="display:flex;"><span>     tjs.openChargePortAsync(options).done(<span style="color:#ed8796">function</span>(openResult) {
</span></span><span style="display:flex;"><span>      console.log(<span style="color:#a6da95">&#34;Charge port is now OPEN&#34;</span>);
</span></span><span style="display:flex;"><span>     });              
</span></span><span style="display:flex;"><span>    }    
</span></span><span style="display:flex;"><span>   });
</span></span><span style="display:flex;"><span>  });
</span></span><span style="display:flex;"><span> };</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Generating Tesla authentication token - cURL script</title>
      <link>/post/2018/08/generating-tesla-authentication-token-curl-script/</link>
      <pubDate>Fri, 10 Aug 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/08/generating-tesla-authentication-token-curl-script/</guid>
      <description>&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; This cURL script doesn&amp;rsquo;t work anymore. This was originally published back in 2018 when it was the best way to do this. Over the last few years however Tesla has deprecated this endpoint (/oauth/token) and moved to a SSO service (auth.tesla.com) which is a completely different approach. I&amp;rsquo;ll have a look and if there is a simple way to do it, then will share it here.&lt;/p&gt;
&lt;p&gt;I did write a simple &lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2018/08/06/windows-tesla-auth-token-generator/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Windows (desktop) app
	&lt;/span&gt;
&lt;/a&gt; called TeslaTokenGenerator, for those who wanted to create authentication tokens for their Tesla, and use with 3rd party apps/data loggers. &lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>UPDATE:</strong> This cURL script doesn&rsquo;t work anymore. This was originally published back in 2018 when it was the best way to do this. Over the last few years however Tesla has deprecated this endpoint (/oauth/token) and moved to a SSO service (auth.tesla.com) which is a completely different approach. I&rsquo;ll have a look and if there is a simple way to do it, then will share it here.</p>
<p>I did write a simple <a
	
		href = "http://desigeek.com/blog/amit/2018/08/06/windows-tesla-auth-token-generator/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Windows (desktop) app
	</span>
</a> called TeslaTokenGenerator, for those who wanted to create authentication tokens for their Tesla, and use with 3rd party apps/data loggers. </p>
<p>TeslaTokenGenerator can also create a <a
	
		href = "https://en.wikipedia.org/wiki/CURL"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		cURL script
	</span>
</a> for you to use, if you prefer not wanting to install anything. It is easy to find this online, but some of you have pinged me to get more details on this. So, I have the script below that you can use. Once you copy this, you will need to update your Tesla account login details (email and password) and run it in a console (command line) and it will all the same API&rsquo;s to create the token, which then you can save.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -X POST -H <span style="color:#a6da95">&#34;Cache-Control: no-cache&#34;</span> -H <span style="color:#a6da95">&#34;Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW&#34;</span> -F <span style="color:#a6da95">&#34;grant_type=password&#34;</span> -F <span style="color:#a6da95">&#34;client_id=81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384&#34;</span> -F <span style="color:#a6da95">&#34;client_secret=c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3&#34;</span> -F <span style="color:#a6da95">&#34;email=YOUR-TESLA-LOGIN-EMAIL@SOMEWHERE.COM&#34;</span> -F <span style="color:#a6da95">&#34;password=YOUR-TESLA-ACCOUNT-PASSWORD&#34;</span> <span style="color:#a6da95">&#34;https://owner-api.teslamotors.com/oauth/token&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>You can see the screenshots of this below too - one in Windows, and another in Linux (well <a
	
		href = "http://desigeek.com/blog/amit/2016/04/19/bash-on-windows-is-real-not-a-vm/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Bash on Windows
	</span>
</a>, but it is real Linux).</p>
<p><p>

    <img src="images/cURL-windows.png" alt=""/>

</p></p>
<p><p>

    <img src="images/cURL-linux.png" alt=""/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>Windows Tesla Auth Token Generator</title>
      <link>/post/2018/08/windows-tesla-auth-token-generator/</link>
      <pubDate>Mon, 06 Aug 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/08/windows-tesla-auth-token-generator/</guid>
      <description>&lt;p&gt;If you have a Tesla, and are using (or wanting to use) 3rd party tools or data loggers, the one think they of course need is to authenticate your details with Tesla. A simple, but insecure way is to use your Tesla credentials - and surprisingly many people just happily share and use this.&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t comfortable doing this - after-all, they have access to your account where you can control a lot of things. Also, there are a few online tools that can generate the auth token, but again I wasn&amp;rsquo;t comfortable, as I did not know what they saved, or what they did not. :)&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you have a Tesla, and are using (or wanting to use) 3rd party tools or data loggers, the one think they of course need is to authenticate your details with Tesla. A simple, but insecure way is to use your Tesla credentials - and surprisingly many people just happily share and use this.</p>
<p>I wasn&rsquo;t comfortable doing this - after-all, they have access to your account where you can control a lot of things. Also, there are a few online tools that can generate the auth token, but again I wasn&rsquo;t comfortable, as I did not know what they saved, or what they did not. :)</p>
<p>So, I wrote a simple Windows app that can allow you to generate a auth token that you can save. The application itself is simple. You enter your Tesla credentials, click on Generate Token and can save the generated token. </p>
<p><p>

    <img src="images/Tesla-token-generator.png" alt="Tesla Token Generator Application"/>

</p></p>
<p>To test, if the generated token is working - click on the Test Token button. If everything is working as expected, you will see a list of vehicles that is associated with your account.</p>
<p>If you prefer to use the cURL script, click on the Generate cURL, will generate this and copy it to your clipboard. And it works across operating systems as you can see below (Windows, and Linux), but should also work on Mac.</p>
<ul>
<li>
<p><p>

    <img src="images/cURL-linux.png" alt=""/>

</p></p>
</li>
<li>
<p><p>

    <img src="images/cURL-windows.png" alt=""/>

</p></p>
</li>
</ul>
<p>I do intent to open source this, so folks can have a look at the code, and the Tesla REST APIs. Until then you can <a
	
		href = "http://desigeek.com/blog/amit/download/tesla-token-generator/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		download the setup from here
	</span>
</a>.</p>
<p>Leave a comment if you have any issues or any requests.</p>
<p><strong>Update:</strong> v1.0.1 Published with minor updates. You can download from the same link above. This adds the revoke screen and some house keeping.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Download Build deck and video (2018)</title>
      <link>/post/2018/05/download-build-deck-and-video-2018/</link>
      <pubDate>Sun, 27 May 2018 00:00:00 +0000</pubDate>
      <guid>/post/2018/05/download-build-deck-and-video-2018/</guid>
      <description>&lt;p&gt;Just as &lt;a
	
		href = &#34;/post/2017/05/download-build-2017-decks-and-video/&#34;
	

	

	&gt;
	
	&lt;span&gt;
		last year
	&lt;/span&gt;
&lt;/a&gt;, I wrote a PowerShell script using which you can download the PowerPoint decks, and, videos from Microsoft Build’s conference, instead of streaming it (or manually download it one by one). You can choose if you want the decks, or the videos, or both. For the videos you can choose the desired resolution (Low, Medium, High) – of course the higher the resolution, the more space is needed. The script also downloads the description and if there is a session image (if there is one).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Just as <a
	
		href = "/post/2017/05/download-build-2017-decks-and-video/"
	

	

	>
	
	<span>
		last year
	</span>
</a>, I wrote a PowerShell script using which you can download the PowerPoint decks, and, videos from Microsoft Build’s conference, instead of streaming it (or manually download it one by one). You can choose if you want the decks, or the videos, or both. For the videos you can choose the desired resolution (Low, Medium, High) – of course the higher the resolution, the more space is needed. The script also downloads the description and if there is a session image (if there is one).</p>
<p>A few points to note:</p>
<ul>
<li>The slides only once downloaded is ~10GB and with videos (high-resolution), the size goes up to 90.5 GB. So make sure you have enough space.</li>
<li>By default the download location is <code>C:\build-2018\</code>; you can change this to whatever you want, but make sure there is a trailing backslash. Think of this as the ‘base’ folder.</li>
<li>For each session a sub-folder with the session name will be created in the ‘base’ folder setup in the previous step.</li>
<li>If a file already exists, it will be <strong>skipped</strong>.</li>
<li>As each file is downloaded, it save it in the root folder and once the download is complete, only then moves it in the relevant subfolder.</li>
<li>If a download fails for some reason to retry it, delete the ‘left over’ file(s) in the base folder and then run the script again. The script itself will &rsquo;eat&rsquo; the exception and move on to the next file.</li>
<li>The video quality parameter is 1 for Low, 2 for Medium, and 3 for High (default).</li>
</ul>
<p>And if you read through, the script is quite self-explanatory.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="137"><a style="outline:none;text-decoration:none;color:inherit" href="#137">137</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="138"><a style="outline:none;text-decoration:none;color:inherit" href="#138">138</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="139"><a style="outline:none;text-decoration:none;color:inherit" href="#139">139</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="140"><a style="outline:none;text-decoration:none;color:inherit" href="#140">140</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="141"><a style="outline:none;text-decoration:none;color:inherit" href="#141">141</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="142"><a style="outline:none;text-decoration:none;color:inherit" href="#142">142</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="143"><a style="outline:none;text-decoration:none;color:inherit" href="#143">143</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="144"><a style="outline:none;text-decoration:none;color:inherit" href="#144">144</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="145"><a style="outline:none;text-decoration:none;color:inherit" href="#145">145</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="146"><a style="outline:none;text-decoration:none;color:inherit" href="#146">146</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="147"><a style="outline:none;text-decoration:none;color:inherit" href="#147">147</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="148"><a style="outline:none;text-decoration:none;color:inherit" href="#148">148</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="149"><a style="outline:none;text-decoration:none;color:inherit" href="#149">149</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="150"><a style="outline:none;text-decoration:none;color:inherit" href="#150">150</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="151"><a style="outline:none;text-decoration:none;color:inherit" href="#151">151</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="152"><a style="outline:none;text-decoration:none;color:inherit" href="#152">152</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="153"><a style="outline:none;text-decoration:none;color:inherit" href="#153">153</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="154"><a style="outline:none;text-decoration:none;color:inherit" href="#154">154</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="155"><a style="outline:none;text-decoration:none;color:inherit" href="#155">155</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="156"><a style="outline:none;text-decoration:none;color:inherit" href="#156">156</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="157"><a style="outline:none;text-decoration:none;color:inherit" href="#157">157</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="158"><a style="outline:none;text-decoration:none;color:inherit" href="#158">158</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="159"><a style="outline:none;text-decoration:none;color:inherit" href="#159">159</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="160"><a style="outline:none;text-decoration:none;color:inherit" href="#160">160</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="161"><a style="outline:none;text-decoration:none;color:inherit" href="#161">161</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="162"><a style="outline:none;text-decoration:none;color:inherit" href="#162">162</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="163"><a style="outline:none;text-decoration:none;color:inherit" href="#163">163</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="164"><a style="outline:none;text-decoration:none;color:inherit" href="#164">164</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="165"><a style="outline:none;text-decoration:none;color:inherit" href="#165">165</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="166"><a style="outline:none;text-decoration:none;color:inherit" href="#166">166</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="167"><a style="outline:none;text-decoration:none;color:inherit" href="#167">167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="168"><a style="outline:none;text-decoration:none;color:inherit" href="#168">168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="169"><a style="outline:none;text-decoration:none;color:inherit" href="#169">169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="170"><a style="outline:none;text-decoration:none;color:inherit" href="#170">170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="171"><a style="outline:none;text-decoration:none;color:inherit" href="#171">171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="172"><a style="outline:none;text-decoration:none;color:inherit" href="#172">172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="173"><a style="outline:none;text-decoration:none;color:inherit" href="#173">173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="174"><a style="outline:none;text-decoration:none;color:inherit" href="#174">174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="175"><a style="outline:none;text-decoration:none;color:inherit" href="#175">175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="176"><a style="outline:none;text-decoration:none;color:inherit" href="#176">176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="177"><a style="outline:none;text-decoration:none;color:inherit" href="#177">177</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="178"><a style="outline:none;text-decoration:none;color:inherit" href="#178">178</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="179"><a style="outline:none;text-decoration:none;color:inherit" href="#179">179</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="180"><a style="outline:none;text-decoration:none;color:inherit" href="#180">180</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="181"><a style="outline:none;text-decoration:none;color:inherit" href="#181">181</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="182"><a style="outline:none;text-decoration:none;color:inherit" href="#182">182</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="183"><a style="outline:none;text-decoration:none;color:inherit" href="#183">183</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="184"><a style="outline:none;text-decoration:none;color:inherit" href="#184">184</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="185"><a style="outline:none;text-decoration:none;color:inherit" href="#185">185</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="186"><a style="outline:none;text-decoration:none;color:inherit" href="#186">186</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="187"><a style="outline:none;text-decoration:none;color:inherit" href="#187">187</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="188"><a style="outline:none;text-decoration:none;color:inherit" href="#188">188</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="189"><a style="outline:none;text-decoration:none;color:inherit" href="#189">189</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="190"><a style="outline:none;text-decoration:none;color:inherit" href="#190">190</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="191"><a style="outline:none;text-decoration:none;color:inherit" href="#191">191</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="192"><a style="outline:none;text-decoration:none;color:inherit" href="#192">192</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="193"><a style="outline:none;text-decoration:none;color:inherit" href="#193">193</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="194"><a style="outline:none;text-decoration:none;color:inherit" href="#194">194</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="195"><a style="outline:none;text-decoration:none;color:inherit" href="#195">195</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="196"><a style="outline:none;text-decoration:none;color:inherit" href="#196">196</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="197"><a style="outline:none;text-decoration:none;color:inherit" href="#197">197</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="198"><a style="outline:none;text-decoration:none;color:inherit" href="#198">198</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Comments that you should read, before you kick this off. Yes, seriously. :)</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 1. Setup the folder where to download using the parameters outlined below</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 2. Loop through and get the slides first</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 3. Finally, loop through and get the videos last</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">param</span> (
</span></span><span style="display:flex;"><span>    [<span style="color:#eed49f">string</span>]<span style="color:#f4dbd6">$path</span> = <span style="color:#a6da95">&#34;C:\build-2018\&#34;</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#eed49f">switch</span>]<span style="color:#f4dbd6">$sessionvideo</span> = <span style="color:#f4dbd6">$true</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#eed49f">int</span>][<span style="color:#91d7e3">ValidateRange</span>(<span style="color:#f5a97f">1</span>,<span style="color:#f5a97f">3</span>)]<span style="color:#f4dbd6">$videoquality</span> = <span style="color:#f5a97f">3</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#eed49f">switch</span>]<span style="color:#f4dbd6">$sessiondeck</span> = <span style="color:#f4dbd6">$true</span>
</span></span><span style="display:flex;"><span> )
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>[<span style="color:#eed49f">Environment</span>]::CurrentDirectory=(<span style="color:#91d7e3">Get-Location</span> -PSProvider FileSystem).<span style="color:#f5a97f">ProviderPath</span> 
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$rss</span> = (<span style="color:#91d7e3">new-object</span> net.webclient)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Filenames might get long, so keep this short!</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#$downloadlocation = &#34;D:\build-2018&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$path</span>)) {
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$fpath</span><span style="color:#a6da95"> dosen&#39;t exist. Creating it...&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$path</span> -type directory 
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">set-location</span> <span style="color:#f4dbd6">$path</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span>(<span style="color:#f4dbd6">$sessiondeck</span>) {  
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Grab the Slides RSS feed - Build 2018</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$slides</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2018/rss/slides&#34;</span>)) 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># ********** download the decks **********</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">try</span> { 
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">foreach</span>(<span style="color:#f4dbd6">$item</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$slides</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.item) {  
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$code</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">split</span>(<span style="color:#a6da95">&#34;/&#34;</span>) | <span style="color:#91d7e3">select </span>-last <span style="color:#f5a97f">1</span>     
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Get the url for the pptx file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$urlpptx</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">enclosure</span>.url)
</span></span><span style="display:flex;"><span>         
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># make the filename readable</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&amp;lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&#39;NEW SESSION&#39;&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;™&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;œ&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$filepptx</span>.Length))
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span> + <span style="color:#a6da95">&#34;.pptx&#34;</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filejpg</span> + <span style="color:#a6da95">&#34;_960.png&#34;</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&amp;lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&#39;NEW SESSION&#39;&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;™&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;œ&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>              
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95"> doesnt exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Make sure the PowerPoint file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$path</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95">&#34;</span>)) {   
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Echo out the file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$wc</span> = (<span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Net</span>.WebClient)  
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Download the pptx file</span>
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Invoke-WebRequest</span> <span style="color:#f4dbd6">$urlpptx</span> -OutFile <span style="color:#f4dbd6">$path</span>\<span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># download the jpg but don&#39;t want to break if this doesn&#39;t exist; hence the nested try blocks</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">if</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">thumbnail</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#f4dbd6">$null</span>) {
</span></span><span style="display:flex;"><span>                        <span style="color:#f4dbd6">$urljpg</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">thumbnail</span>.url)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                        <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$path</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95">&#34;</span>)) {    
</span></span><span style="display:flex;"><span>                            <span style="color:#f4dbd6">$wc</span>.<span style="color:#f5a97f">DownloadFile</span>(<span style="color:#f4dbd6">$urljpg</span>, <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$path</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Image (jpeg) </span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95"> doesn&#39;t exist ... eating the exception and moving on ...&#34;</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>          
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$filepptx</span> <span style="color:#f4dbd6">$folder</span> 
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">#mv $filejpg $folder </span>
</span></span><span style="display:flex;"><span>            } <span style="color:#6e738d;font-style:italic">#endif</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;PPTX: </span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95"> exist; skipping download.&#34;</span>  
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">#try and get the sessions details</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$descriptionFileName</span> = <span style="color:#a6da95">&#34;</span>$(<span style="color:#f4dbd6">$path</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$folder</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$Code</span>.<span style="color:#f5a97f">trim</span>())<span style="color:#a6da95">.txt&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$descriptionFileName</span><span style="color:#a6da95">&#34;</span>)) {
</span></span><span style="display:flex;"><span>                    <span style="color:#f4dbd6">$OutFile</span> = <span style="color:#91d7e3">New-Item</span> -type file <span style="color:#f4dbd6">$descriptionFileName</span> -Force  
</span></span><span style="display:flex;"><span>                    <span style="color:#f4dbd6">$Content</span> = <span style="color:#a6da95">&#34;Title: &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;Presenter: &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;Summary: &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">summary</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;Link: &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                    <span style="color:#6e738d;font-style:italic">#some categories are missing; so need to eat the exception</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#6e738d;font-style:italic">#this is a hack and not very elegant</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>                        <span style="color:#c6a0f6">if</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">category</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#f4dbd6">$null</span>) {
</span></span><span style="display:flex;"><span>                            <span style="color:#f4dbd6">$Content</span> = <span style="color:#f4dbd6">$Content</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;Category: &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">category</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>().<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;+&#34;</span>,<span style="color:#a6da95">&#34; &#34;</span>)
</span></span><span style="display:flex;"><span>                        }
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                        <span style="color:#6e738d;font-style:italic">#do nothing; eat the exception</span>
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">add-content</span> <span style="color:#f4dbd6">$OutFile</span> <span style="color:#f4dbd6">$Content</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        } <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** Downloading all the decks complete ***************&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">catch</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;Oops, could not find any slides.&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>} <span style="color:#6e738d;font-style:italic">#download session-deck </span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># ********** download the videos **********</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># if you don&#39;t want the video but only the slides just comment all the code below in the foreach loop</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span> { 
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># check for video download</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span>(<span style="color:#f4dbd6">$sessionvideo</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">switch</span> (<span style="color:#f4dbd6">$videoquality</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#f5a97f">1</span> {<span style="color:#f4dbd6">$video</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2018/rss/mp3&#34;</span>)); <span style="color:#c6a0f6">break</span>}
</span></span><span style="display:flex;"><span>            <span style="color:#f5a97f">2</span> {<span style="color:#f4dbd6">$video</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2018/rss/mp4&#34;</span>)); <span style="color:#c6a0f6">break</span>}
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">default</span> {<span style="color:#f4dbd6">$video</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2018/rss/mp4high&#34;</span>)); <span style="color:#c6a0f6">break</span>}
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">foreach</span>(<span style="color:#f4dbd6">$item</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$video</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.item) {    
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Grab the URL for the MP4 file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$url</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">enclosure</span>.url)  
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Create the local file name for the video download</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&amp;lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&#39;NEW SESSION&#39;&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;™&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;œ&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$file</span>.Length))
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span> + <span style="color:#a6da95">&#34;.mp4&#34;</span>  
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&amp;lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&#39;NEW SESSION&#39;&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;™&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;œ&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95"> doesn&#39;t exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          
</span></span><span style="display:flex;"><span>      
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Make sure the video file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>))     
</span></span><span style="display:flex;"><span>            {   
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Echo out the  file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$file</span>
</span></span><span style="display:flex;"><span>              
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic"># Download the video file</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">try</span>{
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$path</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>))
</span></span><span style="display:flex;"><span>                    {
</span></span><span style="display:flex;"><span>                        <span style="color:#91d7e3">Invoke-WebRequest</span> <span style="color:#f4dbd6">$url</span> -OutFile <span style="color:#f4dbd6">$path</span>\<span style="color:#f4dbd6">$file</span>
</span></span><span style="display:flex;"><span>              
</span></span><span style="display:flex;"><span>                        <span style="color:#6e738d;font-style:italic">#move it from the current working folder to the target</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$file</span> <span style="color:#f4dbd6">$folder</span>
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                    <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>                        <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Video: </span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95"> - anoter process possibly working on this; skipping download.&#34;</span>
</span></span><span style="display:flex;"><span>                    }
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Video: </span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95"> exist; skipping download.&#34;</span>  
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>          
</span></span><span style="display:flex;"><span>        } <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span>    } <span style="color:#6e738d;font-style:italic">#end - video check</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">catch</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;Oops, could not find any videos or some other error happened.&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>} <span style="color:#6e738d;font-style:italic"># ********** End - download the videos section **********</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** All Done! Woot! ***************&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Download Build (2017) decks and video</title>
      <link>/post/2017/05/download-build-2017-decks-and-video/</link>
      <pubDate>Wed, 24 May 2017 00:00:00 +0000</pubDate>
      <guid>/post/2017/05/download-build-2017-decks-and-video/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Modified the script to handle multiple instances but pay heed to the &lt;a
	
		href = &#34;https://twitter.com/bahree/status/870489834174857216&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		warning here
	&lt;/span&gt;
&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Similar to &lt;a
	
		href = &#34;http://desigeek.com/blog/amit/2016/04/12/download-build-2016-decks-and-videos/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		last year
	&lt;/span&gt;
&lt;/a&gt;, I have a PowerShell script that will allow you to download the various PowerPoint decks and videos to watch locally rather than stream. This makes some improvements from the earlier scripts (e.g. if a file is already downloaded it will skip downloading it again) and does the following:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>Update:</strong> Modified the script to handle multiple instances but pay heed to the <a
	
		href = "https://twitter.com/bahree/status/870489834174857216"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		warning here
	</span>
</a>.</p>
<p>Similar to <a
	
		href = "http://desigeek.com/blog/amit/2016/04/12/download-build-2016-decks-and-videos/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		last year
	</span>
</a>, I have a PowerShell script that will allow you to download the various PowerPoint decks and videos to watch locally rather than stream. This makes some improvements from the earlier scripts (e.g. if a file is already downloaded it will skip downloading it again) and does the following:</p>
<ul>
<li>Creates the relevant folder which includes the Session details (including the Title, and the Presenters)</li>
<li>For each session, saves the description in a text file in the created folder.</li>
<li>Downloads the relevant presentation (if any)</li>
<li>Downloads a jpg which shows the image session – sometimes it is easier just to see the title slide. I thought better to have it and not use it, than the other way.</li>
<li>And finally downloads the high-quality video of that session.</li>
</ul>
<p>In the script, you can change the following (and if you understand Build then this should be easy):</p>
<ul>
<li>Change the path where to download this to (default is d:\build)</li>
<li>Choose a lower quality video if you prefer (which of course takes less space and might not be bad depending on which device you are seeing). Of course this also uses less bandwidth.</li>
<li>Of course. And if you want only the decks, then you can comment out parts of the script where it doesn’t download the video.</li>
</ul>
<p>The script will spit out some basic errors and will &rsquo;eat&rsquo; some of the exceptions that are expected (e.g. every session doesn&rsquo;t have a pptx or a video). That won&rsquo;t break the script, it will just move to the next session.</p>
<p>And finally here is the script:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="137"><a style="outline:none;text-decoration:none;color:inherit" href="#137">137</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="138"><a style="outline:none;text-decoration:none;color:inherit" href="#138">138</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="139"><a style="outline:none;text-decoration:none;color:inherit" href="#139">139</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="140"><a style="outline:none;text-decoration:none;color:inherit" href="#140">140</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="141"><a style="outline:none;text-decoration:none;color:inherit" href="#141">141</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="142"><a style="outline:none;text-decoration:none;color:inherit" href="#142">142</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="143"><a style="outline:none;text-decoration:none;color:inherit" href="#143">143</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="144"><a style="outline:none;text-decoration:none;color:inherit" href="#144">144</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="145"><a style="outline:none;text-decoration:none;color:inherit" href="#145">145</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="146"><a style="outline:none;text-decoration:none;color:inherit" href="#146">146</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="147"><a style="outline:none;text-decoration:none;color:inherit" href="#147">147</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="148"><a style="outline:none;text-decoration:none;color:inherit" href="#148">148</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="149"><a style="outline:none;text-decoration:none;color:inherit" href="#149">149</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="150"><a style="outline:none;text-decoration:none;color:inherit" href="#150">150</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="151"><a style="outline:none;text-decoration:none;color:inherit" href="#151">151</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="152"><a style="outline:none;text-decoration:none;color:inherit" href="#152">152</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="153"><a style="outline:none;text-decoration:none;color:inherit" href="#153">153</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="154"><a style="outline:none;text-decoration:none;color:inherit" href="#154">154</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="155"><a style="outline:none;text-decoration:none;color:inherit" href="#155">155</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="156"><a style="outline:none;text-decoration:none;color:inherit" href="#156">156</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="157"><a style="outline:none;text-decoration:none;color:inherit" href="#157">157</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="158"><a style="outline:none;text-decoration:none;color:inherit" href="#158">158</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="159"><a style="outline:none;text-decoration:none;color:inherit" href="#159">159</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="160"><a style="outline:none;text-decoration:none;color:inherit" href="#160">160</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="161"><a style="outline:none;text-decoration:none;color:inherit" href="#161">161</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="162"><a style="outline:none;text-decoration:none;color:inherit" href="#162">162</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="163"><a style="outline:none;text-decoration:none;color:inherit" href="#163">163</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="164"><a style="outline:none;text-decoration:none;color:inherit" href="#164">164</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="165"><a style="outline:none;text-decoration:none;color:inherit" href="#165">165</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="166"><a style="outline:none;text-decoration:none;color:inherit" href="#166">166</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="167"><a style="outline:none;text-decoration:none;color:inherit" href="#167">167</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="168"><a style="outline:none;text-decoration:none;color:inherit" href="#168">168</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="169"><a style="outline:none;text-decoration:none;color:inherit" href="#169">169</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="170"><a style="outline:none;text-decoration:none;color:inherit" href="#170">170</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="171"><a style="outline:none;text-decoration:none;color:inherit" href="#171">171</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="172"><a style="outline:none;text-decoration:none;color:inherit" href="#172">172</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="173"><a style="outline:none;text-decoration:none;color:inherit" href="#173">173</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="174"><a style="outline:none;text-decoration:none;color:inherit" href="#174">174</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="175"><a style="outline:none;text-decoration:none;color:inherit" href="#175">175</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="176"><a style="outline:none;text-decoration:none;color:inherit" href="#176">176</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="177"><a style="outline:none;text-decoration:none;color:inherit" href="#177">177</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="178"><a style="outline:none;text-decoration:none;color:inherit" href="#178">178</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="179"><a style="outline:none;text-decoration:none;color:inherit" href="#179">179</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="180"><a style="outline:none;text-decoration:none;color:inherit" href="#180">180</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="181"><a style="outline:none;text-decoration:none;color:inherit" href="#181">181</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="182"><a style="outline:none;text-decoration:none;color:inherit" href="#182">182</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="183"><a style="outline:none;text-decoration:none;color:inherit" href="#183">183</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># First setup the folder where to download using the parameters outlined below.</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Second, loop through and get the decks first</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Third. loop through and get the videos last</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Note: IF you don&#39;t want to download the videos, and want only the pptx then comment the section later in the script</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># parameters</span>
</span></span><span style="display:flex;"><span>[<span style="color:#eed49f">Environment</span>]::CurrentDirectory=(<span style="color:#91d7e3">Get-Location</span> -PSProvider FileSystem).<span style="color:#f5a97f">ProviderPath</span> 
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$rss</span> = (<span style="color:#91d7e3">new-object</span> net.webclient)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Filenames might get long, so keep this short!</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$downloadlocation</span> = <span style="color:#a6da95">&#34;D:\build&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$downloadlocation</span>)) { 
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$fpath</span><span style="color:#a6da95"> dosen&#39;t exist. Creating it...&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$downloadlocation</span> -type directory 
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">set-location</span> <span style="color:#f4dbd6">$downloadlocation</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Grab the RSS feed - Build 2017</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$a</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2017/rss/mp4high&#34;</span>)) 
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$b</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2017/rss/slides&#34;</span>)) 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Video quality default is high; you can select regular (mp4) or lower quality (mp3)</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#$a = ($rss.downloadstring(&#34;http://s.ch9.ms/events/build/2017/rss/mp4&#34;)) </span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#$a = ($rss.downloadstring(&#34;http://s.ch9.ms/events/build/2017/rss/mp3&#34;)) </span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># ********** download the decks **********</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span> { 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">foreach</span>(<span style="color:#f4dbd6">$item</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$b</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.item) {   
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$code</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">split</span>(<span style="color:#a6da95">&#34;/&#34;</span>) | <span style="color:#91d7e3">select </span>-last <span style="color:#f5a97f">1</span>     
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Get the url for the pptx file</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$urlpptx</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">enclosure</span>.url)  
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$urljpg</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">thumbnail</span>.url)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># make the filename readable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$filepptx</span>.Length))
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span> + <span style="color:#a6da95">&#34;.pptx&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filejpg</span> + <span style="color:#a6da95">&#34;_960.jpg&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#f4dbd6">$code</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#a6da95">&#34;&#34;</span>) {
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#a6da95">&#34;NoCodeSessions&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95"> dosen&#39;t exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Make sure the PowerPoint file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95">&#34;</span>)) {   
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Echo out the file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$wc</span> = (<span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Net</span>.WebClient)  
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Download the pptx file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Invoke-WebRequest</span> <span style="color:#f4dbd6">$urlpptx</span> -OutFile <span style="color:#f4dbd6">$downloadlocation</span>\<span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># download the jpg but don&#39;t want to break if this doesn&#39;t exist; hence the nested try blocks</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95">&#34;</span>)) {    
</span></span><span style="display:flex;"><span>                    <span style="color:#f4dbd6">$wc</span>.<span style="color:#f5a97f">DownloadFile</span>(<span style="color:#f4dbd6">$urljpg</span>, <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Jpeg </span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95"> doesn&#39;t exist ... eating the exception and moving on ...&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>         
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$filepptx</span> <span style="color:#f4dbd6">$folder</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$filejpg</span> <span style="color:#f4dbd6">$folder</span> 
</span></span><span style="display:flex;"><span>        } <span style="color:#6e738d;font-style:italic">#endif</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;PPTX: </span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95"> exist; skipping download.&#34;</span>  
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    } <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** Downloading all the decks complete ***************&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">catch</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;Oops, could not find any slides.&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># ********** download the videos **********</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># if you don&#39;t want the video but only the slides just comment all the code below in the foreach loop</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span> { 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">foreach</span>(<span style="color:#f4dbd6">$item</span> <span style="color:#c6a0f6">in</span> <span style="color:#f4dbd6">$a</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.item) {
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$code</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">split</span>(<span style="color:#a6da95">&#34;/&#34;</span>) | <span style="color:#91d7e3">select </span>-last <span style="color:#f5a97f">1</span>     
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Grab the URL for the MP4 file</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$url</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">enclosure</span>.url)  
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Create the local file name for the MP4 download</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$file</span>.Length))
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span> + <span style="color:#a6da95">&#34;.mp4&#34;</span>  
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#f4dbd6">$code</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;’&#34;</span>,<span style="color:#a6da95">&#34;&#39;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;â€&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#a6da95">&#34;NoCodeSessions&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95"> doesn&#39;t exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>         
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Make sure the MP4 file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>))     
</span></span><span style="display:flex;"><span>        {   
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Echo out the  file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$file</span>
</span></span><span style="display:flex;"><span>             
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Download the MP4 file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>))
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">Invoke-WebRequest</span> <span style="color:#f4dbd6">$url</span> -OutFile <span style="color:#f4dbd6">$downloadlocation</span>\<span style="color:#f4dbd6">$file</span>
</span></span><span style="display:flex;"><span>             
</span></span><span style="display:flex;"><span>                    <span style="color:#6e738d;font-style:italic">#move it from the current working folder to the target</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$file</span> <span style="color:#f4dbd6">$folder</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Video: </span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95"> - anoter process possibly working on this; skipping download.&#34;</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Video: </span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95"> exist; skipping download.&#34;</span>  
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">#Try and get the Sessions text description</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$OutFile</span> = <span style="color:#91d7e3">New-Item</span> -type file <span style="color:#a6da95">&#34;</span>$(<span style="color:#f4dbd6">$downloadlocation</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$Folder</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$Code</span>.<span style="color:#f5a97f">trim</span>())<span style="color:#a6da95">.txt&#34;</span> -Force  
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$Content</span> = <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$Content</span> = <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">summary</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">description</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$item</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">ToString</span>().<span style="color:#f5a97f">trim</span>() 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">add-content</span> <span style="color:#f4dbd6">$OutFile</span> <span style="color:#f4dbd6">$Content</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>         
</span></span><span style="display:flex;"><span>    } <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">catch</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;Oops, could not find any videos or some other error happened.&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$ErrorMessage</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">Message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$FailedItem</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">Exception</span>.<span style="color:#f5a97f">ItemName</span>
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;\t&#34;</span> <span style="color:#f4dbd6">$ErrorMessage</span> + <span style="color:#a6da95">&#34;\n&#34;</span> + <span style="color:#f4dbd6">$FailedItem</span>
</span></span><span style="display:flex;"><span>} <span style="color:#6e738d;font-style:italic"># ********** End - download the videos section **********</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** All Done! ***************&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>If you read through the script it is pretty self explanatory.</p>
]]></content:encoded>
    </item>
    <item>
      <title>bash on Windows is real &amp; not a VM</title>
      <link>/post/2016/04/bash-on-windows-is-real-not-a-vm/</link>
      <pubDate>Wed, 20 Apr 2016 00:00:00 +0000</pubDate>
      <guid>/post/2016/04/bash-on-windows-is-real-not-a-vm/</guid>
      <description>&lt;p&gt;I have talked to a few folks recently, and they still don’t believe bash on Windows (RS1) is ‘real’ and think it some kind of a VM. No it is not. It is the ‘real’ user mode running on Windows. It is not Cygwin, and it is not a VM. It is essentially all of the user mode (I.e. Linux without the kernel).&lt;/p&gt;
&lt;p&gt;The kernel in this case is a wrapper around the NT kernel that translates the Linux commands to Windows and then things run. As far as Linux is concerned, its the same code and doesn’t have any changes). Technically this is called Windows Subsystem for Linux (WSL).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have talked to a few folks recently, and they still don’t believe bash on Windows (RS1) is ‘real’ and think it some kind of a VM. No it is not. It is the ‘real’ user mode running on Windows. It is not Cygwin, and it is not a VM. It is essentially all of the user mode (I.e. Linux without the kernel).</p>
<p>The kernel in this case is a wrapper around the NT kernel that translates the Linux commands to Windows and then things run. As far as Linux is concerned, its the same code and doesn’t have any changes). Technically this is called Windows Subsystem for Linux (WSL).</p>
<p>On windows, this is installed in the user space; so each user get their own instance effectively which is isolated from the other users. Once you install it (and if you are still reading this, then you probably know how to install it), then this shows up under <code>C:\Users\your-user-ID\AppData\Local\lxss</code>. If you can’t find that folder, you can still type it and navigate to it. Below is  a screen shot on what this looks like:</p>
<p><p>

    <img src="images/image_thumb-3.png" alt="image"/>

</p></p>
<p>It is a little interesting and been mucking around this. Here is you can see the installation of gcc:
<p>

    <img src="images/image_thumb-4.png" alt="image"/>

</p></p>
<p>And here is the output of the CPU details:
<p>

    <img src="images/image_thumb-5.png" alt="image"/>

</p></p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>root@localhost:/proc# cat cpuinfo
</span></span><span style="display:flex;"><span>processor       : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>vendor_id       : GenuineIntel
</span></span><span style="display:flex;"><span>cpu family      : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>model           : <span style="color:#f5a97f">78</span>
</span></span><span style="display:flex;"><span>model name      : Intel<span style="color:#91d7e3;font-weight:bold">(</span>R<span style="color:#91d7e3;font-weight:bold">)</span> Core<span style="color:#91d7e3;font-weight:bold">(</span>TM<span style="color:#91d7e3;font-weight:bold">)</span> i7-6600U CPU @ 2.60GHz
</span></span><span style="display:flex;"><span>stepping        : <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>microcode       : 0xffffffff
</span></span><span style="display:flex;"><span>cpu MHz         : 2808.000
</span></span><span style="display:flex;"><span>cache size      : <span style="color:#f5a97f">256</span> KB
</span></span><span style="display:flex;"><span>physical id     : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>siblings        : <span style="color:#f5a97f">4</span>
</span></span><span style="display:flex;"><span>core id         : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>cpu cores       : <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>apicid          : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>initial apicid  : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>fpu             : yes
</span></span><span style="display:flex;"><span>fpu_exception   : yes
</span></span><span style="display:flex;"><span>cpuid level     : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>wp              : yes
</span></span><span style="display:flex;"><span>flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm pni pclmulqdq est tm2 ssse3 fma cx16 xtpr sse4_1 sse4_2 movbe popcnt aes xsave osxsave avx f16c rdrand hypervisor
</span></span><span style="display:flex;"><span>bogomips        : 5616.00
</span></span><span style="display:flex;"><span>clflush size    : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>cache_alignment : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>address sizes   : <span style="color:#f5a97f">36</span> bits physical, <span style="color:#f5a97f">48</span> bits virtual
</span></span><span style="display:flex;"><span>power management:
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>processor       : <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>vendor_id       : GenuineIntel
</span></span><span style="display:flex;"><span>cpu family      : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>model           : <span style="color:#f5a97f">78</span>
</span></span><span style="display:flex;"><span>model name      : Intel<span style="color:#91d7e3;font-weight:bold">(</span>R<span style="color:#91d7e3;font-weight:bold">)</span> Core<span style="color:#91d7e3;font-weight:bold">(</span>TM<span style="color:#91d7e3;font-weight:bold">)</span> i7-6600U CPU @ 2.60GHz
</span></span><span style="display:flex;"><span>stepping        : <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>microcode       : 0xffffffff
</span></span><span style="display:flex;"><span>cpu MHz         : 2808.000
</span></span><span style="display:flex;"><span>cache size      : <span style="color:#f5a97f">256</span> KB
</span></span><span style="display:flex;"><span>physical id     : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>siblings        : <span style="color:#f5a97f">4</span>
</span></span><span style="display:flex;"><span>core id         : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>cpu cores       : <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>apicid          : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>initial apicid  : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>fpu             : yes
</span></span><span style="display:flex;"><span>fpu_exception   : yes
</span></span><span style="display:flex;"><span>cpuid level     : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>wp              : yes
</span></span><span style="display:flex;"><span>flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm pni pclmulqdq est tm2 ssse3 fma cx16 xtpr sse4_1 sse4_2 movbe popcnt aes xsave osxsave avx f16c rdrand hypervisor
</span></span><span style="display:flex;"><span>bogomips        : 5616.00
</span></span><span style="display:flex;"><span>clflush size    : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>cache_alignment : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>address sizes   : <span style="color:#f5a97f">36</span> bits physical, <span style="color:#f5a97f">48</span> bits virtual
</span></span><span style="display:flex;"><span>power management:
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>processor       : <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>vendor_id       : GenuineIntel
</span></span><span style="display:flex;"><span>cpu family      : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>model           : <span style="color:#f5a97f">78</span>
</span></span><span style="display:flex;"><span>model name      : Intel<span style="color:#91d7e3;font-weight:bold">(</span>R<span style="color:#91d7e3;font-weight:bold">)</span> Core<span style="color:#91d7e3;font-weight:bold">(</span>TM<span style="color:#91d7e3;font-weight:bold">)</span> i7-6600U CPU @ 2.60GHz
</span></span><span style="display:flex;"><span>stepping        : <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>microcode       : 0xffffffff
</span></span><span style="display:flex;"><span>cpu MHz         : 2808.000
</span></span><span style="display:flex;"><span>cache size      : <span style="color:#f5a97f">256</span> KB
</span></span><span style="display:flex;"><span>physical id     : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>siblings        : <span style="color:#f5a97f">4</span>
</span></span><span style="display:flex;"><span>core id         : <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>cpu cores       : <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>apicid          : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>initial apicid  : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>fpu             : yes
</span></span><span style="display:flex;"><span>fpu_exception   : yes
</span></span><span style="display:flex;"><span>cpuid level     : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>wp              : yes
</span></span><span style="display:flex;"><span>flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm pni pclmulqdq est tm2 ssse3 fma cx16 xtpr sse4_1 sse4_2 movbe popcnt aes xsave osxsave avx f16c rdrand hypervisor
</span></span><span style="display:flex;"><span>bogomips        : 5616.00
</span></span><span style="display:flex;"><span>clflush size    : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>cache_alignment : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>address sizes   : <span style="color:#f5a97f">36</span> bits physical, <span style="color:#f5a97f">48</span> bits virtual
</span></span><span style="display:flex;"><span>power management:
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>processor       : <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>vendor_id       : GenuineIntel
</span></span><span style="display:flex;"><span>cpu family      : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>model           : <span style="color:#f5a97f">78</span>
</span></span><span style="display:flex;"><span>model name      : Intel<span style="color:#91d7e3;font-weight:bold">(</span>R<span style="color:#91d7e3;font-weight:bold">)</span> Core<span style="color:#91d7e3;font-weight:bold">(</span>TM<span style="color:#91d7e3;font-weight:bold">)</span> i7-6600U CPU @ 2.60GHz
</span></span><span style="display:flex;"><span>stepping        : <span style="color:#f5a97f">3</span>
</span></span><span style="display:flex;"><span>microcode       : 0xffffffff
</span></span><span style="display:flex;"><span>cpu MHz         : 2808.000
</span></span><span style="display:flex;"><span>cache size      : <span style="color:#f5a97f">256</span> KB
</span></span><span style="display:flex;"><span>physical id     : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>siblings        : <span style="color:#f5a97f">4</span>
</span></span><span style="display:flex;"><span>core id         : <span style="color:#f5a97f">1</span>
</span></span><span style="display:flex;"><span>cpu cores       : <span style="color:#f5a97f">2</span>
</span></span><span style="display:flex;"><span>apicid          : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>initial apicid  : <span style="color:#f5a97f">0</span>
</span></span><span style="display:flex;"><span>fpu             : yes
</span></span><span style="display:flex;"><span>fpu_exception   : yes
</span></span><span style="display:flex;"><span>cpuid level     : <span style="color:#f5a97f">6</span>
</span></span><span style="display:flex;"><span>wp              : yes
</span></span><span style="display:flex;"><span>flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm pni pclmulqdq est tm2 ssse3 fma cx16 xtpr sse4_1 sse4_2 movbe popcnt aes xsave osxsave avx f16c rdrand hypervisor
</span></span><span style="display:flex;"><span>bogomips        : 5616.00
</span></span><span style="display:flex;"><span>clflush size    : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>cache_alignment : <span style="color:#f5a97f">64</span>
</span></span><span style="display:flex;"><span>address sizes   : <span style="color:#f5a97f">36</span> bits physical, <span style="color:#f5a97f">48</span> bits virtual
</span></span><span style="display:flex;"><span>power management:
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>root@localhost:/proc#</span></span></code></pre></td></tr></table>
</div>
</div>
<p>All, in all a very interesting world. A few things to note:</p>
<ul>
<li>This is still in beta, so there will be issues.</li>
<li>It is user mode and not server mode. Live with it.</li>
<li>There would be path issues if you stray into the 256 character limit of Windows and then try and manipulate it in bash.</li>
</ul>
<p>Happy hacking!</p>
]]></content:encoded>
    </item>
    <item>
      <title>Download Build (2016) decks and videos</title>
      <link>/post/2016/04/download-build-2016-decks-and-videos/</link>
      <pubDate>Wed, 13 Apr 2016 00:00:00 +0000</pubDate>
      <guid>/post/2016/04/download-build-2016-decks-and-videos/</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; - Was a typo in the script, which I fixed. If someone had an issue, copy it again and give it a go.&lt;/p&gt;
&lt;p&gt;I prefer to download and see the decks and videos &amp;lsquo;offline&amp;rsquo; instead of streaming, as I can easily pause them and then pick off where I left - especially handy when I need to go to a meeting, or take care of some work, or just on a crappy network.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><strong>Update</strong> - Was a typo in the script, which I fixed. If someone had an issue, copy it again and give it a go.</p>
<p>I prefer to download and see the decks and videos &lsquo;offline&rsquo; instead of streaming, as I can easily pause them and then pick off where I left - especially handy when I need to go to a meeting, or take care of some work, or just on a crappy network.</p>
<p>To that end, I downloaded all the sessions and the videos from Build (2016); I think one of the sessions might have gotten corrupted, but ignoring that I have about 140GB of data downloaded and 219 Sessions.</p>
<p>To do this, I used a Power Shell script that helped me download. The script (below) is simple enough - it loops through and for each session does the following:</p>
<ul>
<li>Creates the relevant folder - this includes the Session code, Title, and the Presenters.</li>
<li>For each session, extracts the abstract / description of that session and saves it in a text file in that folder.</li>
<li>Downloads the presentation for that session.</li>
<li>Downloads a jpg which shows the image session - sometimes it is easier just to see the title slide. I thought better to have it and not use it, than the other way.</li>
<li>And finally downloads the high-quality video of that session. In the script, you can choose a lower quality video if you prefer that. And if you want only the decks, then you can comment out parts of the script where it doesn&rsquo;t download the video.</li>
</ul>
<p>Here is an example on what the output for one of the sessions (P517 - Debugging Events in Edge) looks like:</p>
<p><p>

    <img src="images/Build-2016-download-example.jpg" alt="Build-2016-download-example"/>

</p></p>
<p>A few points to note:</p>
<ul>
<li>In the script, the root folder it tries to create and use is <code>&quot;d:\build&quot;</code>; you might want to change this to something else you prefer or what works for you. I would recommend to not making the root too long as you would get into long file name issues.</li>
<li>The script first loops through and downloads everything else except the videos first. And then 2nd time goes and grabs the videos.</li>
<li>If there is an exception with the images, then it just &rsquo;eats&rsquo; it and moves on. It is not a breaking condition to stop the script.</li>
</ul>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">  1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">  2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">  3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">  4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">  5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">  6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">  7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">  8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">  9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10"> 10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11"> 11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12"> 12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13"> 13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14"> 14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15"> 15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16"> 16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17"> 17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18"> 18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19"> 19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20"> 20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21"> 21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22"> 22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23"> 23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24"> 24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25"> 25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26"> 26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27"> 27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28"> 28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29"> 29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30"> 30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31"> 31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32"> 32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33"> 33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34"> 34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35"> 35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36"> 36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37"> 37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38"> 38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39"> 39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40"> 40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41"> 41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42"> 42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43"> 43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44"> 44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45"> 45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46"> 46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47"> 47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48"> 48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49"> 49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50"> 50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51"> 51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52"> 52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53"> 53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54"> 54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55"> 55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56"> 56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57"> 57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58"> 58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59"> 59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60"> 60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61"> 61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62"> 62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63"> 63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64"> 64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65"> 65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66"> 66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67"> 67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68"> 68</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="69"><a style="outline:none;text-decoration:none;color:inherit" href="#69"> 69</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="70"><a style="outline:none;text-decoration:none;color:inherit" href="#70"> 70</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="71"><a style="outline:none;text-decoration:none;color:inherit" href="#71"> 71</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="72"><a style="outline:none;text-decoration:none;color:inherit" href="#72"> 72</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="73"><a style="outline:none;text-decoration:none;color:inherit" href="#73"> 73</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="74"><a style="outline:none;text-decoration:none;color:inherit" href="#74"> 74</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="75"><a style="outline:none;text-decoration:none;color:inherit" href="#75"> 75</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="76"><a style="outline:none;text-decoration:none;color:inherit" href="#76"> 76</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="77"><a style="outline:none;text-decoration:none;color:inherit" href="#77"> 77</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="78"><a style="outline:none;text-decoration:none;color:inherit" href="#78"> 78</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="79"><a style="outline:none;text-decoration:none;color:inherit" href="#79"> 79</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="80"><a style="outline:none;text-decoration:none;color:inherit" href="#80"> 80</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="81"><a style="outline:none;text-decoration:none;color:inherit" href="#81"> 81</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="82"><a style="outline:none;text-decoration:none;color:inherit" href="#82"> 82</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="83"><a style="outline:none;text-decoration:none;color:inherit" href="#83"> 83</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="84"><a style="outline:none;text-decoration:none;color:inherit" href="#84"> 84</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="85"><a style="outline:none;text-decoration:none;color:inherit" href="#85"> 85</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="86"><a style="outline:none;text-decoration:none;color:inherit" href="#86"> 86</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="87"><a style="outline:none;text-decoration:none;color:inherit" href="#87"> 87</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="88"><a style="outline:none;text-decoration:none;color:inherit" href="#88"> 88</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="89"><a style="outline:none;text-decoration:none;color:inherit" href="#89"> 89</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="90"><a style="outline:none;text-decoration:none;color:inherit" href="#90"> 90</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="91"><a style="outline:none;text-decoration:none;color:inherit" href="#91"> 91</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="92"><a style="outline:none;text-decoration:none;color:inherit" href="#92"> 92</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="93"><a style="outline:none;text-decoration:none;color:inherit" href="#93"> 93</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="94"><a style="outline:none;text-decoration:none;color:inherit" href="#94"> 94</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="95"><a style="outline:none;text-decoration:none;color:inherit" href="#95"> 95</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="96"><a style="outline:none;text-decoration:none;color:inherit" href="#96"> 96</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="97"><a style="outline:none;text-decoration:none;color:inherit" href="#97"> 97</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="98"><a style="outline:none;text-decoration:none;color:inherit" href="#98"> 98</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="99"><a style="outline:none;text-decoration:none;color:inherit" href="#99"> 99</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="100"><a style="outline:none;text-decoration:none;color:inherit" href="#100">100</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="101"><a style="outline:none;text-decoration:none;color:inherit" href="#101">101</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="102"><a style="outline:none;text-decoration:none;color:inherit" href="#102">102</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="103"><a style="outline:none;text-decoration:none;color:inherit" href="#103">103</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="104"><a style="outline:none;text-decoration:none;color:inherit" href="#104">104</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="105"><a style="outline:none;text-decoration:none;color:inherit" href="#105">105</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="106"><a style="outline:none;text-decoration:none;color:inherit" href="#106">106</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="107"><a style="outline:none;text-decoration:none;color:inherit" href="#107">107</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="108"><a style="outline:none;text-decoration:none;color:inherit" href="#108">108</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="109"><a style="outline:none;text-decoration:none;color:inherit" href="#109">109</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="110"><a style="outline:none;text-decoration:none;color:inherit" href="#110">110</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="111"><a style="outline:none;text-decoration:none;color:inherit" href="#111">111</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="112"><a style="outline:none;text-decoration:none;color:inherit" href="#112">112</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="113"><a style="outline:none;text-decoration:none;color:inherit" href="#113">113</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="114"><a style="outline:none;text-decoration:none;color:inherit" href="#114">114</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="115"><a style="outline:none;text-decoration:none;color:inherit" href="#115">115</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="116"><a style="outline:none;text-decoration:none;color:inherit" href="#116">116</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="117"><a style="outline:none;text-decoration:none;color:inherit" href="#117">117</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="118"><a style="outline:none;text-decoration:none;color:inherit" href="#118">118</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="119"><a style="outline:none;text-decoration:none;color:inherit" href="#119">119</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="120"><a style="outline:none;text-decoration:none;color:inherit" href="#120">120</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="121"><a style="outline:none;text-decoration:none;color:inherit" href="#121">121</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="122"><a style="outline:none;text-decoration:none;color:inherit" href="#122">122</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="123"><a style="outline:none;text-decoration:none;color:inherit" href="#123">123</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="124"><a style="outline:none;text-decoration:none;color:inherit" href="#124">124</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="125"><a style="outline:none;text-decoration:none;color:inherit" href="#125">125</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="126"><a style="outline:none;text-decoration:none;color:inherit" href="#126">126</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="127"><a style="outline:none;text-decoration:none;color:inherit" href="#127">127</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="128"><a style="outline:none;text-decoration:none;color:inherit" href="#128">128</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="129"><a style="outline:none;text-decoration:none;color:inherit" href="#129">129</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="130"><a style="outline:none;text-decoration:none;color:inherit" href="#130">130</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="131"><a style="outline:none;text-decoration:none;color:inherit" href="#131">131</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="132"><a style="outline:none;text-decoration:none;color:inherit" href="#132">132</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="133"><a style="outline:none;text-decoration:none;color:inherit" href="#133">133</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="134"><a style="outline:none;text-decoration:none;color:inherit" href="#134">134</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="135"><a style="outline:none;text-decoration:none;color:inherit" href="#135">135</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="136"><a style="outline:none;text-decoration:none;color:inherit" href="#136">136</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># First setup the folder where to download using the parameters outlined below.</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Second, loop through and get the decks first</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Third. loop through and get the videos last</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Note: IF you don&#39;t want to download the videos, and want only the pptx then comment the section later in the script</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># parameters</span>
</span></span><span style="display:flex;"><span>[<span style="color:#eed49f">Environment</span>]::CurrentDirectory=(<span style="color:#91d7e3">Get-Location</span> -PSProvider FileSystem).<span style="color:#f5a97f">ProviderPath</span> 
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$rss</span> = (<span style="color:#91d7e3">new-object</span> net.webclient)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Filenames might get long, so keep this short!</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$downloadlocation</span> = <span style="color:#a6da95">&#34;D:\build&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$downloadlocation</span>)) { 
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$fpath</span><span style="color:#a6da95"> dosen&#39;t exist. Creating it...&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$downloadlocation</span> -type directory 
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">set-location</span> <span style="color:#f4dbd6">$downloadlocation</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Grab the RSS feed - Build 2016</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$a</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2016/rss/mp4high&#34;</span>)) 
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$b</span> = (<span style="color:#f4dbd6">$rss</span>.<span style="color:#f5a97f">downloadstring</span>(<span style="color:#a6da95">&#34;http://s.ch9.ms/events/build/2016/rss/slides&#34;</span>)) 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Video quality default is high; you can select regular (mp4) or lower quality (mp3)</span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#$a = ($rss.downloadstring(&#34;http://s.ch9.ms/events/build/2015/rss/mp4&#34;)) </span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#$a = ($rss.downloadstring(&#34;http://s.ch9.ms/events/build/2015/rss/mp3&#34;)) </span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># ********** download the decks **********</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">try</span> { 
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$b</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.<span style="color:#f5a97f">item</span> | <span style="color:#c6a0f6">foreach</span> {   
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$code</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">split</span>(<span style="color:#a6da95">&#34;/&#34;</span>) | <span style="color:#91d7e3">select </span>-last <span style="color:#f5a97f">1</span>    
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Get the url for the pptx file</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$urlpptx</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">enclosure</span>.url)  
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$urljpg</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">thumbnail</span>.url)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># make the filename readable</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$filepptx</span>.Length))
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filepptx</span> = <span style="color:#f4dbd6">$filepptx</span> + <span style="color:#a6da95">&#34;.pptx&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$filejpg</span> = <span style="color:#f4dbd6">$filejpg</span> + <span style="color:#a6da95">&#34;_960.jpg&#34;</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#f4dbd6">$code</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#a6da95">&#34;&#34;</span>) {
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>             <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$folder</span> = <span style="color:#a6da95">&#34;NoCodeSessions&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95"> dosen&#39;t exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Make sure the PowerPoint file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95">&#34;</span>)) {   
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Echo out the file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$filepptx</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$wc</span> = (<span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Net</span>.WebClient)  
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># Download the pptx file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f4dbd6">$wc</span>.<span style="color:#f5a97f">DownloadFile</span>(<span style="color:#f4dbd6">$urlpptx</span>, <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filepptx</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic"># download the jpg but don&#39;t want to break if this doesn&#39;t exist; hence the nested try blocks</span>
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#f4dbd6">$wc</span>.<span style="color:#f5a97f">DownloadFile</span>(<span style="color:#f4dbd6">$urljpg</span>, <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">catch</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Jpeg </span><span style="color:#f4dbd6">$filejpg</span><span style="color:#a6da95"> doesn&#39;t exist ... eating the exception and moving on ...&#34;</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>         
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$filepptx</span> <span style="color:#f4dbd6">$folder</span> 
</span></span><span style="display:flex;"><span>            <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$filejpg</span> <span style="color:#f4dbd6">$folder</span> 
</span></span><span style="display:flex;"><span>        } <span style="color:#6e738d;font-style:italic">#endif</span>
</span></span><span style="display:flex;"><span>    } <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** Downloading all the decks complete ***************&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">catch</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;Oops, could not find any slides.&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># ********** download the videos **********</span>
</span></span><span style="display:flex;"><span><span style="color:#f4dbd6">$a</span>.<span style="color:#f5a97f">rss</span>.<span style="color:#f5a97f">channel</span>.<span style="color:#f5a97f">item</span> | <span style="color:#c6a0f6">foreach</span> {   
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$code</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">comments</span>.<span style="color:#f5a97f">split</span>(<span style="color:#a6da95">&#34;/&#34;</span>) | <span style="color:#91d7e3">select </span>-last <span style="color:#f5a97f">1</span>    
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Grab the URL for the MP4 file</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$url</span> = <span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Uri</span>(<span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">enclosure</span>.url)  
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Create the local file name for the MP4 download</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34;-&#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">120</span>, <span style="color:#f4dbd6">$file</span>.Length))
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$file</span> = <span style="color:#f4dbd6">$file</span> + <span style="color:#a6da95">&#34;.mp4&#34;</span>  
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (<span style="color:#f4dbd6">$code</span> <span style="color:#91d7e3;font-weight:bold">-ne</span> <span style="color:#a6da95">&#34;&#34;</span>) {
</span></span><span style="display:flex;"><span>         <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$code</span> + <span style="color:#a6da95">&#34; - &#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;:&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;?&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;/&#34;</span>, <span style="color:#a6da95">&#34;-&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;&lt;&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;|&#34;</span>, <span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#39;&#34;&#39;</span>,<span style="color:#a6da95">&#34;&#34;</span>).<span style="color:#f5a97f">Replace</span>(<span style="color:#a6da95">&#34;*&#34;</span>,<span style="color:#a6da95">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>         <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">substring</span>(<span style="color:#f5a97f">0</span>, [<span style="color:#eed49f">System.Math</span>]::Min(<span style="color:#f5a97f">100</span>, <span style="color:#f4dbd6">$folder</span>.Length))
</span></span><span style="display:flex;"><span>         <span style="color:#f4dbd6">$folder</span> = <span style="color:#f4dbd6">$folder</span>.<span style="color:#f5a97f">trim</span>()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$folder</span> = <span style="color:#a6da95">&#34;NoCodeSessions&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">-not</span> (<span style="color:#91d7e3">Test-Path</span> <span style="color:#f4dbd6">$folder</span>)) { 
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">Write-Host</span> <span style="color:#a6da95">&#34;Folder </span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">) dosen&#39;t exist. Creating it...&#34;</span>  
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">New-Item</span> <span style="color:#f4dbd6">$folder</span> -type directory 
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>     
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic"># Make sure the MP4 file doesn&#39;t already exist</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (!(<span style="color:#91d7e3">test-path</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$folder</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>)) {     
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Echo out the  file that&#39;s being downloaded</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$file</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$wc</span> = (<span style="color:#91d7e3">New-Object</span> System.<span style="color:#f5a97f">Net</span>.WebClient)  
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic"># Download the MP4 file</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f4dbd6">$wc</span>.<span style="color:#f5a97f">DownloadFile</span>(<span style="color:#f4dbd6">$url</span>, <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$downloadlocation</span><span style="color:#a6da95">\</span><span style="color:#f4dbd6">$file</span><span style="color:#a6da95">&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#91d7e3">mv </span><span style="color:#f4dbd6">$file</span> <span style="color:#f4dbd6">$folder</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">#Try and get the Sessions text description</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$OutFile</span> = <span style="color:#91d7e3">New-Item</span> -type file <span style="color:#a6da95">&#34;</span>$(<span style="color:#f4dbd6">$downloadlocation</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$Folder</span>)<span style="color:#a6da95">\</span>$(<span style="color:#f4dbd6">$Code</span>.<span style="color:#f5a97f">trim</span>())<span style="color:#a6da95">.txt&#34;</span> -Force  
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$Category</span> = <span style="color:#a6da95">&#34;&#34;</span> ; <span style="color:#f4dbd6">$Content</span> = <span style="color:#a6da95">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">category</span> | <span style="color:#c6a0f6">foreach</span> {<span style="color:#f4dbd6">$Category</span> += <span style="color:#f4dbd6">$_</span> + <span style="color:#a6da95">&#34;,&#34;</span>}
</span></span><span style="display:flex;"><span>    <span style="color:#f4dbd6">$Content</span> = <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">title</span>.<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">creator</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$_</span>.<span style="color:#f5a97f">summary</span>.<span style="color:#f5a97f">trim</span>() + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">`r`n</span><span style="color:#a6da95">&#34;</span> + <span style="color:#f4dbd6">$Category</span>.<span style="color:#f5a97f">Substring</span>(<span style="color:#f5a97f">0</span>,<span style="color:#f4dbd6">$Category</span>.<span style="color:#f5a97f">Length</span> <span style="color:#f5a97f">-1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#91d7e3">add-content</span> <span style="color:#f4dbd6">$OutFile</span> <span style="color:#f4dbd6">$Content</span>       
</span></span><span style="display:flex;"><span>} <span style="color:#6e738d;font-style:italic">#end-loop foreach</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#91d7e3">Write-host</span> <span style="color:#a6da95">&#34;*************** All Done! ***************&#34;</span></span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Unity and Visual Studio</title>
      <link>/post/2016/04/unity-and-visual-studio/</link>
      <pubDate>Wed, 06 Apr 2016 00:00:00 +0000</pubDate>
      <guid>/post/2016/04/unity-and-visual-studio/</guid>
      <description>&lt;p&gt;Have been playing, a little with the new Visual Studio “preview” version (the installation of which is super smooth and takes waaaaay less time!). And as part of that saw the Unity Debugger option. Was this always there or is that new? Or did the Unity Tools beta add it? Interesting times to start playing with this, primarily for AR/VR and #HoloLens.&lt;/p&gt;
&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;images/image.png&#34; alt=&#34;&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Have been playing, a little with the new Visual Studio “preview” version (the installation of which is super smooth and takes waaaaay less time!). And as part of that saw the Unity Debugger option. Was this always there or is that new? Or did the Unity Tools beta add it? Interesting times to start playing with this, primarily for AR/VR and #HoloLens.</p>
<p><p>

    <img src="images/image.png" alt=""/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>Humans and threading</title>
      <link>/post/2016/03/humans-and-threading/</link>
      <pubDate>Sat, 12 Mar 2016 00:00:00 +0000</pubDate>
      <guid>/post/2016/03/humans-and-threading/</guid>
      <description>&lt;p&gt;We,  humans, are multi-threaded by design and can do many things in parallel -  with two exceptions I think. The only two blocking function we have to deal with are sneezing and farting. During these times, all current activity must be suspended for the duration. And of course it can be pretty annoying (or depending on the function, embarrassing).&lt;/p&gt;
&lt;p&gt;So next time you check in some code, think about it -  is this smelly and sneezy (yep, that&amp;rsquo;s a word, now) or have I done the right thing?&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We,  humans, are multi-threaded by design and can do many things in parallel -  with two exceptions I think. The only two blocking function we have to deal with are sneezing and farting. During these times, all current activity must be suspended for the duration. And of course it can be pretty annoying (or depending on the function, embarrassing).</p>
<p>So next time you check in some code, think about it -  is this smelly and sneezy (yep, that&rsquo;s a word, now) or have I done the right thing?</p>
]]></content:encoded>
    </item>
    <item>
      <title>Open Live Writer</title>
      <link>/post/2016/02/open-live-writer/</link>
      <pubDate>Sun, 21 Feb 2016 00:00:00 +0000</pubDate>
      <guid>/post/2016/02/open-live-writer/</guid>
      <description>&lt;p&gt;Just downloaded and installed and posting this via &lt;a
	
		href = &#34;http://openlivewriter.org/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Open Live Writer
	&lt;/span&gt;
&lt;/a&gt;. Remember the lovely (but dead) &lt;a
	
		href = &#34;https://www.microsoft.com/en-us/download/details.aspx?id=8621&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Windows Live Writer
	&lt;/span&gt;
&lt;/a&gt; from Microsoft? Well this is a forked version of that which is open source, based on MIT license.&lt;/p&gt;
&lt;p&gt;The editor is offline and is very similar to word and can support a number of blogging platforms – including the common ones as you would expect. You can muck around the &lt;a
	
		href = &#34;https://github.com/OpenLiveWriter/OpenLiveWriter/archive/0.5.0.0.zip&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		code
	&lt;/span&gt;
&lt;/a&gt; (zip) or check it &lt;a
	
		href = &#34;https://github.com/OpenLiveWriter&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		out on GIT
	&lt;/span&gt;
&lt;/a&gt;. It is still work in progress of course (e.g. Plugin’s are not implemented yet). Irrespective, hugely grateful to &lt;a
	
		href = &#34;https://twitter.com/shanselman&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Scott (Hanselman)
	&lt;/span&gt;
&lt;/a&gt; to get this going.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Just downloaded and installed and posting this via <a
	
		href = "http://openlivewriter.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Open Live Writer
	</span>
</a>. Remember the lovely (but dead) <a
	
		href = "https://www.microsoft.com/en-us/download/details.aspx?id=8621"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Windows Live Writer
	</span>
</a> from Microsoft? Well this is a forked version of that which is open source, based on MIT license.</p>
<p>The editor is offline and is very similar to word and can support a number of blogging platforms – including the common ones as you would expect. You can muck around the <a
	
		href = "https://github.com/OpenLiveWriter/OpenLiveWriter/archive/0.5.0.0.zip"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		code
	</span>
</a> (zip) or check it <a
	
		href = "https://github.com/OpenLiveWriter"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		out on GIT
	</span>
</a>. It is still work in progress of course (e.g. Plugin’s are not implemented yet). Irrespective, hugely grateful to <a
	
		href = "https://twitter.com/shanselman"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Scott (Hanselman)
	</span>
</a> to get this going.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Is rand() harmful?</title>
      <link>/post/2013/11/is-rand-harmful/</link>
      <pubDate>Wed, 06 Nov 2013 00:00:00 +0000</pubDate>
      <guid>/post/2013/11/is-rand-harmful/</guid>
      <description>&lt;p&gt;​I saw &lt;a
	
		href = &#34;http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful&#34;
	

	
		title = &#34;this&#34;
	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		this
	&lt;/span&gt;
&lt;/a&gt; awesome &lt;a
	
		href = &#34;http://video.ch9.ms/sessions/gonat/2013/STLGN13rand.pptx&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		presentation
	&lt;/span&gt;
&lt;/a&gt; on &lt;strong&gt;why rand() is considered harmful&lt;/strong&gt;. When you need a random number, don&amp;rsquo;t call rand() and especially don&amp;rsquo;t say rand() % 100! This presentation will explain why that&amp;rsquo;s so terrible, and how C++11&amp;rsquo;s header can make your life so much easier.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>​I saw <a
	
		href = "http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful"
	

	
		title = "this"
	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		this
	</span>
</a> awesome <a
	
		href = "http://video.ch9.ms/sessions/gonat/2013/STLGN13rand.pptx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		presentation
	</span>
</a> on <strong>why rand() is considered harmful</strong>. When you need a random number, don&rsquo;t call rand() and especially don&rsquo;t say rand() % 100! This presentation will explain why that&rsquo;s so terrible, and how C++11&rsquo;s header can make your life so much easier.</p>
<p>If you need uniqueness and non-deterministic, especially on the context of security or crypto then you need to think about a few things. For example the frequency, non-uniform distribution, and not using a pseudo random number generator (such as <a
	
		href = "http://en.wikipedia.org/wiki/Mersenne_twister"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Mersenne twister
	</span>
</a>) and not a <a
	
		href = "http://en.wikipedia.org/wiki/Linear_congruential_generator"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		linear congruential generator
	</span>
</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>C&#43;&#43; Comment</title>
      <link>/post/2013/09/c-comment/</link>
      <pubDate>Sun, 22 Sep 2013 00:00:00 +0000</pubDate>
      <guid>/post/2013/09/c-comment/</guid>
      <description>&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ed8796&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#8aadf4&#34;&gt;MyFunction&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;// There once was a man named Dave
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ed8796&#34;&gt;int&lt;/span&gt; Result &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;// Whose code just wouldn&amp;#39;t behave
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;&lt;/span&gt;    MyObject &lt;span style=&#34;color:#ed8796&#34;&gt;\&lt;/span&gt;&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;Ptr &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; new &lt;span style=&#34;color:#8aadf4&#34;&gt;MyObject&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;// He left to go to a meetin&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;&lt;/span&gt;    Result &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; Ptr&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#8aadf4&#34;&gt;DoSomething&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;// And left his memory a leakin&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#c6a0f6&#34;&gt;return&lt;/span&gt; Result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <content:encoded><![CDATA[<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-c" data-lang="c"><span style="display:flex;"><span><span style="color:#ed8796">int</span> <span style="color:#8aadf4">MyFunction</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// There once was a man named Dave
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#ed8796">int</span> Result <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Whose code just wouldn&#39;t behave
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    MyObject <span style="color:#ed8796">\</span><span style="color:#91d7e3;font-weight:bold">*</span>Ptr <span style="color:#91d7e3;font-weight:bold">=</span> new <span style="color:#8aadf4">MyObject</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// He left to go to a meetin&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    Result <span style="color:#91d7e3;font-weight:bold">=</span> Ptr<span style="color:#91d7e3;font-weight:bold">-&gt;</span><span style="color:#8aadf4">DoSomething</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// And left his memory a leakin&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    <span style="color:#c6a0f6">return</span> Result;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>How to insult a developer?</title>
      <link>/post/2013/08/how-to-insult-a-developer/</link>
      <pubDate>Tue, 06 Aug 2013 00:00:00 +0000</pubDate>
      <guid>/post/2013/08/how-to-insult-a-developer/</guid>
      <description>&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;http://static.squarespace.com/static/518f5d62e4b075248d6a3f90/t/51bb91f3e4b0405092e56e27/1371247103965/not-restful.jpg?format=750w&#34; alt=&#34;How to insult a developer&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><p>

    <img src="http://static.squarespace.com/static/518f5d62e4b075248d6a3f90/t/51bb91f3e4b0405092e56e27/1371247103965/not-restful.jpg?format=750w" alt="How to insult a developer"/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>How not to handle exceptions!</title>
      <link>/post/2013/07/how-not-to-handle-exceptions/</link>
      <pubDate>Fri, 19 Jul 2013 00:00:00 +0000</pubDate>
      <guid>/post/2013/07/how-not-to-handle-exceptions/</guid>
      <description>&lt;p&gt;Was trying to pay my Electricity bill online via a site called &lt;a
	
		href = &#34;https://www.bangaloreone.gov.in/public/default.aspx&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Bangalore One
	&lt;/span&gt;
&lt;/a&gt;, which is the Governments, premier one-stop shop for Electronic Delivery of Citizen Services.&lt;/p&gt;
&lt;p&gt;I could not pay because it seems like some backend services they need for credit card payment is down. How do I know this? Because the site is revealing too much detail! See the exception details pasted below.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Was trying to pay my Electricity bill online via a site called <a
	
		href = "https://www.bangaloreone.gov.in/public/default.aspx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Bangalore One
	</span>
</a>, which is the Governments, premier one-stop shop for Electronic Delivery of Citizen Services.</p>
<p>I could not pay because it seems like some backend services they need for credit card payment is down. How do I know this? Because the site is revealing too much detail! See the exception details pasted below.</p>
<p>This is a great example of what <strong>not</strong> to do! I have seen this often, and it is lazy developers and even lazier testers who approved this and get this into production. One would have thought that government managing the &ldquo;Silicon Valley of India&rdquo; would know better!</p>
<p>It is also interesting to see that they are on a very old version of .NET - running on v1.1.</p>
<p><em>Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.</em></p>
<p><strong>Description:</strong>
An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.</p>
<p><strong>Exception Details:</strong>
<code>System.Exception: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.</code></p>
<p><strong>Source Error:</strong>
<code>An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.</code></p>
<p><strong>Stack Trace:</strong></p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">8</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span>[Exception: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.]
</span></span><span style="display:flex;"><span>   BangaloreOne.clsBESCOM.fnCheckTransCnt(String LocationRRNo, String StaffCode, Int32 intDeptCode) +381
</span></span><span style="display:flex;"><span>   bOneWebPortal.BESCOMConfirm.Page_Load(Object sender, EventArgs e) +721
</span></span><span style="display:flex;"><span>   System.Web.UI.Control.OnLoad(EventArgs e) +67
</span></span><span style="display:flex;"><span>   System.Web.UI.Control.LoadRecursive() +35
</span></span><span style="display:flex;"><span>   System.Web.UI.Page.ProcessRequestMain() +750
</span></span><span style="display:flex;"><span><span style="color:#ed8796">
</span></span></span><span style="display:flex;"><span><span style="color:#ed8796"></span>Version Information: Microsoft .NET Framework Version:1.1.4322.2300; ASP.NET Version:1.1.4322.2300
</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Writing a compiler using C#</title>
      <link>/post/2012/10/writing-a-compiler-using-c/</link>
      <pubDate>Tue, 02 Oct 2012 00:00:00 +0000</pubDate>
      <guid>/post/2012/10/writing-a-compiler-using-c/</guid>
      <description>&lt;p&gt;I was cleaning out my old papers (finally!) and came across an old paper I had titled &amp;ldquo;&lt;a
	
		href = &#34;https://wiki.dcs.shef.ac.uk/wiki/pub/Ndlprojects/ExtendingMSharpLiterature/BookletCSTools.pdf&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Compiler Writing Tools using C#
	&lt;/span&gt;
&lt;/a&gt;&amp;rdquo; which essentially shows how you can write a number of tools like &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Lex_%28software%29&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		lex
	&lt;/span&gt;
&lt;/a&gt; and &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Yacc&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		yacc
	&lt;/span&gt;
&lt;/a&gt; but instead of C/C++ on Unix, you use C# and .NET.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I was cleaning out my old papers (finally!) and came across an old paper I had titled &ldquo;<a
	
		href = "https://wiki.dcs.shef.ac.uk/wiki/pub/Ndlprojects/ExtendingMSharpLiterature/BookletCSTools.pdf"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Compiler Writing Tools using C#
	</span>
</a>&rdquo; which essentially shows how you can write a number of tools like <a
	
		href = "http://en.wikipedia.org/wiki/Lex_%28software%29"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		lex
	</span>
</a> and <a
	
		href = "http://en.wikipedia.org/wiki/Yacc"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		yacc
	</span>
</a> but instead of C/C++ on Unix, you use C# and .NET.</p>
<p>This paper covers the tokenizer, <a
	
		href = "http://en.wikipedia.org/wiki/Formal_grammar"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		grammar
	</span>
</a>, <a
	
		href = "http://en.wikipedia.org/wiki/Deterministic_finite_automaton"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		DFA
	</span>
</a>, <a
	
		href = "http://en.wikipedia.org/wiki/Nondeterministic_finite_automaton"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		NFA
	</span>
</a>, etc. I think conflicts and precedence is one area it would need a little more work. But overall its a very interesting piece of work - especially for one to learn the ropes if a lot of this is new.</p>
<p>Of course, writing compilers with C, overall is not the most productive experience (it sure is fun though!); if you have never done it before <a
	
		href = "http://www.smlnj.org/sml.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Standard ML
	</span>
</a> or <a
	
		href = "http://www.haskell.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Haskell
	</span>
</a> would be a better place to start. If you want to stick to .NET and roll out your own language (or create extensions) then check out <a
	
		href = "http://msdn.microsoft.com/en-us/magazine/cc136756.aspx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		this paper on MSDN
	</span>
</a>.</p>
<p>All of this brought back memories when I wrote my own C++ compilers mainly with those tools on Unix System V and also on Xenix. Most people did not know (or perhaps remember) that Microsoft had a Unix version called <a
	
		href = "http://en.wikipedia.org/wiki/Xenix"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Xenix
	</span>
</a> which was later bought over by SCO and eventually become part of SCO Unix. Back in the days I use to run a dual-boot machine with Xenix and DOS. 😄</p>
]]></content:encoded>
    </item>
    <item>
      <title>Metro Apps in C&#43;&#43; anyone?</title>
      <link>/post/2012/05/metro-apps-in-c-anyone/</link>
      <pubDate>Sat, 19 May 2012 00:00:00 +0000</pubDate>
      <guid>/post/2012/05/metro-apps-in-c-anyone/</guid>
      <description>&lt;p&gt;In Visual Studio “11” when I try and create a new C++ Metro app using the built-in template, I get the following error: “Can’t find localized resources”.&lt;/p&gt;
&lt;p&gt;I wonder if anyone else has managed to get around this? I am running the Consumer Preview Build of Win 8 (Build 8250).&lt;/p&gt;
&lt;p&gt;&lt;p&gt;

    &lt;img src=&#34;images/image.png&#34; alt=&#34;image&#34;/&gt;

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

    &lt;img src=&#34;images/image1.png&#34; alt=&#34;image&#34;/&gt;

&lt;/p&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In Visual Studio “11” when I try and create a new C++ Metro app using the built-in template, I get the following error: “Can’t find localized resources”.</p>
<p>I wonder if anyone else has managed to get around this? I am running the Consumer Preview Build of Win 8 (Build 8250).</p>
<p><p>

    <img src="images/image.png" alt="image"/>

</p></p>
<p><p>

    <img src="images/image1.png" alt="image"/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>AWS Extension for Visual Studio</title>
      <link>/post/2012/04/aws-extension-for-visual-studio/</link>
      <pubDate>Sat, 14 Apr 2012 00:00:00 +0000</pubDate>
      <guid>/post/2012/04/aws-extension-for-visual-studio/</guid>
      <description>&lt;p&gt;I had forgotten that I had the &lt;a
	
		href = &#34;http://aws.amazon.com/visualstudio/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		AWS Extension for Visual Studio
	&lt;/span&gt;
&lt;/a&gt; installed until recently I noticed AWS Explorer item in the View menu option. This add-in allows you to explore the various features that Amazon exposes right from within Visual Studio. The toolkit makes it easier for developers to debug and deploy a .NET solutions that uses AWS.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I had forgotten that I had the <a
	
		href = "http://aws.amazon.com/visualstudio/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		AWS Extension for Visual Studio
	</span>
</a> installed until recently I noticed AWS Explorer item in the View menu option. This add-in allows you to explore the various features that Amazon exposes right from within Visual Studio. The toolkit makes it easier for developers to debug and deploy a .NET solutions that uses AWS.</p>
<p><p>

    <img src="images/image.png" alt="image"/>

</p></p>
<p>When you install this, you also get AWS SDK for .NET which provides one with all the building blocks that are required for consuming the IaaS services exposed by AWS including <a
	
		href = "http://aws.amazon.com/simpledb/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		SimpleDB
	</span>
</a>, <a
	
		href = "http://aws.amazon.com/s3/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		S3
	</span>
</a> and <a
	
		href = "http://aws.amazon.com/ec2/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		EC2
	</span>
</a>.</p>
<p>If you are or planning to use AWS, this add-in is a must have in my opinion.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Troubleshooting WCF Performance - Part 1</title>
      <link>/post/2011/05/troubleshooting-wcf-performance-part-1/</link>
      <pubDate>Thu, 26 May 2011 00:00:00 +0000</pubDate>
      <guid>/post/2011/05/troubleshooting-wcf-performance-part-1/</guid>
      <description>&lt;p&gt;More related details on Dustin&amp;rsquo;s post - &lt;a
	
		href = &#34;http://blogs.msdn.com/b/endpoint/archive/2011/05/04/wcf-scales-up-slowly-with-bursts-of-work.aspx&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		WCF scales up slowly with bursts of work
	&lt;/span&gt;
&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>More related details on Dustin&rsquo;s post - <a
	
		href = "http://blogs.msdn.com/b/endpoint/archive/2011/05/04/wcf-scales-up-slowly-with-bursts-of-work.aspx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		WCF scales up slowly with bursts of work
	</span>
</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>PowerShell script to kill named processes</title>
      <link>/post/2011/05/powershell-script-to-kill-named-processes/</link>
      <pubDate>Mon, 23 May 2011 00:00:00 +0000</pubDate>
      <guid>/post/2011/05/powershell-script-to-kill-named-processes/</guid>
      <description>&lt;p&gt;There are times when you need to kill a number of processes in one-go like today when Chrome crashed a few times hanging all the running instances – next time Google says, one tab cannot bring down all of them – send them my way :). For such times, a PowerShell script is all you need.&lt;/p&gt;
&lt;p&gt;I wrote up a simple one which takes the process name as input and then kills all the processes which match that name.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>There are times when you need to kill a number of processes in one-go like today when Chrome crashed a few times hanging all the running instances – next time Google says, one tab cannot bring down all of them – send them my way :). For such times, a PowerShell script is all you need.</p>
<p>I wrote up a simple one which takes the process name as input and then kills all the processes which match that name.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8">8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9">9</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Script is not signed, so need this. Set-ExecutionPolicy Unrestricted</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Need to set the param to a variable $target = $args\[0\]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span>(<span style="color:#f4dbd6">$target</span>) { <span style="color:#f4dbd6">$orphanProcs</span> = <span style="color:#91d7e3">get-process</span> | <span style="color:#91d7e3">where </span>{$\_.<span style="color:#f5a97f">Name</span> <span style="color:#91d7e3;font-weight:bold">-eq</span> <span style="color:#f4dbd6">$target</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#Check if list is null; if not kill all the procs if ($soonToBeDeadProcs) { #display list $soonToBeDeadProcs</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#kill list $soonToBeDeadProcs | foreach { $\_.Kill() } } else { Write-Host &#34;Oops, no processes found older with the name: $target&#34; } } else { Write-Host &#34;Oops, no arguments passed. You need to provide one argument (the Process Name).&#34; Write-Host &#34;Example 1: killproc chrome&#34; Write-Host &#34;Example 2: killproc &#39;some other process&#39;&#34; } \[/sourcecode\]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Example Output (Killing Chrome in this case):</p>
<p><code>PS C:\Users\amit.bahree\Desktop&gt; .\killproc.ps1 chrome</code></p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName  
</span></span><span style="display:flex;"><span>\-------  ------    -----      ----- -----   ------     -- ----------- 
</span></span><span style="display:flex;"><span>    139      30    34292      46708   164     3.87    376 chrome  
</span></span><span style="display:flex;"><span>    137      22    20932      33648   149     2.48   1260 chrome  
</span></span><span style="display:flex;"><span>    141      21    17896      31328   148     3.01   3572 chrome  
</span></span><span style="display:flex;"><span>   5434      37    56932      66528   266 1,134.36   4940 chrome  
</span></span><span style="display:flex;"><span>    139      22    20288      33084   150     4.12   5032 chrome  
</span></span><span style="display:flex;"><span>    145      21    16576      31368   149     0.58   5148 chrome  
</span></span><span style="display:flex;"><span>    147      19    14384      26992   150     1.42   5604 chrome  
</span></span><span style="display:flex;"><span>    142      23    32292      37416   156     8.42   6528 chrome  
</span></span><span style="display:flex;"><span>    136      17    12456      23964   142     0.30   6732 chrome  
</span></span><span style="display:flex;"><span>    144      26    27004      39136   156     0.98   6736 chrome  
</span></span><span style="display:flex;"><span>   1586      90   151224     209888   512   395.87   7184 chrome  
</span></span><span style="display:flex;"><span>    138      22    21388      33916   151     3.76   7504 chrome  
</span></span><span style="display:flex;"><span>    123      13     7756      15196   126     0.56   7512 chrome  
</span></span><span style="display:flex;"><span>    142      21    23112      35552   150     2.01   9860 chrome  
</span></span><span style="display:flex;"><span>    140      18    13032      25148   150     1.73  10432 chrome</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Twitter Trends</title>
      <link>/post/2011/03/twitter-trends/</link>
      <pubDate>Fri, 04 Mar 2011 00:00:00 +0000</pubDate>
      <guid>/post/2011/03/twitter-trends/</guid>
      <description>&lt;p&gt;I was excited to find that Twitter had a &lt;a
	
		href = &#34;http://www.json.org/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		JSON
	&lt;/span&gt;
&lt;/a&gt; (Javascript Object Notation) endpoint for the &lt;a
	
		href = &#34;http://search.twitter.com/trends/current.json&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		current trending topics
	&lt;/span&gt;
&lt;/a&gt; and decided to write a simple consumer which can read this and then spit it out in a simple console. And JSON being so simple and more or less “universal” meant that there are multiple implementations for .NET. Of course if you got lots of bandwidth you can roll out your own parser.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I was excited to find that Twitter had a <a
	
		href = "http://www.json.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		JSON
	</span>
</a> (Javascript Object Notation) endpoint for the <a
	
		href = "http://search.twitter.com/trends/current.json"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		current trending topics
	</span>
</a> and decided to write a simple consumer which can read this and then spit it out in a simple console. And JSON being so simple and more or less “universal” meant that there are multiple implementations for .NET. Of course if you got lots of bandwidth you can roll out your own parser.</p>
<p>I ended up using <a
	
		href = "http://james.newtonking.com/projects/json-net.aspx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Json.NET
	</span>
</a>, which in addition to being OpenSource is also one of the most robust utilities which makes working with JSON formatted data dead simple.</p>
<p>The code for the console app is quite straightforward. The static function ReadTrends() retrieves the JSON string from twitter which is then consumed and extracted. The only tricky part was using a constant key; the easiest way I could think of doing this was to replace the date-time stamp with a literal and then use that literal.</p>
<p>Of course this will fail if you the function ReadTrends() is called at (or just before midnight) on Dec 31st and the code returns to the main() function in the new year. I don’t think this is something I am going to put in production and am not going to be too worried about this behaviour.</p>
<p>At the time of writing this, the twitter trends (in JSON) are:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{<span style="color:#ed8796">“trends”:{“2011-03-04</span> <span style="color:#ed8796">17:18:01”:[{“name”:”#coisasderetiro”,”events”:null,”query”:”#coisasderetiro”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”#tigerblood”,”events”:null,”query”:”#tigerblood”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”#blackpeoplemovies”,”events”:null,”query”:”#blackpeoplemovies”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Frying</span> <span style="color:#ed8796">Nemo”,”events”:null,”query”:”Frying</span> <span style="color:#ed8796">Nemo”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Acoustic</span> <span style="color:#ed8796">Aftermath”,”events”:null,”query”:”Acoustic</span> <span style="color:#ed8796">Aftermath”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Blade</span> <span style="color:#ed8796">Runner”,”events”:null,”query”:”Blade</span> <span style="color:#ed8796">Runner”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Fun</span> <span style="color:#ed8796">Race”,”events”:null,”query”:”Fun</span> <span style="color:#ed8796">Race”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Bandra”,”events”:null,”query”:”Bandra”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Mike</span> <span style="color:#ed8796">Huckabee”,”events”:null,”query”:”Mike</span> <span style="color:#ed8796">Huckabee”,”promoted_content”:null</span>}<span style="color:#ed8796">,</span>
</span></span><span style="display:flex;"><span>{<span style="color:#ed8796">“name”:”Arctic</span> <span style="color:#ed8796">Monkeys”,”events”:null,”query”:”Arctic</span> <span style="color:#ed8796">Monkeys”,”promoted_content”:null</span>}<span style="color:#ed8796">]},”as_of”:</span><span style="color:#f5a97f">1299259081</span><span style="color:#ed8796">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>And here is the output in the console. I can see Charlie Sheen’s <strong>#tigerblood</strong> is still trending; and wonder what Artic Monkeys are upto – is there new album out or something?</p>
<p><code>#coisasderetiro</code>
<code>#tigerblood</code>
<code>#blackpeoplemovies</code>
<code>Frying Nemo</code>
<code>Acoustic Aftermath</code>
<code>Blade Runner</code>
<code>Fun Race</code>
<code>Bandra</code>
<code>Mike Huckabee</code>
<code>Arctic Monkeys</code>
<code>All done.  Press any key to continue...</code></p>
<p>And finally here is the code.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="58"><a style="outline:none;text-decoration:none;color:inherit" href="#58">58</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="59"><a style="outline:none;text-decoration:none;color:inherit" href="#59">59</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="60"><a style="outline:none;text-decoration:none;color:inherit" href="#60">60</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="61"><a style="outline:none;text-decoration:none;color:inherit" href="#61">61</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="62"><a style="outline:none;text-decoration:none;color:inherit" href="#62">62</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="63"><a style="outline:none;text-decoration:none;color:inherit" href="#63">63</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="64"><a style="outline:none;text-decoration:none;color:inherit" href="#64">64</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="65"><a style="outline:none;text-decoration:none;color:inherit" href="#65">65</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="66"><a style="outline:none;text-decoration:none;color:inherit" href="#66">66</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="67"><a style="outline:none;text-decoration:none;color:inherit" href="#67">67</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="68"><a style="outline:none;text-decoration:none;color:inherit" href="#68">68</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System.Collections.Generic</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System.Linq</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System.Text</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">Newtonsoft.Json.Linq</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System.Net</span>;
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">System.IO</span>;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">namespace</span> <span style="color:#f5a97f">TwitterTrends</span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">class</span> <span style="color:#eed49f">Program</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">static</span> <span style="color:#c6a0f6">void</span> Main(<span style="color:#ed8796">string</span>[] args)
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">try</span>
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//get the trends</span>
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">string</span> temp = ReadTrends();
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//as the key is a datetime stamp (which is constantly moving), need something constant to interrogate.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//simplest way is to find out the year and then take 19 characters from that which is the datetime stamp</span>
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//replace that with a literal (DESIGEEK.COM in my case) and then use the literal. I don&#39;t think I will</span>
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//be trending on Twitter; but if you are worried then you can use something like a GUID.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//</span>
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">// For example at the time of writing this the datetime stamp was: &#34;2011-03-02 14:20:00&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">string</span> theDate = temp.Substring(temp.IndexOf(DateTime.Now.Year.ToString()), <span style="color:#f5a97f">19</span>);
</span></span><span style="display:flex;"><span>                temp = temp.Replace(theDate, <span style="color:#a6da95">&#34;DESIGEEK.COM&#34;</span>);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//parse the string for the literal</span>
</span></span><span style="display:flex;"><span>                JObject trends = JObject.Parse(temp);
</span></span><span style="display:flex;"><span>                <span style="color:#ed8796">var</span> twitterTrends = <span style="color:#c6a0f6">from</span> t <span style="color:#c6a0f6">in</span> trends[<span style="color:#a6da95">&#34;trends&#34;</span>][<span style="color:#a6da95">&#34;DESIGEEK.COM&#34;</span>]
</span></span><span style="display:flex;"><span>                                    <span style="color:#c6a0f6">select</span> t.Value&lt;<span style="color:#ed8796">string</span>&gt;(<span style="color:#a6da95">&#34;name&#34;</span>);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>                <span style="color:#6e738d;font-style:italic">//iterate through</span>
</span></span><span style="display:flex;"><span>                <span style="color:#c6a0f6">foreach</span> (<span style="color:#ed8796">var</span> item <span style="color:#c6a0f6">in</span> twitterTrends)
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    Console.WriteLine(item);
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">catch</span> (Exception ex)
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                Console.WriteLine(<span style="color:#a6da95">&#34;Following error occured:\n&#34;</span> + ex.ToString());
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            Console.WriteLine(<span style="color:#a6da95">&#34;\nAll done. Press any key to continue...&#34;</span>);
</span></span><span style="display:flex;"><span>            Console.ReadKey();
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#ed8796">private</span> <span style="color:#ed8796">static</span> <span style="color:#ed8796">string</span> ReadTrends()
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">//talk to twitter</span>
</span></span><span style="display:flex;"><span>            WebRequest theRequest = HttpWebRequest.Create(<span style="color:#a6da95">&#34;http://search.twitter.com/trends/current.json&#34;</span>);
</span></span><span style="display:flex;"><span>            HttpWebResponse theResponse = (HttpWebResponse)theRequest.GetResponse();
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">//extract the data</span>
</span></span><span style="display:flex;"><span>            Stream stream = theResponse.GetResponseStream();
</span></span><span style="display:flex;"><span>            StreamReader reader = <span style="color:#c6a0f6">new</span> StreamReader(stream);
</span></span><span style="display:flex;"><span>            <span style="color:#ed8796">string</span> temp = reader.ReadToEnd();
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">//clean up</span>
</span></span><span style="display:flex;"><span>            reader.Close();
</span></span><span style="display:flex;"><span>            stream.Close();
</span></span><span style="display:flex;"><span>            theResponse.Close();
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> temp;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Opensource ZigBee stack?</title>
      <link>/post/2011/01/opensource-zigbee-stack/</link>
      <pubDate>Sun, 16 Jan 2011 00:00:00 +0000</pubDate>
      <guid>/post/2011/01/opensource-zigbee-stack/</guid>
      <description>&lt;p&gt;I was planning on getting the &lt;a
	
		href = &#34;http://www.telegesis.com/products/test_page_2.htm&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Telegesis ETRX2USB
	&lt;/span&gt;
&lt;/a&gt; and wanted to know if there are any open source (or shareware) open source ZigBee stacks that I can use with that?&lt;/p&gt;
&lt;p&gt;I also wanted to know if there is any opensource (or not too expensive), network management or network analyser for a ZigBee network? Essentially I want to be able to programatically view network and node information on the ZigBee network (e.g. S/N ratio, signal strength, etc.) - something similar to &lt;a
	
		href = &#34;http://www.ember.com/products_zigbee_development_tools_desktop.html&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Ember&#39;s Insight Desktop
	&lt;/span&gt;
&lt;/a&gt; which &lt;a
	
		href = &#34;http://www.ember.com/products_insight_desktop_pop.html&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		shows the details
	&lt;/span&gt;
&lt;/a&gt; I am interested in. I could not find anything specific and would be interested in getting ideas.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I was planning on getting the <a
	
		href = "http://www.telegesis.com/products/test_page_2.htm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Telegesis ETRX2USB
	</span>
</a> and wanted to know if there are any open source (or shareware) open source ZigBee stacks that I can use with that?</p>
<p>I also wanted to know if there is any opensource (or not too expensive), network management or network analyser for a ZigBee network? Essentially I want to be able to programatically view network and node information on the ZigBee network (e.g. S/N ratio, signal strength, etc.) - something similar to <a
	
		href = "http://www.ember.com/products_zigbee_development_tools_desktop.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Ember's Insight Desktop
	</span>
</a> which <a
	
		href = "http://www.ember.com/products_insight_desktop_pop.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		shows the details
	</span>
</a> I am interested in. I could not find anything specific and would be interested in getting ideas.</p>
]]></content:encoded>
    </item>
    <item>
      <title>C&#43;&#43; Message queuing options?</title>
      <link>/post/2010/12/c-message-queuing-options/</link>
      <pubDate>Wed, 29 Dec 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/12/c-message-queuing-options/</guid>
      <description>&lt;p&gt;I am thinking of implementing a queue in one of the projects I am working on right now (sorry cannot go into more details until it gets published - hopefully in a few months). Anywyas, this is in C++ which needs to run on Ubuntu and my queueing experience (with C++ or otherwise) is only with &lt;a
	
		href = &#34;http://msdn.microsoft.com/en-us/library/ms711472%28VS.85%29.aspx&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		MSMQ
	&lt;/span&gt;
&lt;/a&gt; which is brilliant, but does not help me here as that run only on Windows. I also cannot use something like &lt;a
	
		href = &#34;http://www.cplusplus.com/reference/stl/queue/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		STL Queue
	&lt;/span&gt;
&lt;/a&gt; as this will need to run across a number of machines and trying to sync between them would a royal pain. In other words, this needs to be distributed and async &amp;ldquo;loose&amp;rdquo; messaging. :-)&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I am thinking of implementing a queue in one of the projects I am working on right now (sorry cannot go into more details until it gets published - hopefully in a few months). Anywyas, this is in C++ which needs to run on Ubuntu and my queueing experience (with C++ or otherwise) is only with <a
	
		href = "http://msdn.microsoft.com/en-us/library/ms711472%28VS.85%29.aspx"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		MSMQ
	</span>
</a> which is brilliant, but does not help me here as that run only on Windows. I also cannot use something like <a
	
		href = "http://www.cplusplus.com/reference/stl/queue/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		STL Queue
	</span>
</a> as this will need to run across a number of machines and trying to sync between them would a royal pain. In other words, this needs to be distributed and async &ldquo;loose&rdquo; messaging. :-)</p>
<p>I am already using <a
	
		href = "http://desigeek.com/blog/amit/2010/11/06/moos/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		MOOS
	</span>
</a>, so one option is for me to continue to use that - however this is for another part of the application and it <em>might</em> be easier for me to use something else (still need to think it through a little more).</p>
<p>These are the requirements (these are must haves!). Also if it makes a difference I am using <a
	
		href = "http://eclipse.org/cdt/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		CDT
	</span>
</a> for this project.</p>
<ul>
<li>Needs to be able to run on Ubuntu 9.04 (and higher)</li>
<li>Needs to be Open Source (cannot be commercial)</li>
<li>Needs to be able to store messages &ldquo;offline&rdquo;</li>
<li>Needs to be able to run on TCP with minimal dependencies. It would be nice not to have a whole bunch of underlying dependencies.</li>
<li>Preferably be easy to use (as a consumer) - I don&rsquo;t have much time to read through loads of documentation just to get my head around the underlying object model and how to use it.</li>
<li>C++ support (if it was not obvious until now)</li>
</ul>
<p>I did a little research online and came across the following, and wanted to get some feedback:</p>
<ul>
<li><a
	
		href = "http://activemq.apache.org/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		ActiveMQ
	</span>
</a> - seems like it has good C++ support via <a
	
		href = "http://activemq.apache.org/cms/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		CMS
	</span>
</a> (C++ Messaging Service).</li>
<li><a
	
		href = "http://aws.amazon.com/sqs/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Amazon SQS
	</span>
</a> -  not sure how good the C++ support is. If there is no library per se that I can use, then writing things around REST APIs might be more painful. Also I suddenly have a dependency to be able to go to the public internet. Also it is not free (though there is a free 100K messages / month).</li>
<li><a
	
		href = "Amateurish"
	

	

	>
	
	<span>
		MQ4CPP
	</span>
</a> - seems quite amateurish (kudos to the guy writing it though - seems like an interesting project to pick up when once has time).</li>
<li><a
	
		href = "http://www.rabbitmq.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		RabbitMQ
	</span>
</a> - I know some guys used this at work (though that was using it in .NET); nothing for C++, but there <a
	
		href = "http://www.rabbitmq.com/download-ex.html#rabbitmq-c"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		some C experimental code
	</span>
</a>; overall does not inspire confidence (in the context of C++).</li>
<li>OpenAMQ - seems quite interesting and also has a <a
	
		href = "http://www.openamq.org/doc:prog-wireapi"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		C++ API
	</span>
</a> based on its WireAPI.</li>
<li>Anything else??</li>
</ul>
<p>At face value it seems like this is down to ActiveMQ and OpenAMQ. Just looking at the quick samples between the two ActiveMQ seems like more <a
	
		href = "http://activemq.apache.org/cms/example.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		C++ friendly
	</span>
</a> and easier to use <a
	
		href = "http://www.openamq.org/doc:prog-wireapi"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		compared to OpenAMQ
	</span>
</a>. Of course this is just the first impression and I could be completely wrong - it is not like I have had a chance to play with this (yet anyways).</p>
<p>Does anyone have any experience and feedback on this matter? Feel free to comment on this post, or tweet me.</p>
]]></content:encoded>
    </item>
    <item>
      <title>SQL Syntax Error (with MySQL)</title>
      <link>/post/2010/12/sql-syntax-error-with-mysql/</link>
      <pubDate>Fri, 24 Dec 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/12/sql-syntax-error-with-mysql/</guid>
      <description>&lt;p&gt;Say you are writing a new stored procedure (for MySQL) and when you execute it, you get an error something like shown below - as you probably figured out all it means is that there is a syntax error with in the SQL. Often the error is misleading especially if it is a complicated query. One easy way to help narrow down the issue is to run it in a SQL Console which usually provides a better clue that can be your pointer to fixing the issue.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Say you are writing a new stored procedure (for MySQL) and when you execute it, you get an error something like shown below - as you probably figured out all it means is that there is a syntax error with in the SQL. Often the error is misleading especially if it is a complicated query. One easy way to help narrow down the issue is to run it in a SQL Console which usually provides a better clue that can be your pointer to fixing the issue.</p>
<p>You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for
the right syntax to use near &lsquo;END&rsquo; at line 17 (errno: 1064). Click &lsquo;Ignore&rsquo; if you&rsquo;d like to have this
error ignored until the end of the script.</p>
<p>If you run this script you will get the above error:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#c6a0f6">DELIMITER</span> <span style="color:#ed8796">$$</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">DROP</span> <span style="color:#c6a0f6">PROCEDURE</span> <span style="color:#c6a0f6">IF</span> <span style="color:#c6a0f6">EXISTS</span> <span style="color:#91d7e3;font-weight:bold">`</span>someSchema<span style="color:#91d7e3;font-weight:bold">`</span>.<span style="color:#91d7e3;font-weight:bold">`</span>sp_someSP<span style="color:#91d7e3;font-weight:bold">`</span><span style="color:#ed8796">$$</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">CREATE</span>  <span style="color:#c6a0f6">DEFINER</span><span style="color:#91d7e3;font-weight:bold">=`</span>someUser<span style="color:#91d7e3;font-weight:bold">`@`</span>someServer<span style="color:#91d7e3;font-weight:bold">`</span> <span style="color:#c6a0f6">PROCEDURE</span> <span style="color:#91d7e3;font-weight:bold">`</span>someSchema<span style="color:#91d7e3;font-weight:bold">`</span>.<span style="color:#91d7e3;font-weight:bold">`</span>sp_someSP<span style="color:#91d7e3;font-weight:bold">`</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">in</span> uavname <span style="color:#91d7e3">varchar</span>(<span style="color:#f5a97f">20</span>)
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">BEGIN</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">SELECT</span> u.id, i.<span style="color:#91d7e3;font-weight:bold">*</span>, ll.<span style="color:#91d7e3;font-weight:bold">*</span>, <span style="color:#c6a0f6">g</span>.<span style="color:#91d7e3;font-weight:bold">*</span>, <span style="color:#c6a0f6">c</span>.<span style="color:#91d7e3;font-weight:bold">*</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">FROM</span>    uav <span style="color:#c6a0f6">as</span> u,
</span></span><span style="display:flex;"><span>    imu <span style="color:#c6a0f6">as</span> i,
</span></span><span style="display:flex;"><span>        uav_ll <span style="color:#c6a0f6">as</span> ll,
</span></span><span style="display:flex;"><span>        gps <span style="color:#c6a0f6">as</span> <span style="color:#c6a0f6">g</span>,
</span></span><span style="display:flex;"><span>        uav_controller <span style="color:#c6a0f6">as</span> <span style="color:#c6a0f6">c</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">WHERE</span>
</span></span><span style="display:flex;"><span>    u.name <span style="color:#91d7e3;font-weight:bold">=</span> uavname <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> i.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> ll.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">g</span>.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">c</span>.uav_id
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">END</span><span style="color:#ed8796">$$</span>
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">DELIMITER</span> ;</span></span></code></pre></td></tr></table>
</div>
</div>
<p>The main issue in my example above was that a delimiter (semi-colon in this case) was missing where the SQL statement finishes i.e. in the last WHERE condition. Here is a snippet of what the updated WHERE clause should look like.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#c6a0f6">WHERE</span>
</span></span><span style="display:flex;"><span>    u.name <span style="color:#91d7e3;font-weight:bold">=</span> uavname <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> i.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> ll.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">g</span>.uav_id <span style="color:#c6a0f6">and</span>
</span></span><span style="display:flex;"><span>        u.id <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">c</span>.uav_id ; <span style="color:#6e738d;font-style:italic">-- semicolon added here
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span><span style="color:#c6a0f6">END</span><span style="color:#ed8796">$$</span></span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Impressed with Doxygen</title>
      <link>/post/2010/10/impressed-with-doxygen/</link>
      <pubDate>Sat, 30 Oct 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/10/impressed-with-doxygen/</guid>
      <description>&lt;p&gt;I have recently started using Doxygen in anger and have been quite impressed with it. In addition to the documentation of code that you would expect, one of the most powerful and coolest features is the ability to create various types of diagrams showing various aspects of the application such as collaboration diagrams object and call graphs, etc.&lt;/p&gt;
&lt;p&gt;The easiest way to configure your application  is to use &lt;a
	
		href = &#34;http://www.stack.nl/~dimitri/doxygen/doxywizard_usage.html&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Doxywizard
	&lt;/span&gt;
&lt;/a&gt;. On Linux, if you do want the object and call graphs then you would need to choose to enable the DOT option. If you do that you will need to have Message Sequence Charts installed (typically found in /usr/bin/mscgen) and also Graphviz. DOT can typically be found in /usr/bin/dot.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have recently started using Doxygen in anger and have been quite impressed with it. In addition to the documentation of code that you would expect, one of the most powerful and coolest features is the ability to create various types of diagrams showing various aspects of the application such as collaboration diagrams object and call graphs, etc.</p>
<p>The easiest way to configure your application  is to use <a
	
		href = "http://www.stack.nl/~dimitri/doxygen/doxywizard_usage.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Doxywizard
	</span>
</a>. On Linux, if you do want the object and call graphs then you would need to choose to enable the DOT option. If you do that you will need to have Message Sequence Charts installed (typically found in /usr/bin/mscgen) and also Graphviz. DOT can typically be found in /usr/bin/dot.</p>
<p>If you are on Windows and use Visual Studio, then there are a few add-ins which will work - with VS.NET <a
	
		href = "http://www.atomineerutils.com/products.php"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		2005, 2008
	</span>
</a> and <a
	
		href = "http://doxygenbrowseraddin.codeplex.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		2010
	</span>
</a>; more details <a
	
		href = "http://doxygenbrowseraddin.codeplex.com/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		here
	</span>
</a>.</p>
<p>Below is a simple example of a collaboration diagram taken from <a
	
		href = "http://www.vtk.org/doc/nightly/html/index.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		VTK project
	</span>
</a>. You can find <a
	
		href = "http://www.stack.nl/~dimitri/doxygen/results.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		more samples
	</span>
</a>. When you do browse the code, click on the Classes link - that is where you can see the various diagrams.</p>
<p><p>

    <img src="images/structvtkKdTree_1_1__cellList__coll__graph.png" alt="Sample Collaboration Diagram"/>

</p></p>
]]></content:encoded>
    </item>
    <item>
      <title>MySQL&#43;&#43;</title>
      <link>/post/2010/10/mysql/</link>
      <pubDate>Sat, 23 Oct 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/10/mysql/</guid>
      <description>&lt;p&gt;I had a need to dump some data I am getting from a real-time sensor network to a database and I choose MySQL - just because it is cheap and cheerful and will fit perfectly with what I am looking for. Now, I have never programmed for MySQL though I have used it in the past as a consumer (e.g. the backend of this blog). MySQL does &lt;a
	
		href = &#34;http://dev.mysql.com/doc/refman/5.0/en/c.html&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		expose a C API
	&lt;/span&gt;
&lt;/a&gt; that one can use, but it seems quite arcane to use and does not quite conform to the C++ style (especially when using STL).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I had a need to dump some data I am getting from a real-time sensor network to a database and I choose MySQL - just because it is cheap and cheerful and will fit perfectly with what I am looking for. Now, I have never programmed for MySQL though I have used it in the past as a consumer (e.g. the backend of this blog). MySQL does <a
	
		href = "http://dev.mysql.com/doc/refman/5.0/en/c.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		expose a C API
	</span>
</a> that one can use, but it seems quite arcane to use and does not quite conform to the C++ style (especially when using STL).</p>
<p>It is at that point that I stumbled across <a
	
		href = "http://tangentsoft.net/mysql&#43;&#43;/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		MySQL++
	</span>
</a> - something I had not heard of and of course not used until now. MySQL++ is just a simple C++ wrapper around the same MySQL C API, but it does follow the C++ STL principles and feels more &rsquo;natural&rsquo; to use. I am still playing with it and getting to know it.</p>
<p>Below is a quick sample on how to connect to a database and retrieve all the records from a table called &lsquo;stock&rsquo;.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="45"><a style="outline:none;text-decoration:none;color:inherit" href="#45">45</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="46"><a style="outline:none;text-decoration:none;color:inherit" href="#46">46</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="47"><a style="outline:none;text-decoration:none;color:inherit" href="#47">47</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="48"><a style="outline:none;text-decoration:none;color:inherit" href="#48">48</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="49"><a style="outline:none;text-decoration:none;color:inherit" href="#49">49</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="50"><a style="outline:none;text-decoration:none;color:inherit" href="#50">50</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="51"><a style="outline:none;text-decoration:none;color:inherit" href="#51">51</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="52"><a style="outline:none;text-decoration:none;color:inherit" href="#52">52</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="53"><a style="outline:none;text-decoration:none;color:inherit" href="#53">53</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="54"><a style="outline:none;text-decoration:none;color:inherit" href="#54">54</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="55"><a style="outline:none;text-decoration:none;color:inherit" href="#55">55</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="56"><a style="outline:none;text-decoration:none;color:inherit" href="#56">56</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="57"><a style="outline:none;text-decoration:none;color:inherit" href="#57">57</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&#34;cmdline.h&#34;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&#34;printdata.h&#34;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;mysql++.h&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;iostream&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;iomanip&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#c6a0f6">namespace</span> std;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#ed8796">int</span> <span style="color:#8aadf4">main</span>(<span style="color:#ed8796">int</span> argc, <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>argv[])
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Get database access parameters from command line
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        <span style="color:#c6a0f6">const</span> <span style="color:#ed8796">char</span><span style="color:#91d7e3;font-weight:bold">*</span> db <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>, <span style="color:#91d7e3;font-weight:bold">*</span>server <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>, <span style="color:#91d7e3;font-weight:bold">*</span>user <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>, <span style="color:#91d7e3;font-weight:bold">*</span>pass <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;&#34;</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (<span style="color:#91d7e3;font-weight:bold">!</span>parse_command_line(argc, argv, <span style="color:#91d7e3;font-weight:bold">&amp;</span>db, <span style="color:#91d7e3;font-weight:bold">&amp;</span>server, <span style="color:#91d7e3;font-weight:bold">&amp;</span>user, <span style="color:#91d7e3;font-weight:bold">&amp;</span>pass)) {
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">1</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#6e738d;font-style:italic">// Connect to the sample database.
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>    mysqlpp<span style="color:#91d7e3;font-weight:bold">::</span>Connection conn(<span style="color:#91d7e3">false</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">if</span> (conn.connect(db, server, user, pass)) {
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">// Retrieve the sample stock table set up by resetdb
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        mysqlpp<span style="color:#91d7e3;font-weight:bold">::</span>Query query <span style="color:#91d7e3;font-weight:bold">=</span> conn.query(<span style="color:#a6da95">&#34;select * from stock&#34;</span>);
</span></span><span style="display:flex;"><span>        mysqlpp<span style="color:#91d7e3;font-weight:bold">::</span>StoreQueryResult res <span style="color:#91d7e3;font-weight:bold">=</span> query.store();
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#6e738d;font-style:italic">// Display results
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>        <span style="color:#c6a0f6">if</span> (res) {
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">// Display header
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>            cout.setf(ios<span style="color:#91d7e3;font-weight:bold">::</span>left);
</span></span><span style="display:flex;"><span>            cout <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> setw(<span style="color:#f5a97f">31</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;Item&#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                    setw(<span style="color:#f5a97f">10</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;Num&#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                    setw(<span style="color:#f5a97f">10</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;Weight&#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                    setw(<span style="color:#f5a97f">10</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;Price&#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#a6da95">&#34;Date&#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> endl <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> endl;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>            <span style="color:#6e738d;font-style:italic">// Get each row in result set, and print its contents
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span>            <span style="color:#c6a0f6">for</span> (size_t i <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>; i <span style="color:#91d7e3;font-weight:bold">&lt;</span> res.num_rows(); <span style="color:#91d7e3;font-weight:bold">++</span>i) {
</span></span><span style="display:flex;"><span>                cout <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> setw(<span style="color:#f5a97f">30</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> res[i][<span style="color:#a6da95">&#34;item&#34;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#39; &#39;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                        setw(<span style="color:#f5a97f">9</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> res[i][<span style="color:#a6da95">&#34;num&#34;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#39; &#39;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                        setw(<span style="color:#f5a97f">9</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> res[i][<span style="color:#a6da95">&#34;weight&#34;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#39; &#39;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                        setw(<span style="color:#f5a97f">9</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> res[i][<span style="color:#a6da95">&#34;price&#34;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#39; &#39;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                        setw(<span style="color:#f5a97f">9</span>) <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> res[i][<span style="color:#a6da95">&#34;sdate&#34;</span>] <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span>
</span></span><span style="display:flex;"><span>                        endl;
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>            cerr <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;Failed to get stock table: &#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> query.error() <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> endl;
</span></span><span style="display:flex;"><span>            <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">1</span>;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">0</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">else</span> {
</span></span><span style="display:flex;"><span>        cerr <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> <span style="color:#a6da95">&#34;DB connection failed: &#34;</span> <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> conn.error() <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> endl;
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> <span style="color:#f5a97f">1</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>invalid use of incomplete type ‘blah&#39;</title>
      <link>/post/2010/04/invalid-use-of-incomplete-type-blah/</link>
      <pubDate>Sun, 18 Apr 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/04/invalid-use-of-incomplete-type-blah/</guid>
      <description>&lt;p&gt;When you try and compile some code and you get an error along the lines of invalid use of an incomplete type &amp;lsquo;whatever type&amp;rsquo; then in most cases it means you need to include the header file where that type is displayed.&lt;/p&gt;
&lt;p&gt;For example I had the following events in my header file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c6a0f6&#34;&gt;protected&lt;/span&gt;&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ed8796&#34;&gt;void&lt;/span&gt; mousePressEvent(QGraphicsSceneMouseEvent &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;event);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ed8796&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#8aadf4&#34;&gt;mouseReleaseEvent&lt;/span&gt;(QGraphicsSceneMouseEvent &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;event);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When when I tried to compile gave the following error: invalid use of incomplete type &lt;code&gt;‘struct QGraphicsSceneMouseEvent’&lt;/code&gt;&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>When you try and compile some code and you get an error along the lines of invalid use of an incomplete type &lsquo;whatever type&rsquo; then in most cases it means you need to include the header file where that type is displayed.</p>
<p>For example I had the following events in my header file:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#c6a0f6">protected</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">void</span> mousePressEvent(QGraphicsSceneMouseEvent <span style="color:#91d7e3;font-weight:bold">*</span>event);
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">void</span> <span style="color:#8aadf4">mouseReleaseEvent</span>(QGraphicsSceneMouseEvent <span style="color:#91d7e3;font-weight:bold">*</span>event);</span></span></code></pre></td></tr></table>
</div>
</div>
<p>When when I tried to compile gave the following error: invalid use of incomplete type <code>‘struct QGraphicsSceneMouseEvent’</code></p>
<p>This was because the compiler could not find details of the struct and hence the details. To fix the problem I need to include the header.</p>
<p>This of course is similar to the <a
	
		href = "/post/2010/01/qpainter-painter-has-initialiser-but-incomplete-type/"
	

	

	>
	
	<span>
		initialized but not complete
	</span>
</a> error but subtly different.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Finding an element in a list</title>
      <link>/post/2010/04/finding-an-element-in-a-list/</link>
      <pubDate>Mon, 05 Apr 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/04/finding-an-element-in-a-list/</guid>
      <description>&lt;p&gt;Often you need to search through an array or list to find a specific element and of course you need this search to be as fast and efficient as possible. One of the best ways to do this is using a binary predicate function.&lt;/p&gt;
&lt;p&gt;A binary function is a function object (which are also called &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Functors&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Functors
	&lt;/span&gt;
&lt;/a&gt;) and is any object which can be called as if it was a function. Depending on your language and platform of choice, &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Function_object&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Function objects
	&lt;/span&gt;
&lt;/a&gt; are also known as &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Callback_%28computer_science%29&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		callback functions
	&lt;/span&gt;
&lt;/a&gt;, &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Function_pointer&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		function pointers
	&lt;/span&gt;
&lt;/a&gt; and &lt;a
	
		href = &#34;http://en.wikipedia.org/wiki/Delegate_%28.NET%29&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		delegates
	&lt;/span&gt;
&lt;/a&gt; (.NET). Generally, there are three types of function objects:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Often you need to search through an array or list to find a specific element and of course you need this search to be as fast and efficient as possible. One of the best ways to do this is using a binary predicate function.</p>
<p>A binary function is a function object (which are also called <a
	
		href = "http://en.wikipedia.org/wiki/Functors"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Functors
	</span>
</a>) and is any object which can be called as if it was a function. Depending on your language and platform of choice, <a
	
		href = "http://en.wikipedia.org/wiki/Function_object"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Function objects
	</span>
</a> are also known as <a
	
		href = "http://en.wikipedia.org/wiki/Callback_%28computer_science%29"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		callback functions
	</span>
</a>, <a
	
		href = "http://en.wikipedia.org/wiki/Function_pointer"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		function pointers
	</span>
</a> and <a
	
		href = "http://en.wikipedia.org/wiki/Delegate_%28.NET%29"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		delegates
	</span>
</a> (.NET). Generally, there are three types of function objects:</p>
<ol>
<li>Generators - function with no arguments</li>
<li>Unary Functions - function with one argument</li>
<li>Binary Functions - functions with two arguments</li>
</ol>
<p>A function object which takes one parameter (i.e. <a
	
		href = "http://www.sgi.com/tech/stl/UnaryFunction.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		unary function
	</span>
</a>) and returns a bool are treated as a special case and are  called Predicate functions.</p>
<p>How do we use it? Say we have a simple data structure called <strong>ContactData</strong> to represent a Contact in an Address book as shown in the code snippet below. We also define a predicate function called <strong>FindAContact</strong>. Now we need to use this predicate function and define another function called  findContact. The findContact function in turn uses <a
	
		href = "http://www.sgi.com/tech/stl/find_if.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		find_if
	</span>
</a>.  find_if takes three parameters, the start of the iterator, the last element and the predicate to use. It returns the first iterator it finds in a given range for which the predicate holds. If no matches are found then  the last element in the iterator is returned.</p>
<p>We also need to ensure we have the relevant includes for this to compile and link properly hence include&rsquo;s below.</p>
<p>The code snippet below shows all that we have discussed.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="18"><a style="outline:none;text-decoration:none;color:inherit" href="#18">18</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="19"><a style="outline:none;text-decoration:none;color:inherit" href="#19">19</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="20"><a style="outline:none;text-decoration:none;color:inherit" href="#20">20</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="21"><a style="outline:none;text-decoration:none;color:inherit" href="#21">21</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="22"><a style="outline:none;text-decoration:none;color:inherit" href="#22">22</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="23"><a style="outline:none;text-decoration:none;color:inherit" href="#23">23</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="24"><a style="outline:none;text-decoration:none;color:inherit" href="#24">24</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="25"><a style="outline:none;text-decoration:none;color:inherit" href="#25">25</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="26"><a style="outline:none;text-decoration:none;color:inherit" href="#26">26</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="27"><a style="outline:none;text-decoration:none;color:inherit" href="#27">27</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="28"><a style="outline:none;text-decoration:none;color:inherit" href="#28">28</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="29"><a style="outline:none;text-decoration:none;color:inherit" href="#29">29</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="30"><a style="outline:none;text-decoration:none;color:inherit" href="#30">30</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="31"><a style="outline:none;text-decoration:none;color:inherit" href="#31">31</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="32"><a style="outline:none;text-decoration:none;color:inherit" href="#32">32</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="33"><a style="outline:none;text-decoration:none;color:inherit" href="#33">33</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="34"><a style="outline:none;text-decoration:none;color:inherit" href="#34">34</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="35"><a style="outline:none;text-decoration:none;color:inherit" href="#35">35</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="36"><a style="outline:none;text-decoration:none;color:inherit" href="#36">36</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="37"><a style="outline:none;text-decoration:none;color:inherit" href="#37">37</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="38"><a style="outline:none;text-decoration:none;color:inherit" href="#38">38</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="39"><a style="outline:none;text-decoration:none;color:inherit" href="#39">39</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="40"><a style="outline:none;text-decoration:none;color:inherit" href="#40">40</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="41"><a style="outline:none;text-decoration:none;color:inherit" href="#41">41</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="42"><a style="outline:none;text-decoration:none;color:inherit" href="#42">42</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="43"><a style="outline:none;text-decoration:none;color:inherit" href="#43">43</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="44"><a style="outline:none;text-decoration:none;color:inherit" href="#44">44</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#ed8796">#</span>include &lt;vector&gt;
</span></span><span style="display:flex;"><span><span style="color:#ed8796">#</span>include &lt;algorithm&gt;
</span></span><span style="display:flex;"><span><span style="color:#ed8796">#</span>include &lt;functional&gt;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">using</span> <span style="color:#f5a97f">namespace</span> std;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">//Simple data structure</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">struct</span> <span style="color:#eed49f">ContactData</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> name;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> addr1;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> addr2;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> addr3;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> city;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> postcode;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> country;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> workPhone;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> homePhone;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> mobilePhone;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> workEmail;
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">string</span> homeEmail;
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">//I am lazy, create a typedef for the vector</span>
</span></span><span style="display:flex;"><span>typedef vector&lt;ContactData&gt; ContactDataArray;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">// predicate function for rapidly searching the Contact data array</span>
</span></span><span style="display:flex;"><span><span style="color:#c6a0f6">struct</span> <span style="color:#eed49f">FindAContact</span>: <span style="color:#ed8796">public</span> std::binary_function&lt;ContactData, std::<span style="color:#ed8796">string</span>, <span style="color:#ed8796">bool</span>&gt;
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">bool</span> <span style="color:#ed8796">operator</span>() (<span style="color:#ed8796">const</span> ContactData &amp;contact, <span style="color:#ed8796">const</span> <span style="color:#ed8796">string</span> &amp;name) <span style="color:#ed8796">const</span>
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#c6a0f6">return</span> (contact.name == name);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>};
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">//If a contact is find it returns that; else returns the iterator&#39;s last element</span>
</span></span><span style="display:flex;"><span>ContactData Contact::findContact(<span style="color:#ed8796">string</span> name)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    ContactDataArray::iterator it = find_if(addressBook.begin(),
</span></span><span style="display:flex;"><span>                        addressBook.end(),
</span></span><span style="display:flex;"><span>                        std::bind2nd(FindAContact(), name)
</span></span><span style="display:flex;"><span>                    );
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">return</span> *it;
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Printing code and making it look pretty</title>
      <link>/post/2010/03/printing-code-and-making-it-look-pretty/</link>
      <pubDate>Mon, 01 Mar 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/03/printing-code-and-making-it-look-pretty/</guid>
      <description>&lt;p&gt;If you are on Linux and want to print some code and also make it look pretty then check out &lt;a
	
		href = &#34;http://www.gnu.org/software/a2ps/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		a2ps
	&lt;/span&gt;
&lt;/a&gt; (Any to postscript filter). Of course if you can avoid printing in the first place and saving paper and trees and make it greener that is ideal - however there are times that is not possible. I tried printing from CDT, but the printing options from CDT just looks plain ugly and big fonts and can spread over 10 pages for a simple code file (spanning 293 lines). Sure I can tweak the font in CDT, but that is the only option available - enter &lt;a
	
		href = &#34;http://www.gnu.org/software/a2ps/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		a2ps
	&lt;/span&gt;
&lt;/a&gt;. It seems to have more options, but I have not had a chance to play with those.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you are on Linux and want to print some code and also make it look pretty then check out <a
	
		href = "http://www.gnu.org/software/a2ps/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		a2ps
	</span>
</a> (Any to postscript filter). Of course if you can avoid printing in the first place and saving paper and trees and make it greener that is ideal - however there are times that is not possible. I tried printing from CDT, but the printing options from CDT just looks plain ugly and big fonts and can spread over 10 pages for a simple code file (spanning 293 lines). Sure I can tweak the font in CDT, but that is the only option available - enter <a
	
		href = "http://www.gnu.org/software/a2ps/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		a2ps
	</span>
</a>. It seems to have more options, but I have not had a chance to play with those.</p>
<p>For example if I wanted a C++ code file called MOOSSniffer.cpp and &ldquo;print it&rdquo; out as PDF then use the command shown below. Here &ldquo;-E&rdquo; is the option to make the code look pretty and the &ldquo;-P pdf&rdquo; is the option for printing to PDF. Next comes the source file (you can also provide multiple files such as *.cpp) and finally the -o option is for the output filename. Of course you will need to install a2ps, which you can do via System -&gt; Admin -&gt; Synaptic Package Manager</p>
<p><strong>a2ps -E -P pdf MOOSSniffer.cpp -o MOOSSniffer.pdf</strong></p>
<p>Now, for some reason the resulting PDF could not be opened in Acrobat Reader, but on my Ubuntu machine, I could open it using the &ldquo;Document Viewer&rdquo; and print it using that. And in case you were curious, the pretty page option came to 3 pages instead of the original 10.</p>
<p>Also no trees were harmed in the making of this post - my printouts were all to PDF and not real paper - but in the end I did print out the 3 page version. :)</p>
]]></content:encoded>
    </item>
    <item>
      <title>Ten commandments of Programming</title>
      <link>/post/2010/02/ten-commandments-of-programming/</link>
      <pubDate>Wed, 17 Feb 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/02/ten-commandments-of-programming/</guid>
      <description>&lt;p&gt;I came across the &lt;a
	
		href = &#34;http://articles.techrepublic.com.com/5100-10878_11-1045782.html&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Ten commandments of Programming
	&lt;/span&gt;
&lt;/a&gt; while looking at a &lt;a
	
		href = &#34;http://stackoverflow.com/questions/2281584/how-to-become-a-software-engineer&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		question on StackOverflow
	&lt;/span&gt;
&lt;/a&gt; and I can&amp;rsquo;t believe I have not seen these before. I think every developer, lead, architect, dba, pm, whoever should print this out! 8-)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Understand and accept that you will make mistakes. The point is to find them early, before they make it into production. Fortunately, except for the few of us developing rocket guidance software at JPL, mistakes are rarely fatal in our industry, so we can, and should, learn, laugh, and move on.&lt;/li&gt;
&lt;li&gt;You are not your code. Remember that the entire point of a review is to find problems, and problems will be found. Don&amp;rsquo;t take it personally when one is uncovered.&lt;/li&gt;
&lt;li&gt;No matter how much &amp;ldquo;karate&amp;rdquo; you know, someone else will always know more. Such an individual can teach you some new moves if you ask. Seek and accept input from others, especially when you think it&amp;rsquo;s not needed.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t rewrite code without consultation. There&amp;rsquo;s a fine line between &amp;ldquo;fixing code&amp;rdquo; and &amp;ldquo;rewriting code.&amp;rdquo; Know the difference, and pursue stylistic changes within the framework of a code review, not as a lone enforcer.&lt;/li&gt;
&lt;li&gt;Treat people who know less than you with respect, deference, and patience. Nontechnical people who deal with developers on a regular basis almost universally hold the opinion that we are prima donnas at best and crybabies at worst. Don&amp;rsquo;t reinforce this stereotype with anger and impatience.&lt;/li&gt;
&lt;li&gt;The only constant in the world is change. Be open to it and accept it with a smile. Look at each change to your requirements, platform, or tool as a new challenge, not as some serious inconvenience to be fought.&lt;/li&gt;
&lt;li&gt;The only true authority stems from knowledge, not from position. Knowledge engenders authority, and authority engenders respect—so if you want respect in an egoless environment, cultivate knowledge.&lt;/li&gt;
&lt;li&gt;Fight for what you believe, but gracefully accept defeat. Understand that sometimes your ideas will be overruled. Even if you do turn out to be right, don&amp;rsquo;t take revenge or say, &amp;ldquo;I told you so&amp;rdquo; more than a few times at most, and don&amp;rsquo;t make your dearly departed idea a martyr or rallying cry.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t be &amp;ldquo;the guy in the room.&amp;rdquo; Don&amp;rsquo;t be the guy coding in the dark office emerging only to buy cola. The guy in the room is out of touch, out of sight, and out of control and has no place in an open, collaborative environment.&lt;/li&gt;
&lt;li&gt;Critique code instead of people—be kind to the coder, not to the code.As much as possible, make all of your comments positive and oriented to improving the code. Relate comments to local standards, program specs, increased performance, etc.&lt;/li&gt;
&lt;/ol&gt;</description>
      <content:encoded><![CDATA[<p>I came across the <a
	
		href = "http://articles.techrepublic.com.com/5100-10878_11-1045782.html"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Ten commandments of Programming
	</span>
</a> while looking at a <a
	
		href = "http://stackoverflow.com/questions/2281584/how-to-become-a-software-engineer"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		question on StackOverflow
	</span>
</a> and I can&rsquo;t believe I have not seen these before. I think every developer, lead, architect, dba, pm, whoever should print this out! 8-)</p>
<ol>
<li>Understand and accept that you will make mistakes. The point is to find them early, before they make it into production. Fortunately, except for the few of us developing rocket guidance software at JPL, mistakes are rarely fatal in our industry, so we can, and should, learn, laugh, and move on.</li>
<li>You are not your code. Remember that the entire point of a review is to find problems, and problems will be found. Don&rsquo;t take it personally when one is uncovered.</li>
<li>No matter how much &ldquo;karate&rdquo; you know, someone else will always know more. Such an individual can teach you some new moves if you ask. Seek and accept input from others, especially when you think it&rsquo;s not needed.</li>
<li>Don&rsquo;t rewrite code without consultation. There&rsquo;s a fine line between &ldquo;fixing code&rdquo; and &ldquo;rewriting code.&rdquo; Know the difference, and pursue stylistic changes within the framework of a code review, not as a lone enforcer.</li>
<li>Treat people who know less than you with respect, deference, and patience. Nontechnical people who deal with developers on a regular basis almost universally hold the opinion that we are prima donnas at best and crybabies at worst. Don&rsquo;t reinforce this stereotype with anger and impatience.</li>
<li>The only constant in the world is change. Be open to it and accept it with a smile. Look at each change to your requirements, platform, or tool as a new challenge, not as some serious inconvenience to be fought.</li>
<li>The only true authority stems from knowledge, not from position. Knowledge engenders authority, and authority engenders respect—so if you want respect in an egoless environment, cultivate knowledge.</li>
<li>Fight for what you believe, but gracefully accept defeat. Understand that sometimes your ideas will be overruled. Even if you do turn out to be right, don&rsquo;t take revenge or say, &ldquo;I told you so&rdquo; more than a few times at most, and don&rsquo;t make your dearly departed idea a martyr or rallying cry.</li>
<li>Don&rsquo;t be &ldquo;the guy in the room.&rdquo; Don&rsquo;t be the guy coding in the dark office emerging only to buy cola. The guy in the room is out of touch, out of sight, and out of control and has no place in an open, collaborative environment.</li>
<li>Critique code instead of people—be kind to the coder, not to the code.As much as possible, make all of your comments positive and oriented to improving the code. Relate comments to local standards, program specs, increased performance, etc.</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Copying strings in C&#43;&#43;</title>
      <link>/post/2010/01/copying-strings-in-cpp/</link>
      <pubDate>Sat, 23 Jan 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/01/copying-strings-in-cpp/</guid>
      <description>&lt;p&gt;Here is a good example on why either you love C++ or hate it with such terse expression oriented code; I think its pretty cool.&lt;/p&gt;
&lt;p&gt;If you want to copy one string to another, one option can be something like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt;6&lt;/a&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ed8796&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#8aadf4&#34;&gt;mycopy&lt;/span&gt;(&lt;span style=&#34;color:#ed8796&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;p, &lt;span style=&#34;color:#ed8796&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;q) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ed8796&#34;&gt;int&lt;/span&gt; len &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; strlen(q);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#c6a0f6&#34;&gt;for&lt;/span&gt;(&lt;span style=&#34;color:#ed8796&#34;&gt;int&lt;/span&gt; i&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#f5a97f&#34;&gt;0&lt;/span&gt;; i&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;lt;&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt;len; i&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p[i] &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; q[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;However this achieves the same thing as above and is more efficient:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here is a good example on why either you love C++ or hate it with such terse expression oriented code; I think its pretty cool.</p>
<p>If you want to copy one string to another, one option can be something like this.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#ed8796">void</span> <span style="color:#8aadf4">mycopy</span>(<span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>p, <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>q) {
</span></span><span style="display:flex;"><span>    <span style="color:#ed8796">int</span> len <span style="color:#91d7e3;font-weight:bold">=</span> strlen(q);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">for</span>(<span style="color:#ed8796">int</span> i<span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">0</span>; i<span style="color:#91d7e3;font-weight:bold">&amp;</span>lt;<span style="color:#91d7e3;font-weight:bold">=</span>len; i<span style="color:#91d7e3;font-weight:bold">++</span>)
</span></span><span style="display:flex;"><span>        p[i] <span style="color:#91d7e3;font-weight:bold">=</span> q[i];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>However this achieves the same thing as above and is more efficient:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#ed8796">void</span> <span style="color:#8aadf4">mycopy</span>(<span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>p, <span style="color:#ed8796">char</span> <span style="color:#91d7e3;font-weight:bold">*</span>q) {
</span></span><span style="display:flex;"><span>    <span style="color:#c6a0f6">while</span>(<span style="color:#91d7e3;font-weight:bold">*</span>p<span style="color:#91d7e3;font-weight:bold">++</span> <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3;font-weight:bold">*</span>q<span style="color:#91d7e3;font-weight:bold">++</span>);
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Of course why would you write your own version when you have standard string copy fundtion <a
	
		href = "http://www.cplusplus.com/reference/clibrary/cstring/strcpy/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		strcpy
	</span>
</a> in <code>&lt;string.h&gt;</code></p>
]]></content:encoded>
    </item>
    <item>
      <title>‘QPainter painter’ has initialiser but incomplete type</title>
      <link>/post/2010/01/qpainter-painter-has-initialiser-but-incomplete-type/</link>
      <pubDate>Sun, 10 Jan 2010 00:00:00 +0000</pubDate>
      <guid>/post/2010/01/qpainter-painter-has-initialiser-but-incomplete-type/</guid>
      <description>&lt;p&gt;If you ever got an error something like [some-class] has initialiser but incomplete type, it basically means the compiler cannot understand the type and you need to add the include for it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt;4&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt;5&lt;/a&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;QPixmap &lt;span style=&#34;color:#8aadf4&#34;&gt;pixmap&lt;/span&gt;(&lt;span style=&#34;color:#f5a97f&#34;&gt;20&lt;/span&gt;,&lt;span style=&#34;color:#f5a97f&#34;&gt;10&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pixmap.fill(Qt&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;white);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;QPainter &lt;span style=&#34;color:#8aadf4&#34;&gt;painter&lt;/span&gt;(&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;pixmap);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;QPen &lt;span style=&#34;color:#8aadf4&#34;&gt;pen&lt;/span&gt;(Qt&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;blue);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Take the code snipped above when you compile it you might get an error something along the lines of the following for line 4.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you ever got an error something like [some-class] has initialiser but incomplete type, it basically means the compiler cannot understand the type and you need to add the include for it.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>QPixmap <span style="color:#8aadf4">pixmap</span>(<span style="color:#f5a97f">20</span>,<span style="color:#f5a97f">10</span>);
</span></span><span style="display:flex;"><span>pixmap.fill(Qt<span style="color:#91d7e3;font-weight:bold">::</span>white);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>QPainter <span style="color:#8aadf4">painter</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>pixmap);
</span></span><span style="display:flex;"><span>QPen <span style="color:#8aadf4">pen</span>(Qt<span style="color:#91d7e3;font-weight:bold">::</span>blue);</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Take the code snipped above when you compile it you might get an error something along the lines of the following for line 4.</p>
<p><code>‘QPainter painter’ has initialiser but incomplete type</code></p>
<p>To fix this you need to include the header file where QPainter is defined. The updated code looks like:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6">6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7">7</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#include</span> <span style="color:#6e738d;font-weight:bold;font-style:italic">&lt;qpainter.h&gt;</span><span style="color:#6e738d;font-style:italic">
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span> 
</span></span><span style="display:flex;"><span>QPixmap <span style="color:#8aadf4">pixmap</span>(<span style="color:#f5a97f">20</span>,<span style="color:#f5a97f">10</span>);
</span></span><span style="display:flex;"><span>pixmap.fill(Qt<span style="color:#91d7e3;font-weight:bold">::</span>white);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>QPainter <span style="color:#8aadf4">painter</span>(<span style="color:#91d7e3;font-weight:bold">&amp;</span>pixmap);
</span></span><span style="display:flex;"><span>QPen <span style="color:#8aadf4">pen</span>(Qt<span style="color:#91d7e3;font-weight:bold">::</span>blue);</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Making sense of primary-expression error</title>
      <link>/post/2009/12/making-sense-of-primary-expression-error/</link>
      <pubDate>Tue, 22 Dec 2009 00:00:00 +0000</pubDate>
      <guid>/post/2009/12/making-sense-of-primary-expression-error/</guid>
      <description>&lt;p&gt;If you are new to C++ then some of the compiler errors can be a bit confusing and daunting at first. For example if the compiler given you an expected primary-expression error (e.g. expected primary-expression before ‘.’ token), at face value it does not make sense.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34; id=&#34;15&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#15&#34;&gt;15&lt;/a&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ed8796&#34;&gt;void&lt;/span&gt; RGBViewer&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;accept() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    QMessageBox&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;information(&lt;span style=&#34;color:#c6a0f6&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;Button Clicked&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;You clicked OK&amp;#34;&lt;/span&gt;, QMessageBox&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;Ok);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    QColor color &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; QColor(Ui_RGBViewerClass.spinBoxRed&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;value(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Ui_RGBViewerClass.spinBoxGreen&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;value(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            Ui_RGBViewerClass.spinBoxBlue&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;value(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f5a97f&#34;&gt;255&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    qDebug() &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; color;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    QBrush &lt;span style=&#34;color:#8aadf4&#34;&gt;brush&lt;/span&gt;(color);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    brush.setStyle(Qt&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;::&lt;/span&gt;SolidPattern);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ui.graphicsView&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;setBackgroundBrush(brush);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ui.graphicsView&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;-&amp;gt;&lt;/span&gt;backgroundBrush();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When I compile the above code snippet, I get the following error from the compiler:&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>If you are new to C++ then some of the compiler errors can be a bit confusing and daunting at first. For example if the compiler given you an expected primary-expression error (e.g. expected primary-expression before ‘.’ token), at face value it does not make sense.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#ed8796">void</span> RGBViewer<span style="color:#91d7e3;font-weight:bold">::</span>accept() {
</span></span><span style="display:flex;"><span>    QMessageBox<span style="color:#91d7e3;font-weight:bold">::</span>information(<span style="color:#c6a0f6">this</span>, <span style="color:#a6da95">&#34;Button Clicked&#34;</span>, <span style="color:#a6da95">&#34;You clicked OK&#34;</span>, QMessageBox<span style="color:#91d7e3;font-weight:bold">::</span>Ok);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    QColor color <span style="color:#91d7e3;font-weight:bold">=</span> QColor(Ui_RGBViewerClass.spinBoxRed<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            Ui_RGBViewerClass.spinBoxGreen<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            Ui_RGBViewerClass.spinBoxBlue<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            <span style="color:#f5a97f">255</span>);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    qDebug() <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> color;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    QBrush <span style="color:#8aadf4">brush</span>(color);
</span></span><span style="display:flex;"><span>    brush.setStyle(Qt<span style="color:#91d7e3;font-weight:bold">::</span>SolidPattern);
</span></span><span style="display:flex;"><span>    ui.graphicsView<span style="color:#91d7e3;font-weight:bold">-&gt;</span>setBackgroundBrush(brush);
</span></span><span style="display:flex;"><span>    ui.graphicsView<span style="color:#91d7e3;font-weight:bold">-&gt;</span>backgroundBrush();
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
<p>When I compile the above code snippet, I get the following error from the compiler:</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1">1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2">2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3">3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4">4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5">5</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>rgbviewer.<span style="color:#91d7e3">cpp</span>: In member function <span style="color:#ed8796">‘</span><span style="color:#c6a0f6">virtual</span> <span style="color:#ed8796">void</span> RGBViewer<span style="color:#91d7e3;font-weight:bold">::</span>accept()<span style="color:#ed8796">’</span><span style="color:#91d7e3;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>rgbviewer.<span style="color:#91d7e3">cpp</span>:<span style="color:#f5a97f">19</span><span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#91d7e3">error</span>: expected primary<span style="color:#91d7e3;font-weight:bold">-</span>expression before <span style="color:#ed8796">‘</span>(<span style="color:#ed8796">’</span> token
</span></span><span style="display:flex;"><span>rgbviewer.<span style="color:#91d7e3">cpp</span>:<span style="color:#f5a97f">19</span><span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#91d7e3">error</span>: expected primary<span style="color:#91d7e3;font-weight:bold">-</span>expression before <span style="color:#ed8796">‘</span>.<span style="color:#ed8796">’</span> token
</span></span><span style="display:flex;"><span>rgbviewer.<span style="color:#91d7e3">cpp</span>:<span style="color:#f5a97f">20</span><span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#91d7e3">error</span>: expected primary<span style="color:#91d7e3;font-weight:bold">-</span>expression before <span style="color:#ed8796">‘</span>.<span style="color:#ed8796">’</span> token
</span></span><span style="display:flex;"><span>rgbviewer.<span style="color:#91d7e3">cpp</span>:<span style="color:#f5a97f">21</span><span style="color:#91d7e3;font-weight:bold">:</span> <span style="color:#91d7e3">error</span>: expected primary<span style="color:#91d7e3;font-weight:bold">-</span>expression before <span style="color:#ed8796">‘</span>.<span style="color:#ed8796">’</span> token</span></span></code></pre></td></tr></table>
</div>
</div>
<p>All this means is that I need to initialize the type &lsquo;Ui_RGBViewerClass&rsquo; as the compiler does not understand it. In other words, I need to use a new.</p>
<p>Here is the &lsquo;fixed&rsquo; version below of the same code snippet.</p>
<div class="highlight"><div style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="1"><a style="outline:none;text-decoration:none;color:inherit" href="#1"> 1</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="2"><a style="outline:none;text-decoration:none;color:inherit" href="#2"> 2</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="3"><a style="outline:none;text-decoration:none;color:inherit" href="#3"> 3</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="4"><a style="outline:none;text-decoration:none;color:inherit" href="#4"> 4</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="5"><a style="outline:none;text-decoration:none;color:inherit" href="#5"> 5</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="6"><a style="outline:none;text-decoration:none;color:inherit" href="#6"> 6</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="7"><a style="outline:none;text-decoration:none;color:inherit" href="#7"> 7</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="8"><a style="outline:none;text-decoration:none;color:inherit" href="#8"> 8</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="9"><a style="outline:none;text-decoration:none;color:inherit" href="#9"> 9</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="10"><a style="outline:none;text-decoration:none;color:inherit" href="#10">10</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="11"><a style="outline:none;text-decoration:none;color:inherit" href="#11">11</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="12"><a style="outline:none;text-decoration:none;color:inherit" href="#12">12</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="13"><a style="outline:none;text-decoration:none;color:inherit" href="#13">13</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="14"><a style="outline:none;text-decoration:none;color:inherit" href="#14">14</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="15"><a style="outline:none;text-decoration:none;color:inherit" href="#15">15</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="16"><a style="outline:none;text-decoration:none;color:inherit" href="#16">16</a>
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2" id="17"><a style="outline:none;text-decoration:none;color:inherit" href="#17">17</a>
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Ui<span style="color:#91d7e3;font-weight:bold">::</span>RGBViewerClass ui; <span style="color:#6e738d;font-style:italic">//Defined in the header; shown here for completness
</span></span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"></span> 
</span></span><span style="display:flex;"><span><span style="color:#ed8796">void</span> RGBViewer<span style="color:#91d7e3;font-weight:bold">::</span>accept() {
</span></span><span style="display:flex;"><span>    QMessageBox<span style="color:#91d7e3;font-weight:bold">::</span>information(<span style="color:#c6a0f6">this</span>, <span style="color:#a6da95">&#34;Button Clicked&#34;</span>, <span style="color:#a6da95">&#34;You clicked OK&#34;</span>, QMessageBox<span style="color:#91d7e3;font-weight:bold">::</span>Ok);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    QColor color <span style="color:#91d7e3;font-weight:bold">=</span> QColor(ui.spinBoxRed<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            ui.spinBoxGreen<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            ui.spinBoxBlue<span style="color:#91d7e3;font-weight:bold">-&gt;</span>value(),
</span></span><span style="display:flex;"><span>            <span style="color:#f5a97f">255</span>);
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    qDebug() <span style="color:#91d7e3;font-weight:bold">&lt;&lt;</span> color;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span>    QBrush <span style="color:#8aadf4">brush</span>(color);
</span></span><span style="display:flex;"><span>    brush.setStyle(Qt<span style="color:#91d7e3;font-weight:bold">::</span>SolidPattern);
</span></span><span style="display:flex;"><span>    ui.graphicsView<span style="color:#91d7e3;font-weight:bold">-&gt;</span>setBackgroundBrush(brush);
</span></span><span style="display:flex;"><span>    ui.graphicsView<span style="color:#91d7e3;font-weight:bold">-&gt;</span>backgroundBrush();
</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table>
</div>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Sandbox is Up!</title>
      <link>/post/2005/03/sandbox-is-up/</link>
      <pubDate>Mon, 21 Mar 2005 00:00:00 +0000</pubDate>
      <guid>/post/2005/03/sandbox-is-up/</guid>
      <description>&lt;p&gt;As promised earlier, the &lt;a
	
		href = &#34;http://desigeek.com/sandbox/&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		Sanbox
	&lt;/span&gt;
&lt;/a&gt; is up - go give it a whirl and let me know your thoughts. Also the code and release build of the &lt;a
	
		href = &#34;http://desigeek.com/sandbox/WorldClock/index.htm&#34;
	

	

	
		target = &#34;_blank&#34;
		rel = &#34;nofollow noopener noreferrer&#34;
		&gt;
	
	&lt;span&gt;
		World Clock
	&lt;/span&gt;
&lt;/a&gt; are up there. Check it out and let me know what you think of it and also any bugs and feature enhancements you would like to see. &lt;p&gt;

    &lt;img src=&#34;http://desigeek.com/weblog/aggbug.aspx?PostID=638&#34; alt=&#34;&#34;/&gt;

&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>As promised earlier, the <a
	
		href = "http://desigeek.com/sandbox/"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		Sanbox
	</span>
</a> is up - go give it a whirl and let me know your thoughts. Also the code and release build of the <a
	
		href = "http://desigeek.com/sandbox/WorldClock/index.htm"
	

	

	
		target = "_blank"
		rel = "nofollow noopener noreferrer"
		>
	
	<span>
		World Clock
	</span>
</a> are up there. Check it out and let me know what you think of it and also any bugs and feature enhancements you would like to see. <p>

    <img src="http://desigeek.com/weblog/aggbug.aspx?PostID=638" alt=""/>

</p></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
