添加项目文件。
This commit is contained in:
9
App.xaml
Normal file
9
App.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<Application x:Class="YwxApp.AiChat.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:YwxApp.AiChat"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
13
App.xaml.cs
Normal file
13
App.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace YwxApp.AiChat;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
|
||||
10
AssemblyInfo.cs
Normal file
10
AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly:ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
54
Commands/ClosingWindowBehavior.cs
Normal file
54
Commands/ClosingWindowBehavior.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interactivity;
|
||||
namespace YwxApp.AiChat.Commands
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Close Window Behavior
|
||||
/// </summary>
|
||||
public class ClosingWindowBehavior : Behavior<Window>
|
||||
{
|
||||
public static readonly DependencyProperty CommandProperty =
|
||||
DependencyProperty.Register("Command", typeof(ICommand), typeof(ClosingWindowBehavior), new PropertyMetadata(null));
|
||||
|
||||
public static readonly DependencyProperty CommandParameterProperty =
|
||||
DependencyProperty.Register("CommandParameter", typeof(object), typeof(ClosingWindowBehavior), new PropertyMetadata(null));
|
||||
|
||||
public ICommand Command
|
||||
{
|
||||
get { return (ICommand)GetValue(CommandProperty); }
|
||||
set { SetValue(CommandProperty, value); }
|
||||
}
|
||||
|
||||
public object CommandParameter
|
||||
{
|
||||
get { return GetValue(CommandParameterProperty); }
|
||||
set { SetValue(CommandParameterProperty, value); }
|
||||
}
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
base.OnAttached();
|
||||
AssociatedObject.Closing += OnClosing;
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
base.OnDetaching();
|
||||
AssociatedObject.Closing -= OnClosing;
|
||||
}
|
||||
|
||||
private void OnClosing(object sender, CancelEventArgs e)
|
||||
{
|
||||
if (Command != null && Command.CanExecute(CommandParameter))
|
||||
{
|
||||
Command.Execute(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
Commands/EventsCommand.cs
Normal file
29
Commands/EventsCommand.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YwxApp.AiChat.Commands
|
||||
{
|
||||
|
||||
public class EventsCommand<T> : ICommand
|
||||
{
|
||||
private readonly Action<T> _execute;
|
||||
private readonly Func<T, bool> _canExecute;
|
||||
public EventsCommand(Action<T> execute, Func<T, bool> canExecute = null)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return _canExecute?.Invoke((T)parameter) ?? true;
|
||||
}
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
_execute((T)parameter);
|
||||
}
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add { CommandManager.RequerySuggested += value; }
|
||||
remove { CommandManager.RequerySuggested -= value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Commands/ObjectPassingCommand.cs
Normal file
29
Commands/ObjectPassingCommand.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YwxApp.AiChat.Commands
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// object parameter passing.
|
||||
/// </summary>
|
||||
public class ObjectPassingCommand : ICommand
|
||||
{
|
||||
public Action<object> execute;
|
||||
public ObjectPassingCommand(Action<object> execute)
|
||||
{
|
||||
this.execute = execute;
|
||||
}
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return CanExecuteChanged != null;
|
||||
}
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
execute?.Invoke(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
Commands/ParameterlessCommand.cs
Normal file
29
Commands/ParameterlessCommand.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YwxApp.AiChat.Commands
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// relay command
|
||||
/// </summary>
|
||||
public class ParameterlessCommand : ICommand
|
||||
{
|
||||
private Action _execute;
|
||||
public ParameterlessCommand(Action execute)
|
||||
{
|
||||
_execute = execute;
|
||||
}
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return CanExecuteChanged != null;
|
||||
}
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
_execute.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
37
Commands/ScrollViewerCommand.cs
Normal file
37
Commands/ScrollViewerCommand.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YwxApp.AiChat.Commands
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Scroll command : The argument object passed by this constructor of this class is ScrollViewer
|
||||
/// </summary>
|
||||
class ScrollViewerCommand : ICommand
|
||||
{
|
||||
private readonly Action<object> _execute;
|
||||
private readonly Predicate<object> _canExecute;
|
||||
|
||||
public ScrollViewerCommand(Action<object> execute, Predicate<object> canExecute = null)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return _canExecute == null || _canExecute(parameter);
|
||||
}
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
_execute(parameter);
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add { CommandManager.RequerySuggested += value; }
|
||||
remove { CommandManager.RequerySuggested -= value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
Data/20250312/20250312_0.txt
Normal file
1
Data/20250312/20250312_0.txt
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
88
MainWindow.xaml
Normal file
88
MainWindow.xaml
Normal file
@@ -0,0 +1,88 @@
|
||||
<Window x:Class="YwxApp.AiChat.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:behaviors="clr-namespace:YwxApp.AiChat.Commands"
|
||||
xmlns:ViewModels="clr-namespace:YwxApp.AiChat.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
Title="ChatAI" Height="600" Width="800" MinHeight="600" MinWidth="800">
|
||||
<Window.DataContext>
|
||||
<ViewModels:MainViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Views/Style/ButtonStyle.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<i:Interaction.Behaviors>
|
||||
<behaviors:ClosingWindowBehavior Command="{Binding ClosingWindowCommand}" />
|
||||
</i:Interaction.Behaviors>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="20"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="10"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="25"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
|
||||
<Rectangle >
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<GradientStop Color="#916CE5" Offset="0.5" />
|
||||
<GradientStop Color="#FFFFFF" Offset="1.5" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Grid.Column="0" >
|
||||
<Rectangle Grid.Row="1" Grid.Column="0">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<GradientStop Color="#9ABAFF" Offset="0.8" />
|
||||
<GradientStop Color="#9ABFAF" Offset="0.3" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<StackPanel Margin="0 0 0 0" Grid.Row="1" Grid.Column="0">
|
||||
<Button Command="{Binding SwitchToViewCommand}" CommandParameter="SettingView" Style="{StaticResource IconButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Resources/setting64.png" Margin="5" />
|
||||
<TextBlock Text="设置" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding SwitchToViewCommand}" CommandParameter="ChatMdView" Style="{StaticResource IconButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Resources/chat64.png" Margin="5"/>
|
||||
<TextBlock Text="会话" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Grid.Column="1" Margin="5">
|
||||
<ContentControl Content="{Binding CurrentView}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3">
|
||||
<Rectangle>
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
|
||||
<GradientStop Color="#FAAFA9" Offset="0.1" />
|
||||
<GradientStop Color="#A4D3A2" Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle >
|
||||
<WrapPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Center" HorizontalAlignment="Right">
|
||||
<Label Content="{Binding CurrentModel}" Width="auto" FontSize="12" Margin="5 0 5 0"/>
|
||||
<Label Content="{Binding CurrentTime}" Background="#00F0BD" Width="auto" FontSize="12" Margin="5 0 5 0"/>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
19
MainWindow.xaml.cs
Normal file
19
MainWindow.xaml.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System.Windows;
|
||||
using YwxApp.AiChat.Services;
|
||||
using YwxApp.AiChat.ViewModels;
|
||||
|
||||
namespace YwxApp.AiChat;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
//OllamaService ollamaService = new();
|
||||
//ollamaService.Configure("http://192.168.1.3:11434", "deepseek-r1:1.5b");
|
||||
//this.DataContext = new ChatViewModel(new OllamaService());
|
||||
}
|
||||
}
|
||||
30
Models/ChatMessage.cs
Normal file
30
Models/ChatMessage.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace YwxApp.AiChat.Models
|
||||
{
|
||||
public class ChatMessage : INotifyPropertyChanged
|
||||
{
|
||||
public enum MessageRole { User, Assistant }
|
||||
|
||||
private MessageRole _role;
|
||||
public MessageRole Role
|
||||
{
|
||||
get => _role;
|
||||
set { _role = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
private string _content;
|
||||
public string Content
|
||||
{
|
||||
get => _content;
|
||||
set { _content = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Models/ModelDescription.cs
Normal file
40
Models/ModelDescription.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YwxApp.AiChat.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// select switch display model description.
|
||||
/// </summary>
|
||||
public class ModelDescription
|
||||
{
|
||||
public const string CodeGemma = "codegemma";
|
||||
public const string Llama32 = "llama3.2";
|
||||
//model list(description)
|
||||
public const string Codellama34b = "codellama:34b";
|
||||
public const string Llava13b = "llava:13b";
|
||||
public const string CommandRLatest = "command-r:latest";
|
||||
public const string Wizardlm2Latest = "wizardlm2:latest";
|
||||
public const string Qwen25CoderLatest = "qwen2.5-coder:latest";
|
||||
public const string Qwen25_14b = "qwen2.5:14b";
|
||||
public const string SamanthaMistralLatest = "samantha-mistral:latest";
|
||||
public const string MistralSmallLatest = "mistral-small:latest";
|
||||
public const string Gemma29b = "gemma2:9b";
|
||||
public const string NemotronMiniLatest = "nemotron-mini:latest";
|
||||
public const string Phi35Latest = "phi3.5:latest";
|
||||
public const string Llama32VisionLatest = "llama3.2-vision:latest";
|
||||
public const string Llama31_8b = "llama3.1:8b";
|
||||
public const string Gemma22b = "gemma2:2b";
|
||||
public const string Qwen27b = "qwen2:7b";
|
||||
public const string Qwen20_5b = "qwen2:0.5b";
|
||||
public const string Llama31_70b = "llama3.1:70b";
|
||||
public const string Llama31Latest = "llama3.1:latest";
|
||||
public const string Llama32Latest = "llama3.2:latest";
|
||||
public const string Llama32_3b = "llama3.2:3b";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
14
Services/IChatService.cs
Normal file
14
Services/IChatService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YwxApp.AiChat.Services
|
||||
{
|
||||
public interface IChatService
|
||||
{
|
||||
Task<string> GetResponseAsync(string prompt);
|
||||
void Configure(string apiKey, string modelName = "gpt-3.5-turbo");
|
||||
}
|
||||
}
|
||||
95
Services/OllamaService.cs
Normal file
95
Services/OllamaService.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using static YwxApp.AiChat.Services.OpenAIService;
|
||||
|
||||
namespace YwxApp.AiChat.Services
|
||||
{
|
||||
public class OllamaService : IChatService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private string _modelName;
|
||||
|
||||
public OllamaService()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
public void Configure(string apiKey, string modelName)
|
||||
{
|
||||
//_httpClient.BaseAddress = new Uri(apiKey);
|
||||
_modelName = modelName;
|
||||
}
|
||||
|
||||
public async Task<string> GetResponseAsync(string prompt)
|
||||
{
|
||||
//var request = new
|
||||
//{
|
||||
// model = "deepseek-r1:1.5b",
|
||||
// prompt = prompt,
|
||||
// stream = false
|
||||
//};
|
||||
|
||||
// var response = await _httpClient.PostAsJsonAsync("http://192.168.1.3:11434/api/generate", request);
|
||||
// var result = await response.Content.ReadFromJsonAsync<OllamaResponse>();
|
||||
|
||||
// return result?.Response ?? "No response";
|
||||
|
||||
var request = new ChatRequest
|
||||
{
|
||||
Messages ={
|
||||
new Message { Role = "user", Content = "你是谁" }
|
||||
}
|
||||
};
|
||||
|
||||
string json = JsonSerializer.Serialize(request, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync("http://192.168.1.3:11434/api/chat", request);
|
||||
// var response = await _httpClient.PostAsJsonAsync("http://192.168.1.3:11434/api/generate", request);
|
||||
// var result = await response.Content.ReadFromJsonAsync<OllamaResponse>();
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
MessageBox.Show(result);
|
||||
|
||||
return "No response";
|
||||
}
|
||||
public class ChatRequest
|
||||
{
|
||||
[JsonPropertyName("messages")]
|
||||
public List<Message> Messages { get; set; } = new List<Message>();
|
||||
|
||||
[JsonPropertyName("model")]
|
||||
public string? Model { get; set; } = "deepseek-r1:1.5b"; // 默认值
|
||||
|
||||
[JsonPropertyName("stream")]
|
||||
public bool Stream { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("temperature")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public double Temperature { get; set; } = 0.01;
|
||||
|
||||
[JsonPropertyName("max_tokens")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||
public int MaxTokens { get; set; } = 1024;
|
||||
}
|
||||
|
||||
public class Message
|
||||
{
|
||||
[JsonPropertyName("role")]
|
||||
public string Role { get; set; } = "user";
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private class OllamaResponse
|
||||
{
|
||||
public string Response { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Services/OpenAIService.cs
Normal file
54
Services/OpenAIService.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace YwxApp.AiChat.Services
|
||||
{
|
||||
public class OpenAIService : IChatService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private string _modelName = "gpt-3.5-turbo";
|
||||
|
||||
public OpenAIService()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_httpClient.BaseAddress = new Uri("https://api.openai.com/v1/");
|
||||
}
|
||||
|
||||
public void Configure(string apiKey, string modelName = "gpt-3.5-turbo")
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", apiKey);
|
||||
_modelName = modelName;
|
||||
}
|
||||
|
||||
public async Task<string> GetResponseAsync(string prompt)
|
||||
{
|
||||
var requestBody = new
|
||||
{
|
||||
model = _modelName,
|
||||
messages = new[] { new { role = "user", content = prompt } }
|
||||
};
|
||||
|
||||
var response = await _httpClient.PostAsJsonAsync("chat/completions", requestBody);
|
||||
var responseContent = await response.Content.ReadFromJsonAsync<OpenAIResponse>();
|
||||
|
||||
return responseContent?.Choices[0].Message.Content ?? "No response";
|
||||
}
|
||||
|
||||
public class Message
|
||||
{
|
||||
public string Content { get; set; }
|
||||
}
|
||||
public class Choice
|
||||
{
|
||||
public Message Message { get; set; }
|
||||
|
||||
}
|
||||
private class OpenAIResponse
|
||||
{
|
||||
public Choice[] Choices { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Utilities/AsyncRelayCommand.cs
Normal file
26
Utilities/AsyncRelayCommand.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace YwxApp.AiChat.Utilities
|
||||
{
|
||||
public class AsyncRelayCommand : ICommand
|
||||
{
|
||||
private readonly Func<Task> _execute;
|
||||
private readonly Func<bool> _canExecute;
|
||||
|
||||
public AsyncRelayCommand(Func<Task> execute, Func<bool> canExecute = null)
|
||||
{
|
||||
_execute = execute;
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged
|
||||
{
|
||||
add => CommandManager.RequerySuggested += value;
|
||||
remove => CommandManager.RequerySuggested -= value;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
|
||||
|
||||
public async void Execute(object parameter) => await _execute();
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
74
Views/ChatMdView.xaml
Normal file
74
Views/ChatMdView.xaml
Normal file
@@ -0,0 +1,74 @@
|
||||
<UserControl x:Class="YwxApp.AiChat.Views.ChatMdView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:markdig="clr-namespace:Markdig.Wpf;assembly=Markdig.Wpf"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources >
|
||||
<ResourceDictionary>
|
||||
<!--Resource dictionary:Add control style.-->
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Style/ButtonStyle.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Background="#0F000F">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="200" />
|
||||
<RowDefinition Height="50" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!--First line: Display output text to "Markdown" container-->
|
||||
<Grid Grid.Row="0">
|
||||
<ScrollViewer Background="#FFFFFF" x:Name="MarkDownScrollViewer">
|
||||
<!--Bind event command to the ScrollViewer-->
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="ScrollChanged">
|
||||
<i:InvokeCommandAction Command = "{Binding ScrollToEndCommand}"
|
||||
CommandParameter="{Binding ElementName=MarkDownScrollViewer}" />
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<!--scrollviewer internal container-->
|
||||
<markdig:MarkdownViewer x:Name="MarkdownOutputBox" Markdown="{Binding MarkdownContent}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<!-- the second line -->
|
||||
<Grid Grid.Row="1">
|
||||
<TextBox x:Name="InputBox"
|
||||
Text="{Binding InputText , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Grid.Row="1" Margin="5" AcceptsReturn="True"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<!--key binding of "Enter"-->
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Command="{Binding SubmitQuestionCommand}" Key="Enter"/>
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
|
||||
<!-- The third line: submit button -->
|
||||
<Grid Grid.Row="2">
|
||||
<WrapPanel Grid.Row="2" HorizontalAlignment="Right">
|
||||
|
||||
<Button x:Name="BtnNewChat" Content="新建会话"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource RoundCornerButtonStyle}"
|
||||
Command="{Binding NewSessionCommand}"
|
||||
Width="100"
|
||||
Height="30"/>
|
||||
<Button x:Name="BtnSubmit" Content="提交"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource RoundCornerButtonStyle}"
|
||||
Command="{Binding SubmitQuestionCommand}"
|
||||
Width="100"
|
||||
Height="30"/>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
37
Views/ChatMdView.xaml.cs
Normal file
37
Views/ChatMdView.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using YwxApp.AiChat.ViewModels;
|
||||
|
||||
namespace YwxApp.AiChat.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// ChatMdView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ChatMdView : UserControl
|
||||
{
|
||||
public ChatMdView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
ChatMdViewModel viewModel;
|
||||
public ChatMdView(ShareOllamaObject shareOllama)
|
||||
{
|
||||
InitializeComponent();
|
||||
viewModel = new ChatMdViewModel();
|
||||
viewModel.SetOllama(shareOllama);
|
||||
this.DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Views/SettingView.xaml
Normal file
58
Views/SettingView.xaml
Normal file
@@ -0,0 +1,58 @@
|
||||
<UserControl x:Class="YwxApp.AiChat.Views.SettingView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<!--Resource dictionary : add the control style-->
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Style/ButtonStyle.xaml"/>
|
||||
<ResourceDictionary Source="Style/TextBoxStyle.xaml"/>
|
||||
<ResourceDictionary Source="Style/LabelStyle.xaml"/>
|
||||
<ResourceDictionary Source="Style/ComboBoxStyle.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid Background="#FFFFFF" HorizontalAlignment="Stretch">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- The first line -->
|
||||
<WrapPanel Grid.Row="0" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<Label Content="Ollama路径:" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
<TextBox x:Name="Tbx_OllamaAppPath" FontSize="12"
|
||||
Text="{Binding OllamaAppPath}"
|
||||
Style="{StaticResource SearchBoxStyle}" Margin="5" />
|
||||
</WrapPanel>
|
||||
|
||||
<!--The second line-->
|
||||
<WrapPanel Grid.Row="1" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<Label Content="Ollama:" VerticalAlignment="Center" Margin="5" />
|
||||
<Label Name="Label_State" Style="{StaticResource RoundLabelStyle}" />
|
||||
<Button Content="打开" Style="{StaticResource RoundCornerButtonStyle}"
|
||||
Command="{Binding StartOllamaServerCommand}"/>
|
||||
</WrapPanel>
|
||||
|
||||
<!--The third line-->
|
||||
<WrapPanel Grid.Row="2" Margin="5" VerticalAlignment="Center" HorizontalAlignment="Left">
|
||||
<Label Content="模型:" VerticalAlignment="Center" Margin="5" />
|
||||
<ComboBox x:Name="Cbx_ModelList" Style="{StaticResource RoundComboBoxStyle}"
|
||||
ItemsSource="{Binding ModelList}"
|
||||
SelectedItem="{Binding SelectedModel}">
|
||||
</ComboBox>
|
||||
<Button Content="刷新" Margin="5" Grid.Row="1"
|
||||
Style="{StaticResource RoundCornerButtonStyle}"
|
||||
Command="{Binding ModelListUpdateCommand}"/>
|
||||
</WrapPanel>
|
||||
<TextBox x:Name="ModelDesciption" Grid.Row="3" IsReadOnly="True"
|
||||
TextWrapping="WrapWithOverflow" Text="{Binding ModelInformation,Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
20
Views/SettingView.xaml.cs
Normal file
20
Views/SettingView.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Windows.Controls;
|
||||
using YwxApp.AiChat.ViewModels;
|
||||
|
||||
namespace YwxApp.AiChat.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// SettingView.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
|
||||
public partial class SettingView : UserControl
|
||||
{
|
||||
SettingViewModel _viewModel;
|
||||
public SettingView(ShareOllamaObject ollama)
|
||||
{
|
||||
InitializeComponent();
|
||||
_viewModel = new SettingViewModel(ollama);
|
||||
this.DataContext = _viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Views/Style/ButtonStyle.xaml
Normal file
33
Views/Style/ButtonStyle.xaml
Normal file
@@ -0,0 +1,33 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<Style x:Key="IconButtonStyle">
|
||||
|
||||
<Setter Property="Button.Background" Value="Transparent"/>
|
||||
<Setter Property="Button.BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Button.BorderThickness" Value="0"/>
|
||||
<Setter Property="Button.Foreground" Value="White"/>
|
||||
<Setter Property="Button.FontSize" Value="12"/>
|
||||
<Setter Property="Button.FontWeight" Value="Bold"/>
|
||||
<Setter Property="Button.Padding" Value="5"/>
|
||||
<Setter Property="Button.Margin" Value="5"/>
|
||||
<Setter Property="Button.Width" Value="150"/>
|
||||
<Setter Property="Button.Height" Value="50"/>
|
||||
<Setter Property="Button.HorizontalAlignment" Value="Left"/>
|
||||
<Setter Property="Button.VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Button.Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="RoundCornerButtonStyle">
|
||||
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
9
Views/Style/ComboBoxStyle.xaml
Normal file
9
Views/Style/ComboBoxStyle.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="SearchBoxStyle">
|
||||
|
||||
</Style>
|
||||
<Style x:Key="RoundComboBoxStyle">
|
||||
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
6
Views/Style/LabelStyle.xaml
Normal file
6
Views/Style/LabelStyle.xaml
Normal file
@@ -0,0 +1,6 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Style x:Key="RoundLabelStyle">
|
||||
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
4
Views/Style/TextBoxStyle.xaml
Normal file
4
Views/Style/TextBoxStyle.xaml
Normal file
@@ -0,0 +1,4 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
</ResourceDictionary>
|
||||
23
YwxApp.AiChat.csproj
Normal file
23
YwxApp.AiChat.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig.Wpf" Version="0.5.0.1" />
|
||||
<PackageReference Include="MvvmLightLibs" Version="5.4.1.1" />
|
||||
<PackageReference Include="OllamaSharp" Version="5.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
25
YwxApp.AiChat.sln
Normal file
25
YwxApp.AiChat.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35825.156 d17.13
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YwxApp.AiChat", "YwxApp.AiChat.csproj", "{E8688B66-249C-4189-A7A4-B57B7D8A8B86}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E8688B66-249C-4189-A7A4-B57B7D8A8B86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E8688B66-249C-4189-A7A4-B57B7D8A8B86}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E8688B66-249C-4189-A7A4-B57B7D8A8B86}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E8688B66-249C-4189-A7A4-B57B7D8A8B86}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D2C58468-159F-4EEA-8C4C-5BB5BF2071E9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
10
appsettings.json
Normal file
10
appsettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"OpenAIConfig": {
|
||||
"ApiKey": "your-api-key",
|
||||
"Model": "gpt-3.5-turbo"
|
||||
},
|
||||
"OllamaConfig": {
|
||||
"BaseUrl": "http://192.168.1.3:11434",
|
||||
"Model": "deepseek-r1:1.5b"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user