<?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: Luân Trương</title>
    <description>The latest articles on DEV Community by Luân Trương (@lun_trng_e0afd1e1a1722).</description>
    <link>https://dev.to/lun_trng_e0afd1e1a1722</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%2F1572191%2Fa554b4e8-55e8-42cc-b6f5-ce53d5cd01bb.jpg</url>
      <title>DEV Community: Luân Trương</title>
      <link>https://dev.to/lun_trng_e0afd1e1a1722</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lun_trng_e0afd1e1a1722"/>
    <language>en</language>
    <item>
      <title>GenAI with LLMs in .NET using Microsoft.Extensions.AI</title>
      <dc:creator>Luân Trương</dc:creator>
      <pubDate>Sun, 02 Feb 2025 14:55:16 +0000</pubDate>
      <link>https://dev.to/lun_trng_e0afd1e1a1722/genai-with-llms-in-net-using-microsoftextensionsai-5fek</link>
      <guid>https://dev.to/lun_trng_e0afd1e1a1722/genai-with-llms-in-net-using-microsoftextensionsai-5fek</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;I&lt;/strong&gt;. &lt;strong&gt;Introduce&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The rise of Generative AI (GenAI) has redefined how developers build intelligent applications. From chatbots that mimic human conversation to code generators that accelerate development, Large Language Models (LLMs) like GPT-4, Llama 3, and Claude 3 are unlocking unprecedented possibilities. But &lt;strong&gt;Why focus on GenAI in .NET, specifically?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. GenAI is Reshaping Software Development&lt;/strong&gt;&lt;br&gt;
     Modern applications demand contextual awareness, natural language interactions, and adaptive decision-making. GenAI bridges this gap by enabling systems to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Automate repetitive tasks(e.g., documents summarization, code suggestions).&lt;/li&gt;
&lt;li&gt; Deliver hyper-personalized user experiences(e.g., dynamic content generation)&lt;/li&gt;
&lt;li&gt; Solve complex problems with human-like reasoning(e.g., data analysis, troubleshooting)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. NET's Enterprise-Grade Ecosytem&lt;/strong&gt;&lt;br&gt;
     While Python dominates AI research, .NET powers mission-critical systems across industries(finance, healthcare, logistics). Integrating GenAI into .NET applications allows enterprise to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Leverage exsiting C#/F# codebases and .NET libraries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintain scalability, security, and complicance standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unify AI capabilities with Microsoft's cloud ecosystem(Azure OpenAI, Semantic Kernel).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. The Emergence of Microsoft.Extension.AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Microsoft's new Microsoft.Extensions.AI library simplifies LLM integration into .NET app using familiar patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Standardized APIs: Interact with multiple LLM providers (OpenAI, Hugging Face, Azure) through a unified interface.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dependency Injection (DI): Seamlessy inject AI services into .ASP .NET core pipelines.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Semantic Kernel Integration: Build modular, AI-driven workflows with C#.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What's next?&lt;/strong&gt;&lt;br&gt;
This article is a result of what I researched and worked on &lt;strong&gt;GenAI with .NET apps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh12zdh6cn7wxset2cban.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh12zdh6cn7wxset2cban.png" alt="Image description" width="800" height="337"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  .NET app with GenAI
&lt;/h2&gt;

&lt;p&gt;Source code for these scenarios can be found at &lt;a href="https://github.com/LuanTruongPTIT/GenAI-Micro" rel="noopener noreferrer"&gt;https://github.com/LuanTruongPTIT/GenAI-Micro&lt;/a&gt; &lt;br&gt;
I use &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; for local development(saving cost), and in a higher environment.&lt;br&gt;
The business use cases for these scenarios are &lt;code&gt;semantic search&lt;/code&gt; and &lt;code&gt;chat completion&lt;/code&gt; (text summary for data seeding actually)&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Before diving into the examples, here's what you need to run LLMs locally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Docker running on your machine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ollama container running with the llama3.2:1b model:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
# Pull the Ollama container
docker run --gpus all -d -v ollama_data:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

# Pull the llama3.2:1b model
docker exec -it ollama ollama pull llama3.2:1b

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

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;A few NuGet packages (I built this using a .NET 9 console application):
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install-Package Microsoft.Extensions.AI # The base AI library
Install-Package Microsoft.Extensions.AI.Ollama # Ollama provider implementation
Install-Package Microsoft.Extensions.Hosting # For building the DI container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Semantic search with GenAI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0p3egtpy14ztzipp7qnf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0p3egtpy14ztzipp7qnf.png" alt="Image description" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The technologies used to implement semantic search in this scenario are pgvector, and its .NET packages. We use the cosine distance searching which is supported by pgvector extension.&lt;/p&gt;

&lt;p&gt;Supported distance functions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;lt;-&amp;gt; - L2 distance&lt;/li&gt;
&lt;li&gt;&amp;lt;#&amp;gt; - (negative) inner product&lt;/li&gt;
&lt;li&gt;&amp;lt;=&amp;gt; - cosine distance&lt;/li&gt;
&lt;li&gt;&amp;lt;+&amp;gt; - L1 distance (added in 0.7.0)&lt;/li&gt;
&lt;li&gt;&amp;lt;~&amp;gt; - Hamming distance (binary vectors, added in 0.7.0)&lt;/li&gt;
&lt;li&gt;&amp;lt;%&amp;gt; - Jaccard distance (binary vectors, added in 0.7.0)
And we use &amp;lt;=&amp;gt; - cosine distance for this scenario
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT p.id, p.description, p.embedding, p.price, p.type, p.updated, p.embedding &amp;lt;=&amp;gt; @__vector_0 AS "Distance"
FROM item.products AS p
ORDER BY p.embedding &amp;lt;=&amp;gt; @__vector_0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The code below processes data query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal class ItemTypesQueryHandler(ProductDbContext dbContext, IProductItemAI productItemAI, ILogger&amp;lt;ItemTypesQueryHandler&amp;gt; logger) : IRequestHandler&amp;lt;ItemTypesQuery, IEnumerable&amp;lt;ItemTypeDto&amp;gt;&amp;gt;
    {
        public async Task&amp;lt;IEnumerable&amp;lt;ItemTypeDto&amp;gt;&amp;gt; Handle(ItemTypesQuery request, CancellationToken cancellationToken)
        {
            ArgumentNullException.ThrowIfNull(request);

            request.Text = request.Text ?? "coffee";

            var vector = await productItemAI.GetEmbeddingAsync(request.Text);

            var itemsWithDistance = await dbContext.Items
                .Select(c =&amp;gt; new { Item = c, Distance = c.Embedding.CosineDistance(vector) })
                .OrderBy(c =&amp;gt; c.Distance)
                .ToListAsync();

            logger.LogDebug("Results from {text}: {results}", request.Text, string.Join(", ", itemsWithDistance.Select(i =&amp;gt; $"{i.Item.Type} =&amp;gt; {i.Distance}")));

            return await Task.FromResult(itemsWithDistance.Select(x =&amp;gt; new ItemTypeDto
            {
                Name = x.Item.Type.ToString(),
                ItemType = x.Item.Type
            }).Distinct());
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chat completion (text summary)
&lt;/h3&gt;

&lt;p&gt;Based on the previous example, we can create a conversation to create data at the beginning of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ProductDbContextSeeder(
    IProductItemAI catalogAI,
    IChatClient chatClient,
    ILogger&amp;lt;ProductDbContextSeeder&amp;gt; logger
    ) : IDbSeeder&amp;lt;ProductDbContext&amp;gt;
{
    private readonly SemaphoreSlim _semaphore = new(1);

    public async Task SeedAsync(ProductDbContext context)
    {
        await _semaphore.WaitAsync();

        try
        {

            context.Database.OpenConnection();
            ((NpgsqlConnection)context.Database.GetDbConnection()).ReloadTypes();

            if (!context.Items.Any())
            {
                await context.Items.ExecuteDeleteAsync();

                var catalogItems = new ItemV2Data();

                if (catalogAI.IsEnabled)
                {
                    logger.LogInformation("Generating {NumItems} embeddings", catalogItems.Count);
                    for (int i = 0; i &amp;lt; catalogItems.Count; i++)
                    {
                        var prompt = $"Generate the description of {catalogItems[i].Type} in max 20 words";
                        var response = await chatClient.CompleteAsync(prompt);
                        catalogItems[i].SetDescription(response.Message?.Text);
                        catalogItems[i].Embedding = await catalogAI.GetEmbeddingAsync(catalogItems[i]);
                    }
                }

                await context.Items.AddRangeAsync(catalogItems);
                logger.LogInformation("Seeded catalog with {NumItems} items", context.Items.Count());
                await context.SaveChangesAsync();
            }

            await context.SaveChangesAsync();
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static class ChatCompletionServiceExtensions
{
    public static void AddChatCompletionService(this IHostApplicationBuilder builder, string serviceName)
    {
        var pipeline = (ChatClientBuilder pipeline) =&amp;gt; pipeline
            .UseFunctionInvocation()
            .UseOpenTelemetry(configure: c =&amp;gt; c.EnableSensitiveData = true);

        if (builder.Configuration["ai:Type"] == "openai")
        {
            builder.AddOpenAIChatClient(serviceName, pipeline);
        }
        else
        {
            builder.AddOllamaChatClient(serviceName, pipeline);
        }
    }
    // remove for brevity
    // ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tracing chat completion to summary text with&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgs6jme8dyx8lp8qi5mod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgs6jme8dyx8lp8qi5mod.png" alt="Image description" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References&lt;br&gt;
&lt;a href="https://github.com/dotnet/extensions/tree/main/src/Libraries" rel="noopener noreferrer"&gt;https://github.com/dotnet/extensions/tree/main/src/Libraries&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/dotnet/ai-samples#microsoftextensionsai-preview" rel="noopener noreferrer"&gt;https://github.com/dotnet/ai-samples#microsoftextensionsai-preview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/dotnet/aspire" rel="noopener noreferrer"&gt;https://github.com/dotnet/aspire&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/CommunityToolkit/Aspire" rel="noopener noreferrer"&gt;https://github.com/CommunityToolkit/Aspire&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/dotnet/eShop" rel="noopener noreferrer"&gt;https://github.com/dotnet/eShop&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/dotnet/eShopSupport" rel="noopener noreferrer"&gt;https://github.com/dotnet/eShopSupport&lt;/a&gt;&lt;br&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/e-shop-infused-with-ai-comprehensive-intelligent-dotnet-app-sample/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/dotnet/e-shop-infused-with-ai-comprehensive-intelligent-dotnet-app-sample/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/dotnet/introducing-microsoft-extensions-ai-preview/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://devblogs.microsoft.com/dotnet/build-gen-ai-with-dotnet-8/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/dotnet/build-gen-ai-with-dotnet-8/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
