<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      在Node終端實現NewBing對話功能

      目錄

      前言

      準備工作

      工作原理

      功能設計

      實現過程

      基礎概念

      代理

      請求

      socket

      控制臺輸入模塊

      配置文件

      bingServer請求

      bingSocket消息

      子線程入口部分

      主線程部分

      工具函數

      效果展示

      寫在最后


      前言

      ChatGPT在當下已然成為炙手可熱的話題了,隨著GPT-4的推出,網上關于其接口的文章也越來越多。但是今天,我們不聊GPT,說說它的老朋友:newbing

      之前我發布了幾篇關于對接openAI以及chatGPT的文章:Node搭建GPT接口Node機器人語音識別及合成,大家對此類文章的興趣度還是挺高的,于是我決定深入探索一下NewBing的接口及對話方式,如果有興趣的話就繼續往下看吧

      準備工作

      工作原理

      首先我們看看NewBing的實現原理是什么

      掛VPN,打開必應,登錄bing賬號

      如果顯示使用Edge打開,我們可以下載一個Edge或者使用chathub插件

      這里我以Edge為例,在Edge中我們可以點擊立即聊天開始使用

      我們打開F12,進入網絡菜單進行hack,輸入一句對話并發送,開啟與newbing的聊天

      可以看到,在發送和接收對話時,瀏覽器發送了一個請求用于新建對話,并建立了websocket連接,最后將對話結果發送到頁面

      功能設計

      知道了程序運行的原理,實現功能就有思路了,我的計劃是在node控制臺中實現一個與NewBing對話的功能,思路如下:

      簡述一下上面的流程,使用者通過命令打開newBing控制臺,直接輸入要發送的對話,等待一段時間后,收到消息反饋,繼續下面的對話

      這種方式不僅僅可以在控制臺中使用,也可以嘗試寫成服務或websocket的形式,提供接口或消息給客戶端調用,這里我就拋磚引玉,將后續的功能留給各位大佬實現

      實現過程

      基礎概念

      代理

      使用proxy-agent模塊可以讓請求和socket代理到VPN所在的端口通過代理訪問Bing獲取消息

      import ProxyAgent from "proxy-agent"
      const agent = ProxyAgent('http://127.0.0.1:10240')// 訪問vpn代理地址

      通過agent參數使用代理功能

      請求

      請求函數使用的是我之前寫的一個工具包,配合配套的catchAwait函數食用更佳

      import { Request, catchAwait } from "utils-lib-js"
      const bingRequest = new Request('https://www.bing.com')// 初始化請求地址
      bingRequest.use("error", console.error)// 攔截拋錯
      const [err, res] = await catchAwait(this.bingRequest.GET("/turing/conversation/create"))// 發起請求

      socket

      WebSocket的使用可以參照之前的文章

      控制臺輸入模塊

      使用readline模塊可以接收控制臺的輸入內容

      import readline from "readline";
      readline.createInterface({
          input: process.stdin,
          output: process.stdout,
      }).question('請輸入:', ()=>{
          // 輸入完成,敲擊了回車
      })

      配置文件

      需要注意的是:bing的cookie可以通過在任意瀏覽器打開NewBing的網站按下F12獲取(前提是登錄了賬號),直接輸入document.cookie獲取

      export const config = {
        cookie: "必應的cookie",
        bingUrl: "https://www.bing.com",
        proxyUrl: "http://127.0.0.1:10240",
        bingSocketUrl: "wss://sydney.bing.com",
      };
      export const conversationTemplate = {
        arguments: [
          {
            source: "cib",
            optionsSets: [
              "deepleo",
              "nlu_direct_response_filter",
              "disable_emoji_spoken_text",
              "responsible_ai_policy_235",
              "enablemm",
              "dtappid",
              "rai253",
              "dv3sugg",
              "h3imaginative",
            ],
            allowedMessageTypes: ["Chat", "InternalSearchQuery"],
            isStartOfSession: true,
            message: {
              author: "user",
              inputMethod: "Keyboard",
              text: "",
              messageType: "Chat",
            },
            conversationId: "",
            conversationSignature: "",
            participant: {
              id: "",
            },
          },
        ],
        invocationId: "0",
        target: "chat",
        type: 4,
      };
      

      bingServer請求

      請求就一個接口,暴露接口給外部獲取

      import { Request, catchAwait, MessageCenter } from "utils-lib-js"
      import { config } from "../config.js"
      // 請求對話信息接口的響應信息
      export type IBingInfo = {
          clientId: string
          conversationId: string
          conversationSignature: string
          result: {
              message: unknown
              value: string
          }
      }
      // 切換可選項,防止報錯
      export type IBingInfoPartial = Partial<IBingInfo>
      // 靜態配置項結構
      export type IConfig = {
          cookie: string
          proxyUrl: string
          bingUrl: string
          bingSocketUrl: string
      }
      // NewBingServer的構造函數配置
      export type IOpts = {
          agent?: any
      }
      export class NewBingServer extends MessageCenter {
          bingInfo: IBingInfo
          readonly bingRequest: Request
          constructor(private opts: IOpts, private _config: IConfig = config) {
              super()
              const { bingUrl } = this._config
              this.bingRequest = new Request(bingUrl)// 初始化請求地址
              this.initServer()// 初始化request: 攔截器等
          }
          // 拋錯事件
          throwErr(err: any) {
              this.emit("new-bing:server:error", err)
          }
          // 重置當前請求
          async reset() {
              this.clearBing()
              const bingInfo = await this.createConversation()
              this.init(bingInfo)
          }
          // 清除當前請求的信息
          clearBing() {
              this.bingInfo = null
          }
          // 賦值當前請求的信息
          init(bingInfo) {
              this.bingInfo = bingInfo
          }
          // 初始化request
          initServer() {
              this.bingRequest.use("error", console.error)
              // .use("response", console.log)
          }
          // 發起請求
          private async createConversation() {
              const { _config, opts, bingInfo } = this
              const { agent } = opts
              if (bingInfo) return bingInfo
              const { cookie } = _config
              const [err, res] = await catchAwait(this.bingRequest.GET("/turing/conversation/create", {}, null, {
                  headers: { cookie },
                  agent
              }))
              if (err) return this.throwErr(err)
              return res
          }
      }

      bingSocket消息

      socket內容比較多,主要是針對不同的message的type進行區分

      import WebSocket, { MessageEvent, Event, ErrorEvent, CloseEvent } from "ws";
      import { getType, IObject, jsonToString, MessageCenter, stringToJson } from "utils-lib-js"
      import { ClientRequestArgs } from "http"
      import { config } from "../config.js"
      import { IConfig, IBingInfoPartial } from "../server/index.js"
      import { setConversationTemplate, Conversation } from '../helpers/index.js'
      const fixStr = ''// 每段對話的標識符,發送接收都有
      // websocket配置
      export type IWsConfig = {
          address: string | URL
          options: WebSocket.ClientOptions | ClientRequestArgs
          protocols: string | string[]
      }
      // 發送socket消息的類型
      export type IMessageOpts = {
          message: string | IObject<any>
      }
      // 發送對話的結構
      export type IConversationMessage = {
          message: string
          invocationId: string | number
      }
      export class NewBingSocket extends MessageCenter {
          private ws: WebSocket // ws實例
          private bingInfo: IBingInfoPartial // 請求拿到的conversation信息
          private convTemp: Conversation.IConversationTemplate // 對話發送的消息模板
          private pingInterval: NodeJS.Timeout | string | number // ping計時器
          constructor(public wsConfig: Partial<IWsConfig>, private _config: IConfig = config) {
              super()
              const { bingSocketUrl } = this._config
              const { address } = wsConfig
              wsConfig.address = bingSocketUrl + address
          }
          // 將conversation信息賦值到消息模板中
          mixBingInfo(bingInfo: IBingInfoPartial) {
              const { conversationId, conversationSignature, clientId } = bingInfo
              this.bingInfo = bingInfo
              this.convTemp = setConversationTemplate({
                  conversationId, conversationSignature, clientId
              })
              return this
          }
          // 創建ws
          createWs() {
              const { wsConfig, ws } = this
              if (ws) return this
              const { address, options, protocols } = wsConfig
              this.ws = new WebSocket(address, protocols, options)
              return this
          }
          // 重置ws
          clearWs() {
              const { ws } = this
              if (ws) {
                  ws.close(4999, 'clearWs')
              }
              this.clearInterval()
              return this
          }
          // 拋錯事件
          private throwErr(err: any) {
              this.emit("new-bing:socket:error", err)
          }
          // 開啟ws后初始化事件
          initEvent() {
              const { ws, error, close, open, message } = this
              if (!ws) this.throwErr("ws未定義,不能初始化事件")
              ws.onerror = error
              ws.onclose = close
              ws.onopen = open
              ws.onmessage = message
              return this
          }
          // 發消息,兼容Object和string
          sendMessage = (opts: IMessageOpts) => {
              const { bingInfo, convTemp, ws } = this
              const { message } = opts
              if (!bingInfo || !convTemp) this.throwErr("對話信息未獲取,或模板信息未配置,請重新獲取信息")
              const __type = getType(message)
              let str = ""
              if (__type === "string") {
                  str = message as string
              } else if (__type === "object") {
                  str = jsonToString(message as IObject<unknown>)
              }
              this.emit("send-message", str)
              ws.send(str + fixStr)
          }
          // 收到消息
          private message = (e: MessageEvent) => {
              this.emit("message", e)
              onMessage.call(this, e)
          }
          // ws連接成功
          private open = (e: Event) => {
              this.emit("open", e)
              const { sendMessage } = this
              sendMessage({ message: { "protocol": "json", "version": 1 } })// 初始化
          }
          // ws關閉
          private close = (e: CloseEvent) => {
              const { ws } = this
              ws.removeAllListeners()
              this.ws = null
              this.emit("close", e)
          }
          // ws出錯
          private error = (e: ErrorEvent) => {
              this.emit("error", e)
              console.log("error");
          }
          // 斷線檢測
          sendPingMsg() {
              const { ws } = this
              if (!ws) this.throwErr("ws未定義,無法發送Ping")
              this.startInterval()
              this.emit("init:finish", {})
          }
          // 開啟斷線定時器
          private startInterval() {
              this.clearInterval()
              this.pingInterval = setInterval(() => {
                  this.sendMessage({ message: { "type": 6 } })
              }, 20 * 1000)
          }
          // 清空斷線定時器
          private clearInterval() {
              const { pingInterval } = this
              if (pingInterval) {
                  clearInterval(pingInterval)
                  this.pingInterval = null
              }
          }
      }
      
      // 接收到消息
      export function onMessage(e: MessageEvent) {
          const dataSource = e.data.toString().split(fixStr)[0]
          const data = stringToJson(dataSource)
          const { type } = data ?? {}
          switch (type) {
              case 1://對話中
                  this.emit("message:ing", data.arguments?.[0]?.messages?.[0]?.text)
                  break;
              case 2://對話完成
                  this.emit("message:finish", data.item?.messages?.[1]?.text)
                  break;
              case 6://斷線檢測
                  // console.log(data);
                  break;
              case 7://Connection closed with an error
                  console.log(data);
                  break;
              default:// 初始化響應
                  this.sendPingMsg()
                  break;
          }
      }
      // 發送聊天消息
      export function sendConversationMessage(params?: IConversationMessage) {
          const { message, invocationId } = params
          const arg = this.convTemp.arguments[0]
          arg.message.text = message
          arg.isStartOfSession = invocationId === 0// 是否是新對話
          this.convTemp.invocationId = invocationId.toString()// 第幾段對話
          this.sendMessage({ message: this.convTemp })
      }
      

      子線程入口部分

      然后通過startBingConversation作為入口函數,對上面的兩個模塊進行調用

      import { NewBingServer, IBingInfoPartial } from "./server/index.js"
      import { NewBingSocket, sendConversationMessage } from "./socket/index.js"
      import { config } from "./config.js"
      import ProxyAgent from "proxy-agent"
      import { parentPort } from "worker_threads";
      
      const { proxyUrl } = config// 代理地址
      const agent = ProxyAgent(proxyUrl)// 訪問vpn代理地址
      // 初始化bing請求
      const bingServer = new NewBingServer({
          agent
      })
      // 初始化bing的websocket消息
      const bingSocket = new NewBingSocket({
          address: "/sydney/ChatHub",
          options: {
              agent
          }
      })
      let invocationId = -1// 同一段對話的id
      let bingInfo: IBingInfoPartial// bing的conversation信息,BingServer請求的結果
      const startBingConversation = async () => {
          initEvent()
          await initBingServer()
          initBingSocket()
      }
      
      const initEvent = () => {
          bingServer.on("new-bing:server:error", (...args) => { throw new Error(...args) })// 請求拋錯
          bingSocket.on("new-bing:socket:error", (...args) => { throw new Error(...args) })// 消息拋錯
          // 接收主線程的消息
          parentPort.on("message", (res) => {
              const { type } = res
              if (type === "sendMessage") {
                  // 發送消息
                  sendConversationMessage.call(bingSocket, { message: res.message, invocationId: ++invocationId })
              }
          })
      }
      const initBingServer = async () => {
          await bingServer.reset()// 重置請求
          bingInfo = bingServer.bingInfo
      }
      const initBingSocket = () => {
          bingSocket.mixBingInfo(bingInfo).createWs().initEvent().on("init:finish", () => {// socket初始化完成
              parentPort.postMessage({
                  type: "init:finish"
              })
          }).on("message:finish", (data = "") => {
              // 一段對話完成
              parentPort.postMessage({
                  type: "message:finish",
                  data
              })
          }).on("message:ing", (data = "") => {
              // 對話時,觸發主線程loading操作
              parentPort.postMessage({
                  type: "message:ing",
                  data
              })
          })
      }
      
      startBingConversation()

      主線程部分

      主線程可以參照之前的打包工具,注冊成系統命令,使用bing啟動,通過readline進行對話交互

      #!/usr/bin/env node
      import { Worker } from "worker_threads";
      import readline from "readline";
      import { defer, logLoop, logOneLine } from "utils-lib-js";
      const NewBing = new Worker("./src/index.js");
      // 工廠模式
      const readlineFactory = () => {
        return readline.createInterface({
          input: process.stdin,
          output: process.stdout,
        });
      };
      let rl, loading;
      // 解決node低版本無readline/promises模塊,將異步函數換成promise
      const readlinePromise = (...args) => {
        const { promise, resolve } = defer();
        rl.question(...args, resolve);
        return promise;
      };
      // 啟動命令輸入
      const start = () => {
        readlinePromise("請輸入:").then((res) => {
          console.log(`你:${res}`);
          NewBing.postMessage({ type: "sendMessage", message: res });
          loading = logLoop(); // 加載中動畫
        });
      };
      // 關閉命令輸入
      const clear = () => {
        rl.close();
        rl = null;
      };
      // 重置
      const reset = () => {
        if (rl) {
          clear();
        }
        rl = readlineFactory();
      };
      // 初始化當前命令窗口
      const initBing = () => {
        reset();
        NewBing.on("message", (res) => {
          switch (res.type) {
            case "message:finish": // 收到消息,重置輸入框,換行
              loading.isStop = true;
              logOneLine(`Bing:${res.data}`, true, true);
            case "init:finish": // 初始化完成
              start();
              break;
            case "message:ing": // 對話中
              // loading = logLoop(loadList);
              break;
          }
        });
      };
      initBing();
      

      工具函數

      
      import { conversationTemplate } from "../config.js"
      import { readFileSync, writeFileSync } from "fs"
      let conTemp: Conversation.IConversationTemplate = conversationTemplate
      export namespace Conversation {
          // 對話模型類型
          // Creative:創造力的,Precise:精確的,Balanced:平衡的
          type ConversationStyle = 'Creative' | 'Precise' | 'Balanced'
          // 對話方式
          type ConversationType = 'SearchQuery' | 'Chat' // bing搜索,聊天
          // 模型映射
          export enum ConversationStr {
              Creative = 'h3imaginative',
              Precise = 'h3precise',
              Balanced = 'galileo'
          }
          // 發起對話時傳入的參數
          export type IConversationOpts = {
              convStyle: ConversationStyle
              messageType: ConversationType
              conversationId: string
              conversationSignature: string
              clientId: string
          }
          type IMessage = {
              author: string,
              text: string,
              messageType: ConversationType,
          }
          type IArguments = {
              source: string
              optionsSets: string[]
              allowedMessageTypes: string[]
              isStartOfSession: boolean
              message: IMessage
              conversationId: string
              conversationSignature: string
              participant: {
                  id: string
              }
          }
          // 發起對話的模板
          export type IConversationTemplate = {
              arguments: IArguments[]
              invocationId: string
              target: string
              type: number
          }
      }
      // 默認使用平衡類型
      const { Balanced } = Conversation.ConversationStr
      // 數據文件緩存(暫時沒用上,調試的時候用的)
      export function ctrlTemp(path?: string): any
      export function ctrlTemp(path?: string, file?: any): void
      export function ctrlTemp(path: string = "./temp", file?: string) {
          try {
              if (file) {
                  return writeFileSync(path, file, "utf8")
              }
              return readFileSync(path, "utf8")
          } catch (error) { }
      }
      
      // 配置socket鑒權及消息模板
      export function setConversationTemplate(params: Partial<Conversation.IConversationOpts> = {}): Conversation.IConversationTemplate {
          const { convStyle = Balanced, messageType = "Chat", conversationId,
              conversationSignature, clientId } = params
          if (!conversationId || !conversationSignature || !clientId) return null
          const args = conTemp.arguments[0]
          conTemp.arguments[0] = {
              ...args,
              conversationId,
              conversationSignature,
              participant: { id: clientId }
          }
          args.optionsSets.push(convStyle)// 這里傳入對話風格
          args.message.messageType = messageType// 這里傳入對話類型
          return conTemp
      }

      效果展示

      我們使用npm link綁定全局命令

      然后使用bing運行命令,并輸入對話

      寫在最后

      以上就是文章全部內容了,文章主要講述了在node中實現一個與newbing對話的案例,希望能對你有幫助,對文章有任何問題歡迎評論或私信。

      感謝你看到了這里,如果覺得文章不錯的話,還望三連支持一下,非常感謝!

      源碼:Node-NewBing: 基于node+NewBing提供的AI模型做的案例

      posted @ 2023-04-13 11:31  阿宇的編程之旅  閱讀(96)  評論(0)    收藏  舉報  來源
      主站蜘蛛池模板: 国产精品午夜精品福利| 色综合久久久久综合体桃花网 | 国内精品久久久久影院日本| 国产愉拍精品手机| 18分钟处破好疼哭视频在线观看| 国产麻豆一区二区精彩视频| 日韩人妻中文字幕精品| 欧美成人精品三级网站| 国产乱妇乱子视频在播放| 女同在线观看亚洲国产精品 | 国产成人综合色就色综合| 噜妇插内射精品| 久久这里只精品国产2| 国产久久热这里只有精品| 亚洲欧美综合精品成人导航| 亚洲人成网网址在线看| 精品日韩亚洲av无码| 亚洲乱码中文字幕小综合| 耒阳市| 久久婷婷五月综合色和啪| 国产福利深夜在线播放| 亚洲精品美女久久7777777| 天天摸天天做天天添欧美| 内射一区二区三区四区| 嘉峪关市| 熟妇无码熟妇毛片| 国产SUV精品一区二区四| 中文字幕日韩人妻一区| 好吊视频一区二区三区人妖| 人人妻人人澡人人爽人人精品电影| 亚洲av无码成人精品区一区| 国产一区二区三区AV在线无码观看 | 国产一区二区三区怡红院| 在线观看无码av免费不卡网站| 国内少妇人妻偷人精品视频| 国产久免费热视频在线观看| 少妇特黄a一区二区三区| 男女无遮挡激情视频| 九九热免费精品在线视频| 亚洲日韩AV秘 无码一区二区| 在线看无码的免费网站|