添加项目文件。
This commit is contained in:
261
ViewModels/ChatMdViewModel.cs
Normal file
261
ViewModels/ChatMdViewModel.cs
Normal file
@@ -0,0 +1,261 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
182
ViewModels/MainViewModel.cs
Normal file
182
ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using YwxApp.AiChat.Commands;
|
||||
using YwxApp.AiChat.Views;
|
||||
|
||||
namespace YwxApp.AiChat.ViewModels
|
||||
{
|
||||
public class MainViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Field | Property | Collection | Command
|
||||
|
||||
#region Field
|
||||
private object _currentView; //The current view object.
|
||||
private string _currentTime; //The current time.
|
||||
private string _currentModel; //The current model name.
|
||||
private DispatcherTimer _timer; //Time label timer.
|
||||
private ShareOllamaObject _ollamaObject; //OllamaAPI object.
|
||||
#endregion
|
||||
|
||||
#region Property
|
||||
public object CurrentView
|
||||
{
|
||||
get => _currentView;
|
||||
set
|
||||
{
|
||||
_currentView = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string CurrentTime
|
||||
{
|
||||
get => _currentTime;
|
||||
set
|
||||
{
|
||||
_currentTime = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string CurrentModel
|
||||
{
|
||||
get => _currentModel;
|
||||
set
|
||||
{
|
||||
_currentModel = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Collection
|
||||
private ObservableCollection<UserControl> _viewList;
|
||||
private ObservableCollection<UserControl> ViewList
|
||||
{
|
||||
get => _viewList;
|
||||
set
|
||||
{
|
||||
_viewList = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Command
|
||||
public ICommand SwitchToViewCommand { get; }
|
||||
public ICommand ClosingWindowCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public MainViewModel()
|
||||
{
|
||||
//Initialize Ollama object.
|
||||
_ollamaObject = new ShareOllamaObject();
|
||||
|
||||
//bind command method
|
||||
SwitchToViewCommand = new ObjectPassingCommand(OnSwitchToView);
|
||||
ClosingWindowCommand = new EventsCommand<CancelEventArgs>(OnClosingWindow);
|
||||
|
||||
//create view
|
||||
_viewList = new ObservableCollection<UserControl>();
|
||||
ViewList.Add(new SettingView(_ollamaObject));
|
||||
ViewList.Add(new ChatMdView(_ollamaObject));
|
||||
|
||||
//Set the default display of subview 1.
|
||||
CurrentModel = _ollamaObject.Ollama.SelectedModel;
|
||||
InitializeTimer();
|
||||
CurrentView = ViewList[0];
|
||||
}
|
||||
|
||||
#region The window close event
|
||||
/// <summary>
|
||||
///trigger close event
|
||||
/// </summary>
|
||||
private void OnClosingWindow(CancelEventArgs e)
|
||||
{
|
||||
if (MessageBox.Show("确定要关闭程序吗?", "确认关闭", MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
e.Cancel = true;
|
||||
else ClearingResources();
|
||||
}
|
||||
/// <summary>
|
||||
/// Clear the resource.
|
||||
/// </summary>
|
||||
private void ClearingResources()
|
||||
{
|
||||
ShareOllamaObject.CloseProcess("ollama_llama_server");
|
||||
Debug.Print($"{ShareOllamaObject.GetProgramName()}:关闭成功...");
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Other mothod
|
||||
//Initialize time label timer //Each one second update once
|
||||
private void InitializeTimer()
|
||||
{
|
||||
_timer = new DispatcherTimer();
|
||||
_timer.Interval = TimeSpan.FromSeconds(1);
|
||||
_timer.Tick += Timer_Tick;
|
||||
_timer.Start();
|
||||
}
|
||||
//update current time
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
CurrentTime = DateTime.Now.ToString("HH:mm:ss");
|
||||
CurrentModel = _ollamaObject.Ollama.SelectedModel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Command method
|
||||
|
||||
#region View switch
|
||||
//set the view
|
||||
public void OnSwitchToView(object operationItem)
|
||||
{
|
||||
var viewObj = ViewList.FirstOrDefault(viewObj => viewObj.GetType().Name.Equals(operationItem));
|
||||
if (viewObj == null)
|
||||
{
|
||||
var newViewObj = new UserControl();
|
||||
switch (operationItem)
|
||||
{
|
||||
case "ChatMdView":
|
||||
newViewObj = new ChatMdView(_ollamaObject);
|
||||
break;
|
||||
case "SettingView":
|
||||
newViewObj = new SettingView(_ollamaObject);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ViewList.Add(newViewObj);
|
||||
CurrentView = newViewObj;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentView = viewObj;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Property changed event
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
216
ViewModels/SettingViewModel.cs
Normal file
216
ViewModels/SettingViewModel.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
using Microsoft.Win32;
|
||||
using OllamaSharp;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using YwxApp.AiChat.Commands;
|
||||
using YwxApp.AiChat.Models;
|
||||
namespace YwxApp.AiChat.ViewModels
|
||||
{
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 0、Current class:
|
||||
/// </summary>
|
||||
public class SettingViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Field | Property | Collection | Command
|
||||
|
||||
#region Field
|
||||
private string _selectedModel; //select model
|
||||
private string _modelInfo; //model info
|
||||
private SolidColorBrush _labelBackgroundColor; //color style
|
||||
private readonly ShareOllamaObject _ollama; //OllamaAPI object.
|
||||
#endregion
|
||||
|
||||
#region Property
|
||||
public string OllamaAppPath
|
||||
{
|
||||
get { return _ollama.OllamaAppPath; }
|
||||
set { _ollama.OllamaAppPath = value; OnPropertyChanged(); }
|
||||
}
|
||||
public string SelectedModel
|
||||
{
|
||||
get => _selectedModel;
|
||||
set
|
||||
{
|
||||
if (_selectedModel != value)
|
||||
{
|
||||
_selectedModel = value;
|
||||
ResetModelName();
|
||||
}
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string ModelInformation
|
||||
{
|
||||
get => _modelInfo;
|
||||
set
|
||||
{
|
||||
_modelInfo = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public SolidColorBrush LabelBackgroundColor
|
||||
{
|
||||
get => _labelBackgroundColor;
|
||||
set
|
||||
{
|
||||
if (_labelBackgroundColor != value)
|
||||
{
|
||||
_labelBackgroundColor = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Collection
|
||||
public ObservableCollection<string> ModelList
|
||||
{
|
||||
get { return _ollama.ModelList; }
|
||||
set { _ollama.ModelList = value; OnPropertyChanged(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Command
|
||||
public ICommand OpenFileDialogCommand { get; } //select Ollama application file path command.
|
||||
public ICommand GetModelListCommand { get; } //get model list command.
|
||||
public ICommand ModelListUpdateCommand { get; } //model list update command.
|
||||
public ICommand StartOllamaServerCommand { get; } //start ollam server command.
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public SettingViewModel(ShareOllamaObject ollama)
|
||||
{
|
||||
_ollama = ollama;
|
||||
Task task = OnGetModelList();
|
||||
OpenFileDialogCommand = new ParameterlessCommand(() => OnSelectOllamaAppPathDialog());
|
||||
GetModelListCommand = new ParameterlessCommand(async () => await OnGetModelList());
|
||||
ModelListUpdateCommand = new ParameterlessCommand(async () => await OnModelListUpdate());
|
||||
StartOllamaServerCommand = new ParameterlessCommand(async () => OnStartOllamaServer());
|
||||
SetConnected();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region other method
|
||||
///set ollama model server application object.
|
||||
public void SetOllamaApiClient(OllamaApiClient ollama)
|
||||
{
|
||||
_ollama.Ollama = ollama;
|
||||
}
|
||||
|
||||
// set the connection states color
|
||||
public void SetConnected()
|
||||
{
|
||||
if (_ollama.OllamaEnabled)
|
||||
{
|
||||
LabelBackgroundColor = Brushes.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
LabelBackgroundColor = Brushes.Red;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// reset the model
|
||||
/// </summary>
|
||||
private void ResetModelName()
|
||||
{
|
||||
_ollama.OllamaEnabled = false;
|
||||
_ollama.Ollama.SelectedModel = SelectedModel;
|
||||
ModelInformationChanged();
|
||||
_ollama.OllamaEnabled = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// model info changed
|
||||
/// </summary>
|
||||
public void ModelInformationChanged()
|
||||
{
|
||||
string modelName = SelectedModel.Split(':')[0].ToLower();
|
||||
string modelInfoPath = $"{Environment.CurrentDirectory}\\model introduction\\{modelName}.txt";
|
||||
string info = string.Empty;
|
||||
if (File.Exists(modelInfoPath))
|
||||
{
|
||||
info = File.ReadAllText(modelInfoPath);
|
||||
}
|
||||
//MessageBox.Show(modelInfoPath);
|
||||
switch (modelName)
|
||||
{
|
||||
case ModelDescription.Llama32:
|
||||
ModelInformation = info;
|
||||
break;
|
||||
case ModelDescription.CodeGemma:
|
||||
ModelInformation = info;
|
||||
break;
|
||||
default:
|
||||
ModelInformation = "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region command trigger method
|
||||
private void OnStartOllamaServer()
|
||||
{
|
||||
if (!_ollama.OllamaEnabled)
|
||||
{
|
||||
_ollama.StartOllama(OllamaAppPath, SelectedModel);
|
||||
}
|
||||
}
|
||||
private void OnSelectOllamaAppPathDialog()
|
||||
{
|
||||
OpenFileDialog openFileDialog = new();
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
OllamaAppPath = openFileDialog.FileName;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// get the model list
|
||||
/// </summary>
|
||||
private async Task OnGetModelList()
|
||||
{
|
||||
try
|
||||
{
|
||||
//ModelList.Clear();
|
||||
ModelList = (ObservableCollection<string>)_ollama.GetModelList();
|
||||
Debug.Print($"ModelList count: {ModelList.Count}");
|
||||
SelectedModel = _ollama.Ollama.SelectedModel;
|
||||
var modelName = ModelList.FirstOrDefault(name => name.Equals(SelectedModel));
|
||||
if (ModelList.Count > 0 && modelName != null)
|
||||
{
|
||||
SelectedModel = ModelList[ModelList.Count - 1];
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// update the model list
|
||||
/// </summary>
|
||||
private async Task OnModelListUpdate()
|
||||
{
|
||||
MessageBox.Show($"List Update");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region property changed event
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
298
ViewModels/ShareOllamaObject.cs
Normal file
298
ViewModels/ShareOllamaObject.cs
Normal file
@@ -0,0 +1,298 @@
|
||||
using OllamaSharp;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
|
||||
namespace YwxApp.AiChat.ViewModels
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 0、Current class:
|
||||
/// </summary>
|
||||
public class ShareOllamaObject
|
||||
{
|
||||
#region Field | Property | Collection | Command
|
||||
|
||||
#region Field
|
||||
private bool _ollamaEnabled = false; //ollama connected state
|
||||
private string _ollamaAppPath; //ollama app path.
|
||||
private int recordIndex = 0; //current record index.
|
||||
private string _currentPath; //current record;
|
||||
|
||||
private Chat chat; //build interactive chat model object.
|
||||
private OllamaApiClient _ollama; //OllamaAPI object.
|
||||
#endregion
|
||||
|
||||
#region Property
|
||||
public string OllamaAppPath
|
||||
{
|
||||
get { return _ollamaAppPath; }
|
||||
set { _ollamaAppPath = value; }
|
||||
}
|
||||
public bool OllamaEnabled
|
||||
{
|
||||
get { return _ollamaEnabled; }
|
||||
set { _ollamaEnabled = value; }
|
||||
}
|
||||
public OllamaApiClient Ollama
|
||||
{
|
||||
get { return _ollama; }
|
||||
set { _ollama = value; }
|
||||
}
|
||||
public Chat Chat
|
||||
{
|
||||
get { return chat; }
|
||||
set { chat = value; }
|
||||
}
|
||||
public string CurrentPath
|
||||
{
|
||||
get => _currentPath;
|
||||
}
|
||||
public int RecordIndex
|
||||
{
|
||||
get => recordIndex;
|
||||
set
|
||||
{
|
||||
recordIndex = value;
|
||||
_currentPath = $"{Environment.CurrentDirectory}//Data//{DateTime.Today.ToString("yyyyMMdd")}" +
|
||||
$"//{DateTime.Today.ToString("yyyyMMdd")}_{recordIndex}.txt";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Collection
|
||||
public ObservableCollection<string> ModelList { get; set; }
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public ShareOllamaObject()
|
||||
{
|
||||
RecordIndex = 0;
|
||||
WriteDataToFileAsync("");
|
||||
Init(OllamaAppPath, "llama3.2:9b");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region other method
|
||||
/// <summary>
|
||||
/// initialite method
|
||||
/// </summary>
|
||||
private void Init(string appPath, string modelName)
|
||||
{
|
||||
OllamaAppPath = appPath;
|
||||
try
|
||||
{
|
||||
// 设置默认设备为GPU
|
||||
Environment.SetEnvironmentVariable("OLLAMA_DEFAULT_DEVICE", "gpu");
|
||||
//判断路径是否存在
|
||||
if (OllamaAppPath == string.Empty || OllamaAppPath == null) OllamaAppPath = @"ollama app.exe";
|
||||
//路径存在获取应用名
|
||||
if (File.Exists(OllamaAppPath)) OllamaAppPath = Path.GetFileName(OllamaAppPath);
|
||||
//获取环境Ollama环境变量:用于找到 :ollama app.exe
|
||||
var filePath = FindExeInPath(OllamaAppPath);
|
||||
//如果路径存在,启动Ollama
|
||||
if (File.Exists(filePath)) CheckStartProcess(OllamaAppPath);
|
||||
//连接Ollama,并设置初始模型
|
||||
_ollama = new OllamaApiClient(new Uri("http://localhost:11434"));
|
||||
//获取本地可用的模型列表
|
||||
ModelList = (ObservableCollection<string>)GetModelList();
|
||||
var tmepModelName = ModelList.FirstOrDefault(name => name.ToLower().Contains("llama3.2"));
|
||||
if (tmepModelName != null) _ollama.SelectedModel = tmepModelName;
|
||||
else if (ModelList.Count > 0) _ollama.SelectedModel = ModelList[ModelList.Count - 1];
|
||||
|
||||
if (ModelList.FirstOrDefault(name => name.Equals(modelName)) != null) _ollama.SelectedModel = modelName;
|
||||
|
||||
//Ollama服务启用成功
|
||||
OllamaEnabled = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
OllamaEnabled = false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// update the model selected by Ollama
|
||||
/// </summary>
|
||||
public void UpdataSelectedModel(string model)
|
||||
{
|
||||
Ollama.SelectedModel = model;
|
||||
OllamaEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start Ollama app and relevant server.
|
||||
/// </summary>
|
||||
public async void StartOllama(string appPath, string modelName)
|
||||
{
|
||||
Init(appPath, modelName); await Task.Delay(1);
|
||||
}
|
||||
/// <summary>
|
||||
/// get model list
|
||||
/// </summary>
|
||||
public Collection<string> GetModelList()
|
||||
{
|
||||
var models = _ollama.ListLocalModelsAsync();
|
||||
var modelList = new ObservableCollection<string>();
|
||||
foreach (var model in models.Result)
|
||||
{
|
||||
modelList.Add(model.Name);
|
||||
}
|
||||
return modelList;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region starting or closeing method of Ollama(server).
|
||||
/// <summary>
|
||||
/// Finds whether the specified application name is configured in the system environment.
|
||||
/// If it exists, return the full path, otherwise return null
|
||||
/// </summary>
|
||||
public static string FindExeInPath(string exeName)
|
||||
{
|
||||
// get environment variable "Path" value
|
||||
var pathVariable = Environment.GetEnvironmentVariable("PATH");
|
||||
|
||||
// Split string
|
||||
string[] paths = pathVariable.Split(Path.PathSeparator);
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
string fullPath = Path.Combine(path, exeName);
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Startup program Specifies a program, enters a program name, and determines whether the program is running.
|
||||
/// If it is running, exit directly, otherwise run the program according to the input path.
|
||||
/// </summary>
|
||||
public static void CheckStartProcess(string processPath)
|
||||
{
|
||||
string processName = Path.GetFileName(processPath);
|
||||
CheckStartProcess(processName, processPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Startup program Specifies a program, enters a program name, and determines whether the program is running.
|
||||
/// If it is running, exit directly, otherwise run the program according to the input path.
|
||||
/// </summary>
|
||||
public static void CheckStartProcess(string processName, string processPath)
|
||||
{
|
||||
// Check whather the program is running
|
||||
if (!IsProcessRunning(processName))
|
||||
{
|
||||
Console.WriteLine($"{processName} is not running. Starting the process...");
|
||||
StartProcess(processPath);
|
||||
}
|
||||
else Console.WriteLine($"{processName} is already running.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enter the program path to start the program
|
||||
/// </summary>
|
||||
public static void StartProcess(string processPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(processPath);
|
||||
Console.WriteLine("Process started successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error starting process: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whather the process is running
|
||||
/// </summary>
|
||||
public static bool IsProcessRunning(string processName)
|
||||
{
|
||||
Process[] processes = Process.GetProcessesByName(processName);
|
||||
return processes.Length > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// close the process with the specify name.
|
||||
/// </summary>
|
||||
/// <param name="processName"></param>
|
||||
public static void CloseProcess(string processName)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var process in Process.GetProcessesByName(processName))
|
||||
{
|
||||
process.Kill();
|
||||
process.WaitForExit();
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"无法关闭【{processName}】进程: {ex.Message}");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// get current process name
|
||||
/// </summary>
|
||||
public static string GetProgramName()
|
||||
{
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
return assembly.GetName().Name;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region File save
|
||||
/// <summary>
|
||||
/// Save record
|
||||
/// </summary>
|
||||
public void WriteDataToFileAsync(string data, int retryCount = 5, int delayMilliseconds = 500)
|
||||
{
|
||||
//Get the directory where the file located.
|
||||
string directoryPath = Path.GetDirectoryName(CurrentPath);
|
||||
|
||||
// if directory exists't ,create directory(include all must directory).
|
||||
if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
}
|
||||
|
||||
for (int i = 0; i < retryCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream fs = new FileStream(CurrentPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
|
||||
using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8))
|
||||
{
|
||||
writer.WriteAsync(data);
|
||||
}
|
||||
return; // successful writed exit the loop.
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (i == retryCount - 1)
|
||||
{
|
||||
throw; //If the maximum number of retries is reached , a exception is thrown
|
||||
}
|
||||
Task.Delay(delayMilliseconds); // Wait a while and try again
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw; //other exception is thrown
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user