<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jim Bennett</title>
    <description>The latest articles on Forem by Jim Bennett (@jimbobbennett).</description>
    <link>https://forem.com/jimbobbennett</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F113783%2Fff54a8a6-ef4c-4b98-aa6e-339db4cd548a.jpg</url>
      <title>Forem: Jim Bennett</title>
      <link>https://forem.com/jimbobbennett</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jimbobbennett"/>
    <language>en</language>
    <item>
      <title>Build GitHub Actions faster with act</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Mon, 23 Sep 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/build-github-actions-faster-with-act-56j1</link>
      <guid>https://forem.com/jimbobbennett/build-github-actions-faster-with-act-56j1</guid>
      <description>&lt;p&gt;If I said to you that I made 34 commits to a repo, each one with small changes to a single file, with commit messages like “Hoping this works”, “Please work”, and “For f*cks sake, work this time”, you would know exactly why - I’m setting up a &lt;strong&gt;CI/CD pipeline&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;We’ve all been through this - having to commit our changes to GitHub to test them out as there is no way to do it locally, and ending up with way too many commits because our only feedback loop is to run and check the logs. Compound this with repo permissions where every commit needs to be a PR that is reviewed and approved, and you have way too much work and time spent working on what should be a simple task. If only there was a better way…&lt;/p&gt;

&lt;p&gt;There is! This post is all about &lt;a href="https://nektosact.com/introduction.html" rel="noopener noreferrer"&gt;&lt;strong&gt;act&lt;/strong&gt;&lt;/a&gt;, a tool for running &lt;strong&gt;GitHub Actions&lt;/strong&gt; locally, so you can debug and fix them before committing to your repo. It shows act off using the real world example of the GitHub Action I created for the &lt;a href="https://github.com/pieces-app/pieces-os-client-sdk-for-csharp" rel="noopener noreferrer"&gt;Pieces for Developers C# SDK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is act
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Think globally, &lt;code&gt;act&lt;/code&gt; locally”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The goal of &lt;a href="https://nektosact.com/introduction.html" rel="noopener noreferrer"&gt;&lt;strong&gt;act&lt;/strong&gt;&lt;/a&gt; is to provide a way to spin up a GitHub Action locally, running them in a container as if they were running in GitHub. It can handle environment variables, secrets, using external actions and more, with a file system setup that mimics what GitHub has. And best of all, this is a free, open source project!&lt;/p&gt;

&lt;h3&gt;
  
  
  Install act
&lt;/h3&gt;

&lt;p&gt;Installation of act is pretty simple. It uses &lt;a href="https://www.docker.com" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;, so you need that installed (or a compatible container engine), then after that you can install from your package manager of choice. All the options are listed in &lt;a href="https://nektosact.com/installation/index.html" rel="noopener noreferrer"&gt;the act installation guide&lt;/a&gt;. I’m a mac user, so used &lt;a href="https://formulae.brew.sh/formula/act#default" rel="noopener noreferrer"&gt;homebrew&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install act

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run act
&lt;/h3&gt;

&lt;p&gt;You can run act from the command line. When run, it will run all the jobs in your &lt;code&gt;.github/workflows&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;The first time you run act, it will give you a choice of what type of container you want to run everything in - from huge with everything, to tiny with minimal support for external actions. I chose the middle ground, with a 500MB download (which conveniently enough I was able to do on airplane WiFi).&lt;/p&gt;

&lt;p&gt;You can configure which jobs are run by passing &lt;a href="https://nektosact.com/usage/index.html#events" rel="noopener noreferrer"&gt;events&lt;/a&gt; to the command line. This allows you to simulate a pull request, or a push to a branch. For example, to only run actions triggered by a push, run:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use act to build an action
&lt;/h2&gt;

&lt;p&gt;I recently used act to help me build out a GitHub Action to build and publish the &lt;a href="https://github.com/pieces-app/pieces-os-client-sdk-for-csharp" rel="noopener noreferrer"&gt;Pieces for Developers C# SDK&lt;/a&gt; to nuget. This action needs to do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Be triggered from a new tag&lt;/li&gt;
&lt;li&gt;Install .NET 8 (the current LTS version)&lt;/li&gt;
&lt;li&gt;Compile the SDK with a release build, passing the version from the tag to the build command&lt;/li&gt;
&lt;li&gt;Sign the created nuget package (this is created on build) using a cert and password from the secrets&lt;/li&gt;
&lt;li&gt;Push the package to nuget, using an API key that is a secret&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Use external actions with act
&lt;/h3&gt;

&lt;p&gt;Act has full support for external actions made available with a &lt;code&gt;uses&lt;/code&gt; statement. It will clone the action locally, then run it with docker so that it runs as if it was in a GitHub Action. In my case, this means I can check out my code, and install .NET using the default GitHub &lt;code&gt;actions/setup-dotnet&lt;/code&gt; action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Release

on:
 push:
 tags:
 - '*'

jobs:
 publish-to-nuget:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v4
 - name: Setup .NET
 uses: actions/setup-dotnet@v4
 with:
 dotnet-version: 8.0.*

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Top tip - running an action that installs a large tool like .NET on airplane WiFi is not recommended! Ask me how I know…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use a tag with act
&lt;/h3&gt;

&lt;p&gt;The next step is to build the nuget package. As part of this, I want to set the version from the tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Build
 working-directory: ./src/Client
 run: dotnet build -c Release -p:Version=${GITHUB_REF#refs/tags/v}

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

&lt;/div&gt;



&lt;p&gt;GitHub automatically passes this tag through to the action as a &lt;a href="https://docs.github.com/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables" rel="noopener noreferrer"&gt;default environment variable&lt;/a&gt; when run inside GitHub Actions. With act, I can also set environment variables either by passing them into the command line, or adding them to a .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITHUB_REF=refs/tags/v0.0.7-beta

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

&lt;/div&gt;



&lt;p&gt;This allows me to set the tag used for each run, in this example to &lt;code&gt;refs/tags/v0.0.7-beta&lt;/code&gt;. The syntax of my build command, &lt;code&gt;${GITHUB_REF#refs/tags/v}&lt;/code&gt; strips the &lt;code&gt;refs/tags/v&lt;/code&gt; section, so my build sets the version to &lt;code&gt;0.0.7-beta&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use secrets with act
&lt;/h3&gt;

&lt;p&gt;GitHub Actions supports &lt;a href="https://docs.github.com/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions" rel="noopener noreferrer"&gt;secrets&lt;/a&gt; - special values you can configure as the owner of the repo that are surfaced to your actions, but not visible to anyone looking at the repo. These are great for things like API keys or passwords. In my case I need 3 secrets - a certificate to sign my nuget package, a password for the certificate, and an API key for nuget to show it’s me uploading the package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Get the nuget signing certificate
 id: cert_file
 uses: timheuer/base64-to-file@v1.2
 with:
 fileName: 'certfile.pfx'
 encodedString: ${{ secrets.NUGET_CERTIFICATE }}
- name: Sign the nuget package
 working-directory: ./src/Client
 run: |
 dotnet nuget sign ./bin/Release/Pieces.OS.Client.${GITHUB_REF#refs/tags/v}.nupkg
 --certificate-path /tmp/certfile.pfx
 --certificate-password ${{ secrets.NUGET_CERTIFICATE_PASSWORD }}
 --timestamper http://timestamp.digicert.com
- name: Push to NuGet
 working-directory: ./src/Client
 run: |
 dotnet nuget push ./bin/Release/*.nupkg -k ${{ secrets.NUGET_API_KEY }}
 -s https://nuget.org

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

&lt;/div&gt;



&lt;p&gt;With act, you can &lt;a href="https://nektosact.com/usage/index.html#secrets" rel="noopener noreferrer"&gt;pass secrets using a &lt;code&gt;.secrets&lt;/code&gt; file&lt;/a&gt;. This is similar to the &lt;code&gt;.env&lt;/code&gt; file, just with your secrets in it. I created a local &lt;code&gt;.secrets&lt;/code&gt; file with the secrets I needed.&lt;/p&gt;

&lt;p&gt;For the signing certificate, I need a &lt;code&gt;.pfx&lt;/code&gt; file, so my way to do this is to encode the contents of the file in base64, upload that as a secret, then use the &lt;code&gt;timheuer/base64-to-file@v1.2&lt;/code&gt; action to convert that secret to a file on the local file system. More details in &lt;a href="https://www.timheuer.com/blog/use-nuget-with-github-actions-github-packages/" rel="noopener noreferrer"&gt;this blog post from Tim&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NUGET_API_KEY=&amp;lt;key&amp;gt;
NUGET_CERTIFICATE_PASSWORD=&amp;lt;password&amp;gt;
NUGET_CERTIFICATE=&amp;lt;base64 encoded cert&amp;gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Always add the &lt;code&gt;.secrets&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; to avoid accidentally exposing them by adding this file to your repo!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test it out
&lt;/h2&gt;

&lt;p&gt;With my environment variables and secrets all set up, I was able to test out my action. As always, things failed with each run, but I was able to iterate locally without needing to push my action, raise a PR, get it reviewed and approved, merge it, then tag each time.&lt;/p&gt;

&lt;p&gt;The kind of errors I hit were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not setting the working directory&lt;/li&gt;
&lt;li&gt;Setting the version of the build wrong&lt;/li&gt;
&lt;li&gt;Getting the path of the certificate file wrong&lt;/li&gt;
&lt;li&gt;Inconsistent case of &lt;code&gt;release&lt;/code&gt; and &lt;code&gt;Release&lt;/code&gt; for the output folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these are easy mistakes to make when you can’t run locally, and would normally take multiple fixes. All fixed locally before I committed my code.&lt;/p&gt;

&lt;p&gt;The action ran as if I was inside a GitHub Action - not only did it actually check out my code and build it, it also signed the nuget package and pushed it to the nuget repo. I could see the result with the new package visible inside nuget. This gave me the confidence to raise my PR.&lt;/p&gt;

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

&lt;p&gt;If you are using GitHub Actions, act is an invaluable tool to help you develop your actions and test them locally. Check it out at &lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;github.com/nektos/act&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>cicd</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
    <item>
      <title>Circles of DevRel part 1 - grow your circle of influence through collaboration</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Tue, 20 Aug 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/circles-of-devrel-part-1-grow-your-circle-of-influence-through-collaboration-o00</link>
      <guid>https://forem.com/jimbobbennett/circles-of-devrel-part-1-grow-your-circle-of-influence-through-collaboration-o00</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is the first part of a multipart blog post focusing on looking at DevRel through the idea of circles of influence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As &lt;a href="https://dev.to/jimbobbennett/so-what-exactly-is-dev-rel-1g5e-temp-slug-1442456"&gt;dev rel people&lt;/a&gt;, part of our job is to drive awareness of our product. If you think in terms of sales/marketing, this is feeding the top of the funnel. If you think in terms of the &lt;a href="https://www.devrel.agency/developerjourney" rel="noopener noreferrer"&gt;developer journey&lt;/a&gt;, this is the discover phase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pt3un0lai0ibayekjjr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pt3un0lai0ibayekjjr.jpeg" alt="A sales funnel - awareness, interest, decision, action" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju7pp4gaazt9zisle5zn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju7pp4gaazt9zisle5zn.jpeg" alt="The developer journey - discover, evaluate, learn, build, scale" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However you look at it, it is about helping developers to discover your product, inspiring them to evaluate it, learn more, and ultimately use it in their day to day.&lt;/p&gt;

&lt;p&gt;This post focuses on being better at driving awareness by considering circles of influence, and looking at how you can collaborate with others to drive more awareness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Successfully driving awareness
&lt;/h2&gt;

&lt;p&gt;What does success look like when driving awareness? I’m sure there are many opinions on this, but mine is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can I make a &lt;strong&gt;new&lt;/strong&gt; audience aware of product/feature/thing?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Emphasis on the &lt;strong&gt;new&lt;/strong&gt; part. To be successful we need to focus on that new. This can be completely new people who are not aware of your product, or existing customers who are not aware of feature X, or new product Y.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev Rel circle of influence
&lt;/h2&gt;

&lt;p&gt;As dev rel people, we all have what I think of as a &lt;em&gt;circle of influence&lt;/em&gt;, either personally or on our company properties. This might be our social followers, the subscribers to our newsletter, readers of our employers blog, or folks who follow us in other ways. Although we want to grow this circle, in general this stays reasonably static with steady growth as we do more activities.&lt;/p&gt;

&lt;p&gt;Each of these content and social locations has a circle of influence, and they all wrap up to your one overarching circle of influence, encompassing personal and company circles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusedf2eg9jwpjet01vit.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusedf2eg9jwpjet01vit.jpeg" alt="One large circle of influence made of multiple smaller circles based on different audiences" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we just use these same circles to promote our thing, then we are at risk of preaching to the converted. It’s hard to get net new signups by tweeting to the same circle of people, once we reach saturation there is no more potential customers to reach.&lt;/p&gt;

&lt;p&gt;Although it can be good to reach the same circle, for example to keep customers updated on new features, or support community heroes, we risk the echo chamber. The same people attend your livestream and you are spending hours with no quantifiable return on your time as you are not driving awareness. The same people attend your Twitter space, or watch your talk, or read your blog.&lt;/p&gt;

&lt;p&gt;To drive more awareness, you need to grow that audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grow your audience
&lt;/h2&gt;

&lt;p&gt;To get more return on your efforts and grow that new audience, you need to do one of 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grow your circle of influence&lt;/li&gt;
&lt;li&gt;Overlap with someone else’s circle of influence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first part is outside the scope of this post, the rest of this post focuses on the second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overlap with another circle of influence
&lt;/h2&gt;

&lt;p&gt;The easiest way to two your circle of influence is to leverage the connections you already have to create collaborative content. By that I mean build new content with someone else who has a circle of influence, that way your content reaches their circle as well as yours.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Collaboration on content
&lt;/h3&gt;

&lt;p&gt;As dev rel people, we create content. Some of this content is directly about our product, but very few products are used completely in isolation. Instead we integrate with others. Sometimes directly - using a web framework on a site deployed to a hosting provider, sometimes indirectly - using AI dev tool to write code to talk to a service.&lt;/p&gt;

&lt;p&gt;It’s very normal for us to create this kind of content, so the obvious thing to do is to do this in collaboration with a dev rel for that integration. This is a win-win situation. You both have content that is relevant to your product, it’s been easier to create as 2 of you are working on it, and most importantly this overlaps your circles of influence.&lt;/p&gt;

&lt;p&gt;For example, at Pieces we have an AI tool that leverages a range of context such as code snippets. If I wanted to write code using a specific cloud service, chances are I have a range of code snippets saved, such as the code to save an object into storage, or post a message to a queue.&lt;/p&gt;

&lt;p&gt;If I write a blog post about ‘using Pieces to manage code snippets to interact with cloud service’, not only does it show a real world use case for Pieces, but it also shows a way to interact with the cloud service easier. This is great content for me and can reach my circle of influence when published to my audience. if however, I collaborate on this with a dev rel from the cloud service, the content will then reach their circle of influence as well.&lt;/p&gt;

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

&lt;p&gt;Suddenly they reach my circle of influence, and I reach theirs. This increases the reach of the content, and drives that awareness we want towards new developers. And importantly this is a mutually beneficial relationship. I’m not taking advantage of someone else’s influence, we are sharing our reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Venn diagram of circles of influence
&lt;/h3&gt;

&lt;p&gt;When we think of collaboration, we need to consider 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is there a logical content overlap that tells a good story?&lt;/li&gt;
&lt;li&gt;Do we have a sufficiently unique circle of influence?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first point refers to the content. Is it worth us collaborating? If there is no logical story, then no. A blog post on using a flutter UI component with a k8s management pipeline makes no sense. This content won’t resonate with our audience. Instead we want to collaborate with people who are likely to have an interest in using both products. Using Pieces with a popular cloud tool for example, lots of Pieces users want to use various cloud tools, and cloud tool users may want to manage code snippets and can benefit from Pieces. For collaborative content to work there needs to be overlap in the needs, desires, or tech stacks of the audience.&lt;/p&gt;

&lt;p&gt;On the other hand, we don’t want our circles of influence to overlap too much. If the Venn diagram of my circle of influence and my collaborators is a circle, then we fail in our goal of reaching new developers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1uw9l29pjdammce4z4h.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1uw9l29pjdammce4z4h.jpeg" alt="A Venn diagram that’s actually a circle" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do you ensure this? I don’t have a magic formula, it’s all gut feel, but sometimes just looking at the number of mutuals on social can be enough. Harder with things like corporate blog reach, so a lot is finger in the air guess work. The best way is to try it, track metrics, and see if there is net new growth. If you collaborate with multiple people you should be able to measure from new sign ups what topics work well and double down on those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fairness of contributions
&lt;/h2&gt;

&lt;p&gt;Now this benefit is not always equal - sometimes the collaborator has a much bigger reach, or a much smaller reach. Personally I believe in sharing the love, so the way you ‘pay back’ working with someone with a much larger audience is by working with someone else with a smaller audience. We are a community of dev rel people, so we help the community without transactionality. If you work for a very small, new, growing company then you don’t have much reach to bring to a FAANG dev rel for example, but pay it back later. Collaborate now, then as you grow, support the next generation of dev rels with small circles of influence.&lt;/p&gt;

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

&lt;p&gt;We all have a circle of influence. To avoid constantly speaking to the same people, consider how collaborations can increase the circle of influence of both you and your collaborators.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>So what exactly is Dev Rel?</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Thu, 25 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/so-what-exactly-is-dev-rel-30ee</link>
      <guid>https://forem.com/jimbobbennett/so-what-exactly-is-dev-rel-30ee</guid>
      <description>&lt;p&gt;I work in developer relations - usually abbreviated to dev rel. Despite this being a popular field, with a lot of companies hiring dev rel folks such as developer advocates, there is still a lot of confusion around what developer relations actually is. In this post, I want to give my opinion on what it is, how it can contribute to your companies success, and also a little bit of what it isn’t.&lt;/p&gt;

&lt;p&gt;Before we dive in, I do want to note that dev rel can be many, many things. Although there are similarities in what dev rel folks do, I think &lt;a href="https://twitter.com/kelseyhightower" rel="noopener noreferrer"&gt;Kelsey Hightower&lt;/a&gt; sums it up best:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1742380457801711678-952" src="https://platform.twitter.com/embed/Tweet.html?id=1742380457801711678"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1742380457801711678-952');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1742380457801711678&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Developer relations is about building relations with developers, and covers a multitude of roles. I’ve written this post more from the perspective of developer advocacy, but I do give a call out to the rest of the folks who help build relationships with developers later in this post.&lt;/p&gt;

&lt;p&gt;So let’s dig in to this fun topic! This is a long post and a bit of a brain dump, so please share feedback! Oh, and I won’t be covering metrics, that is a &lt;a href="https://www.amazon.com/Business-Value-Developer-Relations-Communities/dp/1484237471/ref=sr_1_1" rel="noopener noreferrer"&gt;book in itself&lt;/a&gt;…&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I describe my job
&lt;/h2&gt;

&lt;p&gt;One lesson I’ve learned from famed tech storyteller &lt;a href="https://twitter.com/docjamesw" rel="noopener noreferrer"&gt;James Whittaker&lt;/a&gt; is to have a story that describes what you do. This is currently mine:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I help developers be successful with Pieces for developers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Prior to my current role, I was at Microsoft, where it was ‘I help developers be successful with microsoft technologies’&lt;/p&gt;

&lt;p&gt;This is a very simple statement but a lot to unpack, and I feel it sums up developer relations in very broad strokes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I &lt;strong&gt;help developers&lt;/strong&gt; be &lt;strong&gt;successful&lt;/strong&gt; with &lt;strong&gt;Pieces for developers&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I help developers&lt;/strong&gt; - The core of developer relations is helping. I used to work for &lt;a href="https://twitter.com/chadfowler" rel="noopener noreferrer"&gt;Chad Fowler&lt;/a&gt; at Microsoft, and he gave a guiding principle for the team that I still follow today. “Help first, sell last”. Dev rel focuses on &lt;strong&gt;helping&lt;/strong&gt; developers. By &lt;strong&gt;helping&lt;/strong&gt; we can have an authentic relationship as we are here to help, not to convince you to buy anything. By &lt;strong&gt;helping&lt;/strong&gt; we can solve your problems and make you more productive, or help you ship what you need. Sell last, not because sales are not important - they are, I like getting paid as Lego is expensive, but sales will happen because the product you are advocating for solves their needs and they or their company decide to buy or expand their usage. I’ll come back to sales later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;be successful&lt;/strong&gt; - Developers often don’t often care about your product or company. Some do (and these are your source of community heros), but most want to build the thing and go home. Anything you can do to help this happen is a good thing. Sometimes this is awareness of your product and how it can help their particular case, other times it’s content to show them how to do the thing, be it docs, videos, tutorials or other types of content. Sometimes it’s hand-holding where you work one-on-one with the developer. As a dev rel, your content helps them be successful so they can finish their work and go home.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;with Pieces for developers&lt;/strong&gt; - Insert your company or product here. At the end of the day you work for someone, they are paying the bills, and you need developers to use their technology so you can get paid. If they are successful with your technology, your company gets paid and you get paid. Sometimes the &lt;strong&gt;successful with&lt;/strong&gt; part is just using your technology, but most of the time it is integrating your technology with another technology. For example, Pieces for developers is an AI research and coding assistant, and is most powerful integrated into the IDE and browser you use on a daily basis, so developers can research, collaborate, and code with AI assistance, build what they need quickly, and, yes, go home.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What does ‘be successful’ look like?
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier - developers want to do their job and go home, so being successful is about empowering developers to do just that by using your tool or product. To achieve this success, they need to take a number of steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Discover&lt;/li&gt;
&lt;li&gt;Evaluate&lt;/li&gt;
&lt;li&gt;Learn&lt;/li&gt;
&lt;li&gt;Build&lt;/li&gt;
&lt;li&gt;Scale&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This list is taken from &lt;a href="https://www.devrel.agency/developerjourney" rel="noopener noreferrer"&gt;the Developer Journey&lt;/a&gt; from &lt;a href="https://www.devrel.agency/" rel="noopener noreferrer"&gt;DevRel.Agency&lt;/a&gt; which is an excellent resource for planning your dev rel strategy. I like to split this list into 2 sections - outbound and inbound. Advocating to, and advocating for. Discover and to some degree Evaluate are about outbound advocacy, you enable these by advocating to the developer. For sales and marketing folks, this is the top of the funnel. Evaluate, Learn, Build and Scale are more focused on inbound advocacy, or advocating for the developer. This is the middle to the bottom of the funnel.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outbound advocacy/Advocating to - providing a mix of first and third party content to drive awareness. This is how you help your company be successful.&lt;/li&gt;
&lt;li&gt;Inbound advocacy/Advocating for - providing first party content to help the developer learn, and providing product feedback to help the product be successful. This is how you help your users be successful.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;In a perfect world you go where developers are (Discover), and bring them back to you (Evaluate, Learn, Build, Scale).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At every step you will lose developers, just like a typical sales funnel. Your goal as dev rel is to ensure that as much is in place as possible to avoid developers falling out of the funnel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discover
&lt;/h3&gt;

&lt;p&gt;To be successful with your product, developers need to be aware of it! Put simply if they don’t know about your product, they will never use it. This is your job as dev rel to raise awareness. You do this by going where developers are, and raising awareness of your product.&lt;/p&gt;

&lt;p&gt;Now who are these ‘developers’? A lot of that depends on your product. Ideally you should define one or more ‘ideal customer profiles’ or ICPs that provide a rough summary of who your users might be. You can then target content to these.&lt;/p&gt;

&lt;p&gt;For smaller companies, this awareness is around both product and company. For larger companies, this awareness is usually around product. For example, with Pieces for developers I have to raise awareness of the company and the product. At Microsoft I was raising awareness of individual products or product areas - after all, most developers have heard of Microsoft.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Going where developers are, and raising awareness of your product&lt;/strong&gt; - again, a ‘simple` sentence that has a lot of meaning.&lt;/p&gt;

&lt;h4&gt;
  
  
  Go where developers are
&lt;/h4&gt;

&lt;p&gt;Developers will not come to your docs, blog, LinkedIn page or Discord server if they don’t know about you. You need to share your message in places where those developers are. There are a lot of places, some where developers are seeking answers, some where discovery is more organic. The main thing here though is to be authentic! Help first, sell last.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Conferences - Developers come to these to learn, often with specific problems in mind from their job. The reach is small, but you can have deeper conversations, learn what their problems or use cases are, and show how your product can help. Conferences are &lt;em&gt;very&lt;/em&gt; expensive when you count travel, time and sponsorship, but can lead to some big deals if, and only if, you meet the right people there. The return on investment (ROI) can vary dramatically. It’s also hard to measure the ROI as someone who you didn’t track as a lead may be responsible for a huge sale!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Workshops - Workshops are an interesting way to reach developers. Often these take up a large amount of a developers time, or even cost money. If a developer is wiling to make that kind of commitment, then they are interested in solving a problem that your product might help with. Small turn out, but very engaged.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meetups and smaller community events - These are events usually with a technology focus. If your product fits into that technology (such as talking about using Pieces with Jupyter notebooks at a Python meetup), they can be a relatively cheap and low lift way to put your message to the audience. In person meetups might come with travel cost and maybe pizza and drinks, remote ones can be done from home. The audiences are typically small, and the ROI can vary. Meetup organizers are always looking for speakers - they are the commodity in the shortest supply, so it is relatively easy to get a speaking slot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Podcasts and live streams - There is a wealth of podcasts and livestreams out there, some focusing on specific technologies, some very general. The hosts of these shows are always on the lookout for new guests and in return let you promote your product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blog posts - Although your first party blog might be ok for organic search, third party blogs are a great place to put content. They likely have a large readership, and allow you to put your message across to their audience. I can cross post to a popular tech blog from a smaller company blog and get 100x or even 1000x views in a few days!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tech communities - If you are a large company, you may already have a first party tech community that you can promote your product to. For smaller companies there are third party communities you can reach out to. Get involved, show how your product can help, and that can drive awareness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Organic search - There are a huge number of &lt;a href="https://www.hanselman.com/blog/dark-matter-developers-the-unseen-99" rel="noopener noreferrer"&gt;dark matter developers&lt;/a&gt; who will not see your product at conferences or communities, instead they will search for a solution to their problem. This is where good web properties with good SEO can help.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Raise awareness
&lt;/h4&gt;

&lt;p&gt;To raise awareness of your product you have to help first, sell last. What I mean by this is when you go where developers are, they have problems to solve so they can go home. You have to ensure the message you have is about how your product can solve their problem. This is the focus of your content, and where storytelling is important. Tell the stories of how your product can solve similar problems and you will hook the developer in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bring an authentic message - Your product is the hero in a story starring the developer who is trying to defeat the monster that is a problem. The developer wants to fight the ogre in the swamp that is manually managing code snippets and context across their IDE, research in their browser, and conversations in their collaboration tools, and on their journey to the ogre’s lair discovers Pieces for Developers which gives them the magic to do this. OK - not a talk you would give, but linking it back to the classic storytelling - a protagonist, an antagonist, a journey, a &lt;a href="https://en.wikipedia.org/wiki/MacGuffin" rel="noopener noreferrer"&gt;MacGuffin&lt;/a&gt;, and a happy ending. Your product is the MacGuffin, your content is the guide on the journey.&lt;/li&gt;
&lt;li&gt;Know your audience - The most important thing about content creation is knowing your audience. If you are reaching out to a Python community, then don’t show off a TypeScript SDK. Talking about API governance at a conference focusing on UI design is a waste of time. Pick the places where developers are who your product can help.&lt;/li&gt;
&lt;li&gt;Reuse content - Content creation is hard and time consuming. Always think about how you can re-use content to get more mileage. You have a tutorial on using yor product with AI? Can you make a workshop, video or conference talk out of this? You are going to a conference to give a talk? Are there any meetups local to the conference where you can give the same talk at?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Evaluate
&lt;/h3&gt;

&lt;p&gt;Once developers are aware of your product, they want to evaluate it. This will be where you can start to bring them back to your first party content.&lt;/p&gt;

&lt;p&gt;To evaluate your product, a developer may do one or more of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consume more content to see if your product matches their use case&lt;/li&gt;
&lt;li&gt;Sign up for/install your product&lt;/li&gt;
&lt;li&gt;Work through a ‘hello world’, a tutorial, quickstart or other getting started guide&lt;/li&gt;
&lt;li&gt;Join your community, such as your Discord server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of this is content. If you are a developer advocate, you might be responsible for creating this content, or you may have a technical writing team to do it for you.&lt;/p&gt;

&lt;p&gt;The goal here is to get the developer quickly to a point where they feel your product can solve their problem. You need to provide a mixture of content so that the developer can see something that is close to their use case so they can do a relevant evaluation. Obviously this is hard as there are infinite use cases, but you should be able to pick a few broad topics that cover most, especially if you have defined a number of ICPs.&lt;/p&gt;

&lt;p&gt;To evaluate, you need as few gates as possible. Yeah, an email address to sign up is normal, but if a developer has to talk to a person to get a trial then it won’t happen. Your product needs to be self service, with a free tier that is enough to fully evaluate. As a dev rel, you are responsible for product feedback, and that includes feedback on the size of this trial. As an engineer yourself, can you evaluate with the available trial? If not, it needs to be changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn
&lt;/h3&gt;

&lt;p&gt;Once the developer has evaluated your product an decided it is for them, they need to learn more. This is where good documentation is vital. If the developer cannot find out how to solve their problems, then they will go elsewhere. As a dev rel you may be responsible for the docs, or you may have a team to do this. At the very least, you are responsible for product feedback, and that includes feedback on documentation. Yes - documentation &lt;strong&gt;is&lt;/strong&gt; a product and anyone who says different is very, very wrong.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it’s not documented it doesn’t exist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In dev rel, you should always be reading and using your docs. Any thing missing or wrong - you should work to get it fixed, whether that is writing those docs yourself, or working with the documentation team to get them fixed.&lt;/p&gt;

&lt;p&gt;Your documentation needs to help the developer learn all they need. This means you need a mixture of docs - reference documentation, tutorials, concepts, all packaged up in a way that quickly allows the developer to get their answers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;

&lt;p&gt;After learning what they need, the developer will start building with your product. Learn and Build usually go hand in hand - reading docs whilst building out with your product. Building is the phase where they are in proof-of-concept (PoC), or even production mode. By now they may have paid you a little, or have approval to pay you once their trial runs out. For your company to get the bigger payout, they need to be successful.&lt;/p&gt;

&lt;p&gt;This is where support, community and documentation are important. You don’t want the developer to get stuck on the build out, not complete, and not convert from a free trial. You may have a support team to help, but dev rel folks should be monitoring the popular support requests and use that as feedback on what content to create. How you do this depends on your support team - maybe a meeting once a month to get the most popular issues, then you write docs for these.&lt;/p&gt;

&lt;p&gt;Developers like communities - they want to ask questions on stack overflow or a Discord server. Again, in dev rel part of this sits on your shoulders. Even if you have a support team to do this, you need to be aware of the trends to build content. If you don’t have a support team then set aside some time each day or week to scan stack overflow and your community tools to provide help, and encourage engineers to do this to!&lt;/p&gt;

&lt;p&gt;If you see developers being active with your product then dropping off, don’t be afraid to reach out to them. Help first and all that. ‘Hey, I saw you were using our tool then stopped at this point, are you stuck on something that I can help with?’ type messages can achieve a lot. Often providing a simple answer or guidance can unblock them and get them back building. And of course, update this into your documentation!&lt;/p&gt;

&lt;h3&gt;
  
  
  Scale
&lt;/h3&gt;

&lt;p&gt;By scale, I mean developers scaling out what they have built. Using your product more, using it in different ways, using different parts of it. Taking what they have built and moving it to production.&lt;/p&gt;

&lt;p&gt;Hopefully by this point, your job is pretty much done. The main responsibility you have here is to ensure the content is available to show how to scale. For example, with Pieces for Developers, scale to me is getting all developers in a company to adopt Pieces, and leverage the collaboration features.&lt;/p&gt;

&lt;p&gt;For developers to scale like this, they need to know how. Not just the steps to take, but the why of each step so they can apply it to their use case. For example, if you have developers deploying your product to public clouds, then you want docs on how to do it on AWS, Azure and GCP. If you only have AWS docs and they use Azure, then they will struggle to scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s after scale?
&lt;/h3&gt;

&lt;p&gt;At this point the developer journey is not done. Yes they have their problem solved and they can go home, but a new day is a new problem. The cycle starts again with whatever new problem they have, or whatever new product you have for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling dev rel
&lt;/h2&gt;

&lt;p&gt;So far I’ve talked about the journey developers take and how dev rel fits in. The biggest issue dev rels have with this is one of scale. You likely have a small dev rel team, but a huge user base, so how do you scale?&lt;/p&gt;

&lt;p&gt;As a note, and I did say I won’t cover metrics here, you can only scale if you can show you are scaling. And that means metrics and tracking. You do need to be able to show that scaling is working.&lt;/p&gt;

&lt;p&gt;There are a number of ways to scale, and here are a few.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce, reuse, recycle
&lt;/h3&gt;

&lt;p&gt;A lot of dev rel time is spent on content, be that writing talks, creating videos, writing sample code, or writing blog posts. The most effective thing you can do is re-use as much as possible. Spend less time creating, more time sharing. The downside is that a lot of dev rel folks love building the new shiny thing, but you need to avoid the temptation to create a new talk every time.&lt;/p&gt;

&lt;p&gt;For example - back at Microsoft I created a sample mobile app with Xamarin to show some of the Azure AI services. Here’s how I scaled it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub repo with code samples shared on social and blogs&lt;/li&gt;
&lt;li&gt;Feedback on the docs based off building this app&lt;/li&gt;
&lt;li&gt;A video showing the app and code on a popular show&lt;/li&gt;
&lt;li&gt;A workshop that taught people to build the app.

&lt;ul&gt;
&lt;li&gt;Gave this to team mates to run&lt;/li&gt;
&lt;li&gt;Ran this at multiple events&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;One code sample became a lot of content, reaching probably at least 500 workshop attendees - and if someone is attending a workshop then they are very engaged and less likely to fall out of the funnel. Oh, and in the case of this workshop, one group of attendees went on to build an app in production for &lt;a href="https://devblogs.microsoft.com/xamarin/customer-showcase-the-academy-of-motion-picture-arts-sciences/" rel="noopener noreferrer"&gt;the Oscars&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Online vs in-person
&lt;/h3&gt;

&lt;p&gt;This is a thorny topic as there are very different opinions and data, but online reaches orders of magnitude more people than in-person for the same amount of effort. I can go to a conference, spend 2 days traveling and 2 days at the event to reach maybe 100 people with a talk. Or I can give that same talk 10 times in less time on-line to different communities and reach thousands. Microsoft saw this with MS Build - instead of 6,000 in-person attendees then had 160,000 online.&lt;/p&gt;

&lt;p&gt;Now there are lies, damned lies, and statistics - so how important is this difference?&lt;/p&gt;

&lt;p&gt;Well the jury is still out on that. In person means more engagement. If someone comes to a talk they are genuinely interested. If they are watching online are they actually watching? Or is it on in the background whilst they do something else.&lt;/p&gt;

&lt;p&gt;I wish I had hard data for this, but I don’t so my gut feel is a mix of both. Having seen a good ROI from in-person events that are targeted to a relevant audience (for example talking about AI coding assistants at a dev tools focused event), there is benefit to in-person, but it doesn’t scale like on-line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reach more developers by going where developers are
&lt;/h3&gt;

&lt;p&gt;To scale well you need reach. I’ve seen for example the same post get 3,000 views on DZone, and less than 25 on dev.to. By putting your content where developers are it helps scale. This also means your content needs to be of the right type for where those developers are. There’s always debate on blogs vs video, but my take is both and measure. YouTube is the worlds number 2 search engine, so a well constructed video with a good description and title can reach a massive audience. But then there are those who prefer to read, or skim read, or want a TikTok.&lt;/p&gt;

&lt;p&gt;And when I say measure - try to get good data. Views on a blog may mean a lot of engaged developers, or lots of quick view and bounce. Are you seeing folks following links from the blogs to your properties (you are tracking this right?).&lt;/p&gt;

&lt;p&gt;Same with YouTube views. I remember back at Microsoft a load of congratulatory emails and lots of talk about an amazing video that a colleague did on generative AI that got 40,000 views. Even went way up the chain. Ignoring how 40K is a small number for a multi-trillion dollar company, no-one bothered to validate the views. If they had, they would have noticed that most of the 40k views were around 5 seconds and were from ads. Yup - there was an experiment done to run videos as ads to boost views, and that is why this had so many. Obviously for political reasons, when this was pointed out it was hushed up…&lt;/p&gt;

&lt;p&gt;So, go where developers are, validate your reach there, and use that data to help scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow trends
&lt;/h3&gt;

&lt;p&gt;Another way to scale is to create content to ‘go viral’, that is follow trends, If a topic is getting a lot of interest, see how you can apply your product to that topic. LLMs and Generative AI is big, so I created a video using a popular AI framework from Microsoft just as that framework was getting promotion by them. It has my highest personal video views! This was simply by riding the promotional wave from Microsoft.&lt;/p&gt;

&lt;h3&gt;
  
  
  Community
&lt;/h3&gt;

&lt;p&gt;Community is how you can really scale! A good community becomes almost like a volunteer dev rel team. Having been a Xamarin MVP and a Microsoft MVP I know well how community will promote a product in their own time and at their own expense - I’ve spent my own money on travel just to talk about a technology owned by a trillion dollar company.&lt;/p&gt;

&lt;p&gt;Community helps with the scale simply by shear volume. A community of 10 heros can reach substantially more that 1 dev rel, including reaching geographies and time zones that are not easily available. A community hero in China giving a talk on your product in Mandarin can reach developers an English-only speaking developer advocate in the US simply cannot reach. The more you grow your community, the more they will promote your product.&lt;/p&gt;

&lt;p&gt;The advantage of your community is they are already engaged. people join your community because at the very least they are aware of your product, though more likely a user. Folks don’t discover your slack by chance and hang out.&lt;/p&gt;

&lt;p&gt;So how to do you grow your community? Well a ‘community’ has 3 types of members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Passive - these are the folks who join your slack to ask a question, maybe follow you on social and leave. You need to support them as they are users, but they won’t help you scale. What they are helpful for is product feedback - the questions they are asking, should they be answered in the docs? Are they reporting bugs or fewture requests?. Your goal here is to make them active, but be aware most of them simply won’t be.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Active - these are the folks who are active in your community space. They might be answering questions on your Discord, or maybe writing the odd blog post on your product. They can help you scale support, as well as driving some awareness. Your goal here is to convert these to heros.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Heros - these are your very active, highly engaged, community superstars. These are community members who actively promote your product. Maybe then run a user group, or regularly speak at events about your technology. Maybe they write blog posts, or create videos. Their message is the most authentic - they are talking about your product because they like it, not because they are paid (like you). Your goal here is to support them as much as possible. At the very least they should have free access to your product, along with swag. Ideally you should provide swag for them to distribute like bags of stickers, custom rewards like personal shirts, and a public way to highlight them. Recognition is usually the most valuable currency here, not just recognizing then publicly, but by gathering feedback from them. Getting time with product or engineering is a great way to not only make them feel special, but also can give you important feedback. If you have the budget, provide them sponsorship such as paying conference travel, or buying pizza for their user group. You can also help provide them with content, such as demos and slides for a talk.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was listening to &lt;a href="https://www.youtube.com/live/VZWRLN6K65I?si=TplbU7SrRf0xGAci" rel="noopener noreferrer"&gt;an episode of Dev Relish today&lt;/a&gt; that described community as a Tamagotchi. For it to live and thrive it takes regular feeding. Not much effort, but small and continual. You need to build that muscle of regularly ‘feeding’ your community.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else is dev rel?
&lt;/h2&gt;

&lt;p&gt;I feel the focus of this post has been more on the advocacy side of dev rel, as that is the area I am in. I do want to acknowledge that there are many other facets to developer relations that help build a relationship with developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical writing teams - dev rels sometimes own and write docs, other times there are dedicated teams. And this is not just docs, it can be online learning tools or other such learning materials&lt;/li&gt;
&lt;li&gt;Content creators and supporters - some companies have dedicated studio teams, or storytellers, or demo writing teams&lt;/li&gt;
&lt;li&gt;Developer-focused engineers - some engineers focus on helping developers directly, rather then by building products. This includes generating SDKs, or building helper scripts, sample code, or other tools&lt;/li&gt;
&lt;li&gt;Community managers - in an ideal world you would have a community manager to feed the Tamagotchi. These are folks whose skills lie in managing communities, such as program management.&lt;/li&gt;
&lt;li&gt;Marketing - marketing spread the word, and can be your friend. They also usually have money which helps with swag!&lt;/li&gt;
&lt;li&gt;Events teams - if you are running an in-house event, or attending an external one, an event team can make this a much smoother process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m sure there are more. I know some will say ’this is not dev rel’, but it is. It may not be dev advocacy, but dev rel is a whole world more than that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is not dev rel?
&lt;/h2&gt;

&lt;p&gt;So far I’ve talked a lot about what is dev rel and how you can scale. Lets take a second to talk about what is not dev rel. This is another topic where there are many opinions, but my (potentially 🌶️) opinion is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are personally responsible for getting money then it is not dev rel&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we all want to get paid, and really the end outcome of dev rel is more customers, so dev rel is part of the sales funnel. But to me the line is when money is involved, such as negotiating contracts, commissions etc. then that is sales. Help first, sell last - to authentically help someone it needs to come from a place of wanting to help, not from driving more sales. An authentic dev rel should be willing to help you spend less by optimizing what you use, or telling you outright if the product will not help you. Having worked in places where sales will sell something that doesn’t exist (yup, happened to me), I’ve seen that money and commission are not a good driver of the right behaviors, and do not help with authentic conversations.&lt;/p&gt;

&lt;p&gt;I’m not covering metrics here, but dev rel should never be measured sales. Yes - tracking dev rel qualified leads is good, but not ‘dev advocate X made this $100K sale’&lt;/p&gt;

&lt;p&gt;Help first, sell last - and let someone else sell.&lt;/p&gt;

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

&lt;p&gt;I hope this brain dump sheds some light on what I think dev rel is. As with anything, ask 10 dev rels what it is and you’ll get 12 answers at least. There’s so much I’ve missed - I literally could write a book on this topic, but I won’t - I’ll save that for the experts.&lt;/p&gt;

&lt;p&gt;Remember: &lt;strong&gt;Help first, sell last&lt;/strong&gt;. Authentically go where developers are and bring them back to your product, content and communities.&lt;/p&gt;

&lt;p&gt;I’d love your thoughts in the comments, or reach out to me on social: &lt;a href="https://linktr.ee/jimbobbennett" rel="noopener noreferrer"&gt;linktr.ee/jimbobbennett&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>devex</category>
    </item>
    <item>
      <title>How to correctly review a pull request</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Mon, 01 Apr 2024 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/how-to-correctly-review-a-pull-request-4pha</link>
      <guid>https://forem.com/jimbobbennett/how-to-correctly-review-a-pull-request-4pha</guid>
      <description>&lt;p&gt;One of my popular sayings is that development is a team sport. We don’t code in isolation, instead engineers are part of a team that delivers value to users and customers, often made up of software. And for a team to deliver its best code, there needs to be shared ownership of the code. This means that more than one developer is involved in each code change to ensure that it is correct, appropriate, and that the knowledge of this change is shared amongst the team. While there are many techniques for this (such as pair and mob programming), the most popular one is a simple pull request, or PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a pull request?
&lt;/h2&gt;

&lt;p&gt;A pull request, or PR, is a git feature that allows you to request that the owner of some code pulls your changes. You make a code change on a fork or branch, and raise a pull request to merge your code into the golden source, origin, or upstream repo. Essentially this is how you get your code published to the team ready to be deployed to production.&lt;/p&gt;

&lt;p&gt;The aim of a pull request is to get another set of eyes over your code. Before a PR is merged, it is reviewed. The reviewer can check the code for correctness, style, if it actually implements the feature it’s meant to, things like that. This is the opportunity for a second pair of eyes to catch any errors, for knowledge sharing so more than one member of the team understands the new code, and a chance for more senior developers to help guide and upskill more junior developers.&lt;/p&gt;

&lt;p&gt;The problem with PRs though, is often the reviews are done badly. The infamous LGTM 👍 (looks good to me). Especially with large PRs that take a lot of time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0hzm3bywknig0nuqj8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0hzm3bywknig0nuqj8h.png" alt="A twitter post from I am developer saying 10 lines of code = 10 issues. 500 lines of code = looks fine." width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So why are we so bad at reviewing PRs?
&lt;/h2&gt;

&lt;p&gt;The reason is simple - time. Reviewing a 5 line change is potentially quick, so a reviewer can check it, and ask for changes in a short space of time. Give a developer 100 files then the review is often little more than a cursory glace as there isn’t enough time to do a full review. After all reviewing code isn’t delivering features!&lt;/p&gt;

&lt;p&gt;Having seen some very bad PR reviews in my time, and poor code released because of it, I thought I’d scribble down some of my top tips for doing PR reviews right. Add any more thoughts to the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do a better job of reviewing PRs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Allocate time to do it
&lt;/h3&gt;

&lt;p&gt;This is often something that you as a reviewer can’t do, but something you should advocate for and your leadership needs to understand. PR reviews take time. If you expect an engineer to be 100% on feature work, not only do you risk burnout, but it means there is literally no time to review PRs. Everything, no matter how bad will be LGTM 👍, merged and cause problems downstream.&lt;/p&gt;

&lt;p&gt;This makes sense for engineers - there’s not time to review a PR to catch a bug, but it’s easy to get time allocated in a sprint to fix the bugs that get through. Your processes drive poor engineering practice.&lt;/p&gt;

&lt;p&gt;Ideally an engineer should be 60% feature work, 20% run the business, 20% spare (vacations, sick time, dealing with weird issues that reduce your ability to work like your OS shitting itself). The 20% run the business should be enough to review PRs, and if not - allocate more time (or move to pair programming, but that takes more to convince management who see people as resources with linear delivery capability).&lt;/p&gt;

&lt;p&gt;Now you have time to do a proper PR review, next you need to review the ticket!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Review the ticket
&lt;/h3&gt;

&lt;p&gt;All good PRs start with a ticket of some description. This is (typically) a ticket in Jira that details the work that is done. But these are not code, so why do you care if you are reviewing a PR? Well the ticket explains the intention of the change. What bug is being fixed, what feature is being added. Without a good detailed ticket, how can you very the code works?&lt;/p&gt;

&lt;p&gt;So what makes a good ticket? This is blog post in its own right, but here are some thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It exists. Simply having a ticket is a start. No ticket, then no change. All PRs without a ticket should be rejected as how can you ensure the change is correct?&lt;/li&gt;
&lt;li&gt;It explains the why of the change. When you know why you can think about the implementation from the perspective of the end user. Helps when you think about the test cases or ways to test the code.&lt;/li&gt;
&lt;li&gt;It explains the change in as much detail as possible. Move a button? Ok, where, by how much. Add a new API? What endpoint, what is the request and response body?&lt;/li&gt;
&lt;li&gt;It defines some test cases. In a perfect world these should be exhaustive, but it should at least direct some ways to verify the change. Add a feature to calculate X - he are some sample inputs and outputs.&lt;/li&gt;
&lt;li&gt;It defines any impact on other parts of the system. Does it require a downstream change? Should documentation be updated?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you are happy with the ticket you can review the code. Not happy - punt it back to the developer or product owner to get it to a good state. Sometimes just by doing this you will catch things the developer missed and get bugs fixed or missing features implemented before you have even looked at the PR.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Does the code work?
&lt;/h3&gt;

&lt;p&gt;Code should work. The ticket defines the change, and the code should implement it. Before reviewing the code, I like to take it for a spin and see if it actually does what it is meant to do in the context of the whole system. Yes, this usually means some manual testing but again you can catch issues quickly. Not always easy, but there are usually ways and test harnesses to test features in components of a larger system. This is where a good ticket also helps - it can define what you need to test, expected outputs for inputs, and give you a good direction to test edge cases.&lt;/p&gt;

&lt;p&gt;But what about unit tests? I’ll cover these next, but the presence of unit tests doesn’t always mean tha the code works. I’ve had situations when reviewing a PR where all the tests passed but the code didn’t do what was needed when I tested the whole system. Multiple times I showed the engineer the issue, they added unit tests, told me it worked because their unit tests passed, but it didn’t work.&lt;/p&gt;

&lt;p&gt;For example, if you have an add function and write the following tests: add 2 and 2 to get 4, add 3 and 1 to get 4, and 0 and 4 to get 4. Do these tests show the code works, or that you have a function that always returns 4?&lt;/p&gt;

&lt;p&gt;There’s no point in reviewing code if it doesn’t work.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Does the code have automated tests?
&lt;/h3&gt;

&lt;p&gt;The next step with testing - what about the automated tests? Does the code have unit test, integration tests, UI tests etc. If not - should it (probably). If yes, do they run, pass, and actually test the code?&lt;/p&gt;

&lt;p&gt;I find reviewing the test code first is a good thing to do. If there are obvious test cases missed, edge cases not considered, or poor tests, then you want these fixed before you review the core code.&lt;/p&gt;

&lt;p&gt;Can you see in the PR that the tests have run and passed? As above - no point in reviewing code that doesn’t work. A PR should run as many unit test as is practical, and there should be guards that block merges if any tests fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Can you understand the code?
&lt;/h3&gt;

&lt;p&gt;Now it is time to review the code. The first thing to review is do you understand it? If you don’t understand the code and what it does then you can’t review it. It should be reasonably obvious to someone who understands the system and context of the changes. It should also be readable. Does the code have sensible names for variables, functions, and classes? Are there code comments explaining the why of the code?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Anyone who says code comments are not needed is wrong. Yes - well named things explain what the code does, but comments explain the why. There is no need to comment a getter to say it gets a value, but you want to explain why you are getting that value in the code and doing things with it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Readability is important in code. I know some developers like to play code golf and solve a problem in as few lines as possible, but sacrificing readability is a terrible thing. You are writing code so humans can understand it, so don’t be clever, be clear, concise, and obvious. When reviewing code is it clear to the reader what it is doing and why? Your code should be obvious to the most junior developer on the team - remember code is owned by the team, not the engineer who wrote it, so needs to be accessible to everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Is the code good?
&lt;/h3&gt;

&lt;p&gt;Lastly is the code ‘good’ - and by good I mean following team standards. This could include coding standards (tools help here), but also does it follow conventions, is it implemented in a consistent way with the rest of the code base.&lt;/p&gt;

&lt;p&gt;For example - if your code writes a message to the user, is this message defined in a constants file with other messages for i18n? Does it conform to a11y standards? Is the naming convention consistent?&lt;/p&gt;

&lt;p&gt;This is often subjective, so having a good team agreement with coding standards can help with this.&lt;/p&gt;

&lt;p&gt;if it is all good - approve that PR! Any issues, then it’s time to leave review comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commenting on PRs and requesting changes
&lt;/h2&gt;

&lt;p&gt;You’ve reviewed the code and it’s time to leave some comments. Although this post is about reviewing, here are some thoughts on leaving review comments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be kind. Don’t be an arse, don’t be Linus Torvalds. The person receiving these comments is human and a team mate. Be kind and constructive with all your comments.&lt;/li&gt;
&lt;li&gt;Provide detail in the comments. If something needs to be changed, explain why (e.g. move this to a constants file as per our standards).&lt;/li&gt;
&lt;li&gt;Understand that everyone codes differently and personal opinions have no place. You would have written it differently? Well if the way it is done is ok, then don’t impose personal opinions on the implementation. If you ask 20 developers how to do something you’ll get 30 responses, not all are wrong.&lt;/li&gt;
&lt;li&gt;For more junior developers, this is an opportunity for learning and growth. Give guidance with changes that help with that growth&lt;/li&gt;
&lt;li&gt;Use things like suggestions for small changes like typos to make it quicker to apply changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Go review PRs
&lt;/h2&gt;

&lt;p&gt;This was a quick and dirty post to get some thoughts down on this. Let me know your thoughts in the comments!&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Connect to localhost from inside a dev container</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Mon, 20 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/connect-to-localhost-from-inside-a-dev-container-5e5g</link>
      <guid>https://forem.com/jimbobbennett/connect-to-localhost-from-inside-a-dev-container-5e5g</guid>
      <description>&lt;p&gt;I do a lot of work in dev containers (for example, using &lt;a href="https://dev.to/liblaber/installing-liblab-into-a-devcontainer-using-npm-41e5"&gt;liblab inside of one&lt;/a&gt;), often hosting APIs. One problem I used to often hit is how can I host an API in one container, and access it from another dev container? This post shows you how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem - ’localhost’ in a container
&lt;/h2&gt;

&lt;p&gt;So the issue is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dev container A&lt;/strong&gt; exposes an API on port 8000, and this is forwarded to localhost&lt;/li&gt;
&lt;li&gt;I can access this port at &lt;code&gt;localhost:8000&lt;/code&gt; either inside &lt;strong&gt;dev container A&lt;/strong&gt; , or from my local machine as the dev container forwards the port&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev container B&lt;/strong&gt; needs to access the API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem here is &lt;strong&gt;dev container B&lt;/strong&gt; has no access to my local machine. Docker forwards the ports from &lt;strong&gt;dev container A&lt;/strong&gt; to localhost automatically as soon as I run the API. But ports from my local machine are not forwarded into dev containers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;strong&gt;Dev container A&lt;/strong&gt; accesses &lt;code&gt;localhost:8000&lt;/code&gt;, it gets the API running on &lt;strong&gt;Dev container A&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If my local machine accesses &lt;code&gt;localhost:8000&lt;/code&gt;, it gets the API running on &lt;strong&gt;Dev container A&lt;/strong&gt; because the port on &lt;strong&gt;Dev container A&lt;/strong&gt; is forwarded to localhost, as far as the local machine is concerned, 8000 is open and accepting requests.&lt;/li&gt;
&lt;li&gt;If &lt;strong&gt;Dev container B&lt;/strong&gt; accesses &lt;code&gt;localhost:8000&lt;/code&gt; it will fail, as there is nothing running on port 8000 inside that container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how can one dev container access ports exposed by another?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the host.docker.internal network
&lt;/h2&gt;

&lt;p&gt;Docker has a fix for this! It exposes a ‘special’ &lt;a href="https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host" rel="noopener noreferrer"&gt;DNS called &lt;code&gt;host.docker.internal&lt;/code&gt;&lt;/a&gt; that essentially gives to access to the local machines network. Rather than access localhost, you access this named device instead.&lt;/p&gt;

&lt;p&gt;So from &lt;strong&gt;Dev container B&lt;/strong&gt; , you access &lt;code&gt;host.docker.internal:8000&lt;/code&gt; and boom! &lt;strong&gt;Dev container B&lt;/strong&gt; can access the API from &lt;strong&gt;Dev container A&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;For example, you are testing out an SDK created against an API you are running on port 8000 via a dev container (for example, the &lt;a href="https://github.com/liblaber/llama-store" rel="noopener noreferrer"&gt;liblab llama store&lt;/a&gt;), and you want to take advantage of the &lt;a href="https://developers.liblab.com/cli/config-file-overview-customizations/#devcontainer" rel="noopener noreferrer"&gt;dev containers feature of liblab to open our SDK in a container&lt;/a&gt;. You can set the &lt;code&gt;baseUrl&lt;/code&gt; in your &lt;code&gt;liblab.config.json&lt;/code&gt; file to &lt;code&gt;http://host.docker.internal:8000&lt;/code&gt;, then open the generated SDK in its dev container!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "sdkName": "llama-game",
 "specFilePath": "http://localhost:8000/openapi.json",
 "baseUrl": "http://host.docker.internal:8000",
 "languages": [
 "python"
 ],
 "createDocs": true,
 "customizations": {
 "devContainer": true
 }
}

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

&lt;/div&gt;



&lt;p&gt;Done! The SDK now defaults to use the docker internal network for testing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Environment(Enum):
 """The environments available for this SDK"""

 DEFAULT = "http://host.docker.internal:8000"

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

&lt;/div&gt;



&lt;p&gt;Don’t forget to set it to your production URL once you are ready to publish.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Avoiding automation to keep your job</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Mon, 06 Nov 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/avoiding-automation-to-keep-your-job-3924</link>
      <guid>https://forem.com/jimbobbennett/avoiding-automation-to-keep-your-job-3924</guid>
      <description>&lt;p&gt;Will technology, automation, or AI take our jobs? This is something we hear a lot at the moment - but it’s always been this way. Right back at the start of my career I automated something that was canned the moment I left as folks were afraid of their job.&lt;/p&gt;

&lt;p&gt;Some background to why I am writing this post. I was reading a tweet today from &lt;a href="https://twitter.com/MichelleBakels" rel="noopener noreferrer"&gt;Michelle Bakels&lt;/a&gt; that was a cautionary tale from her enterprise days.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1721597776893489190-85" src="https://platform.twitter.com/embed/Tweet.html?id=1721597776893489190"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1721597776893489190-85');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1721597776893489190&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I’m not going to dump the entire text here - go read the tweet as it’s fascinating, but the TL;DR is folks are scared of telling IT what they do in case it is automated and they are out of a job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Will technology replace ?
&lt;/h2&gt;

&lt;p&gt;This is something that is constantly talked about in the tech world, though feels like it has really come to the surface recently with the rise of AI. Will technology replace us? Will AI take our jobs (one of the catalysts of the recent SAG-AFTRA strikes in the TV and movie industry)? Will self-driving cars replace taxies? Will cars replace horses? Will movable type replace handwritten books? The list goes on.&lt;/p&gt;

&lt;p&gt;Rather than dive into this discussion, I thought it would be fun to tell a story of my own - of how I automated something during an internship, and how it got killed the minute I left as folks were worried it would replace their jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jim gets an internship at Marconi
&lt;/h2&gt;

&lt;p&gt;This was just after I graduated in 1999 - I started working on a summer internship at Marconi (&lt;a href="https://youtu.be/K1b8AhIsSYQ?si=i5wCmF8Y2Vbin0cn" rel="noopener noreferrer"&gt;plays the mamba, listen to the radio&lt;/a&gt;) in their underwater weapons division. This was probably the start of my route towards being a pacifist and hating all weapons and the military industrial complex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IvuzE0Uy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jimbobbennett.dev/blogs/fear-of-automation/spearfish-torpedo.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IvuzE0Uy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jimbobbennett.dev/blogs/fear-of-automation/spearfish-torpedo.webp" alt="A spearfish torpedo" width="600" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My job was to analyze torpedo data. Marconi built these $1M Spearfish torpedoes that didn’t work very well, and they would do trials on them in various parts of the world (Faslane in Scotland, deep water testing in the Caribbean). The testing torpedoes would record data about their decisions, as well as telemetry such as depth, speed, location etc. They would also record radar data and what they identified in the way of ships. The goal was to drop a torpedo in the water and send it at a ship, then gather the data afterwards to see if it did the right thing. Not quite testing in production as the torpedoes had no explosives, but the closest you can get.&lt;/p&gt;

&lt;h3&gt;
  
  
  The reporting process
&lt;/h3&gt;

&lt;p&gt;Once the trails were over, someone had to analyze the results, and this was the team I was on. The process was horribly manual:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remotely log into a mainframe and request a printout of the data from the torpedo trial as a hex dump on green and white continuous feed paper.&lt;/li&gt;
&lt;li&gt;Work through the hex dump using highlighters to ‘read’ what was happening. Hex codes marked the steps in the output, and each one had so many bytes of information. For example, one hex code was for direction and you knew that meant the next X bytes were the direction information, then the next character was the next set of data. Very laborious and error prone&lt;/li&gt;
&lt;li&gt;Enter all this data into an Excel spreadsheet showing each step&lt;/li&gt;
&lt;li&gt;Extract location and direction data and put this into another page on the spreadsheet&lt;/li&gt;
&lt;li&gt;Generate a chart to show the torpedo’s movements&lt;/li&gt;
&lt;li&gt;Create a word doc with all the steps that the torpedo did, along with the charts.&lt;/li&gt;
&lt;li&gt;Actually do the analysis and look for issues for the software teams to investigate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(There are probably more steps which I have forgotten as it was 24 years ago)&lt;/p&gt;

&lt;p&gt;Apart from the last step which actually used your brain, the rest was repetitive and dull. It could easily take a week or to to get a report ready to be able to do the final analysis step. And there was a whole team doing this, most of them ex-Ministry of Defense folks who just wanted to get their pension.&lt;/p&gt;

&lt;h3&gt;
  
  
  In comes Jim with an idea!
&lt;/h3&gt;

&lt;p&gt;So I had an idea! I was an engineer after all. I could automate all the things, I’d be the hero, save the company money and everyone would love me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrgmd8g47g1ff1fu8xa0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrgmd8g47g1ff1fu8xa0.gif" alt="A man in period costume holding a cup of tea saying how charmingly naive" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I set about doing this whilst I was working on a report.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate all the things
&lt;/h3&gt;

&lt;p&gt;Firstly I had to automate getting the data - can’t do this from a printer. Luckily the mainframe that I logged into to print could just dump the data to the screen. How could I get this in a way I could do things with it? Excel!&lt;/p&gt;

&lt;p&gt;So I wrote some good old VBA in Excel to connect over SSH to the mainframe and download all the data. I had one control sheet that you enter the trial number into, a button to click and away it would go downloading the data and filling out a second sheet.&lt;/p&gt;

&lt;p&gt;Next was replacing the highlighter pen! Easy to do in Excel, work through each cell to get the action for each step, highlight the cells as if I was using a highlighter pen and paper, and extract the data for each action.&lt;/p&gt;

&lt;p&gt;Once I had the actions, it was easy to convert from the hex codes to the steps information that was wanted. Same with locations - easy to extract and plot the chart.&lt;/p&gt;

&lt;p&gt;Finally it was time to create a word doc - and VBA can do this nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  The result
&lt;/h3&gt;

&lt;p&gt;All in all I spent about 4 weeks on this, and was able to generate a report ready for analysis in about 10 minutes, as opposed to 1-2 weeks. I packaged this as an add-in, so anyone could use it - just add the add-in, enter the trial number and away it went and spat out the doc.&lt;/p&gt;

&lt;p&gt;I even gave it a cool name: Spearfish Trials Analysis Report Writers Automated Reporting Suite - or STAR WARS for short (this was a few months after Episode 1 came out after all).&lt;/p&gt;

&lt;p&gt;I was impressed, I could save everyone time!&lt;/p&gt;

&lt;h3&gt;
  
  
  The reception
&lt;/h3&gt;

&lt;p&gt;Everyone hated it. Simple as that. I was told there was no way it could be better than a human, and one of the top report writers challenged me to produce a better report. In 10 minutes I had one, in 4 days she had her version, and she’d made some mistakes - which made her hate it (and me) even more.&lt;/p&gt;

&lt;p&gt;After I left, it was deleted and everyone forgot about its existence. It took me a while to figure out why - everyone was afraid. This one spreadsheet could replace almost the entire team of 7 people. You’d just need this spreadsheet and 1 person to do the analysis as the bulk of the job was manually processing reports. I could have put 6 people out of work in just 4 weeks.&lt;/p&gt;

&lt;p&gt;Regardless of how you feel about waste in enterprises, this was 6 humans, with families who had houses, mortgages, rent, bills, who needed to eat and to live. The lives of 6 families who could have been destroyed with one spreadsheet.&lt;/p&gt;

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

&lt;p&gt;Yes, automation is coming. Yes, technology can solve so many things. Yes, in theory if a job is replaced by automation then folks can focus their knowledge and energy onto solving bigger problems. But the reality is business is run by people who want to make as much money as possible, so automation is a way they can cut jobs and boost shareholder value, whatever that is (I’m guessing making rich folks richer).&lt;/p&gt;

&lt;p&gt;This is not always the case, and this is a very cynical and dystopian view, but in most cases this is the truth. Even in the high-paying tech world of multi-trillion dollar companies, folks are dropped in an instant to boost share prices.&lt;/p&gt;

&lt;p&gt;So it’s not surprising folks are worried about computers taking their jobs. If we are in the throes of an AI revolution, then maybe we need to really push the conversations around &lt;a href="https://en.wikipedia.org/wiki/Universal_basic_income" rel="noopener noreferrer"&gt;universal basic income&lt;/a&gt;, and taxing large corporations and billionaires to pay for it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>excel</category>
      <category>career</category>
    </item>
    <item>
      <title>Installing LibLab into a devcontainer using NPM</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Mon, 07 Aug 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/liblaber/installing-liblab-into-a-devcontainer-using-npm-41e5</link>
      <guid>https://forem.com/liblaber/installing-liblab-into-a-devcontainer-using-npm-41e5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In this post we look at how to add NPM packages to a devcontainer to use in VS Code locally or in a GitHub codespace&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I recently started at &lt;a href="https://liblab.com" rel="noopener noreferrer"&gt;LibLab&lt;/a&gt; as a Principal Developer Advocate. LibLab is a tool that converts APIs into language SDKs, something I’m a big fan of as I love typesafety and the inherent documentation that comes with a good SDK. Obviously my first job was to dive into building out an API and creating an SDK from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Devcontainers
&lt;/h2&gt;

&lt;p&gt;I love &lt;a href="https://containers.dev/" rel="noopener noreferrer"&gt;devcontainers&lt;/a&gt; - they allow me to create a repo that anyone can open and instantly have a fully configured environment that just works! My standard process when creating any sample code is to start with a devcontainer. In this case, I’m creating a &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; application, and building an SDK from that.&lt;/p&gt;

&lt;p&gt;I start with a Python container - this is a pre-configured container that I can just spin up inside VS Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0vaixl346lh47idemwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk0vaixl346lh47idemwc.png" alt="The Python 3 devcontainers option in the vs code select dev container configuration" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once in this container I can create my API, then build the SDK using LibLab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add LibLab to the container
&lt;/h2&gt;

&lt;p&gt;LibLab is available as a &lt;a href="https://developers.liblab.com/docs/getting-started" rel="noopener noreferrer"&gt;CLI that you install via NPM&lt;/a&gt;. There are a number of ways you can install NPM packages in the container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install it manually in the &lt;code&gt;postCreateCommand&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use a &lt;code&gt;DOCKERFILE&lt;/code&gt; and install it there&lt;/li&gt;
&lt;li&gt;Use the NPM package feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went for option 3 - use the NPM package feature. This avoids having to create a &lt;code&gt;DOCKERFILE&lt;/code&gt; and configure my &lt;code&gt;devcontainer.json&lt;/code&gt; to use it, and means I don’t have to add more to my &lt;code&gt;postCreateCommand&lt;/code&gt; which already installs pip packages.&lt;/p&gt;

&lt;p&gt;You can add this feature manually into the &lt;code&gt;devcontainer.json&lt;/code&gt; file, or use the command palette:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf0xbzh40b7p1x7dtqof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsf0xbzh40b7p1x7dtqof.png" alt="Selecting the NPM package feature from the command palette" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add manually, add the &lt;code&gt;ghcr.io/devcontainers-contrib/features/npm-package:1&lt;/code&gt; feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 ...
 "features": {
 "ghcr.io/devcontainers-contrib/features/npm-package:1": {}
 }
}

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

&lt;/div&gt;



&lt;p&gt;Once added, the feature is configured using the &lt;code&gt;package&lt;/code&gt; and &lt;code&gt;version&lt;/code&gt; parameters. THe &lt;code&gt;package&lt;/code&gt; should be set to &lt;code&gt;liblab&lt;/code&gt;, the name of the NPM package for LibLab. The &lt;code&gt;version&lt;/code&gt; parameter defaults to &lt;code&gt;latest&lt;/code&gt; and I always want the latest LibLab CLI installed, so this can be left out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 ...
 "features": {
 "ghcr.io/devcontainers-contrib/features/npm-package:1": {
 "package": "liblab"
 }
 }
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rebuild the container
&lt;/h2&gt;

&lt;p&gt;Once this is set up you can rebuild the container and the LibLab SDK will be available in your terminal!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vscode ➜ /workspaces/python $ liblab
The offical LibLab CLI

VERSION
 liblab/0.9.20 linux-arm64 node-v18.17.0

USAGE
 $ liblab [COMMAND]

TOPICS
 hooks Bootstrap an SDKs hooks.

COMMANDS
 build Build SDK(s) and Docs for your API.
 help Display help for liblab.
 init Create a liblab folder for housing files relevant to SDK and Doc generation.
 login Login to your account.
 logout Logout of your account.
 post-build Run extra tasks after code generation.
 pr Create a PR for all the generated SDKs.
 register Register for a new liblab account.
 reset-password Reset your password.
 token Create a new login token for CI/CD.

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;p&gt;Want to learn more about LibLab for automating the generation of SDKs from your APIs? Get in touch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://linktr.ee/JimBobBennett" rel="noopener noreferrer"&gt;Contact me via social&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.gg/F8aECHbRkV" rel="noopener noreferrer"&gt;Join our Discord&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>npm</category>
      <category>devcontainer</category>
      <category>liblab</category>
      <category>vscode</category>
    </item>
    <item>
      <title>3D Printed Clippy Powered by ChatGPT has won a Telly award</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Thu, 01 Jun 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/3d-printed-clippy-powered-by-chatgpt-has-won-a-telly-award-29gk</link>
      <guid>https://forem.com/jimbobbennett/3d-printed-clippy-powered-by-chatgpt-has-won-a-telly-award-29gk</guid>
      <description>&lt;p&gt;This year is the 44th annual &lt;a href="https://www.tellyawards.com" rel="noopener noreferrer"&gt;Telly Awards&lt;/a&gt;. The Telly Awards honor excellence in video and television across all screens.&lt;/p&gt;

&lt;p&gt;The Telly Awards was founded in 1979 to honor excellence in local, regional and cable television commercials with non-broadcast video and television programming added soon after. With the recent evolution and rise of digital video (web series, VR, 360 and beyond), the Telly Awards today also reflects and celebrates this exciting new era of the moving image on and offline.&lt;/p&gt;

&lt;p&gt;The Telly Awards annually showcases the best work created within television and across video, for all screens. Receiving over 12,000 entries from all 50 states and 5 continents, Telly Award winners represent work from some of the most respected advertising agencies, television stations, production companies and publishers from around the world. The Telly Awards recognizes work that has been created on the behalf of a client, for a specific brand and/or company (including your own) or self-directed as a creative endeavor.&lt;/p&gt;

&lt;p&gt;This year we entered the &lt;strong&gt;Build a 3D Printed Clippy Powered by ChatGPT from OpenAI, Azure Cognitive Services&lt;/strong&gt; episode of Let’s get personal: computing, and we won a &lt;a href="https://www.tellyawards.com/winners/2023/online/general-talk-show-interview" rel="noopener noreferrer"&gt;Silver award in the online talk show/interview category&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Check out the video here:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/6OYGeOwd6RY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>clippy</category>
      <category>video</category>
    </item>
    <item>
      <title>Run Azure Functions with Python on Apple Silicon</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Wed, 17 May 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/azure/run-azure-functions-with-python-on-apple-silicon-545l</link>
      <guid>https://forem.com/azure/run-azure-functions-with-python-on-apple-silicon-545l</guid>
      <description>&lt;p&gt;If you are an Apple user and you want to build Azure Functions using Python, then you’ve been out of luck. The Azure Functions core tools don’t support Python functions on Arm64. There is a workaround, and this post shows you how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arm64 support in Azure Functions
&lt;/h2&gt;

&lt;p&gt;Apple moved to an Arm64 architecture with their Apple Silicon processors (currently M1 and M2) back in November 2020, with their entire linup now using Apple Silicon, except for the rather outdated Mac Pro. Despite this, support for running Python functions on Arm64 has not yet materialized. You can create functions with &lt;code&gt;func init&lt;/code&gt; and &lt;code&gt;func new&lt;/code&gt;, but when you run them, you get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ functions-m1 func start
Found Python version 3.10.11 (python3).

Azure Functions Core Tools
Core Tools Version: 4.0.5095 Commit hash: N/A (64-bit)
Function Runtime Version: 4.16.5.20396

[2023-05-08T21:29:33.030Z] Failed to initialize worker provider for: /opt/homebrew/Cellar/azure-functions-core-tools@4/4.0.5095/workers/python
[2023-05-08T21:29:33.030Z] Microsoft.Azure.WebJobs.Script: Architecture Arm64 is not supported for language python.
[2023-05-08T21:29:33.409Z] Failed to initialize worker provider for: /opt/homebrew/Cellar/azure-functions-core-tools@4/4.0.5095/workers/python
[2023-05-08T21:29:33.409Z] Microsoft.Azure.WebJobs.Script: Architecture Arm64 is not supported for language python.
[2023-05-08T21:29:33.558Z] A host error has occurred during startup operation '2a13f8c8-f2e4-4da0-a88c-205dbd3065c2'.
[2023-05-08T21:29:33.558Z] Microsoft.Azure.WebJobs.Script: Did not find functions with language [python].
[2023-05-08T21:29:33.564Z] Failed to stop host instance '1e7799d1-a87f-404a-9bc1-858c7724aeec'.
[2023-05-08T21:29:33.564Z] Microsoft.Azure.WebJobs.Host: The host has not yet started.
Value cannot be null. (Parameter 'provider')

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

&lt;/div&gt;



&lt;p&gt;Microsoft originally targeted support for their Python 3.10 release, but missed this. Currently they are saying it will come with Python 3.11 support, as documented in &lt;a href="https://github.com/Azure/azure-functions-python-worker/issues/915" rel="noopener noreferrer"&gt;this GitHub issue&lt;/a&gt;. Their suggested workaround involves changing your Terminal to always run under Rosetta 2 which feels like a bad way to do things. There is a better workaround, leveraging dev containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev containers
&lt;/h2&gt;

&lt;p&gt;Dev containers are docker containers that you run and connect to from VS Code. As a user these are seamless - you can still access the file system from outside of VS Code, but internally the dev environment runs in the container, with all your dependencies installed in the container instead of on your host OS. You can read about these on &lt;a href="https://code.visualstudio.com/docs/devcontainers/containers" rel="noopener noreferrer"&gt;the VS Code developing inside containers documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker supports running x86 and x64 containers on Apple Silicon Macs using either QEMU emulation, or using Rosetta 2. This means you can create an x64 container and run this on your Mac, running the Azure Functions core tools inside the container! Pretty cool, hey!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure docker
&lt;/h2&gt;

&lt;p&gt;You need &lt;a href="https://www.docker.com" rel="noopener noreferrer"&gt;docker&lt;/a&gt; installed, so get this installed first. Once installed, you need to configure a few things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;From the docker menu item, select &lt;strong&gt;Settings&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From the &lt;strong&gt;General&lt;/strong&gt; section, ensure &lt;em&gt;Use Virtualization framework&lt;/em&gt; is selected&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhc79iv0vp24qrgii57al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhc79iv0vp24qrgii57al.png" alt="The Use Virtualization framework in the general tab of the settings dialog" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the &lt;strong&gt;Features in development&lt;/strong&gt; section, select the &lt;strong&gt;Beat Features&lt;/strong&gt; and select &lt;em&gt;Use Rosetta for x86/amd64 emulation on Apple Silicon&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczipttieqhk8knu6pbvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczipttieqhk8knu6pbvb.png" alt="The Use Rosetta for x86/amd64 emulation on Apple Silicon setting from the Features in development section" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apply these changes and docker desktop will restart.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create the dev container
&lt;/h2&gt;

&lt;p&gt;The default Azure Functions and Python3 dev container is ideal for this as it is an x86/x64 container.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Make sure docker is running.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a folder for your Azure Functions app and open it in VS Code (or open the one you already have created)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure you have the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers" rel="noopener noreferrer"&gt;Dev Containers extension&lt;/a&gt; installed. This also comes with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack" rel="noopener noreferrer"&gt;Remote Development extension Pack&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the command palette and select &lt;em&gt;Dev Containers: Add Dev Container configuration files…&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplpn9lmph7133vn7fox5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplpn9lmph7133vn7fox5.png" alt="The Dev Containers: Add Dev Container configuration files command palette option" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select &lt;em&gt;Show all definitions&lt;/em&gt; to see all the dev container options&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Search for and select &lt;em&gt;Azure Functions &amp;amp; Python 3&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3cmfqdph1psdhjho8xd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3cmfqdph1psdhjho8xd.png" alt="The definitions filtered by Azure Functions with the cursor over Azure Functions &amp;amp; Python 3" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select any other features you want. You don’t need any for Azure Functions to work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The container files will be built, and an option will pop up to re-open the folder in the container. Select &lt;strong&gt;Reopen in Container&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Once the repo has been re-opened in the container, open the terminal and you will see it marked as running under Rosetta&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8plelvqaujy9kisxo90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8plelvqaujy9kisxo90.png" alt="The terminal with an indicator on the toolbar showing it is running under Rosetta" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you don’t already have an Azure Functions app, you can now create one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run your Azure Functions app!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you commit the devcontainer files to GitHub, you will also be able to open this in a Codespace and have it pre-configured with everything you need. You can also add more things to the container depending on your needs, such as extensions that are automatically loaded when the container is loaded, install things, whatever you need! Check out the &lt;a href="https://code.visualstudio.com/docs/devcontainers/containers" rel="noopener noreferrer"&gt;the VS Code developing inside containers documentation&lt;/a&gt; to learn more.&lt;/p&gt;

</description>
      <category>python</category>
      <category>azurefunctions</category>
      <category>applesilicon</category>
      <category>azure</category>
    </item>
    <item>
      <title>Build a ChatGPT clone in .NET using OpenAI and Semantic Kernel</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Tue, 11 Apr 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/dotnet/build-a-chatgpt-clone-in-net-using-openai-and-semantic-kernel-28pa</link>
      <guid>https://forem.com/dotnet/build-a-chatgpt-clone-in-net-using-openai-and-semantic-kernel-28pa</guid>
      <description>&lt;p&gt;ChatGPT is the latest big thing, reaching over 100 million users in just 2 months. It’s being used by millions of people for all manner of tasks, and it feels like every day there are 10 new startups leveraging either ChatGPT or the large language models (LLMs) that power it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ua5bh7pqih7o4ppr2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ua5bh7pqih7o4ppr2p.png" alt="Mugatu meme of Chat GPT, so hot right now" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what if you want to build your own version of ChatGPT, or another app that leverages an LLM? Out of the box, LLMs just provide an API to send a prompt and get a response, so what tools are there to help?&lt;/p&gt;

&lt;p&gt;One tool is Semantic Kernel, an open source .NET library from Microsoft that is designed to do a lot of the heavy lifting when it comes to interacting with LLMs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ud7limv4gghgtj1f7lt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ud7limv4gghgtj1f7lt.png" alt="Tge sematic kernel pattern of pink circles with different shades for each quadrant" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you prefer video, here’s a 10 minute video version of this blog post&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Pg-9W0a1OXw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How does ChatGPT work with an LLM?
&lt;/h2&gt;

&lt;p&gt;ChatGPT allows you to have a conversation with a large language model, with ChatGPT being able to base the response to one question on the results of previous questions.&lt;/p&gt;

&lt;p&gt;For example, if you ask it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the most recent star wars movie?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will respond something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most recent Star Wars movie is “Star Wars: The Rise of Skywalker,” which was released in December 2019. It is the ninth and final installment in the Skywalker saga.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you then ask a follow up question such as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who is the main character?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The response will ‘know’ that the question refers to the movie the Rise of Skywalker:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The main character in “Star Wars: The Rise of Skywalker” is Rey, played by Daisy Ridley. She is a powerful Jedi who is trying to find her place in the galaxy and confronts the evil First Order, led by Kylo Ren, played by Adam Driver. The movie also features other beloved characters from the Star Wars franchise, such as Finn, Poe Dameron, Chewbacca, and C-3PO.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LLMs, the core technology behind ChatGPT, don’t chat – they take a prompt and return a response, that’s all. To chat, they need to be guided by having access to the previous questions and responses.&lt;/p&gt;

&lt;p&gt;ChatGPT works by doing the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user enters a question, referred to as a prompt&lt;/li&gt;
&lt;li&gt;This prompt is enriched by a system prompt that helps guide the LLM, such as ‘you are a helpful chatbot’&lt;/li&gt;
&lt;li&gt;The prompt is sent to the LLM and the response is returned to the user&lt;/li&gt;
&lt;li&gt;The user enters a second question&lt;/li&gt;
&lt;li&gt;This time the prompt is enriched by the system prompt as well as the previous question and response&lt;/li&gt;
&lt;li&gt;The prompt is sent to the LLM and the response is returned to the user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, when the second question is sent, the full prompt might be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a helpful chatbot. You were asked ‘What is the most recent star wars movie?’ and you answered ‘The most recent Star Wars movie is “Star Wars: The Rise of Skywalker,” which was released in December 2019. It is the ninth and final installment in the Skywalker saga’. Who is the main character?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To implement your own ChatGPT you need to implement code to get the prompt from the user, enrich it with a system prompt, send it to the LLM, get the response, then enrich further questions with the previous questions and responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can semantic kernel help?
&lt;/h2&gt;

&lt;p&gt;Semantic Kernel provides a load of features to help you implement this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Services to interact with LLMs from OpenAI either directly or via the Azure OpenAI service&lt;/li&gt;
&lt;li&gt;System prompts to pre-configure the conversation&lt;/li&gt;
&lt;li&gt;Chat history, or ‘memory’&lt;/li&gt;
&lt;li&gt;Semantic functions that can be defined in code such as getting user input and returning the response, or defined as prompts that can be sent to the LLM&lt;/li&gt;
&lt;li&gt;Function pipelines to chain semantic functions&lt;/li&gt;
&lt;li&gt;Core skills such as interacting with HTTP APIs or files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Semantic functions and pipelines
&lt;/h3&gt;

&lt;p&gt;The core of semantic kernel is a pipeline that runs a series of functions. These functions either take text, return text, or both. Large language models work with text, so text is the main way to interact. The idea of the pipeline is text is passed through from function to function.&lt;/p&gt;

&lt;p&gt;For example, for a ChatGPT clone, you could have a pipeline that gets input from the user, sends it to the LLM with the previous questions and responses, then send the response back to the user.&lt;/p&gt;

&lt;p&gt;For a sample project, check out &lt;a href="https://github.com/jimbobbennett/console-gpt" rel="noopener noreferrer"&gt;github.com/jimbobbennett/console-gpt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this code, there is a &lt;code&gt;ConsoleGPTService&lt;/code&gt; that has a pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ISKFunction&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_speechSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Listen"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_chatSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Prompt"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_speechSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Respond"&lt;/span&gt;&lt;span class="p"&gt;]};&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_semanticKernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pipeline runs 3 semantic functions, routing the output of one into the input of the other. Semantic functions are C# functions that are marked with an attribute and loaded into the pipeline.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Listen&lt;/code&gt; function in this code is defined in the &lt;code&gt;ConsoleSkill&lt;/code&gt; and gets data from the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Get console input."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listen"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SKContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadLine&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns what the used inputs, and this is passed to the &lt;code&gt;Prompt&lt;/code&gt; semantic function in the &lt;code&gt;ChatSkill&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Send a prompt to the LLM."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Prompt"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;_chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_chatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GenerateMessageAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_chatHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_chatRequestSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="c1"&gt;// Add the interaction to the chat history.&lt;/span&gt;
 &lt;span class="n"&gt;_chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAssistantMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses the &lt;code&gt;AddOpenAIChatCompletionService&lt;/code&gt;, a service provided by the semantic kernel to interact with OpenAI. This service is a chat service, so manages history - when you call it you pass the complete chat history including a system prompt that sets up the conversation, and all the prompts and responses.&lt;/p&gt;

&lt;p&gt;The output of this is then passed to a &lt;code&gt;Respond&lt;/code&gt; semantic function to write it to the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Write a response to the console."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SKFunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Respond"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SKContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using semantic functions you can quickly swap out functions by implementing the same signature in multiple places and bringing the one you want into the semantic kernel. You can also construct pipelines on the fly, adding sections if you need.&lt;/p&gt;

&lt;p&gt;Semantic functions can also be created using prompts rather than code. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;poemPrompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
&lt;/span&gt; &lt;span class="n"&gt;Take&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="s"&gt;"{{$INPUT}}"&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;poem&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;iambic&lt;/span&gt; &lt;span class="n"&gt;pentameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
 &lt;span class="s"&gt;""";
&lt;/span&gt;
&lt;span class="n"&gt;_poemFunction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_semanticKernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateSemanticFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poemPrompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then add this to the pipeline, and the output from the previous function in the pipeline is passed to the &lt;code&gt;{{$INPUT}}&lt;/code&gt; variable in the prompt. In this case using the LLM to convert to a poem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ISKFunction&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_speechSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Listen"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_chatSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Prompt"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;_poemFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_speechSkill&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Respond"&lt;/span&gt;&lt;span class="p"&gt;]};&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_semanticKernel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RunAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means for the prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is the most recent star wars movie?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You might get this poem:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Force of Fate has brought us here,&lt;/p&gt;

&lt;p&gt;To see the Rise of Skywalker near,&lt;/p&gt;

&lt;p&gt;The ninth and final part of this tale,&lt;/p&gt;

&lt;p&gt;Released in December, ’twas no fail.&lt;/p&gt;

&lt;p&gt;Star Wars, the movie, we all know,&lt;/p&gt;

&lt;p&gt;The saga of the Skywalkers show,&lt;/p&gt;

&lt;p&gt;The Force will live on, we can see,&lt;/p&gt;

&lt;p&gt;In this movie, ’tis our destiny.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;You can get the code on my &lt;a href="https://github.com/jimbobbennett/console-gpt" rel="noopener noreferrer"&gt;console-gpt repo on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;p&gt;Here are some more resources for Semantic Kernel&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/semantic-kernel" rel="noopener noreferrer"&gt;The Semantic Kernel documentation on Microsoft Learn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/semantic-kernel" rel="noopener noreferrer"&gt;The Semantic Kernel repo on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aka.ms/sk/discord" rel="noopener noreferrer"&gt;The Semantic Kernel Discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/semantic-kernel/" rel="noopener noreferrer"&gt;The Semantic Kernel blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>chatgpt</category>
      <category>ai</category>
      <category>generativeai</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>An intro to generative AI created by generative AI!</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Fri, 31 Mar 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/an-intro-to-generative-ai-created-by-generative-ai-jl5</link>
      <guid>https://forem.com/jimbobbennett/an-intro-to-generative-ai-created-by-generative-ai-jl5</guid>
      <description>&lt;p&gt;Generative AI is the new hotness - using AI models trained on a huge amount of data that can generate new data. This includes generating text or images. I decided to create a video giving an explanation of generative AI using - you guessed it, generative AI!&lt;/p&gt;

&lt;p&gt;Check out the video here:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/OUpW9O3zabs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating this video
&lt;/h2&gt;

&lt;p&gt;To create this video I needed a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An image of a person to use for the presenter&lt;/li&gt;
&lt;li&gt;A script&lt;/li&gt;
&lt;li&gt;A way to convert the script to spoken audio&lt;/li&gt;
&lt;li&gt;A way to make the image move in time with the audio&lt;/li&gt;
&lt;li&gt;A YouTube description&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My plan was to do all of this using AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the image
&lt;/h3&gt;

&lt;p&gt;To create the image, I decided to use &lt;a href="https://openai.com/product/dall-e-2" rel="noopener noreferrer"&gt;DALL-E-2&lt;/a&gt;, the latest image generation model from OpenAI. This model takes a prompt and generates an image. You can either use it through their website at &lt;a href="https://labs.openai.com/" rel="noopener noreferrer"&gt;labs.openai.com&lt;/a&gt;, or you can generate images using their &lt;a href="https://platform.openai.com/docs/api-reference/images" rel="noopener noreferrer"&gt;API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When creating a prompt you need to be as descriptive as possible so that the AI can generate the image. The website generates 4 images for each prompt, all different.&lt;/p&gt;

&lt;p&gt;This is the prompt I used:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A hyper realistic bust upwards portrait of a classically beautiful mid-20s woman of indeterminate race with rainbow hair, who looks like a cross between a CEO and an opera singer and is posed facing the camera. She is wearing a colorful dress and is standing in front of a plain black backdrop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are the 4 images created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0acn91mz76r4szvrjs6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0acn91mz76r4szvrjs6.jpg" alt="4 images of women with rainbow hair and colorful dresses" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I picked the one with the full head as I wanted to animate the full face.&lt;/p&gt;

&lt;p&gt;This took a few iterations to get a prompt that created images I liked, such as age, outfit and style of dress. Most of the images were of the right quality, but some had a few quirks. For example, the eyes in this image were not well generated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7813u5nl2khhgfehetp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7813u5nl2khhgfehetp.png" alt="A womans face with mismatched eye size and color, with artifacts on the eyes" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DALL-E creates square images, at a default of 1024x1024, so I knew I would need to add extra sizing on the sides or top and bottom to get the right aspect ratio. THis is why I added &lt;strong&gt;standing in front of a plain black backdrop&lt;/strong&gt; to the prompt, so I could fill out the extra space with a single color.&lt;/p&gt;

&lt;p&gt;Once generated, I downloaded the image, and then resized it to a higher resolution using the &lt;a href="https://www.pixelmator.com/blog/2019/12/17/all-about-the-new-ml-super-resolution-feature-in-pixelmator-pro/" rel="noopener noreferrer"&gt;ML super resolution feature of Pixelmator Pro&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the script
&lt;/h3&gt;

&lt;p&gt;Next up was something for the generated AI character to say. For this I decided to use &lt;a href="https://chat.openai.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; with the &lt;a href="https://openai.com/research/gpt-4" rel="noopener noreferrer"&gt;GPT4 model&lt;/a&gt;. I decided to sign up for this at US$20 a month rather than wait, so will see if it is a worthwhile investment.&lt;/p&gt;

&lt;p&gt;ChatGPT generates text for you based of textual prompts, and has a ‘memory’ to remember your conversation and use this as the input to the next prompts, allowing you to have a conversation-like discussion.&lt;/p&gt;

&lt;p&gt;This is the prompt I used:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are Ganya, the CEO of an AI company and your company is called Alvie. Write a 5 minute talk that covers an introduction to generative AI and how it can benefit businesses. This talk needs to be entertaining with a couple of jokes, but also informative. The audience for this talk is a group of students who don’t have strong technical knowledge.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The name Ganya is a unisex name used with Hebrew origins, and means clever. Alvie is a English female name, and is short for Alvina. It is a name which means wise friend. I picked these names at random from a baby name site listing names that mean intelligent.&lt;/p&gt;

&lt;p&gt;This is the output it generated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ladies and gentlemen, future tech wizards, and brilliant entrepreneurs, thank you for inviting me to speak today. I am Ganya, the CEO of Alvie, and I’ll be your digital tour guide on this wild journey through the fascinating world of generative AI. So, buckle up, because we’re about to embark on an adventure that’s more thrilling than binge-watching a season of your favorite sci-fi show!&lt;/p&gt;

&lt;p&gt;Now, you might be wondering, “What the heck is generative AI?” Well, you know how your phone’s predictive text often tries to guess what you’re typing before you even finish typing it? Yeah, that’s a tiny taste of generative AI! It’s like a mind-reading superpower, but for machines. And it’s not only limited to text, it can be used for images, music, and even entire virtual worlds.&lt;/p&gt;

&lt;p&gt;You see, generative AI is the cool cousin of traditional AI. Instead of being the nerdy, know-it-all who’s good at math, generative AI is the creative type who spends their time painting, writing poetry, or designing websites. It’s the kind of AI that dares to dream, and it’s revolutionizing the way we think about technology.&lt;/p&gt;

&lt;p&gt;But let’s take a step back and look at the big picture. Generative AI is like a chef, and data is its secret ingredient. It takes massive amounts of data, mixes it all up, and uses this delicious concoction to create brand-new content, whether that’s text, images, or music. Essentially, it’s a digital Picasso with a dash of Shakespeare and a sprinkle of Mozart.&lt;/p&gt;

&lt;p&gt;Now, I know what you’re thinking: “That’s all great, Ganya, but how can generative AI help my business? I’m not running an art gallery or a concert hall!” Well, fear not! Generative AI is a versatile beast and can benefit businesses in many ways.&lt;/p&gt;

&lt;p&gt;For example, take customer service. We all know how frustrating it can be to deal with a grumpy customer service rep who clearly didn’t have their morning coffee. Enter generative AI: it can be used to create friendly, informative, and highly responsive chatbots that can handle thousands of customer inquiries at once, without ever needing a caffeine fix. That’s what I call efficiency!&lt;/p&gt;

&lt;p&gt;Speaking of efficiency, generative AI can also help companies optimize their workflow. Imagine having a smart algorithm that can analyze complex data sets and automatically generate insightful reports for your team. It’s like having an intern who never gets tired, never complains, and doesn’t need a salary. The only downside? You can’t send it out to fetch you coffee, but hey, nobody’s perfect!&lt;/p&gt;

&lt;p&gt;Now, I can’t let you leave without a joke or two, can I? So, here’s one for you: Why did the generative AI go to therapy? Because it had deep learning issues! (Pause for laughter) Okay, okay, I promise I won’t quit my day job.&lt;/p&gt;

&lt;p&gt;But in all seriousness, folks, generative AI is an incredible tool that can transform the way we do business. It can help companies save time, money, and resources, while also enabling us to create new, innovative products and services. As we embark on this exciting journey together, let’s remember that the future of AI is not just about cold, hard numbers; it’s about tapping into the boundless creativity that lies within each and every one of us.&lt;/p&gt;

&lt;p&gt;Thank you for your time, and I hope this little chat has sparked your imagination and inspired you to explore the fantastic world of generative AI!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Convert the script to spoken audio
&lt;/h3&gt;

&lt;p&gt;There are a number of tools that can take text and create spoken audio. The best ones use AI to make the voice sound lifelike with changes in timing and intonation that mimic how a real person would speak. As a Microsoft person I could have used the &lt;a href="https://speech.microsoft.com/" rel="noopener noreferrer"&gt;Cognitive services speech service&lt;/a&gt; but this would have meant writing code, and I couldn’t be bothered…&lt;/p&gt;

&lt;p&gt;Instead I used &lt;a href="https://elevenlabs.io" rel="noopener noreferrer"&gt;Eleven Labs&lt;/a&gt; which has a free tier for up to 10,000 characters per month. You can select from a range of different voices, paste in your text and it creates the audio as an MP3 file. There is a limit of 2,500 characters per conversion, so I had to do it in 2 chunks as my text was 3,335 characters long, then use Camtasia to stitch the audio files together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a video speaking the audio
&lt;/h3&gt;

&lt;p&gt;The next step was to create the video by mashing up the audio and the image, and animating the image to speak the words. There are a number of tools that could contribute to this, such as &lt;a href="https://github.com/adobe-research/MakeItTalk" rel="noopener noreferrer"&gt;Make it talk from Adobe research&lt;/a&gt;, but I wanted a solution that I didn’t need to run myself.&lt;/p&gt;

&lt;p&gt;I ended up using &lt;a href="https://www.d-id.com" rel="noopener noreferrer"&gt;D-ID&lt;/a&gt; which is not cheap! They have a 14 day free tier for up to 5 minutes of video, but beyond that it gets very expensive. It works really well though! You can either use one of their avatars, or upload an image yourself. Once they have the image, you provide either the script and it will generate the audio for you (using the Microsoft Cognitive services under the hood), or upload the audio.&lt;/p&gt;

&lt;p&gt;It then allows you to download the final video, and because I had a square image it sized it to 1280x1280. I then open up Camtasia one more time to create 2 videos with this - one at 1920x1080 for YouTube, and one at 1080x1920 for Instagram, filling the extra space around the video with the background color which was slightly off-black.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a YouTube video
&lt;/h3&gt;

&lt;p&gt;Now I had my video, it was time to upload to YouTube. I uploaded the video and knocked up a simple thumbnail with text for the video name manually. Tools like DALL-E are not good for adding text to images, so something to research is a tool for this.&lt;/p&gt;

&lt;p&gt;I then needed a description, so it was time to go back to ChatGPT! The prompt I used was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate a YouTube description for a video with the following text:&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;I then added the complete script to the prompt (I cut it off here for brevity). ChatGPT then generated the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Title: Exploring the World of Generative AI with Alvie CEO Ganya&lt;/p&gt;

&lt;p&gt;Description:&lt;/p&gt;

&lt;p&gt;🚀 Join Ganya, the CEO of Alvie, as she takes you on an exciting and informative journey through the world of generative AI! In this video, Ganya breaks down what generative AI is, how it works, and its potential to revolutionize the way we do business. From creative applications like art, music, and writing to practical uses in customer service and workflow optimization, generative AI offers a world of possibilities.&lt;/p&gt;

&lt;p&gt;🤖 Key Points:&lt;br&gt;
0:00 - Introduction and Welcome&lt;br&gt;
1:05 - What is Generative AI?&lt;br&gt;
2:38 - How Generative AI Works: Data and Creativity&lt;br&gt;
4:15 - Benefits of Generative AI for Businesses&lt;br&gt;
6:45 - The Future of AI and Creativity&lt;br&gt;
8:30 - Jokes and Closing Remarks&lt;/p&gt;

&lt;p&gt;🔮 Don’t miss this opportunity to learn about the fantastic world of generative AI and its potential to unlock boundless creativity in technology and business. Hit the subscribe button to stay updated on the latest advancements and insights from Alvie!&lt;/p&gt;

&lt;p&gt;#GenerativeAI #Alvie #AIinBusiness&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty good stuff! I removed the &lt;code&gt;#Alvie&lt;/code&gt; tag as this makes no sense as Alvie doesn’t exist, and had to re-do the time stamps for the chapter markers as ChatGPT has no way of knowing the timings. But otherwise it was perfect!&lt;/p&gt;

&lt;p&gt;I added a disclaimer at the start of the description to say this was AI generated, and listed the tools.&lt;/p&gt;

&lt;p&gt;And I was done! Video created and uploaded all powered by generative AI!&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s next?
&lt;/h3&gt;

&lt;p&gt;My next job is to try to automate all this into a tool so I can give it a prompt and have it create and upload the full video without any manual intervention.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>ai</category>
      <category>generativeai</category>
      <category>openai</category>
    </item>
    <item>
      <title>Embedding audio files in a Hugo site</title>
      <dc:creator>Jim Bennett</dc:creator>
      <pubDate>Fri, 31 Mar 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/jimbobbennett/embedding-audio-files-in-a-hugo-site-34e</link>
      <guid>https://forem.com/jimbobbennett/embedding-audio-files-in-a-hugo-site-34e</guid>
      <description>&lt;p&gt;I was writing a post today and wanted to embed an mp3 file of some text to speech output. Hugo doesn’t support this natively using shortcodes, so I needed a way to add these, ideally without adding any HTML.&lt;/p&gt;

&lt;p&gt;To do this, I needed to create my own shortcode implementation. As it turns out, based on the &lt;a href="https://gohugo.io/templates/shortcode-templates/" rel="noopener noreferrer"&gt;Hugo shortcode guide&lt;/a&gt;, these are not to complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the shortcode
&lt;/h2&gt;

&lt;p&gt;Shortcodes live in a folder called &lt;code&gt;shortcodes&lt;/code&gt; in your &lt;code&gt;layout&lt;/code&gt; folder and are implemented as HTML files, named as &lt;code&gt;&amp;lt;shortcode&amp;gt;.html&lt;/code&gt;. For example, if you wanted to create a shortcode called &lt;code&gt;audio&lt;/code&gt; you would create the file &lt;code&gt;layout/shortcodes/audio.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Shortcodes are snippets of HTML that can be passed named parameters. The HTML for an HTML audio player is:&lt;/p&gt;

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

&amp;lt;audio controls preload="auto"&amp;gt;
 &amp;lt;source src="file.mp3"&amp;gt;
&amp;lt;/audio&amp;gt;



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

&lt;/div&gt;

&lt;p&gt;Shortcodes can also be parameterized with name parameters that you can get using the &lt;code&gt;{{ .Get "name" }}&lt;/code&gt; method, passing the name of the parameter. For the audio shortcode, I need to pass in the audio file, so this can be a parameter. This is set in the &lt;code&gt;src&lt;/code&gt; field, so I called this parameter &lt;code&gt;src&lt;/code&gt;:&lt;/p&gt;

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

&amp;lt;audio controls preload="auto"&amp;gt;
 &amp;lt;source src="{{ .Get "src" }}"&amp;gt;
&amp;lt;/audio&amp;gt;



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

&lt;/div&gt;

&lt;p&gt;Done! This is my entire shortcode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the shortcode
&lt;/h2&gt;

&lt;p&gt;Once my shortcode was written, it was easy to use. I added the mp3 file I want to play to the folder for my blog post, and added the shortcode tag in the markdown file for the post:&lt;/p&gt;

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

{&amp;lt; audio src="intro-to-generative-ai.m4a" &amp;gt;}


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

&lt;/div&gt;

&lt;p&gt;Once done, the audio player appears on my page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymjbbvnalnk66w8f7lbq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymjbbvnalnk66w8f7lbq.png" alt="The audio player on a blog post page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use this yourself
&lt;/h2&gt;

&lt;p&gt;If you want to use this shortcode, you can find it &lt;a href="https://github.com/jimbobbennett/jims-blog/blob/main/blog/layouts/shortcodes/audio.html" rel="noopener noreferrer"&gt;on the GitHub repo for this site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>webdev</category>
      <category>audio</category>
      <category>html</category>
    </item>
  </channel>
</rss>
