跨平台`ChatGpt` 客户端

跨平台,chatgpt,客户端 · 浏览次数 : 567

小编点评

**方法** * `InitializeComponent` * `SendBorder_OnPointerEntered` * `SendMessage_OnClick` * `Minimize_OnClick` * `Maximize_OnClick` * `SendTextBox_OnKeyDown` **属性** * `ChatShowKey` * `Message` **事件** * `PointerEntered` * `Click` * `KeyDown` **方法** * `InitializeComponent` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 * 响应异常 **属性** * `messages` **事件** * `PointerEntered` **方法** * `SendBorder_OnPointerEntered` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages` **事件** * `PointerEntered` **方法** * `SendMessage_OnClick` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages` **事件** * `PointerEntered` **方法** * `Minimize_OnClick` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages` **事件** * `PointerEntered` **方法** * `Maximize_OnClick` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages` **事件** * `PointerEntered` **方法** * `SendTextBox_OnKeyDown` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages` **事件** * `PointerEntered` **方法** * `InitializeComponent` * 从token获取最近五条聊天数据 * 添加数据到消息列表中 * 发送消息 **属性** * `messages`

正文

跨平台ChatGpt 客户端

一款基于Avalonia实现的跨平台ChatGpt客户端 ,通过对接ChatGpt官方提供的ChatGpt 3.5模型实现聊天对话

实现创建ChatGpt的项目名称 ,项目类型是Avalonia MVVM

添加项目需要使用的Nuget


    <ItemGroup>
        <PackageReference Include="Avalonia" Version="11.0.0-preview5" />
        <PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview5" />
        <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview5" />
        <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
        <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
        <PackageReference Include="FreeSql.Provider.Sqlite" Version="3.2.690" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
        <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
        <PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
        <PackageReference Include="Avalonia.Svg.Skia" Version="11.0.0-preview5" />
    </ItemGroup>

ViewLocator.cs代码修改

using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using ChatGPT.ViewModels;

namespace ChatGPT;

public class ViewLocator : IDataTemplate
{
    public Control? Build(object? data)
    {
        if (data is null)
            return null;

        var name = data.GetType().FullName!.Replace("ViewModel", "View");
        var type = Type.GetType(name);

        if (type != null)
        {
            return (Control)Activator.CreateInstance(type)!;
        }

        return new TextBlock { Text = name };
    }

    public bool Match(object? data)
    {
        return data is ViewModelBase;
    }
}

创建MainApp.cs文件

using Microsoft.Extensions.DependencyInjection;

namespace ChatGPT;

public static class MainApp
{
    private static IServiceProvider ServiceProvider;

    public static ServiceCollection CreateServiceCollection()
    {
        return new ServiceCollection();
    }

    public static IServiceProvider Build(this IServiceCollection services)
    {
        return ServiceProvider = services.BuildServiceProvider();
    }
    
    public static T GetService<T>()
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetService<T>();
    }
    
    public static IEnumerable<T> GetServices<T>()
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetServices<T>();
    }

    public static object? GetService(Type type)
    {
        if (ServiceProvider is null)
        {
            throw new ArgumentNullException(nameof(ServiceProvider));
        }
        return ServiceProvider.GetService(type);
    }
}

创建GlobalUsing.cs文件 全局引用

global using System.Reactive;
global using Avalonia;
global using Avalonia.Controls;
global using ChatGPT.ViewModels;
global using Avalonia;
global using Avalonia.Controls.ApplicationLifetimes;
global using Avalonia.Markup.Xaml;
global using ChatGPT.ViewModels;
global using ChatGPT.Views;
global using System;
global using System.Collections.Generic;
global using ReactiveUI;

修改App.axaml代码文件

<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="using:ChatGPT"
             xmlns:converter="clr-namespace:ChatGPT.Converter"
             RequestedThemeVariant="Light"
             x:Class="ChatGPT.App">
    <Application.Resources>
        <converter:HeightConverter x:Key="HeightConverter" />
    </Application.Resources>
    <Application.DataTemplates>
        <local:ViewLocator/>
    </Application.DataTemplates>

    <Application.Styles>
        <FluentTheme DensityStyle="Compact"/>
    </Application.Styles>
    
</Application>

修改App.axaml.cs代码文件

using Avalonia.Platform;
using Avalonia.Svg.Skia;
using ChatGPT.Options;
using Microsoft.Extensions.DependencyInjection;

namespace ChatGPT;

public partial class App : Application
{
    public override void Initialize()
    {
        GC.KeepAlive(typeof(SvgImageExtension).Assembly);
        GC.KeepAlive(typeof(Avalonia.Svg.Skia.Svg).Assembly);

        var services = MainApp.CreateServiceCollection();

        services.AddHttpClient("chatGpt")
            .ConfigureHttpClient(options =>
            {
                var chatGptOptions = MainApp.GetService<ChatGptOptions>();
                if (!string.IsNullOrWhiteSpace(chatGptOptions?.Token))
                {
                    options.DefaultRequestHeaders.Add("Authorization",
                        "Bearer " + chatGptOptions?.Token.TrimStart().TrimEnd());
                }
            });

        services.AddSingleton<ChatGptOptions>(ChatGptOptions.NewChatGptOptions());

        services.AddSingleton(new FreeSql.FreeSqlBuilder()
            .UseConnectionString(FreeSql.DataType.Sqlite,
                "Data Source=chatGpt.db;Pooling=true;Min Pool Size=1")
            .UseAutoSyncStructure(true) //自动同步实体结构到数据库
            .Build());

        services.Build();

        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = new MainViewModel()
            };
        }

        var notifyIcon = new TrayIcon();
        notifyIcon.Menu ??= new NativeMenu();
        notifyIcon.ToolTipText = "ChatGPT";

        var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();

        notifyIcon.Icon = new WindowIcon(assets.Open(new Uri("avares://ChatGPT/Assets/chatgpt.ico")));
        var exit = new NativeMenuItem()
        {
            Header = "退出ChatGPT"
        };

        exit.Click += (sender, args) => Environment.Exit(0);
        notifyIcon.Menu.Add(exit);

        base.OnFrameworkInitializationCompleted();
    }
}

修改MainWindow.axaml文件

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:viewModels="clr-namespace:ChatGPT.ViewModels"
        xmlns:pages="clr-namespace:ChatGPT.Pages"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="ChatGPT.Views.MainWindow"
        ExtendClientAreaToDecorationsHint="True"
        ExtendClientAreaChromeHints="NoChrome"
        ExtendClientAreaTitleBarHeightHint="-1"
        Height="{Binding Height}"
        MinHeight="500"
        MinWidth="800"
        Width="1060"
        Name="Main">

    <Design.DataContext>
        <viewModels:MainViewModel />
    </Design.DataContext>

    <StackPanel Name="StackPanel" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <WrapPanel Name="WrapPanel" VerticalAlignment="Stretch" Height="{Binding ElementName=StackPanel, Path=Height}">
            <StackPanel MaxWidth="55" Width="55">
                <DockPanel Background="#2E2E2E" Height="{Binding Height}">
                    <StackPanel DockPanel.Dock="Top">
                        <StackPanel Margin="0,32,0,0"></StackPanel>
                        <StackPanel Margin="8">
                            <Image Source="/Assets/avatar.png"></Image>
                        </StackPanel>
                        <StackPanel Name="ChatStackPanel" Margin="15">
                            <Image Source="/Assets/chat-1.png"></Image>
                        </StackPanel>
                    </StackPanel>

                    <StackPanel Margin="5" VerticalAlignment="Bottom" DockPanel.Dock="Bottom">
                        <StackPanel VerticalAlignment="Bottom" Name="FunctionStackPanel">
                            <Menu HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                <MenuItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                    <MenuItem.Header>
                                        <Image Margin="5" Height="20" Width="20" Source="/Assets/function.png"></Image>
                                    </MenuItem.Header>
                                    <MenuItem Click="Setting_OnClick" Name="Setting" Header="设置" />
                                </MenuItem>
                            </Menu>
                        </StackPanel>
                    </StackPanel>
                </DockPanel>
            </StackPanel>

            <Border Width="250" MaxWidth="250" BorderBrush="#D3D3D3" BorderThickness="0,0,1,0">
                <StackPanel>
                    <pages:ChatShowView Name="ChatShowView"/>
                </StackPanel>
            </Border>

            <StackPanel>
                <StackPanel Height="{Binding Height}" HorizontalAlignment="Center" VerticalAlignment="Center">
                    <pages:SendChat DataContext="{Binding SendChatViewModel}"></pages:SendChat>
                </StackPanel>
            </StackPanel>
        </WrapPanel>
    </StackPanel>
</Window>

修改MainWindow.axaml.cs文件

using Avalonia.Interactivity;
using ChatGPT.Pages;

namespace ChatGPT.Views;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var observer = Observer.Create<Rect>(rect =>
        {
            if (ViewModel is null) return;

            ViewModel.SendChatViewModel.Height = (int)rect.Height;
            ViewModel.SendChatViewModel.Width = (int)rect.Width - 305;
            ViewModel.Height = (int)rect.Height;
            ViewModel.SendChatViewModel.ShowChatPanelHeight =
                (int)rect.Height - ViewModel.SendChatViewModel.SendPanelHeight - 60;
        });

        this.GetObservable(BoundsProperty).Subscribe(observer);

        ChatShowView = this.Find<ChatShowView>(nameof(ChatShowView));

        ChatShowView.OnClick += view =>
        {
            ViewModel.SendChatViewModel.ChatShow = view;
        };
    }

    private MainViewModel ViewModel => DataContext as MainViewModel;

    private void Setting_OnClick(object? sender, RoutedEventArgs e)
    {
        var setting = new Setting
        {
            DataContext = ViewModel.SettingViewModel
        };
        setting.Show();
    }
}

提供部分代码 所有源码都是开源,链接防止最下面

效果图

SendChat.axaml.cs中提供了请求ChatGpt 3.5的实现

using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using ChatGPT.Model;
using Notification = Avalonia.Controls.Notifications.Notification;

namespace ChatGPT.Pages;

public partial class SendChat : UserControl
{
    private readonly HttpClient http;

    private WindowNotificationManager? _manager;

    public SendChat()
    {
        http = MainApp.GetService<IHttpClientFactory>().CreateClient("chatGpt");
        InitializeComponent();
        DataContextChanged += async (sender, args) =>
        {
            if (DataContext is not SendChatViewModel model) return;
            if (model.ChatShow != null)
            {
                var freeSql = MainApp.GetService<IFreeSql>();
                try
                {
                    var values = await freeSql.Select<ChatMessage>()
                        .Where(x => x.ChatShowKey == model.ChatShow.Key)
                        .OrderBy(x => x.CreatedTime)
                        .ToListAsync();

                    foreach (var value in values)
                    {
                        model.messages.Add(value);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
            else
            {
                model.ChatShowAction += async () =>
                {
                    var freeSql = MainApp.GetService<IFreeSql>();

                    var values = await freeSql.Select<ChatMessage>()
                        .Where(x => x.Key == model.ChatShow.Key)
                        .OrderBy(x => x.CreatedTime)
                        .ToListAsync();

                    foreach (var value in values)
                    {
                        model.messages.Add(value);
                    }
                };
            }
        };
    }

    private void InitializeComponent()
    {
        AvaloniaXamlLoader.Load(this);
    }

    protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnAttachedToVisualTree(e);
        var topLevel = TopLevel.GetTopLevel(this);
        _manager = new WindowNotificationManager(topLevel) { MaxItems = 3 };
    }

    private void Close_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;
    }

    private SendChatViewModel ViewModel => DataContext as SendChatViewModel;

    private void Thumb_OnDragDelta(object? sender, VectorEventArgs e)
    {
        var thumb = (Thumb)sender;
        var wrapPanel = (WrapPanel)thumb.Parent;
        wrapPanel.Width += e.Vector.X;
        wrapPanel.Height += e.Vector.Y;
    }

    private void SendBorder_OnPointerEntered(object? sender, PointerEventArgs e)
    {
    }

    private async void SendMessage_OnClick(object? sender, RoutedEventArgs e)
    {
        await SendMessageAsync();
    }

    private void Minimize_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.WindowState = WindowState.Minimized;
    }

    private void Maximize_OnClick(object? sender, RoutedEventArgs e)
    {
        var window = TopLevel.GetTopLevel(this) as Window;
        window.WindowState = window.WindowState switch
        {
            WindowState.Maximized => WindowState.Normal,
            WindowState.Normal => WindowState.Maximized,
            _ => window.WindowState
        };
    }

    private async void SendTextBox_OnKeyDown(object? sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            await SendMessageAsync();
        }
    }

    private async Task SendMessageAsync()
    {
        try
        {
            if (ViewModel?.ChatShow?.Key == null)
            {
                _manager?.Show(new Notification("提示", "请先选择一个对话框!", NotificationType.Warning));
                return;
            }

            // 获取当前程序集 assets图片
            // var uri = new Uri("avares://ChatGPT/Assets/avatar.png");
            // // 通过uri获取Stream
            // var bitmap = new Bitmap(AvaloniaLocator.Current.GetService<IAssetLoader>().Open(uri));

            var model = new ChatMessage
            {
                ChatShowKey = ViewModel.ChatShow.Key,
                // Avatar = bitmap,
                Title = "token",
                Content = ViewModel.Message,
                CreatedTime = DateTime.Now,
                IsChatGPT = false
            };

            // 添加到消息列表
            ViewModel.messages.Add(model);

            // 清空输入框
            ViewModel.Message = string.Empty;

            // 获取消息记录用于AI联系上下文分析 来自Token的代码
            var message = ViewModel.messages
                .OrderByDescending(x => x.CreatedTime) // 拿到最近的5条消息
                .Take(5)
                .OrderBy(x => x.CreatedTime) // 按时间排序
                .Select(x => x.IsChatGPT
                    ? new
                    {
                        role = "assistant",
                        content = x.Content
                    }
                    : new
                    {
                        role = "user",
                        content = x.Content
                    }
                )
                .ToList();

            // 请求ChatGpt 3.5最新模型 来自Token的代码
            var responseMessage = await http.PostAsJsonAsync("https://api.openai.com/v1/chat/completions", new
            {
                model = "gpt-3.5-turbo",
                temperature = 0,
                max_tokens = 2560,
                user = "token",
                messages = message
            });

            // 获取返回的消息 来自Token的代码
            var response = await responseMessage.Content.ReadFromJsonAsync<GetChatGPTDto>();

            // 获取当前程序集 assets图片
            // uri = new Uri("avares://ChatGPT/Assets/chatgpt.ico");

            var chatGptMessage = new ChatMessage
            {
                ChatShowKey = ViewModel.ChatShow.Key,
                // Avatar = new Bitmap(AvaloniaLocator.Current.GetService<IAssetLoader>().Open(uri)),
                Title = "ChatGPT",
                Content = response.choices[0].message.content,
                IsChatGPT = true,
                CreatedTime = DateTime.Now
            };
            // 添加到消息列表 来自Token的代码
            ViewModel.messages.Add(chatGptMessage);

            var freeSql = MainApp.GetService<IFreeSql>();
            await freeSql
                .Insert(model)
                .ExecuteAffrowsAsync();

            await freeSql
                .Insert(chatGptMessage)
                .ExecuteAffrowsAsync();
        }
        catch (Exception e)
        {
            // 异常处理 
            _manager?.Show(new Notification("提示", "在请求AI服务时出现错误!请联系管理员!", NotificationType.Error));
        }
    }
}

实现发送前需要将之前的最近五条数据得到跟随当前数据一块发送,为了让其ChatGpt可以联系上下文,这样回复的内容更准确,聊天的数据使用Sqlite本地存储,为了轻量使用ORM采用FreeSql,界面仿制微信的百分之八十的还原度,基本上一直,而且源码完全开源。

来自token的分享

GitHub开源地址: https://github.com/239573049/ChatGpt.Desktop

与跨平台`ChatGpt` 客户端相似的内容:

跨平台`ChatGpt` 客户端

跨平台ChatGpt 客户端 一款基于Avalonia实现的跨平台ChatGpt客户端 ,通过对接ChatGpt官方提供的ChatGpt 3.5模型实现聊天对话 实现创建ChatGpt的项目名称 ,项目类型是Avalonia MVVM , 添加项目需要使用的Nuget包

一个跨平台的`ChatGPT`悬浮窗工具

# 一个跨平台的`ChatGPT`悬浮窗工具 使用`avalonia`实现的`ChatGPT`的工具,设计成悬浮窗,并且支持插件。 ## 如何实现悬浮窗? 在使用`avalonia`实现悬浮窗也是非常的简单的。 实现我们需要将窗体设置成无边框 在`Window`根节点添加一下属性,想要在Linux下

Ui2Code+ChatGPT助力低代码搭建

低代码开发平台(LCDP),是低代码或无代码通过快速搭建配置的方式完成一个应用程序的开发与上线,可视化低代码就是可视化的DSL,它的优点更多的是来源可视化,相对的,它的局限性也还是来源于可视化,复杂的业务逻辑用低代码可能会更加复杂。低代码应该是特定领域问题的简化和抽象,如果只是单纯将原有的编码工作转换为 GUI 的模式,并没有多大意义。

基于ChatGPT的API的C#接入研究

今年开年,最火的莫过于ChatGPT的相关讨论,这个提供了非常强大的AI处理,并且整个平台也提供了很多对应的API进行接入的处理,使得我们可以在各种程序上无缝接入AI的后端处理,从而实现智能AI的各种应用。ChatGPT的API可以在前端,以及一些后端进行API的接入,本篇随笔主要介绍基于ChatGPT的API的C#接入研究。

ChatGPT插件开发实战

1.概述 ChatGPT是一款由OpenAI推出的先进对话模型,其强大的自然语言处理能力使得它成为构建智能对话系统和人机交互应用的理想选择。为了进一步拓展ChatGPT的功能和适应不同领域的需求,OpenAI提供了插件开发平台,让开发者可以定制化和扩展ChatGPT的能力。 2.内容 OpenAI

震惊!火爆全网的ChatGPT背后使用的数据库居然是……

摘要:ChatGPT承认了自己背后使用的数据库是Cassandra。 OpenAI最近发布的AI驱动的智能聊天机器人ChatGPT在互联网上掀起了一阵风暴,热衷于尝试这一新AI成果的网民不在少数。ChatGPT针对网友广泛的问题提供了非常有针对性的回答,其不可思议的能力成为各大媒体平台的头条新闻,其

Seal AppManager v0.2 发布:进一步简化应用部署体验

经过近3个月的研发,Seal AppManager v0.2 已正式发布。 Seal AppManager 是一款基于平台工程理念的应用统一部署管理平台,于今年4月首次推出。在上一版本中,我们已经释出**集成 ChatGPT 简化服务模板代码生成、云成本可视化、动态环境管理**等功能,通过降低基础设

claude3国内API接口对接

众所周知,由于地理位置原因,Claude3不对国内开放,而国内的镜像网站使用又贵的离谱! 因此,团队萌生了一个想法:为什么不创建一个一站式的平台,让用户能够通过单一的接口与多个模型交流呢?这样,用户就可以轻松地比较不同模型的表现,并根据需要选择最合适的一个。于是诞生了这个ChatGPT,Claude

QtCreator 跨平台开发添加动态库教程(以OpenCV库举例)- Windows篇

Qt具有跨平台的特性,即Qt数据结构与算法库本身跨平台和编译脚本(.pro)跨平台。在同时具有Windows下和Linux开发的需求时,最好的建议是使用QtCreator来开发,虽然也可以使用其他的IDE配合CMake等方式,但使用QtCreator更加方便,并且操作环境完全一致。QtCreator

.NET跨平台框架选择之一 - Avalonia UI

本文阅读目录 1. Avalonia UI简介 Avalonia UI文档教程:https://docs.avaloniaui.net/docs/getting-started 随着跨平台越来越流行,.NET支持跨平台至今也有十几年的光景了(Mono开始)。 但是目前基于.NET的跨平台,大多数还是