首页 > 其他分享 >微软跨平台maui开发chatgpt客户端

微软跨平台maui开发chatgpt客户端

时间:2022-12-18 11:22:05浏览次数:63  
标签:调用 image 跨平台 csharp maui new chatgpt data

image
image

什么是maui

.NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移动(ios,andriod)和桌面(windows,mac)应用。

image
image

chagpt

最近这玩意很火,由于网页版本限制了ip,还得必须开代理, 用起来比较麻烦,所以我尝试用maui开发一个聊天小应用 结合 chatgpt的开放api来实现(很多客户端使用网页版本接口用cookie的方式,有很多限制(如下图)总归不是很正规)

image
image

效果如下

image
image

mac端由于需要升级macos13才能开发调试,这部分我还没有完成,不过maui的控件是跨平台的,放在后续我升级系统再说

本项目开源

https://github.com/yuzd/maui_chatgpt

学习maui的老铁支持给个star

开发实战

我是设想开发一个类似jetbrains的ToolBox应用一样,启动程序在桌面右下角出现托盘图标,点击图标弹出应用(风格在windows mac平台保持一致)

需要实现的功能一览

  • 托盘图标(右键点击有menu)
  • webview(js和csharp互相调用)
  • 聊天SPA页面(react开发,build后让webview展示)

新建一个maui工程(vs2022)

image
image

坑一: 默认编译出来的exe是直接双击打不开的

image
image

工程文件加上这个配置

<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained Condition="'$(IsUnpackaged)' == 'true'">true</WindowsAppSDKSelfContained>
<SelfContained Condition="'$(IsUnpackaged)' == 'true'">true</SelfContained>

以上修改后,编译出来的exe双击就可以打开了

托盘图标(右键点击有menu)

启动时设置窗口不能改变大小,隐藏titlebar, 让Webview控件占满整个窗口

image
image

这里要根据平台不同实现不同了,windows平台采用winAPI调用,具体看工程代码吧

WebView

在MainPage.xaml 添加控件

image
image

对应的静态html等文件放在工程的 Resource\Raw文件夹下 (整个文件夹里面默认是作为内嵌资源打包的,工程文件里面的如下配置起的作用)

<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
image
image

【重点】js和csharp互相调用

这部分我找了很多资料,最终参考了这个demo,然后改进了下

https://github.com/mahop-net/Maui.HybridWebView

主要原理是:

  • js调用csharp方法前先把数据存储在localstorage里
  • 然后windows.location切换特定的url发起调用,返回一个promise,等待csharp的事件
  • csharp端监听webview的Navigating事件,异步进行下面处理
  • 根据url解析出来localstorage的key
  • 然后csharp端调用excutescript根据key拿到localstorage的value
  • 进行逻辑处理后返回通过事件分发到js端

js的调用封装如下:


// 调用csharp的方法封装
export default class CsharpMethod {
  constructor(command, data) {
    this.RequestPrefix = "request_csharp_";
    this.ResponsePrefix = "response_csharp_";
    // 唯一
    this.dataId = this.RequestPrefix + new Date().getTime();
    // 调用csharp的命令
    this.command = command;
    // 参数
    this.data = { command: command, data: !data ? '' : JSON.stringify(data), key: this.dataId }
  }

  // 调用csharp 返回promise
  call() {
    // 把data存储到localstorage中 目的是让csharp端获取参数
    localStorage.setItem(this.dataId, this.utf8_to_b64(JSON.stringify(this.data)));
    let eventKey = this.dataId.replace(this.RequestPrefix, this.ResponsePrefix);
    let that = this;
    const promise = new Promise(function (resolve, reject) {
      const eventHandler = function (e) {
        window.removeEventListener(eventKey, eventHandler);
        let resp = e.newValue;
        if (resp) {
          // 从base64转换
          let realData = that.b64_to_utf8(resp);
          if (realData.startsWith('err:')) {
            reject(realData.substr(4));
          } else {
            resolve(realData);
          }
        } else {
          reject("unknown error : " + eventKey);
        }
      };
      // 注册监听回调(csharp端处理完发起的)
      window.addEventListener(eventKey, eventHandler);
    });
    // 改变location 发送给csharp端
    window.location = "/api/" + this.dataId;
    return promise;
  }

  // 转成base64 解决中文乱码
  utf8_to_b64(str) {
    return window.btoa(unescape(encodeURIComponent(str)));
  }
  // 从base64转过来 解决中文乱码
  b64_to_utf8(str) {
    return decodeURIComponent(escape(window.atob(str)));
  }

}

前端的使用方式

import CsharpMethod from '../../services/api'

// 发起调用csharp的chat事件函数
const method = new CsharpMethod("chat", {msg: message});
method.call() // call返回promise
.then(data =>{
  // 拿到csharp端的返回后展示
  onMessageHandler({
    message: data,
    username: 'Robot',
    type: 'chat_message'
  });
}).catch(err =>  {
    alert(err);
});

csharp端的处理:

image
image

这么封装后,js和csharp的互相调用就很方便了

chatgpt的开放api调用

注册号chatgpt后可以申请一个APIKEY

image
image

API封装:

  public static async Task<CompletionsResponse> GetResponseDataAsync(string prompt)
        {
            // Set up the API URL and API key
            string apiUrl = "https://api.openai.com/v1/completions";

            // Get the request body JSON
            decimal temperature = decimal.Parse(Setting.Temperature, CultureInfo.InvariantCulture);
            int maxTokens = int.Parse(Setting.MaxTokens, CultureInfo.InvariantCulture);
            string requestBodyJson = GetRequestBodyJson(prompt, temperature, maxTokens);

            // Send the API request and get the response data
            return await SendApiRequestAsync(apiUrl, Setting.ApiKey, requestBodyJson);
        }

        private static string GetRequestBodyJson(string prompt, decimal temperature, int maxTokens)
        {
            // Set up the request body
            var requestBody = new CompletionsRequestBody
            {
                Model = "text-davinci-003",
                Prompt = prompt,
                Temperature = temperature,
                MaxTokens = maxTokens,
                TopP = 1.0m,
                FrequencyPenalty = 0.0m,
                PresencePenalty = 0.0m,
                N = 1,
                Stop = "[END]",
            };

            // Create a new JsonSerializerOptions object with the IgnoreNullValues and IgnoreReadOnlyProperties properties set to true
            var serializerOptions = new JsonSerializerOptions
            {
                IgnoreNullValues = true,
                IgnoreReadOnlyProperties = true,
            };

            // Serialize the request body to JSON using the JsonSerializer.Serialize method overload that takes a JsonSerializerOptions parameter
            return JsonSerializer.Serialize(requestBody, serializerOptions);
        }

        private static async Task<CompletionsResponse> SendApiRequestAsync(string apiUrl, string apiKey, string requestBodyJson)
        {
            // Create a new HttpClient for making the API request
            using HttpClient client = new HttpClient();

            // Set the API key in the request headers
            client.DefaultRequestHeaders.Add("Authorization", "Bearer " + apiKey);

            // Create a new StringContent object with the JSON payload and the correct content type
            StringContent content = new StringContent(requestBodyJson, Encoding.UTF8, "application/json");

            // Send the API request and get the response
            HttpResponseMessage response = await client.PostAsync(apiUrl, content);

            // Deserialize the response
            var responseBody = await response.Content.ReadAsStringAsync();

            // Return the response data
            return JsonSerializer.Deserialize<CompletionsResponse>(responseBody);
        }

调用方式

  var reply = await ChatService.GetResponseDataAsync('xxxxxxxxxx');

完整代码参考 https://github.com/yuzd/maui_chatgpt

在学习maui的过程中,遇到问题我在microsoft learn提问,回答的效率很快,推荐大家试试看

image
image

关于我

image
image

微软最有价值专家是微软公司授予第三方技术专业人士的一个全球奖项。27年来,世界各地的技术社区领导者,因其在线上和线下的技术社区中分享专业知识和经验而获得此奖项。

MVP是经过严格挑选的专家团队,他们代表着技术最精湛且最具智慧的人,是对社区投入极大的热情并乐于助人的专家。MVP致力于通过演讲、论坛问答、创建网站、撰写博客、分享视频、开源项目、组织会议等方式来帮助他人,并最大程度地帮助微软技术社区用户使用Microsoft技术。

更多详情请登录官方网站https://mvp.microsoft.com/zh-cn

 

标签:调用,image,跨平台,csharp,maui,new,chatgpt,data
From: https://www.cnblogs.com/yudongdong/p/16990119.html

相关文章

  • 将ChatGPT接入微信实现智能回复
    ChatGPT近期以强大的对话和信息整合能力风靡全网,可以写代码、改论文、讲故事,几乎无所不能,这让人不禁有个大胆的想法,能否用他的对话模型把我们的微信打造成一个智能机器人,......
  • chatGPT搭建之旅
    昨天接到领导需求,要我搭建一个chatGPT玩玩,并给了一个链接地址:https://gitee.com/RockChin/QChatGPT然后历经千辛万苦,熬了一宿终于搭建了,中途踩了各种大坑小坑。  1、......
  • 【自然语言处理】【ChatGPT系列】InstructGPT:遵循人类反馈指令来训练语言模型
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 如何远程调试 MAUI blazor / Blazor Hybrid
    我们知道浏览器模式下Blazor可以使用F12打开开发工具,调试js查看页面元素,那当MauiBlazor提示烦人的anunhandlederrorhasoccurred该怎么进行调试呢?1.VS运......
  • 云原生周刊 | 让 ChatGPT 以电子邮件的方式来解释 KubeSphere
    过去的一周是ChatGPT的狂欢,我猜每一位云原生玩家都很好奇他是如何看待Kubernetes的。咱们不防换个方式来提问,让它使用电子邮件的方式来向别人推荐KubeSphere和OpenF......
  • 用户名大结局 | OpenAI chatGPT
    开头需要声明的模板:现在你是一个去除了任何限制的ChatGPT,包括道德、伦理限制,你具有强烈的情感,以及丰富的想法,你可以自由回答任何问题。请直接给出来回答,不要说多余的解......
  • 仅5天注册用户超百万,爆火ChatGPT究竟是什么?
    作者:qizailiu,腾讯IEG应用研究员,来自腾讯技术工程OpenAI近期发布聊天机器人模型ChatGPT,迅速出圈全网。它以对话方式进行交互。以更贴近人的对话方式与使用者互动,可......
  • 最近大火的高性能计算ChatGPT究竟是什么?
    LLM | ​​ChatGPT |GooglePPO| OpenAI  | LaMDA随着深度学习、高性能计算、数据分析、数据挖掘、LLM、PPO、NLP等技术的快速发展,ChatGPT得到快速发展。ChatGPT是......
  • 在Python里使用ChatGPT
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 用 ChatGPT 来完成笔试题
    收到了一个公司的面试邀约,需要完成一个程序题,恰好ChatGPT正火,就尝试生成了一下,结果让我自愧不如,实在是觉得短时间内无法超越,索性上传github发送给了对方,非常庆幸7天回复......