Thursday, July 2, 2026

Build an Image to Text Extractor using OpenAi

Build an Image-to-Text Extractor in C# Using OpenAI Vision API (.NET Console Application)

Need to extract text from images using C#? In this tutorial, you'll build a simple Image to Text Generator using the OpenAI Vision API and .NET. The application reads an image, sends it to OpenAI, and returns all visible text from the image.


Prerequisites

  • Visual Studio 2022
  • .NET 8 SDK
  • OpenAI API Key
  • Basic knowledge of C#

Project Structure

ImageToTextGenerator
│
├── Services
│   ├── IImageToTextService.cs
│   └── ImageToTextService.cs
│
├── Program.cs
├── appsettings.json
└── ImageToTextGenerator.csproj

Step 1: Create Interface (IImageToTextService.cs)

Create a new interface inside the Services folder.

namespace ImageToTextGenerator.Services;

public interface IImageToTextService
{
    Task<string> ExtractTextAsync(
        string imagePath,
        CancellationToken cancellationToken = default);
}

Step 2: Create ImageToTextService

Create a new class named ImageToTextService.cs inside the Services folder.

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Configuration;

namespace ImageToTextGenerator.Services;

public class ImageToTextService : IImageToTextService
{
    private readonly HttpClient _httpClient;
    private readonly string _modelId;
    private readonly string _apiKey;

    public ImageToTextService(
        HttpClient httpClient,
        IConfiguration configuration)
    {
        _httpClient = httpClient;

        _modelId = configuration["OpenAI:ModelId"]
            ?? throw new InvalidOperationException(
                "OpenAI:ModelId not found in appsettings.json.");

        _apiKey = configuration["OpenAI:ApiKey"]
            ?? throw new InvalidOperationException(
                "OpenAI:ApiKey not found in appsettings.json.");
    }

    public async Task<string> ExtractTextAsync(
        string imagePath,
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(imagePath))
            throw new FileNotFoundException(
                $"Image not found: {imagePath}",
                imagePath);

        var bytes = await File.ReadAllBytesAsync(
            imagePath,
            cancellationToken);

        var base64 = Convert.ToBase64String(bytes);

        var mimeType = GetMimeType(imagePath);

        var dataUrl = $"data:{mimeType};base64,{base64}";

        var payload = new
        {
            model = _modelId,
            messages = new object[]
            {
                new
                {
                    role = "user",
                    content = new object[]
                    {
                        new
                        {
                            type = "text",
                            text = "Extract all text visible in this image. Return only the text, no commentary."
                        },
                        new
                        {
                            type = "image_url",
                            image_url = new
                            {
                                url = dataUrl
                            }
                        }
                    }
                }
            }
        };

        using var request = new HttpRequestMessage(
            HttpMethod.Post,
            "chat/completions")
        {
            Content = JsonContent.Create(payload)
        };

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", _apiKey);

        using var response = await _httpClient.SendAsync(
            request,
            cancellationToken);

        var body = await response.Content.ReadAsStringAsync(
            cancellationToken);

        if (!response.IsSuccessStatusCode)
            throw new HttpRequestException(
                $"OpenAI endpoint returned {(int)response.StatusCode}: {body}");

        using var doc = JsonDocument.Parse(body);

        return doc.RootElement
            .GetProperty("choices")[0]
            .GetProperty("message")
            .GetProperty("content")
            .GetString() ?? string.Empty;
    }

    private static string GetMimeType(string path)
        => Path.GetExtension(path).ToLowerInvariant() switch
        {
            ".jpg" or ".jpeg" => "image/jpeg",
            ".png" => "image/png",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".bmp" => "image/bmp",
            _ => throw new NotSupportedException(
                $"Unsupported image extension: {Path.GetExtension(path)}")
        };
}

Step 3: Program.cs

Configure dependency injection, load configuration, and call the image extraction service.

using ImageToTextGenerator.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

try
{
    var config = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile(
            "appsettings.json",
            optional: false,
            reloadOnChange: false)
        .Build();

    var endpoint = config["OpenAI:Endpoint"]
        ?? throw new InvalidOperationException(
            "OpenAI:Endpoint not found in appsettings.json.");

    if (!Uri.TryCreate(
            endpoint,
            UriKind.Absolute,
            out var endpointUri))
    {
        throw new InvalidOperationException(
            $"OpenAI:Endpoint '{endpoint}' is not a valid absolute URI.");
    }

    var baseAddress = endpoint.EndsWith('/')
        ? endpointUri
        : new Uri(endpoint + "/");

    var services = new ServiceCollection();

    services.AddSingleton(config);

    services.AddHttpClient<IImageToTextService, ImageToTextService>(http =>
    {
        http.BaseAddress = baseAddress;
        http.Timeout = TimeSpan.FromMinutes(3);
    });

    await using var provider =
        services.BuildServiceProvider();

    string? imagePath =
        @"C:\Screenshot 2026-07-02 143404.png";

    if (string.IsNullOrWhiteSpace(imagePath))
        throw new InvalidOperationException(
            "No image path provided.");

    imagePath = imagePath.Trim().Trim('"');

    var service =
        provider.GetRequiredService<IImageToTextService>();

    var text = await service.ExtractTextAsync(imagePath);

    Console.WriteLine();
    Console.WriteLine("---- Extracted text ----");
    Console.WriteLine(text);

    return 0;
}
catch (FileNotFoundException ex)
{
    Console.Error.WriteLine($"File error: {ex.Message}");
    return 1;
}
catch (NotSupportedException ex)
{
    Console.Error.WriteLine($"Unsupported input: {ex.Message}");
    return 1;
}
catch (InvalidOperationException ex)
{
    Console.Error.WriteLine($"Configuration error: {ex.Message}");
    return 1;
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"HTTP error: {ex.Message}");
    return 1;
}
catch (TaskCanceledException)
{
    Console.Error.WriteLine("Request timed out (3 minutes).");
    return 1;
}
catch (Exception ex)
{
    Console.Error.WriteLine(
        $"Fatal error: {ex.GetType().Name}: {ex.Message}");
    return 1;
}

appsettings.json

Create an appsettings.json file in your project.

{
  "OpenAI": {
    "Endpoint": "https://api.openai.com/v1/",
    "ModelId": "gpt-4.1-mini",
    "ApiKey": "YOUR_OPENAI_API_KEY"
  }
}

How It Works

  1. Reads the image from disk.
  2. Converts the image into Base64.
  3. Creates a Data URL.
  4. Sends the image to the OpenAI Chat Completions API.
  5. Uses a Vision-capable model to read the image.
  6. Returns only the extracted text.

Sample Output

---- Extracted text ----

Invoice Number: INV-2026-105
Customer Name: John Smith
Date: 02 July 2026
Total Amount: $325.00

Conclusion

You have successfully built an Image to Text Generator in C# using the OpenAI Vision API. This approach works for screenshots, scanned documents, invoices, forms, receipts, and many other image types. You can further enhance the application by adding batch processing, PDF support, drag-and-drop, or exporting extracted text to Word, Excel, or a database.

Build a interactive chatbot using AI modal using SQL database with .NET Semantic Kernel plugin

Build an Interactive AI Chatbot Using SQL Server and .NET Semantic Kernel Plugins

Artificial Intelligence has transformed the way modern applications interact with users. Instead of creating countless APIs for every business operation, developers can now use Large Language Models (LLMs) together with Semantic Kernel plugins to allow AI to retrieve live information directly from enterprise databases.

In this tutorial, you'll learn how to build an Interactive Insurance Chatbot using .NET 8, Microsoft Semantic Kernel, SQL Server, Entity Framework Core, and OpenAI Chat Completion. The chatbot understands natural language questions and automatically calls a Semantic Kernel plugin whenever data needs to be fetched from SQL Server.

What You'll Build

By the end of this tutorial, you'll have an AI-powered console chatbot capable of answering questions such as:

  • Show all insurance customers.
  • Who has Medical Insurance?
  • List all Quarterly premium customers.
  • Show policies started in 2019.
  • What is Rohit Parmar's premium amount?
  • List all Life Insurance policies.

Unlike traditional chatbots that rely on predefined responses, this chatbot retrieves real-time information directly from SQL Server using Semantic Kernel's automatic function calling feature.

Solution Architecture

User
   │
   ▼
OpenAI Chat Model
   │
Semantic Kernel
   │
Insurance Plugin
   │
Insurance Business Layer
   │
Entity Framework Core
   │
SQL Server Database

Whenever the user asks a question related to insurance records, the AI automatically invokes the appropriate plugin, retrieves data from SQL Server, and generates a natural language response based on the returned records.

Technologies Used

  • .NET 8
  • Microsoft Semantic Kernel
  • OpenAI Chat Completion
  • Entity Framework Core
  • SQL Server
  • Dependency Injection
  • C#
  • Semantic Kernel Plugins

Prerequisites

Before starting this project, make sure you have the following installed:

  • Visual Studio 2022
  • .NET 8 SDK
  • SQL Server
  • SQL Server Management Studio (SSMS)
  • OpenAI API Key
  • Microsoft Semantic Kernel NuGet Packages
  • Entity Framework Core SQL Server Provider
Note: This tutorial uses Semantic Kernel Function Calling to automatically invoke database functions whenever the AI determines that external data is required to answer the user's question.

Step 1 – Prepare the Insurance Table with Mock Data

The first step is to create an Insurance table in SQL Server and insert a few sample records. These records will be queried by the chatbot through Entity Framework Core.


CREATE TABLE Insurance
(
    firstname           VARCHAR(100),
    lastname            VARCHAR(100),
    email               VARCHAR(100),
    contactNumber       VARCHAR(10),
    insurancetype       VARCHAR(100),
    insurancestartdate  DATE,
    premium             DECIMAL,
    premiumfrequency    VARCHAR(100)
);

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Rohit', 'Parmar', 'rohit.parmar@gmail.com', '8454042163', 'Term', '03-12-2016', '5656.00', 'Quaterly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Shailash', 'Singh', 'shailash.singh@gmail.com', '8158042151', 'Term', '03-12-2009', '15236.00', 'Half Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Chinmay', 'Halankar', 'chinmay.halankar@gmail.com', '6454032121', 'Medical', '03-12-2018', '51626.00', 'Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Malay', 'Mondal', 'malay.mondal@gmail.com', '7654048723', 'Term', '03-12-2019', '8923.00', 'Quaterly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Rupesh', 'Patil', 'rupesh.patil@gmail.com', '9454043400', 'Life Insurance', '03-12-2010', '3486.00', 'Half Yearly');

INSERT INTO Insurance
(firstname, lastname, email, contactNumber, insurancetype, insurancestartdate, premium, premiumfrequency)
VALUES
('Anjali', 'Shinde', 'angali.shinde@gmail.com', '9654090160', 'Endowment Plan', '03-12-2012', '7106.00', 'Quaterly');

Step 2 – Create the Insurance Model

Create Insurance.cs inside the Models folder.


using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;

namespace InteractiveChatBot.Models;

[Table("Insurance")]
[Keyless]
public class Insurance
{
    [Column("firstname")]
    public string? FirstName { get; set; }

    [Column("lastname")]
    public string? LastName { get; set; }

    [Column("email")]
    public string? Email { get; set; }

    [Column("contactNumber")]
    public string? ContactNumber { get; set; }

    [Column("insurancetype")]
    public string? InsuranceType { get; set; }

    [Column("insurancestartdate")]
    public DateTime? InsuranceStartDate { get; set; }

    [Column("premium")]
    public decimal? Premium { get; set; }

    [Column("premiumfrequency")]
    public string? PremiumFrequency { get; set; }
}

Step 3 – Create AppDbContext

Create AppDbContext.cs.



using InteractiveChatBot.Models;
using Microsoft.EntityFrameworkCore;

namespace InteractiveChatBot.Data;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet Insurance => Set();
}

Step 4 – Create InsuranceBusiness.cs

The business layer retrieves insurance records using Entity Framework Core.


using InteractiveChatBot.Data;
using InteractiveChatBot.Models;
using Microsoft.EntityFrameworkCore;

namespace InteractiveChatBot.Business
{
    public class InsuranceBusiness
    {
        private readonly AppDbContext _appContext;

        public InsuranceBusiness(AppDbContext appContext)
        {
            _appContext = appContext;
        }

        public async Task> ReadInsuranceAsync()
        {
            return await _appContext.Insurance
                .AsNoTracking()
                .ToListAsync();
        }
    }
}

Step 5 – Create InsurancePlugin.cs

The Semantic Kernel plugin exposes the database method as an AI function.


using System.ComponentModel;
using InteractiveChatBot.Business;
using InteractiveChatBot.Models;
using Microsoft.SemanticKernel;

namespace InteractiveChatBot.Plugins;

public class InsurancePlugin
{
    private readonly InsuranceBusiness _business;

    public InsurancePlugin(InsuranceBusiness business)
    {
        _business = business;
    }

    [KernelFunction("read_insurance")]
    [Description("Reads all insurance records from the database. Returns firstname, lastname, email, contact number, insurance type, insurance start date, premium, and premium frequency for every record.")]
    public async Task> ReadInsuranceAsync()
    {
        try
        {
            return await _business.ReadInsuranceAsync();
        }
        catch (Exception ex)
        {
            throw new KernelException($"Failed to read Insurance table: {ex.Message}", ex);
        }
    }
}

Step 6 – Configure Program.cs

Program.cs wires together SQL Server, Dependency Injection, Semantic Kernel, OpenAI Chat Completion, automatic function calling, and the interactive console chat loop.


using InteractiveChatBot.Business;
using InteractiveChatBot.Data;
using InteractiveChatBot.Plugins;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

try
{
    var config = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
        .Build();

    var connectionString = config.GetConnectionString("DefaultConnection")
        ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

    var openAiModelId = config["OpenAI:ModelId"]
        ?? throw new InvalidOperationException("OpenAI:ModelId not found in appsettings.json.");

    var openAiApiKey = config["OpenAI:ApiKey"];
    if (string.IsNullOrWhiteSpace(openAiApiKey))
        throw new InvalidOperationException("OpenAI:ApiKey is missing in appsettings.json.");

    var openAiEndpoint = config["OpenAI:Endpoint"]
        ?? throw new InvalidOperationException("OpenAI:Endpoint not found in appsettings.json.");

    if (!Uri.TryCreate(openAiEndpoint, UriKind.Absolute, out var endpointUri))
        throw new InvalidOperationException($"OpenAI:Endpoint '{openAiEndpoint}' is not a valid absolute URI.");

    var services = new ServiceCollection();
    services.AddDbContext(options => options.UseSqlServer(connectionString));
    services.AddScoped();
    services.AddScoped();
    
services.AddSingleton(_ => new HttpClient
{
    BaseAddress = endpointUri,
    Timeout = TimeSpan.FromMinutes(3)
});

services.AddScoped(sp =>
{
    var builder = Kernel.CreateBuilder();

    builder.AddOpenAIChatCompletion(
        modelId: openAiModelId,
        apiKey: openAiApiKey,
        httpClient: sp.GetRequiredService());

    var kernel = builder.Build();

    kernel.Plugins.AddFromObject(
        sp.GetRequiredService(),
        "Insurance");

    return kernel;
});

await using var provider = services.BuildServiceProvider();
using var scope = provider.CreateScope();

var kernel = scope.ServiceProvider.GetRequiredService();
var chat = kernel.GetRequiredService();

var executionSettings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var history = new ChatHistory(
    "You are an insurance assistant. When the user asks anything about policy holders, " +
    "insurance types, premiums, or start dates, call the Insurance.read_insurance function to fetch the data and answer based on the returned rows. Be concise.");
    
  using var cts = new CancellationTokenSource();

Console.CancelKeyPress += (_, e) =>
{
    e.Cancel = true;
    cts.Cancel();
};

Console.WriteLine("Insurance chatbot ready. Type 'exit' to quit (or Ctrl+C).");

while (!cts.IsCancellationRequested)
{
    Console.Write("\nYou: ");
    var input = Console.ReadLine();

    if (input is null) break;
    if (string.IsNullOrWhiteSpace(input)) continue;
    if (input.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase))
        break;

    history.AddUserMessage(input);

    try
    {
        var reply = await chat.GetChatMessageContentAsync(
            history,
            executionSettings,
            kernel,
            cts.Token);

        Console.WriteLine($"\nBot: {reply.Content}");
        history.Add(reply);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("\nRequest cancelled.");
        history.RemoveAt(history.Count - 1);
    }
    catch (HttpOperationException ex)
    {
        Console.Error.WriteLine(
            $"\nLLM endpoint error ({(int?)ex.StatusCode}): {ex.Message}");

        history.RemoveAt(history.Count - 1);
    }
}

catch (KernelException ex)
{
    Console.Error.WriteLine($"\nKernel error: {ex.Message}");
    history.RemoveAt(history.Count - 1);
}
catch (Exception ex)
{
    Console.Error.WriteLine($"\nUnexpected error: {ex.Message}");
    history.RemoveAt(history.Count - 1);
}

return 0;
}
catch (InvalidOperationException ex)
{
    Console.Error.WriteLine($"Configuration error: {ex.Message}");
    return 1;
}
catch (FileNotFoundException ex)
{
    Console.Error.WriteLine($"Missing file: {ex.Message}");
    return 1;
}
catch (Exception ex)
{
    Console.Error.WriteLine($"Fatal error: {ex.GetType().Name}: {ex.Message}");
    return 1;
}

Sample Questions

  • Show all insurance records.
  • Who has Medical insurance?
  • Which policies started in 2019?
  • Show Quarterly premium customers.
  • What is Rohit's premium amount?







Conclusion

You have successfully built an AI-powered chatbot that can answer natural language questions using live SQL Server data through .NET Semantic Kernel plugins. This architecture cleanly separates AI reasoning from business logic and can easily be extended for CRM, HR, Healthcare, Banking, and Inventory Management system.

Tuesday, September 23, 2025

Building a Simple C++ Method Extractor in C#

 

Extracting C++ Method Bodies with C#

When working with large C++ projects, sometimes all you need is a quick way to extract specific method bodies for debugging, refactoring, or documentation. Instead of building a full-fledged parser, you can rely on C# with regex and brace matching to create a lightweight and practical utility.

In this blog, we’ll walk through a step-by-step implementation of a tool that extracts method bodies from .cpp files. The process involves a simple main program, a model class, and the core extractor that handles regex parsing and safe brace matching.

Step 1 — Main Program (Program.cs)

The entry point initializes the extractor with a .cpp file path and prints out the results:

class Program
{
    static void Main(String[] args)
    {
        var methodBody = CppRegexExtractor.ExtractMethodsBodyWithRegex(@"C:\Temp\Dummy!.cpp");
        Console.WriteLine(methodBody);
        Console.ReadLine();
    }
}

This ensures the tool can read a given C++ source file and display the extracted method bodies.

Step 2 — Model Class (ProgramBody.cs)

We define a simple model to store each method’s name (Key) and body content (Content):

using System;

namespace ExtractCppCode
{
    public class ProgramBody
    {
        public string Key { get; set; }
        public string Content { get; set; }
    }
}

This acts as a container for extracted results, making it easier to handle multiple methods.

Step 3 — Core Extractor (CppRegexExtractor.cs)

Here’s where the real logic lives. The extractor uses regex to detect method signatures, then employs a brace matching algorithm to safely capture the full method body (ignoring braces inside strings or comments).

Key steps include:

  1. Regex pattern to detect C++ method signatures.
  2. Brace matching to find the correct closing brace.
  3. Optional sub-method detection to include methods invoked inside a target method (like SaveProduct).
using ExtractCppCode;
using System.Text;
using System.Text.RegularExpressions;

public static class CppRegexExtractor
{
    public static string ExtractMethodsBodyWithRegex(string cppFilePath)
    {
        if (!File.Exists(cppFilePath))
        {
            Console.WriteLine($"Error: File not found at {cppFilePath}");
            return "";
        }

        Console.WriteLine("Start Extracting Methods..........");

        List<ProgramBody> lines = new List<ProgramBody>();
        List<ProgramBody> subLines = new List<ProgramBody>();
        StringBuilder builder = new StringBuilder();

        string cppContent = File.ReadAllText(cppFilePath);

        // Regex for method signatures
        string pattern = @"(?<retType>[\w\s\*&<>:]+)\s+(?<className>[\w:]+::)?(?<methodName>\w+)\s*\((?<params>.*?)\)\s*\{";
        Regex regex = new Regex(pattern, RegexOptions.Multiline | RegexOptions.ExplicitCapture);
        MatchCollection matches = regex.Matches(cppContent);

        foreach (Match match in matches)
        {
            string methodName = match.Groups["methodName"].Value;
            if (methodName == "if") continue; // skip keywords

            int bodyStartIdx = match.Index + match.Length - 1;
            int bodyEndIdx = FindMatchingBrace(cppContent, bodyStartIdx);

            string result = cppContent.Substring(match.Index, bodyEndIdx - match.Index + 1);
            lines.Add(new ProgramBody { Key = methodName, Content = result });
        }

        // Extract SaveProduct + its submethods
        var selectedMethod = lines.Find(x => x.Key == "PreSaveUpdate");
        if (selectedMethod != null)
        {
            builder.Append(selectedMethod.Content);

            string subPattern = @"\b(?<methodName>\w+)\s*\((?<arguments>[^)]*)\)";
            MatchCollection subMatches = Regex.Matches(selectedMethod.Content, subPattern);

            foreach (Match subMatch in subMatches)
            {
                string subMethodName = subMatch.Groups["methodName"].Value.Trim();
                if (subMethodName == selectedMethod.Key || subMethodName == "if") continue;

                var subItem = lines.Find(y => y.Key == subMethodName);
                if (subItem != null)
                {
                    subLines.Add(new ProgramBody
                    {
                        Key = subMethodName,
                        Content = subItem.Content
                    });
                }
            }

            foreach (var line in subLines)
            {
                builder.Append(line.Content);
            }
        }

        Console.WriteLine("Extracting Methods Completed..........");
        return builder.ToString();
    }

    // Finds the matching closing brace
    private static int FindMatchingBrace(string source, int openingBraceIdx)
    {
        if (source[openingBraceIdx] != '{')
            throw new ArgumentException("openingBraceIdx must point at a '{' character.");

        int depth = 0;
        bool inSingleLineComment = false;
        bool inMultiLineComment = false;
        bool inString = false;
        bool inChar = false;
        bool escaped = false;

        for (int i = openingBraceIdx; i < source.Length; i++)
        {
            char c = source[i];

            if (escaped) { escaped = false; continue; }
            if (c == '\\' && (inString || inChar)) { escaped = true; continue; }

            if (inSingleLineComment) { if (c == '\n') inSingleLineComment = false; continue; }
            if (inMultiLineComment) { if (c == '*' && i + 1 < source.Length && source[i + 1] == '/') { inMultiLineComment = false; i++; } continue; }

            if (inString) { if (c == '"') inString = false; continue; }
            if (inChar) { if (c == '\'') inChar = false; continue; }

            if (c == '/' && i + 1 < source.Length)
            {
                char next = source[i + 1];
                if (next == '/') { inSingleLineComment = true; i++; continue; }
                if (next == '*') { inMultiLineComment = true; i++; continue; }
            }
            if (c == '"') { inString = true; continue; }
            if (c == '\'') { inChar = true; continue; }

            if (c == '{') depth++;
            else if (c == '}')
            {
                depth--;
                if (depth == 0) return i;
            }
        }

        throw new InvalidOperationException("Unbalanced braces detected.");
    }
}

Sample Input — Dummy.cpp

Here’s a test C++ file containing various methods, including comments and tricky braces inside strings:

#include <iostream>
#include <string>

// A helper method with braces inside strings and comments
int helper1(int x)
{
    // This brace } in a comment should be ignored
    std::string s = "example with brace } inside string";
    if (x > 0) {
        return x + 1;
    }
    return 0;
}

/* Multiline comment with { and } that should be ignored */

void helper2()
{
    for (int i = 0; i < 3; ++i)
    {
        std::cout << "loop " << i << std::endl;
    }
}

// The main method we want to extract
bool SaveProduct(int id)
{
    std::cout << "SaveProduct called" << std::endl;
    int r = helper1(id);
    if (r > 0)
    {
        helper2();
        return true;
    }
    return false;
}

// Another unrelated method
double unrelated(double a, double b)
{
    return a * b;
}

Expected Extracted Output

When the extractor runs, it captures SaveProduct along with its submethods helper1 and helper2:

bool SaveProduct(int id)
{
    std::cout << "SaveProduct called" << std::endl;
    int r = helper1(id);
    if (r > 0)
    {
        helper2();
        return true;
    }
    return false;
}

int helper1(int x)
{
    // This brace } in a comment should be ignored
    std::string s = "example with brace } inside string";
    if (x > 0) {
        return x + 1;
    }
    return 0;
}

void helper2()
{
    for (int i = 0; i < 3; ++i)
    {
        std::cout << "loop " << i << std::endl;
    }
}

Sunday, September 14, 2025

Building a ChatGPT Chatbot in ASP.NET WebForms

 

Build a Simple ChatGPT WebForm App in ASP.NET (C#)

If you want to integrate ChatGPT into your ASP.NET WebForms project, here’s a step-by-step guide with complete code. We’ll create a small app where users can type prompts, send them to the OpenAI API, and display the assistant’s response — just like a mini chat interface.

Step 1: Define Models (Models.cs)

Create a folder named Models and add a file called MoModels.cs..Models.cs

.
This file defines the data structures for requests and responses.
using System;

namespace ChatGPT.Models
{
    public class ChatMessage
    {
        public string Role { get; set; } = string.Empty;
        public string Content { get; set; } = string.Empty;
    }

    public class Usage
    {
        public int? Prompt_Tokens { get; set; }
        public int? Completion_Tokens { get; set; }
        public int? Total_Tokens { get; set; }
    }

    public class Choice
    {
        public int Index { get; set; }
        public ChatMessage? Message { get; set; }
        public string? Finish_Reason { get; set; }
    }

    public class ChatResponse
    {
        public string? Id { get; set; }
        public string? Object { get; set; }
        public int Created { get; set; }
        public string? Model { get; set; }
        public Choice[]? Choices { get; set; }
        public Usage? Usage { get; set; }
    }

    public class ChatRequest
    {
        public string model { get; set; } = string.Empty;
        public ChatMessage[]? messages { get; set; }
        public int? max_tokens { get; set; }
    }
}

Step 2: Create OpenAI Client (OpenAiClient.cs)

Now, add a Controller folder and create OpenAiClient.cs.
This class handles communication with the OpenAI API.

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using ChatGPT.Models;

namespace ChatGPT.Controller
{
    public class OpenAiClient : IDisposable
    {
        private readonly HttpClient _httpClient;
        private readonly string _apiKey;
        private bool _disposed;

        public OpenAiClient(string apiKey)
        {
            _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
            _httpClient = new HttpClient();
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
        }

        public async Task<ChatMessage> GetChatCompletionAsync(List<ChatMessage> conversationHistory)
        {
            if (conversationHistory == null) throw new ArgumentNullException(nameof(conversationHistory));

            var requestData = new ChatRequest
            {
                model = "gpt-3.5-turbo",   // Change to your preferred model
                messages = conversationHistory.ToArray(),
                max_tokens = 150
            };

            var jsonPayload = JsonConvert.SerializeObject(requestData);
            var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
            var apiUrl = "https://api.openai.com/v1/chat/completions";

            try
            {
                HttpResponseMessage response = await _httpClient.PostAsync(apiUrl, content).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var chatResponse = JsonConvert.DeserializeObject<ChatResponse>(responseBody);

                if (chatResponse?.Choices?.Length > 0)
                {
                    return chatResponse.Choices[0].Message ?? new ChatMessage { Role = "assistant", Content = string.Empty };
                }

                return new ChatMessage { Role = "assistant", Content = "No response received." };
            }
            catch (HttpRequestException ex)
            {
                return new ChatMessage { Role = "assistant", Content = $"Error calling OpenAI API: {ex.Message}" };
            }
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _httpClient?.Dispose();
                _disposed = true;
            }
        }
    }
}

Step 3: Build the Frontend (Home.aspx)

Create a new WebForm page named Home.aspx.
This will provide the UI for sending prompts and displaying responses.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.cs" Inherits="ChatGPT.Home" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>ChatGPT WebForm</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <table width="99%">
                <tr>
                    <td>
                        <asp:TextBox ID="PromptTextBox" runat="server" 
                                     TextMode="MultiLine" Rows="5" Width="400" style="width:100%;"></asp:TextBox>
                    </td>
                </tr>
                <tr>
                    <td>
                        <asp:Button ID="SendButton" runat="server" 
                                    Text="Get AI Response" OnClick="SendButton_Click" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <div id="chatWindow" runat="server" 
                             style="height: 400px; overflow-y: scroll; border: 1px solid #ccc; padding: 10px;">
                            <!-- Chat history will appear here -->
                        </div>
                    </td>
                </tr>
            </table>
        </div>
    </form>
</body>
</html>

Step 4: Handle Backend Logic (Home.aspx.cs)

Finally, implement the backend logic in Home.aspx.cs.

using System;
using System.Collections.Generic;
using System.Web.UI;
using ChatGPT.Models;
using ChatGPT.Controller;

namespace ChatGPT
{
    public partial class Home : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected async void SendButton_Click(object sender, EventArgs e)
        {
            string apiKey = "YOUR_API_KEY";   // Replace with your API key

            using (var openAiClient = new OpenAiClient(apiKey))
            {
                if (ViewState["ConversationHistory"] == null)
                {
                    ViewState["ConversationHistory"] = new List<ChatMessage>();
                }

                var conversationHistory = (List<ChatMessage>)ViewState["ConversationHistory"];
                string promptText = PromptTextBox.Text;

                if (!string.IsNullOrWhiteSpace(promptText))
                {
                    conversationHistory.Add(new ChatMessage { Role = "user", Content = promptText });
                    var aiResponse = await openAiClient.GetChatCompletionAsync(conversationHistory);
                    conversationHistory.Add(aiResponse);

                    // Save state
                    ViewState["ConversationHistory"] = conversationHistory;

                    // Clear textbox
                    PromptTextBox.Text = string.Empty;

                    // Render chat
                    RenderChatWindow(conversationHistory);
                }
            }
        }

        private void RenderChatWindow(List<ChatMessage> conversationHistory)
        {
            chatWindow.InnerHtml = string.Empty;

            foreach (var message in conversationHistory)
            {
                string cssClass = (message.Role == "user") ? "user-message" : "assistant-message";
                chatWindow.InnerHtml += $"<div class=\"{cssClass}\"><b>{message.Role}:</b><pre>{System.Web.HttpUtility.HtmlEncode(message.Content)}</pre></div>";
            }
        }
    }
}

If you are using.net framework < 4.6 then use below method to call api


public string GetChatCompletion(string promptText, string selectedModel)
{
    var requestData = new ChatRequest
    {
        model = selectedModel,
        messages = new ChatMessage[]
        {
            new ChatMessage { role = "user", content = promptText }
        }
    };

    var jsonPayload = JsonConvert.SerializeObject(requestData);
    var apiUrl = "https://api.openai.com/v1/chat/completions";

    try
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
        request.Method = "POST";
        request.ContentType = "application/json";
        request.Headers["Authorization"] = "Bearer " + _apiKey;

        byte[] bytes = Encoding.UTF8.GetBytes(jsonPayload);

        using (Stream stream = request.GetRequestStream())
        {
            stream.Write(bytes, 0, bytes.Length);
            stream.Close();
        }

        WebResponse response = request.GetResponse();
        using (var reader = new StreamReader(response.GetResponseStream()))
        {
            var responseBody = reader.ReadToEnd();
            var chatResponse = JsonConvert.DeserializeObject<ChatResponse>(responseBody);

            if (chatResponse?.Choices?.Length > 0)
            {
                return pichatResponse.Choices[0].Message.Content.Trim();
            }
            return "No response received.";
        }
    }
    catch (HttpRequestException ex)
    {
        return $"Error calling OpenAI API: {ex.Message}";
    }
}






Friday, March 15, 2024

Schedular Services using Coravel in .net core

Simple example of how to schedule job using Coravel in .net core
Coravel gives you a zero-configuration queue that runs in-memory. This is useful to offload long-winded tasks to the background instead of making your users wait for their HTTP request to finish.

Step 1 : Create a console app named CorvelJob

Step 2 : Install Coravel, Microsoft.Extension.DependencyInjection & Microsoft.Extension.Hosting

Step 3 : Below code written in Program.cs will run in every 2 seconds

using Coravel;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var builder = Host.CreateApplicationBuilder();

builder.Services.AddScheduler();

var app = builder.Build();

app.Services.UseScheduler(schedular =>
{
    schedular.ScheduleAsync(
        async () =>
        {
            await Task.Delay(20);
            Console.WriteLine("Schiedular Time : " + DateTime.Now);
        }).EverySeconds(2);


});
app.Run();

OutPut : 

Schiedular Time : 3/15/2024 3:02:40 PM
Schiedular Time : 3/15/2024 3:02:42 PM
Schiedular Time : 3/15/2024 3:02:44 PM

You can also configure above code that run only on weekend that to on Sunday

schedular.ScheduleAsync(
        async () =>
        {
            await Task.Delay(20);
            Console.WriteLine("Schiedular Time : " + DateTime.Now);
        }).Weekly().Weekend().Sunday().Once();

 

you can invoke you class file also. Let take an example of MailerTask which sends mail only on weekend

Create MailerTask.cs

using Coravel;
using CorvelJob;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var builder = Host.CreateApplicationBuilder();

builder.Services.AddScheduler();
builder.Services.AddTransient<MailerTask>();
var app = builder.Build();
app.Services.UseScheduler(schedular =>
{
    schedular.Schedule<MailerTask>()
        .Weekly().Weekend().Sunday()
        .PreventOverlapping("identifier");


});
app.Run();

 

Output : 

info: CorvelJob.MailerTask[0]

   Mail sent @ 03/15/2024 15:13:20

info: CorvelJob.MailerTask[0]

   Mail sent @ 03/15/2024 15:15:20

Saturday, October 16, 2021

Compare validator using reactive forms in angular

 Comparing two input values using reactive forms in angular






Step 1 : Validation.ts


import { AbstractControl, ValidatorFn } from '@angular/forms';

export default class Validation {
  static match(controlName: string, checkControlName: string): ValidatorFn {
    return (controls: AbstractControl) => {
      const control = controls.get(controlName);
      const checkControl = controls.get(checkControlName);

      if (checkControl?.errors && !checkControl.errors.matching) {
        return null;
      }

      if (control?.value !== checkControl?.value) {
        controls.get(checkControlName)?.setErrors({ matching: true });
        return { matching: true };
      } else {
        return null;
      }
    };
  }
}

Step 2 : app-component.html

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <form (ngSubmit)="onSubmit()" [formGroup]="form" autocomplete="off">
        <div id="user-data" formGroupName="userData">
          <div class="form-group">
            <label for="username" class="text-warning">User Name</label>
            <input type="username" placeholder="User Name" name="username" formControlName="username"
              class="form-control" />
            <span class="text-danger"
              *ngIf="!(form.get('userData.username')?.valid) && form.get('userData.username')?.touched">Please enter
              user name</span>
          </div>
          <div class="form-group">
            <label for="email" class="text-warning">Email</label>
            <input type="text" placeholder="Email" name="email" class="form-control" formControlName="email" />
            <span class="text-danger"
              *ngIf="!(form.get('userData.email')?.valid) && form.get('userData.email')?.touched">Please enter valid
              email</span>
          </div>
        </div>
        <div class="form-group">
          <label for="password" class="text-warning">Password</label>
          <input type="password" class="form-control" name="password" formControlName="password"
            [ngClass]="{ 'is-invalid':  f.password.errors }"
            placeholder="Password" />
          <span class="text-danger" *ngIf="f.password.errors?.required && f.password.touched">Please enter
            password</span>
          <span class="text-danger" *ngIf="f.password.errors?.minlength && f.password.touched">Password must be 6
            characters
          </span>
        </div>
        <div class="form-group">
          <label for="confirmpassword" class="text-warning">Confirm Password</label>
          <input type="password" class="form-control" name="confirmpassword" formControlName="confirmpassword"
            [ngClass]="{ 'is-invalid':  f.confirmpassword.errors }"
            placeholder="Confirm Password" />
          <span class="text-danger" *ngIf="f.confirmpassword.errors?.required && f.confirmpassword.touched">Please enter
            confirm password</span>
          <span class="text-danger" *ngIf="f.confirmpassword.errors?.matching && f.confirmpassword.touched">Confirm
            Password does not match</span>
        </div>

        <h4 *ngIf="hobbies.length < 2; else t" class="text-warning">Your Hobby</h4>
        <ng-template #t>
          <h4 class="text-warning">Your Hobbies</h4>
        </ng-template>

        <div class="form-group" formArrayName="hobbies">
          <button type="button" class="btn btn-success" (click)="onAddingHobby()">Add Hobby</button>
          <div id="dynamicControls" class="form-group" *ngFor="let l of hobbies.controls; let i = index">
            <span class="col-xs-5">
              <input type="text" [placeholder]="'Hobby ' + (i + 1)" [formControlName]="i" class="form-control"></span>
            <span><button id="removeControl" type="button" class="btn btn-danger" (click)="onRemovingHobby(i)">X
              </button></span>
          </div>
        </div>
        <button type="submit" [disabled]="!this.form.valid" class="btn btn-primary">Submit</button>
      </form>
    </div>
  </div>
</div>


Step 3 : app-component.ts

import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import Validation from './utils/validation';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit {
  excludedUserName: string[] = ['Test', 'test'];
  form!: FormGroup;

  constructor(private formBuilder: FormBuilder) { }
 
  ngOnInit(): void {
    this.form = this.formBuilder.group({
      'userData': new FormGroup({
        'email': new FormControl(null, [Validators.required, Validators.email]),
        'username': new FormControl(null, [Validators.required,
        this.excludedUserNames.bind(this)])
      }),
      'password': new FormControl(null, [Validators.required, Validators.minLength(6)]),
      'confirmpassword': new FormControl(null, Validators.required),
      'hobbies': new FormArray([])
    }, {
      validators: [Validation.match('password', 'confirmpassword')]
    });
  }

  onSubmit() {
    console.log(this.form);
  }

  onAddingHobby() {
    const control = new FormControl(null, Validators.required);
    this.hobbies.push(control);
  }
  onRemovingHobby(i: number) {
    this.hobbies.removeAt(i);
  }

  excludedUserNames(fromControl: FormControl): { [s: string]: boolean } | null {
    if (this.excludedUserName.indexOf(fromControl.value) !== -1) {
      return { 'excludedUserName': true };
    }
    return null;
  }

  get hobbies() {
    return this.form.get('hobbies') as FormArray;
  }
 
  get f(): { [key: string]: AbstractControl } {
    return this.form.controls;
  }
}

Friday, October 15, 2021

Adding dynamic controls using reactive forms in angular

Adding dynamic controls using reactive forms




In the above example we are capturing user name , email and his/her hobbies. User can add more than one hobby. So we add hobby control dynamically.

Step 1 : app-component.html

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <form (ngSubmit)="onSubmit()" [formGroup]="form" autocomplete="off">
        <div id="user-data" formGroupName="userData">
          <div class="form-group">
            <label for="username" class="text-warning">User Name</label>
            <input type="username" placeholder="User Name" name="username" formControlName="username"
              class="form-control" />
            <span class="text-danger"
              *ngIf="!(form.get('userData.username')?.valid) && form.get('userData.username')?.touched">Please enter
              user name</span>
          </div>
          <div class="form-group">
            <label for="email" class="text-warning">Email</label>
            <input type="text" placeholder="Email" name="email" class="form-control" formControlName="email" />
            <span class="text-danger"
              *ngIf="!(form.get('userData.email')?.valid) && form.get('userData.email')?.touched">Please enter valid
              email</span>
          </div>
        </div>
        <h4 *ngIf="hobbies.length < 2; else t" class="text-warning">Your Hobby</h4>
        <ng-template #t>
          <h4 class="text-warning">Your Hobbies</h4>
        </ng-template>

        <div class="form-group" formArrayName="hobbies">
          <button type="button" class="btn btn-success" (click)="onAddingHobby()">Add Hobby</button>
          <div id="dynamicControls" class="form-group" *ngFor="let hobby of hobbies.controls; let i = index">
            <span class="col-xs-5">
              <input type="text" [placeholder]="'Hobby ' + (i + 1)" [formControlName]="i" class="form-control"></span>
            <span><button id="removeControl" type="button" class="btn btn-danger" (click)="onRemovingHobby(i)">X
              </button></span>
          </div>
        </div>
        <button type="submit" [disabled]="!this.form.valid" class="btn btn-primary">Submit</button>
      </form>
    </div>
  </div>
</div>

Step 2 : app-component.ts

import { Component, OnInit } from '@angular/core';
import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  excludedUserName: string[] = ['Test', 'test'];
  form!: FormGroup;
  ngOnInit(): void {
    this.form = new FormGroup({
      'userData': new FormGroup({
        'email': new FormControl(null, [Validators.required, Validators.email]),
        'username': new FormControl(null, [Validators.required,
                                this.excludedUserNames.bind(this)]),
      }),

      'hobbies': new FormArray([])
    });
  }
  onSubmit() {
    console.log(this.form);
  }

  onAddingHobby() {
    const control = new FormControl(null, Validators.required);
    this.hobbies.push(control);
  }
  onRemovingHobby(i: number) {
    this.hobbies.removeAt(i);
  }

  excludedUserNames(fromControl: FormControl): { [s: string]: boolean } | null {
    if (this.excludedUserName.indexOf(fromControl.value) !== -1) {
      return { 'excludedUserName': true };
    }
    return null;
  }

  get hobbies() {
    return this.form.get('hobbies') as FormArray;
  }
}