@BigSquare 님께서 NixOS에서 rust tauri + C# blazor wasm 를 위한 설정 글에서 댓글 달아주신 GitHub - LittleLittleCloud/RazorConsole: Build interactive console applications with .NET Razor and Spectre.Console 프로젝트의 샘플을 보고, FBA 버전으로 만들어본다면 재미있을 것 같아서 바로 만들어봤습니다. ![]()
Components/App.razor
@namespace LLMAgentTUI.Components
@using System
@using System.Collections.Generic
@using System.Linq
@using Microsoft.AspNetCore.Components
@using RazorConsole.Components
@using Spectre.Console
@inject ChatService ChatService
<Figlet Content="ChatBot" />
<Align Horizontal="@HorizontalAlignment.Center">
<Markup Content="AI-Powered Console ChatBot • Tab to change focus • Enter to submit • Ctrl+C to exit" Foreground="@Color.Grey58" />
</Align>
<Padder Padding="@(new(1, 0, 0, 0))">
<Rows>
@if (_messages.Count == 0)
{
<Markup Content="No messages yet. Type a message below to start chatting." Foreground="@Color.Grey" />
}
else
{
foreach (var message in _messages)
{
<Padder Padding="@(new(0, 1, 0, 0))">
<Markup Content="@($"{(message.IsUser ? "You" : "Bot")}")" Foreground="@(message.IsUser? Color.Green: Color.Blue)" />
<Markup Content="@message.Content" Foreground="@(message.IsUser? Color.Grey: Color.Default)" />
</Padder>
}
}
@if (_isProcessing)
{
<Padder Padding="@(new(0, 0, 0, 1))">
<Columns>
<RazorConsole.Components.Spinner SpinnerType="@Spectre.Console.Spinner.Known.Dots" />
<Markup Content="AI is thinking..." Foreground="@Color.Grey" Decoration="@Decoration.Italic" />
</Columns>
</Padder>
}
</Rows>
</Padder>
<TextInput @bind-Value="_currentInput" Placeholder="Type your message here..." OnSubmit="SendMessage" Expand="true" />
<Padder Padding="@(new(1, 0, 0, 0))">
<TextButton Content="Send" OnClick="SendMessage" BackgroundColor="@Color.Blue" FocusedColor="@Color.DodgerBlue1" />
</Padder>
@code {
private List<ChatMessage> _messages = new();
private string _currentInput = string.Empty;
private bool _isProcessing = false;
private async Task SendMessage()
{
if (string.IsNullOrWhiteSpace(_currentInput))
{
return;
}
var userMessage = _currentInput.Trim();
_currentInput = string.Empty;
_messages.Add(new ChatMessage
{
Content = userMessage,
IsUser = true
});
StateHasChanged();
_isProcessing = true;
StateHasChanged();
try
{
var response = await ChatService.SendMessageAsync(userMessage);
_messages.Add(new ChatMessage
{
Content = response,
IsUser = false
});
}
catch (Exception ex)
{
_messages.Add(new ChatMessage
{
Content = $"[red]Error: {ex.Message}[/]",
IsUser = false
});
}
finally
{
_isProcessing = false;
StateHasChanged();
}
}
public class ChatMessage
{
public required string Content { get; set; }
public bool IsUser { get; set; }
}
}
Directory.Build.props
다른 설정 없이 아래와 같이 파일을 만들어둔 것만으로 Razor SDK의 내장 설정 덕분에 .razor 파일이 자동으로 불러와집니다.
<Project>
<ItemGroup>
</ItemGroup>
</Project>
Program.cs
#!/usr/bin/env dotnet
// Source: https://github.com/LittleLittleCloud/RazorConsole/tree/main/examples/LLMAgentTUI
#:sdk Microsoft.NET.Sdk.Razor
#:property OutputType=Exe
#:package RazorConsole.Core@0.0.5
#:package Microsoft.Extensions.AI@9.10.0
#:package Microsoft.Extensions.AI.Ollama@9.7.0-preview.1.25356.2
#:package Microsoft.Extensions.AI.OpenAI@9.10.0-preview.1.25513.3
#:package Microsoft.Extensions.Hosting@10.0.0-rc.2.25502.107
#:package Spectre.Console@0.52.1-preview.0.5
using Microsoft.Extensions.AI;
using LLMAgentTUI.Components;
using Microsoft.Extensions.DependencyInjection;
using OpenAI;
using RazorConsole.Core;
// Get API key from environment variable or use Ollama as default
var useOllama = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
await AppHost.RunAsync<App>(null, builder =>
{
builder.ConfigureServices(services =>
{
if (useOllama)
{
// Use Ollama with local model
services.AddChatClient(client =>
new OllamaChatClient(new Uri("http://localhost:11434"), "gpt-oss:20b"));
}
else
{
// Use OpenAI
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
services.AddChatClient(client =>
new OpenAIClient(apiKey).GetChatClient("gpt-5-mini").AsIChatClient());
}
services.AddSingleton<ChatService>();
});
builder.Configure(options =>
{
options.AutoClearConsole = false;
});
});
public sealed class ChatService
{
private readonly IChatClient _chatClient;
private readonly List<ChatMessage> _conversationHistory = new();
public ChatService(IChatClient chatClient)
{
_chatClient = chatClient;
}
public async Task<string> SendMessageAsync(string message)
{
_conversationHistory.Add(new ChatMessage(ChatRole.User, message));
var response = await _chatClient.GetResponseAsync(_conversationHistory).ConfigureAwait(false);
var assistantMessage = response.Text ?? "No response from the AI.";
_conversationHistory.Add(new ChatMessage(ChatRole.Assistant, assistantMessage));
return assistantMessage;
}
}
이와 같이 구성한 후, Ollama 또는 OpenAI API 키 발급 후 OPENAI_API_KEY 환경 변수에 대입하여 아래 명령어를 실행하면 스크린샷처럼 UI가 잘 꾸며진 TUI 프로그램이 만들어집니다. ![]()
dotnet run --no-cahce .\Program.cs
