Files
YwxApp.AiChat/ViewModels/ChatMdViewModel.cs
2025-03-12 20:02:52 +08:00

262 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Markdig.Wpf;
using OllamaSharp;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using System.Windows.Input;
using YwxApp.AiChat.Commands;
namespace YwxApp.AiChat.ViewModels
{
/// <summary>
/// 0、Current class:
/// </summary>
public class ChatMdViewModel : INotifyPropertyChanged
{
#region Field | Property | Collection | Command
#region Field
private string? _inputText; //User input text.
private Chat? chat; //Build interactive chat.
private ShareOllamaObject _ollama; //share Ollama object.
private CancellationTokenSource _cancellationTokenSource; //Termination chat Token
private bool _useExtensions = true; //whether enable Markdown extensions function.
private string _markdownContent; //Markdown context.
private MarkdownViewer markdownViewer; //Markdwon viewer.
private bool _isAutoScrolling = false; //whather enable scroll
private double _textWidth; // MarkdownViewer width
#endregion
#region Property : Support property changed notify.
//InputText
public string? InputText
{
get => _inputText;
set
{
_inputText = value;
OnPropertyChanged();
}
}
public string MarkdownContent
{
get => _markdownContent;
set
{
_markdownContent = value;
// Notify property changed if needed
OnPropertyChanged();
}
}
public double TextWidth
{
get => _textWidth;
set
{
_textWidth = value;
OnPropertyChanged();
}
}
#endregion
#region Collection:
#endregion
#region Command: Builde Command generate response command
public ICommand? SubmitQuestionCommand { get; }
//stop current chat
public ICommand? StopCurrentChatCommand { get; }
//new chat
public ICommand? NewSessionCommand { get; }
//scroll to MarkdownViewer end command
public ICommand ScrollToEndCommand { get; }
#endregion
#endregion
#region Constructor : Initialize
public ChatMdViewModel()
{
// initialize object
markdownViewer = new MarkdownViewer();
_cancellationTokenSource = new CancellationTokenSource();
//generate command
SubmitQuestionCommand = new ParameterlessCommand(async () => OnSubmitQuestion());
StopCurrentChatCommand = new ParameterlessCommand(OnStopCurrentChat);
NewSessionCommand = new ParameterlessCommand(OnNewSessionCommand);
//markdown reletive command
ScrollToEndCommand = new ScrollViewerCommand(OnScrollToEnd);
OnLoadRecord();
}
#endregion
#region other method
#region other
//setting Ollama
public void SetOllama(ShareOllamaObject ollama)
{
_ollama = ollama;
}
//check chat state
private bool CheckChatState()
{
if (_ollama.Ollama == null || _ollama.OllamaEnabled == false)
{
MarkdownContent += "server not open...";
return false;
}
if (_ollama.Ollama.SelectedModel == null)
{
MarkdownContent += "model not select...";
return false;
}
if (string.IsNullOrWhiteSpace(InputText))
{
MarkdownContent += "text is null ...";
return false;
}
return true;
}
//trigger sroll to end
private void OnScrollToEnd(object parameter)
{
var scrollViewer = parameter as ScrollViewer;
if (scrollViewer != null && _isAutoScrolling)
{
scrollViewer.ScrollToEnd();
TextWidth = scrollViewer.Width;
}
}
#endregion
#region Mardown command binding method
//loaded history record
public void OnLoadRecord()
{
OutText(File.ReadAllText($"{Environment.CurrentDirectory}//Data//" +
$"{DateTime.Today.ToString("yyyyMMdd")}//{DateTime.Today.ToString("yyyyMMdd")}_0.txt"));
}
#endregion
#endregion
#region command method
/// <summary>
/// Submit question: Submit problem to the AI and get the output result
/// </summary>
private async void OnSubmitQuestion()
{
try
{
// Checks whether the string is empty, empty, or contains only whitespace characters
if (CheckChatState())
{
_isAutoScrolling = true; //enable auto scroll
//ToggleExtensions();
string input = InputText;
InputText = string.Empty;
string output = string.Empty;
OutText($"{Environment.NewLine}");
OutText($"## 【User】{Environment.NewLine}");
OutText($">{input}{Environment.NewLine}");
OutText($"## 【AI】{Environment.NewLine}");
//
output += ($"{Environment.NewLine}");
output += ($"## 【User】{Environment.NewLine}");
output += ($">{input}{Environment.NewLine}");
output += ($"## 【AI】{Environment.NewLine}");
if (input.Equals("/clearContext"))
{
chat = new Chat(_ollama.Ollama);
_ollama.RecordIndex++;
return;
}
#region Start answer :Mode two => chat mode
if (chat == null)
{
chat = new Chat(_ollama.Ollama);
_ollama.RecordIndex++;
}
_cancellationTokenSource = new CancellationTokenSource();
await foreach (var answerToken in chat.SendAsync(input, _cancellationTokenSource.Token))
{
OutText(answerToken);
output += (answerToken);
await Task.Delay(20);
Debug.Print(answerToken);
}
OutText($"{Environment.NewLine}");
_ollama.WriteDataToFileAsync(output);
#endregion
}
}
catch (Exception ex)
{
OutText($"Error: {ex.Message}{Environment.NewLine}");
}
_isAutoScrolling = false;
}
/// <summary>
/// New build chat.
/// </summary>
private void OnNewSessionCommand()
{
OnStopCurrentChat();
if (chat != null)
{
chat.SendAsync("/clearContext");
if (_ollama != null)
chat = new Chat(_ollama.Ollama);
}
OutText($"{string.Empty}{Environment.NewLine}");
}
/// <summary>
/// stop chat.
/// </summary>
private void OnStopCurrentChat()
{
_cancellationTokenSource?.Cancel();
Task.Delay(100);
OutText($"{string.Empty}{Environment.NewLine}");
MarkdownContent = string.Empty;
}
/// <summary>
/// output Text to Markdown.
/// </summary>
/// <param name="text"></param>
public void OutText(string text)
{
MarkdownContent += text;
}
#endregion
#region Method that trigger a property changed event.
/// <summary>
/// OnPropertyChangedTrigger a property changed event.
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}