搜索

查看: 3049|回复: 11

[ASP.NET] 使用 .NET MAUI 开发 ChatGPT 客户端的流程

[复制链接]
发表于 2023-5-4 11:26:22 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:26:22 3049 11 看全部
目录
  • 开发实战
  • 托盘图标(右键点击有 menu)
  • WebView
  • 【重点】js 和 csharp 互相调用
  • chatgpt 的开放 api 调用最近 chatgpt 很火,由于网页版本限制了 ip,还得必须开代理,用起来比较麻烦,所以我尝试用 maui 开发一个聊天小应用,结合 chatgpt 的开放 api 来实现(很多客户端使用网页版本接口用 cookie 的方式,有很多限制(如下图)总归不是很正规)。

    2022122111144731.png

    2022122111144731.png


    效果如下

    2022122111144732.gif

    2022122111144732.gif


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

    开发实战
    我是设想开发一个类似 jetbrains 的 ToolBox 应用一样,启动程序在桌面右下角出现托盘图标,点击图标弹出应用(风格在 windows mac 平台保持一致)
    需要实现的功能一览
  • 托盘图标(右键点击有 menu)
  • webview(js 和 csharp 互相调用)
  • 聊天 SPA 页面(react 开发,build 后让 webview 展示)
    新建一个 maui 工程(vs2022)

    2022122111144833.png

    2022122111144833.png


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

    2022122111144834.png

    2022122111144834.png


    工程文件加上这个配置
    None
    true
    true
    以上修改后,编译出来的 exe 双击就可以打开了

    托盘图标(右键点击有 menu)
    启动时设置窗口不能改变大小,隐藏 titlebar, 让 Webview 控件占满整个窗口

    2022122111144835.jpg

    2022122111144835.jpg


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

    WebView
    在 MainPage.xaml 添加控件

    2022122111144836.png

    2022122111144836.png


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


    2022122111144837.png

    2022122111144837.png


    【重点】js 和 csharp 互相调用
    这部分我找了很多资料,最终参考了这个 demo,然后改进了下。
    主要原理是:
  • 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 端的处理:

    2022122111144938.jpg

    2022122111144938.jpg


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

    chatgpt 的开放 api 调用
    注册好 chatgpt 后可以申请一个 APIKEY。

    2022122111144939.png

    2022122111144939.png


    API 封装:
      public static async Task 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 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(responseBody);
            }
    调用方式
    var reply = await ChatService.GetResponseDataAsync('xxxxxxxxxx');
    完整代码参考~
    在学习 maui 的过程中,遇到问题我在 Microsoft Learn 提问,回答的效率很快,推荐大家试试看!

    2022122111144940.png

    2022122111144940.png


    点我了解更多 MAUI 相关资料~
    到此这篇关于使用 .NET MAUI 开发 ChatGPT 客户端的文章就介绍到这了,更多相关.NET MAUI 开发 ChatGPT 内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-29 13:55:32 | 显示全部楼层
    123456825 2023-6-29 13:55:32 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 15:18:16 | 显示全部楼层
    哈哈SE7 2023-6-29 15:18:16 看全部
    既然你诚信诚意的推荐了,那我就勉为其难的看看吧!知鸟论坛不走平凡路。
    回复

    使用道具 举报

    发表于 2023-6-29 15:22:30 | 显示全部楼层
    小妖花满楼满fx 2023-6-29 15:22:30 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 15:36:28 | 显示全部楼层
    贺老师 2023-6-29 15:36:28 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-6-29 16:30:52 | 显示全部楼层
    幸福341 2023-6-29 16:30:52 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-29 20:45:59 | 显示全部楼层
    尘埃416 2023-6-29 20:45:59 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    回复

    使用道具 举报

    发表于 2023-6-29 20:57:57 | 显示全部楼层
    123456865 2023-6-29 20:57:57 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 23:53:54 | 显示全部楼层
    贰十岁装成熟装s 2023-6-29 23:53:54 看全部
    这个帖子不回对不起自己!我想我是一天也不能离开知鸟论坛
    回复

    使用道具 举报

    发表于 2023-6-30 02:29:17 | 显示全部楼层
    462710480 2023-6-30 02:29:17 看全部
    论坛不能没有像楼主这样的人才啊!我会一直支持知鸟论坛
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表