262 lines
8.4 KiB
C#
262 lines
8.4 KiB
C#
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>
|
||
/// OnPropertyChanged:Trigger a property changed event.
|
||
/// </summary>
|
||
public event PropertyChangedEventHandler? PropertyChanged;
|
||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||
{
|
||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||
}
|
||
#endregion
|
||
}
|
||
}
|