2023-02-15 用go gin 简单接入gpt3 stream

核心知识点:

gin 加载html
chat gpt Stream
gin stream
js EventSource

关于gpt3中的上下文:

只要将之前的问题和回答用换行符号拼接 最后再加新的的问题即可

关于参数MaxTokens:

返回最大文本数,openai 也是基于这个参数收费

源码:

用html简单写个简易前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="result">

</div>

<div>
    <input type="text" id="msg">
    <button type="button" onclick="submit()">提交!</button>
</div>
</body>
<script>
    class Chat {
        constructor(q, a) {
            this.q = q;
            this.a = a;
        }
    }

    const chats = [];

    function submit() {
        let question = document.getElementById("msg").value
        if (question === undefined || question === "") {
            alert("please input question")
        }
        const chat = new Chat();
        chat.q = question;
        chat.a = "";
        const stream = new EventSource("/stream?msg=" + chat.q + "&chats=" + JSON.stringify(chats))
        stream.addEventListener("message", function (e) {
            let parse = JSON.parse(e.data);
            chat.a += parse["text"]
            document.getElementById("result").innerHTML = chat.a
        });
        stream.addEventListener("stop", function (e) {
            stream.close()
            chat.a += "\n"
            chats.push(chat)
            document.getElementById("result").innerHTML = chat.a
            console.log(e)
        });
        stream.onerror = function (event) {
            stream.close()
            console.log(event)
        }
    }

</script>
</html>

go

gin提供服务
go-gp3 调用openai接口

package main

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "github.com/gin-gonic/gin"
    gogpt "github.com/sashabaranov/go-gpt3"
    "io"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"
)
type SteamRequest struct {
    Msg   string `form:"msg" binding:"required"`
    Chats string `form:"chats" binding:"required"`
}
type Chat struct {
    A string `form:"a"`
    Q string `form:"q"`
}

func setupRouter() *gin.Engine {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")
    r.GET("/", func(c *gin.Context) {
        c.HTML(http.StatusOK, "index.html", gin.H{
            "title": "Main website",
        })
    })
    
    r.GET("/stream", func(c *gin.Context) {
        var request SteamRequest
        err := c.ShouldBindQuery(&request)
        if err != nil {
            log.Fatal(err.Error())
        }
        log.Println("request" + request.Msg)
        var chats []Chat
        errs := json.Unmarshal([]byte(request.Chats), &chats)
        if errs != nil {
            fmt.Println("json unmarshal error:", errs)
        }
        var question = ""
        if len(chats) != 0 {
            for i := 0; i < len(chats); i++ {
                chat := chats[i]
                question += chat.Q + "\n" + chat.A
            }
        }
        question += request.Msg

        client := gogpt.NewClient("you token")
        ctx := context.Background()

        req := gogpt.CompletionRequest{
            Model:     gogpt.GPT3TextDavinci003,
            MaxTokens: 5,
            Prompt:    question,
            Stream:    true,
        }
        stream, err := client.CreateCompletionStream(ctx, req)
        if err != nil {
            return
        }

        chanStream := make(chan string, 100)
        go func() {
            defer stream.Close()
            defer close(chanStream)
            for {
                response, err := stream.Recv()
                if errors.Is(err, io.EOF) {
                    fmt.Println("Stream finished")
                    chanStream <- "<!finish>"
                    return
                }

                if err != nil {
                    fmt.Printf("Stream error: %v\n", err)
                    chanStream <- "<!error>"
                    return
                }
                if len(response.Choices) == 0 {
                    fmt.Println("Stream finished")
                    chanStream <- "<!finish>"
                    return
                }
                data, err := json.Marshal(response.Choices[0])
                chanStream <- string(data)
                fmt.Printf("Stream response: %v\n", response.Choices[0])
            }
        }()

        c.Stream(func(w io.Writer) bool {
            if msg, ok := <-chanStream; ok {
                if msg == "<!finish>" {
                    c.SSEvent("stop", "finish")
                }
                if msg == "<!error>" {
                    c.SSEvent("stop", "error")
                }
                c.SSEvent("message", msg)
                fmt.Printf("message: %v\n", msg)

                return true
            }
            return false
        })

    })
    return r
}

func main() {
    gin.SetMode(gin.ReleaseMode)
    router := setupRouter()
    srv := &http.Server{
        Addr:    ":8080",
        Handler: router,
    }
    go func() {
        // 服务连接
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()

    // 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
    quit := make(chan os.Signal)
    signal.Notify(quit, os.Interrupt)
    <-quit
    log.Println("Shutdown Server ...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server Shutdown:", err)
    }
    log.Println("Server exiting")
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容