<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maho Pacheco</title>
    <description>The latest articles on DEV Community by Maho Pacheco (@mahopacheco).</description>
    <link>https://dev.to/mahopacheco</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%2F125108%2F995e499f-8304-4e25-9c87-825a670c8297.png</url>
      <title>DEV Community: Maho Pacheco</title>
      <link>https://dev.to/mahopacheco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mahopacheco"/>
    <language>en</language>
    <item>
      <title>OpenAI and .NET tutorial with Semantic Kernel - Part 1</title>
      <dc:creator>Maho Pacheco</dc:creator>
      <pubDate>Sun, 31 Mar 2024 22:20:00 +0000</pubDate>
      <link>https://dev.to/mahopacheco/openai-and-net-tutorial-with-semantic-kernel-part-1-2g9i</link>
      <guid>https://dev.to/mahopacheco/openai-and-net-tutorial-with-semantic-kernel-part-1-2g9i</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;If you prefer video, there is a &lt;a href="https://youtu.be/UQleZ68EOZQ"&gt;YouTube version of this blog-post&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since the release of LLMs, I have been extremely enthusiastic about exploring such a fun tool to play with. I won't delve into discussions about how transformative it is or will be, but rather focus on the enjoyable aspect of using this technology.&lt;/p&gt;

&lt;p&gt;In the broader context of ML/AI and data science, Python stands out as the most commonly used programming language. Despite my open-source enthusiasm, I have always found C# to be remarkable. With the introduction of Mono.NET and later dotnet core, I have happily used it in Linux environments. It's no surprise that when working on projects related to ML/AI, C# and .NET are my preferred choices.&lt;/p&gt;

&lt;p&gt;Over the past few months, I've transitioned from being a mere user to someone who shares what I've learned. I find the topics of evaluating models and Responsible AI particularly fascinating. I've created some code samples that I'm excited to share.&lt;/p&gt;

&lt;p&gt;However, in line with my approach to sharing knowledge in other topics, I prefer to establish a common ground or baseline understanding. Therefore, I've decided to start from the ground up, sharing and teaching how to build a small tool based on OpenAI or other LLMs. I'll then progress to unit testing, evaluating models, and observability – all in C# and dotnet. I acknowledge that there are already numerous Python projects in this space, and it is my desire to include the dotnet community.&lt;/p&gt;

&lt;p&gt;So, buckle up! This will be a 4-5 post series, and I may even record a couple of videos for the first time in my life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Knowledge
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Understanding of what OpenAI is and the concept of a LLM.&lt;/li&gt;
&lt;li&gt;Optional but highly recommended basic knowledge of Azure (primarily for deploying an Azure OpenAI model).&lt;/li&gt;
&lt;li&gt;Familiarity with C# and .NET (expertise is not necessary; basic understanding will suffice).&lt;/li&gt;
&lt;li&gt;Know how to Google/Bing/search the internet. If any section does not have detailed instructions is on purpose, there is maybe plenty of articles already out there of how to do that specific step.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tech stuff
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A computer with Mac, Linux or Windows&lt;/li&gt;
&lt;li&gt;dotnet 8.0 installed, or, a docker enabled machine, or, a github account&lt;/li&gt;
&lt;li&gt;This tutorial will use VSCode, but any editor (vim, visual studio code, notepad++, wherever) should suffice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Idea Overview
&lt;/h2&gt;

&lt;p&gt;I have decided we will build a short post content generator. Think of something to generate tweets/toots, mastodon posts, or LinkedIn posts, but very short (100-200 characters). Feel free to build something different, I actually encourage to try something different, this tutorial should be still helpful.&lt;/p&gt;

&lt;p&gt;We will provide 3 inputs: style, topic, and persona field. The prompt would look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a [persona] expert. Generate a short tweet in a [style] style about [topic].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are a marketing expert. Generate a short tweet in a sarcastic style about the challenges of creating a viral campaign in summer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may be asking, what is the different between creating something&lt;br&gt;
from scratch than only using a ChatGPT agent? Well, you could integrate it with other products, platforms by doing your own API. You could start improving it with your own examples, you could even use it with a very small non-OpenAI local LLM to run it in a non-cloud environment. And of course, you can just have fun learning. There is a lot of benefits.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 0
&lt;/h2&gt;

&lt;p&gt;If you already have dotnet installed skip to Step 1. If you don't, you can use a ready to use devcontainer from this repo, just clone the repo, open it in vscode and start the remote container. If you don't have a docker enabled machine, you could always use GitHub Codespaces.&lt;/p&gt;

&lt;p&gt;This is one of those things where there are many posts out there that explain this part better than me, so I am on purpose not adding instructions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1
&lt;/h2&gt;

&lt;p&gt;So, we will start by creating a new command line project, we will open a terminal (bash or powershell should work) and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new console &lt;span class="nt"&gt;-n&lt;/span&gt; PostGenerator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this will generate a &lt;code&gt;Program.cs&lt;/code&gt; and postgenerator.csproj&lt;/p&gt;

&lt;p&gt;Now we will add a couple of packages, the first one is Semantic Kernel. Semantic Kernel is one of my favorite libraries, to play with OpenAI. So we enter the folder &lt;code&gt;PostGenerator&lt;/code&gt; and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.SemanticKernel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also add a couple of extensions that are useful from the beginning, the first is about Dependency Injection, and the second logging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Logging
dotnet add package Microsoft.Extensions.Logging.Console
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may argue, why we need this from day zero or with a prototype.&lt;br&gt;
With the time and experience you start answering to each question with "depends" and at the same time getting more opinionated in the way of work. This is one of those topics where there is no right or wrong (depends), but I have found very useful to use DI and Logging extensions from the beginning to avoid refactor my stuff later. You may agree or not, and that is fine.&lt;/p&gt;

&lt;p&gt;The next one is more controversial, it is a library to handle the arguments in my command-line tool. I think (opinion) that is the same effort to handle the arguments array (and ugly effort), than implement it with the right devx with a utility library. So let's install, &lt;code&gt;System.CommandLine&lt;/code&gt;, which is in preview, hence the need of the &lt;code&gt;--prerelease&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package System.CommandLine &lt;span class="nt"&gt;--prerelease&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's verify that our program still builds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
...
Build succeeded.
    0 Warning&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;
    0 Error&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;

Time Elapsed 00:00:04.13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;Now to the fun part, let's scallfold the command line utility. We will start by adding 3 parameters: &lt;code&gt;--persona&lt;/code&gt;, &lt;code&gt;--topic&lt;/code&gt;, and &lt;code&gt;--style&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.CommandLine.Parsing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.CommandLine&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;personaOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Option&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="s"&gt;"--persona"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IsRequired&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&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;topicOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Option&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="s"&gt;"--topic"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IsRequired&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&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;styleOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Option&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="s"&gt;"--style"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IsRequired&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also add a "root" command, and the "create-post" command.&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;rootCommand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RootCommand&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;createPostCommand&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"create-post"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Create a new post"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;personaOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topicOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;styleOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;rootCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: Fun fact, if you don't add the AddOption, it will still work, but things like IsRequired will be ignored.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We finally execute the command and return the result. Notice how we don't need to specify the main void static args, but we will use the &lt;code&gt;args&lt;/code&gt; variable. Cool, right?&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;result&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;rootCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&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;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;dotnet run
Required &lt;span class="nb"&gt;command &lt;/span&gt;was not provided.

Description:

Usage:
  PostGenerator &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;options]

Options:
  &lt;span class="nt"&gt;--version&lt;/span&gt;       Show version information
  -?, &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--help&lt;/span&gt;  Show &lt;span class="nb"&gt;help &lt;/span&gt;and usage information

Commands:
  create-post  Create a new post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we provide the &lt;code&gt;create-post&lt;/code&gt; command, we get a blank response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$dotnet&lt;/span&gt; run &lt;span class="nt"&gt;--&lt;/span&gt; create-post &lt;span class="nt"&gt;--persona&lt;/span&gt; &lt;span class="s2"&gt;"software developer"&lt;/span&gt; &lt;span class="nt"&gt;--topic&lt;/span&gt; &lt;span class="s2"&gt;"unit tests"&lt;/span&gt; &lt;span class="nt"&gt;--style&lt;/span&gt; &lt;span class="s2"&gt;"sarcastic"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because there is no handler for the command, let's add one just before adding the &lt;code&gt;createPost&lt;/code&gt; command to the root command.&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;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;persona&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&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="s"&gt;$"Command requested for &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;persona&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Command not implemented"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;personaOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topicOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styleOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other of personaOption, topicOption, styleOption, does not really matter, the user can provide them in any order, we just need to make sure it matches the order of the arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - DI &amp;amp; Logging
&lt;/h2&gt;

&lt;p&gt;Let's implement Depencency Injection with Logging. Observability has 3 pilars: logging, metrics, and traces. Logging is basically all messages that would be useful to troubleshoot an issue later, but also helpful to send messages to the user. Dotnet provides an excellent logging mechanism, and several extensions. We can include these two:&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="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.DependencyInjection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dependency Injection is just a fancy repository for objects that will be used on the application. In long running systems, it is more clear the advantage of it, for example in web systems we can reuse the same http client and avoid instantiating a new object in each request and stagnating memory. &lt;/p&gt;

&lt;p&gt;There are many ways to store/instantiate these objects, the two extremes is to create one each time we require one from the service provider, and the other is having a singleton, the same exact object.&lt;/p&gt;

&lt;p&gt;We will create a new method at the end to configure these services. As it becomes more complex it can be their own class, or even extensions, but for now a method will be enough.&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="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You would want to call this as one of the first things in your script, to avoid doing nasty things and not using it, so we add this, just after the usings.&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;serviceCollection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we will configure our logging using one of the built-in extensions:&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="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;serviceCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddLogging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSimpleConsole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimestampFormat&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hh:mm:ss "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--debug"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetMinimumLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to play with the formatters just check this. &lt;/p&gt;

&lt;p&gt;As you may notice we are hijacking the &lt;code&gt;--debug&lt;/code&gt; argument here, we need to add it to all the commands as well:&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;debugOption&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"--debug"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;"Enables debug logging"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;rootCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddGlobalOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debugOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we will replace that nasty &lt;code&gt;Console.WriteLine&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="n"&gt;createPostCommand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetHandler&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;persona&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&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;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;LogDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Command requested for &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;persona&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Command not implemented"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;personaOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;topicOption&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;styleOption&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beautiful. We could abstract the handler to a different class, but for now we should be happy with the result. Let's test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--&lt;/span&gt; create-post &lt;span class="nt"&gt;--debug&lt;/span&gt; &lt;span class="nt"&gt;--persona&lt;/span&gt; &lt;span class="s2"&gt;"influencer"&lt;/span&gt; &lt;span class="nt"&gt;--topic&lt;/span&gt; &lt;span class="s2"&gt;"software dev"&lt;/span&gt; &lt;span class="nt"&gt;--style&lt;/span&gt; &lt;span class="s2"&gt;"sarcastic"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with debug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run &lt;span class="nt"&gt;--&lt;/span&gt; create-post &lt;span class="nt"&gt;--debug&lt;/span&gt; &lt;span class="nt"&gt;--persona&lt;/span&gt; &lt;span class="s2"&gt;"influencer"&lt;/span&gt; &lt;span class="nt"&gt;--topic&lt;/span&gt; &lt;span class="s2"&gt;"software dev"&lt;/span&gt; &lt;span class="nt"&gt;--style&lt;/span&gt; &lt;span class="s2"&gt;"sarcastic"&lt;/span&gt; &lt;span class="nt"&gt;--debug&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Amazing, we are ready to start adding LLMs stuff, you can find the our full code in &lt;a href="https://github.com/mahomedalid/TheRaccoonBytes/blob/main/README.md"&gt;my repo for this LLM series&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>openai</category>
      <category>chatgpt</category>
      <category>dotnet</category>
      <category>llms</category>
    </item>
    <item>
      <title>Bringing your site to the Fediverse: A practical guide for static sites - Part 1</title>
      <dc:creator>Maho Pacheco</dc:creator>
      <pubDate>Sat, 16 Mar 2024 22:17:00 +0000</pubDate>
      <link>https://dev.to/mahopacheco/bringing-your-site-to-the-fediverse-a-practical-guide-for-static-sites-part-1-2op8</link>
      <guid>https://dev.to/mahopacheco/bringing-your-site-to-the-fediverse-a-practical-guide-for-static-sites-part-1-2op8</guid>
      <description>&lt;p&gt;I spent the last couple of weeks getting this blog onto the social web (aka fediverse), investing an hour here and there (I hope you are reading this from Mastodon, btw). It was fun and a very exciting learning experience, and I want to share not only my reflections but also create a guide so others can do the same, to move the needle a little bit in the right direction.&lt;/p&gt;

&lt;p&gt;First of all, ActivityPub is a very nice idea; I love how much it has grown and the adoption that is growing day by day. I am not an expert in ActivityPub; I am far from being one. My only technical experience can be counted in literally ten days since this first post. But I have grown fond of it, and I am now #TeamActivityPub. So much so that I want to explain to others how to achieve the same on their own sites.&lt;/p&gt;

&lt;p&gt;But before explaining how to do it, I want to share my vision of why you should do it. (If you have already decided to do it, you may skip this part).&lt;/p&gt;

&lt;p&gt;You may have heard all these nice things about the indieweb, the bad corporations, and privacy. And I agree with almost all of them, but there is also the aspect of innovation which not many times is covered. ActivityPub, the Fediverse, or the SocialWeb are enablers of innovation, and we should make sure others see it. If you want to create a new app, a new social network, or a new product, you don't need to start from zero to build a user base (or what is harder, a community); you can (and should) just focus on &lt;strong&gt;building that thing that adds value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Bear with me; this is important to explain how to implement ActivityPub on your website. (Because at the end we are not going to implement the whole ActivityPub protocol, just the parts that make sense for you).&lt;/p&gt;

&lt;p&gt;As you already know, Mastodon is a microblogging platform, a clone of Twitter, but the underlying protocol/technology, ActivityPub, is much more than that. It describes concepts that repeat in almost all social networks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can create content, described as a note.&lt;/li&gt;
&lt;li&gt;You have an identity, or user.&lt;/li&gt;
&lt;li&gt;You can follow and unfollow other users or people.&lt;/li&gt;
&lt;li&gt;You can "like" and "reply" to other content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are others, but these base 4 concepts repeat themselves in each social network, no matter if it's MySpace, LinkedIn, or TikTok. These features have been implemented over and over, since Google+ and Orkut where a thing.&lt;/p&gt;

&lt;p&gt;That is why ActivityPub is the base of other implementations besides microblogging; take as examples &lt;a href="https://join-lemmy.org/"&gt;Lemmy&lt;/a&gt; (a Reddit clone) and &lt;a href="https://pixelfed.org/"&gt;PixelFed&lt;/a&gt; (an Instagram clone). They look very different from Mastodon, but they are interconnected (aka federated). This means that I can follow a community from Lemmy, not only with my Mastodon account, but with my Mastodon app. It may be that when I favorite a Lemmy-created link in Mastodon means no more than a thumbs up (for Mastodon), but for Lemmy (remember, a Reddit clone) it can be used to upvote a post. Or a photo of a cat on Mastodon could just be yet another post, but it could be beautifully presented on Pixelfed. I personally want to see the GitHub feed to federate in the socialweb (maybe someone can help to it in GitLab).&lt;/p&gt;

&lt;p&gt;But also you do not need to reimplement ActivityPub to create some new instance on a server; you can just consume the APIs on the client side. I can create my own For You algorithm, an app for weather alerts, or a news aggregator (like Google News). Flipboard is a content curation site, for example, and it is interconnected with the socialweb.&lt;/p&gt;

&lt;p&gt;And this leads me to your blog, a newsletter, or news site, which also:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publishes content.&lt;/li&gt;
&lt;li&gt;Has an identity or multiple (for example, a news site could have @&lt;a href="mailto:tech@nytimes.com"&gt;tech@nytimes.com&lt;/a&gt;, @&lt;a href="mailto:politics@wsj.com"&gt;politics@wsj.com&lt;/a&gt;, etc.).&lt;/li&gt;
&lt;li&gt;You can subscribe to a blog or newsletter (follow/unfollow).&lt;/li&gt;
&lt;li&gt;You can "like" or "comment" (reply).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Which btw, there is nothing on ActivityPub that says you cannot make your subscriptors (followers) paid-only, a follow request can be accepted after you verify any sort of payment. But the point is that imagine that you won't need to create yet another account on each newspaper to be able to discuss or comment on their content. Or that you don't need to create an account on Substack to follow an author. Or that you don't even need to create a user on Google or Facebook to like a photo.&lt;/p&gt;

&lt;p&gt;That is what ActivityPub, the Fediverse, and the Social Web bring: interoperability. This can go further, as in distributed dentity verification (you'll be able to own your identity across instances), but that is another topic. For now, I want to convince you that if you have a site that publishes content, it should be in the Fediverse and Social Web. If not for principles, for numbers: there is already 2 million active users every month can read, consume, and engage with your content. You don't need to create a user base, the user base already exists.&lt;/p&gt;

&lt;p&gt;It appears that WordPress, Medium, Threads, Flipboard understood this, and with their actions, they are helping to convince others that this is no longer just an ideological matter but a practical one. There are some people who said this may not succeed, but what I have seen is that it has already done.&lt;/p&gt;

&lt;p&gt;It may not go mainstream to replace Twitter, but that is not the purpose of the Fediverse. That is not the real question. The question is: how can we harness the power of interoperability to enhance accessibility, engagement, and inclusivity within our online communities and content distribution platforms? By integrating with ActivityPub and embracing the principles of the Fediverse and the Social Web, we open doors to a vast network of users who can seamlessly interact with our content without barriers such as mandatory account creation or platform restrictions.&lt;/p&gt;

&lt;p&gt;Moreover, we contribute to the democratization of online spaces, allowing individuals to maintain control over their digital identities and fostering a more decentralized and open internet ecosystem. This not only benefits users but also offers significant advantages for content creators and publishers, as it expands their reach and facilitates meaningful interactions with their audience.&lt;/p&gt;

&lt;p&gt;In essence, the question is not whether the Fediverse will replace mainstream platforms but rather how we can leverage its potential to create more inclusive, user-centric online experiences that prioritize accessibility and collaboration. This is the promise of the world-wide-web, interconnected websites, but also interconnected people. And if you site is not part of it, well, then keep reading.&lt;/p&gt;

&lt;p&gt;You can find &lt;a href="https://maho.dev/2024/02/a-guide-to-implement-activitypub-in-a-static-site-or-any-website/"&gt;the series here&lt;/a&gt; or in &lt;a href="https://mahopacheco.substack.com/"&gt;my substack&lt;/a&gt; and follow the blog (&lt;a href="https://hachyderm.io/@blog@maho.dev"&gt;@blog@maho.dev&lt;/a&gt;) in the Fediverse/SocialWeb!&lt;/p&gt;

</description>
      <category>fediverse</category>
      <category>mastodon</category>
      <category>activitypub</category>
      <category>staticsites</category>
    </item>
  </channel>
</rss>
