增加远程链接项

This commit is contained in:
yangwx
2025-03-12 23:13:02 +08:00
parent 3ccd6d9a39
commit cb5c895d33
16 changed files with 103 additions and 249 deletions

View File

@@ -1,8 +1,8 @@

using Microsoft.Xaml.Behaviors;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace YwxApp.AiChat.Commands
{

View File

@@ -1,6 +1,6 @@
<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:i="http://schemas.microsoft.com/xaml/behaviors"
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"
@@ -18,13 +18,13 @@
<ResourceDictionary Source="Views/Style/ButtonStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<i:Interaction.Behaviors>
</Window.Resources>
<i:Interaction.Behaviors>
<behaviors:ClosingWindowBehavior Command="{Binding ClosingWindowCommand}" />
</i:Interaction.Behaviors>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
@@ -52,7 +52,7 @@
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<StackPanel Margin="0 0 0 0" Grid.Row="1" Grid.Column="0">
<StackPanel Margin="0 0 0 0" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center">
<Button Command="{Binding SwitchToViewCommand}" CommandParameter="SettingView" Style="{StaticResource IconButtonStyle}">
<StackPanel Orientation="Horizontal">
<Image Source="/Resources/setting64.png" Margin="5" />

View File

@@ -1,6 +1,4 @@
using System.Windows;
using YwxApp.AiChat.Services;
using YwxApp.AiChat.ViewModels;
namespace YwxApp.AiChat;

File diff suppressed because one or more lines are too long

View File

@@ -1,14 +0,0 @@
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");
}
}

View File

@@ -1,95 +0,0 @@
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; }
}
}
}

View File

@@ -1,54 +0,0 @@
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; }
}
}
}

View File

@@ -1,26 +0,0 @@
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();
}
}

View File

@@ -90,7 +90,7 @@ namespace YwxApp.AiChat.ViewModels
ViewList.Add(new ChatMdView(_ollamaObject));
//Set the default display of subview 1.
CurrentModel = _ollamaObject.Ollama.SelectedModel;
// CurrentModel = _ollamaObject.Ollama.SelectedModel;
InitializeTimer();
CurrentView = ViewList[0];
}
@@ -129,7 +129,7 @@ namespace YwxApp.AiChat.ViewModels
private void Timer_Tick(object sender, EventArgs e)
{
CurrentTime = DateTime.Now.ToString("HH:mm:ss");
CurrentModel = _ollamaObject.Ollama.SelectedModel;
// CurrentModel = _ollamaObject.Ollama.SelectedModel;
}
#endregion

View File

@@ -35,6 +35,16 @@ namespace YwxApp.AiChat.ViewModels
get { return _ollama.OllamaAppPath; }
set { _ollama.OllamaAppPath = value; OnPropertyChanged(); }
}
public string RemoteHost
{
get { return _ollama.RemoteHost; }
set { _ollama.RemoteHost = value; OnPropertyChanged(); }
}
public string RemotePort
{
get { return _ollama.RemotePort; }
set { _ollama.RemotePort = value; OnPropertyChanged(); }
}
public string SelectedModel
{
get => _selectedModel;
@@ -84,6 +94,7 @@ namespace YwxApp.AiChat.ViewModels
public ICommand GetModelListCommand { get; } //get model list command.
public ICommand ModelListUpdateCommand { get; } //model list update command.
public ICommand StartOllamaServerCommand { get; } //start ollam server command.
public ICommand ConnectRemotelyOllamaServerCommand { get; }
#endregion
#endregion
@@ -92,13 +103,19 @@ namespace YwxApp.AiChat.ViewModels
public SettingViewModel(ShareOllamaObject ollama)
{
_ollama = ollama;
Task task = OnGetModelList();
// Task task = OnGetModelList();
OpenFileDialogCommand = new ParameterlessCommand(() => OnSelectOllamaAppPathDialog());
GetModelListCommand = new ParameterlessCommand(async () => await OnGetModelList());
ModelListUpdateCommand = new ParameterlessCommand(async () => await OnModelListUpdate());
StartOllamaServerCommand = new ParameterlessCommand(async () => OnStartOllamaServer());
ConnectRemotelyOllamaServerCommand = new ParameterlessCommand(async () => OnConnectRemotelyStartOllamaServer());
SetConnected();
}
private void OnConnectRemotelyStartOllamaServer()
{
_ollama.ConnectRemotelyOllamaServer();
}
#endregion
#region other method
@@ -185,6 +202,8 @@ namespace YwxApp.AiChat.ViewModels
ModelList = (ObservableCollection<string>)_ollama.GetModelList();
Debug.Print($"ModelList count: {ModelList.Count}");
SelectedModel = _ollama.Ollama.SelectedModel;
SelectedModel = ModelList.FirstOrDefault();
ResetModelName();
var modelName = ModelList.FirstOrDefault(name => name.Equals(SelectedModel));
if (ModelList.Count > 0 && modelName != null)
{

View File

@@ -22,13 +22,25 @@ namespace YwxApp.AiChat.ViewModels
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 string _remoteHost = "192.168.1.6"; //current record;
private string _remotePort = "11434"; //current record;
private string _currentPath;
private Chat chat; //build interactive chat model object.
private OllamaApiClient _ollama; //OllamaAPI object.
#endregion
#region Property
public string RemoteHost
{
get { return _remoteHost; }
set { _remoteHost = value; }
}
public string RemotePort
{
get { return _remotePort; }
set { _remotePort = value; }
}
public string OllamaAppPath
{
get { return _ollamaAppPath; }
@@ -76,7 +88,7 @@ namespace YwxApp.AiChat.ViewModels
{
RecordIndex = 0;
WriteDataToFileAsync("");
Init(OllamaAppPath, "llama3.2:9b");
Init(OllamaAppPath, "llama3.2:9b");
}
#endregion
@@ -100,7 +112,7 @@ namespace YwxApp.AiChat.ViewModels
//如果路径存在启动Ollama
if (File.Exists(filePath)) CheckStartProcess(OllamaAppPath);
//连接Ollama并设置初始模型
_ollama = new OllamaApiClient(new Uri("http://localhost:11434"));
_ollama = new OllamaApiClient(new Uri("http://192.168.1.6:11434"));
//获取本地可用的模型列表
ModelList = (ObservableCollection<string>)GetModelList();
var tmepModelName = ModelList.FirstOrDefault(name => name.ToLower().Contains("llama3.2"));
@@ -117,6 +129,13 @@ namespace YwxApp.AiChat.ViewModels
OllamaEnabled = false;
}
}
public void ConnectRemotelyOllamaServer() {
_ollama = new OllamaApiClient(new Uri("http://192.168.1.6:11434"));
//获取本地可用的模型列表
ModelList = (ObservableCollection<string>)GetModelList();
}
/// <summary>
/// update the model selected by Ollama
/// </summary>

View File

@@ -1,10 +1,9 @@
<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"
xmlns:markdig="clr-namespace:Markdig.Wpf;assembly=Markdig.Wpf" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources >
@@ -15,11 +14,11 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="#0F000F">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="200" />
<RowDefinition Height="120" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
@@ -29,15 +28,13 @@
<!--Bind event command to the ScrollViewer-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="ScrollChanged">
<i:InvokeCommandAction Command = "{Binding ScrollToEndCommand}"
CommandParameter="{Binding ElementName=MarkDownScrollViewer}" />
<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>
</Grid>
<!-- the second line -->
<Grid Grid.Row="1">
<TextBox x:Name="InputBox"

View File

@@ -15,44 +15,43 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="#FFFFFF" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="80"/>
<RowDefinition Height="80"/>
<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">
<GroupBox Header="本地模式" >
<WrapPanel Margin="5,0,0,0" 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"
MinWidth="180" VerticalAlignment="Center" VerticalContentAlignment="Center" />
<Label Name="Label_State" Style="{StaticResource RoundLabelStyle}" />
<Button Content="打开" Style="{StaticResource RoundCornerButtonStyle}" Command="{Binding StartOllamaServerCommand}"/>
</WrapPanel>
</GroupBox>
<GroupBox Header="远程模式" Grid.Row="1">
<WrapPanel Orientation="Horizontal" Grid.Row="0" >
<TextBlock Text="Host:" FontSize="14" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBox Text="{Binding RemoteHost}" MinWidth="120" FontSize="14" Margin="5"/>
<TextBlock Text="Port:" FontSize="14" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBox Text="{Binding RemotePort}" MinWidth="60" FontSize="14" Margin="5" Padding="5"/>
<Button Content="运行" FontSize="12" Command="{Binding ConnectRemotelyOllamaServerCommand}"/>
</WrapPanel>
</GroupBox>
<WrapPanel Grid.Row="2" Margin="5,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.ColumnSpan="2">
<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}"/>
<ComboBox x:Name="Cbx_ModelList" Style="{StaticResource RoundComboBoxStyle}" ItemsSource="{Binding ModelList}" SelectedItem="{Binding SelectedModel}"
MinWidth="180" VerticalAlignment="Center" VerticalContentAlignment="Center" />
<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}"/>
<TextBox x:Name="ModelDesciption" Grid.Row="3" IsReadOnly="True" TextWrapping="WrapWithOverflow" Text="{Binding ModelInformation,Mode=OneWay}" Grid.ColumnSpan="2"/>
</Grid>
</UserControl>
</UserControl>

View File

@@ -1,6 +1,10 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Button.Padding" Value="3,5"/>
</Style>
<Style x:Key="IconButtonStyle">
<Setter Property="Button.Background" Value="Transparent"/>
@@ -9,9 +13,8 @@
<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.Padding" Value="3,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"/>

View File

@@ -1,4 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TextBox">
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</ResourceDictionary>

View File

@@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="Markdig.Wpf" Version="0.5.0.1" />
<PackageReference Include="MvvmLightLibs" Version="5.4.1.1" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="OllamaSharp" Version="5.1.4" />
</ItemGroup>