Compare commits
2 Commits
7e9d4e5052
...
89286b7e73
Author | SHA1 | Date |
---|---|---|
Yiyang Kang | 89286b7e73 | |
Yiyang Kang | 9e7432a1a1 |
21
assistant.go
21
assistant.go
|
@ -87,7 +87,7 @@ func matchAssistantConversation(botUsr *tele.User, msg *tele.Message) []*tele.Me
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type assistantStreamedResponseCb func(text string, finished bool) (*tele.Message, error)
|
type assistantStreamedResponseCb func(text string, finished bool) error
|
||||||
|
|
||||||
func assistantStreamedResponse(request openai.ChatRequest, cb assistantStreamedResponseCb) error {
|
func assistantStreamedResponse(request openai.ChatRequest, cb assistantStreamedResponseCb) error {
|
||||||
logger.Debugw("Openai chat request", "req", request)
|
logger.Debugw("Openai chat request", "req", request)
|
||||||
|
@ -111,7 +111,7 @@ func assistantStreamedResponse(request openai.ChatRequest, cb assistantStreamedR
|
||||||
nErrs := 0
|
nErrs := 0
|
||||||
go func() {
|
go func() {
|
||||||
respBuilder := strings.Builder{}
|
respBuilder := strings.Builder{}
|
||||||
minWait := time.After(1 * time.Second)
|
minWait := time.After(time.Second)
|
||||||
for {
|
for {
|
||||||
var (
|
var (
|
||||||
nNewChunk int
|
nNewChunk int
|
||||||
|
@ -149,9 +149,9 @@ func assistantStreamedResponse(request openai.ChatRequest, cb assistantStreamedR
|
||||||
}
|
}
|
||||||
|
|
||||||
respoText := respBuilder.String() + assistantWritingSign
|
respoText := respBuilder.String() + assistantWritingSign
|
||||||
minWait = time.After(691 * time.Millisecond) // renew the timer
|
minWait = time.After(time.Second) // renew the timer
|
||||||
|
|
||||||
if _, err := cb(respoText, false); err != nil {
|
if err := cb(respoText, false); err != nil {
|
||||||
logger.Warnw("failed to send partial update", "error", err)
|
logger.Warnw("failed to send partial update", "error", err)
|
||||||
nErrs += 1
|
nErrs += 1
|
||||||
if nErrs > 3 {
|
if nErrs > 3 {
|
||||||
|
@ -165,7 +165,7 @@ func assistantStreamedResponse(request openai.ChatRequest, cb assistantStreamedR
|
||||||
}
|
}
|
||||||
|
|
||||||
respText := respBuilder.String()
|
respText := respBuilder.String()
|
||||||
if _, err = cb(respText, true); err != nil {
|
if err = cb(respText, true); err != nil {
|
||||||
logger.Warnw("assistant: failed to send message", "error", err)
|
logger.Warnw("assistant: failed to send message", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -213,6 +213,7 @@ func handleAssistantConversation(c tele.Context, thread []*tele.Message) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(convMsgs) == 0 {
|
if len(convMsgs) == 0 {
|
||||||
|
// It turns out that this will never happen because Telegram splits messages when they exceed a certain length.
|
||||||
return c.Reply("Your message is too long (Sorry!)")
|
return c.Reply("Your message is too long (Sorry!)")
|
||||||
}
|
}
|
||||||
for l := len(convMsgs) - 1; l >= 0; l-- {
|
for l := len(convMsgs) - 1; l >= 0; l-- {
|
||||||
|
@ -226,14 +227,10 @@ func handleAssistantConversation(c tele.Context, thread []*tele.Message) error {
|
||||||
MaxTokens: 2048,
|
MaxTokens: 2048,
|
||||||
}
|
}
|
||||||
|
|
||||||
typingNotifyCh := make(chan struct{})
|
typingNotifyCh := setTyping(c)
|
||||||
go func() {
|
|
||||||
defer close(typingNotifyCh)
|
|
||||||
_ = c.Bot().Notify(lastMsg.Chat, tele.Typing)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var replyMsg *tele.Message
|
var replyMsg *tele.Message
|
||||||
reqErr := assistantStreamedResponse(req, func(text string, finished bool) (*tele.Message, error) {
|
reqErr := assistantStreamedResponse(req, func(text string, finished bool) error {
|
||||||
var err error
|
var err error
|
||||||
if replyMsg == nil {
|
if replyMsg == nil {
|
||||||
<-typingNotifyCh
|
<-typingNotifyCh
|
||||||
|
@ -247,7 +244,7 @@ func handleAssistantConversation(c tele.Context, thread []*tele.Message) error {
|
||||||
logger.Warnw("failed to cache message", "error", err)
|
logger.Warnw("failed to cache message", "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return replyMsg, err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
if reqErr != nil {
|
if reqErr != nil {
|
||||||
|
|
9
bot.go
9
bot.go
|
@ -117,3 +117,12 @@ func stickerFromID(id string) *tele.Sticker {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setTyping(c tele.Context) chan error {
|
||||||
|
resultCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
defer close(resultCh)
|
||||||
|
resultCh <- c.Bot().Notify(c.Chat(), tele.Typing)
|
||||||
|
}()
|
||||||
|
return resultCh
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
tele "gopkg.in/telebot.v3"
|
tele "gopkg.in/telebot.v3"
|
||||||
|
@ -72,13 +71,6 @@ func handleTranslateBtn(c tele.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// pretend to be typing
|
|
||||||
if err := c.Bot().Notify(msg.Chat, tele.Typing); err != nil {
|
|
||||||
logger.Warnf("failed to send typing action: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ai := openai.NewClient(config.OpenAIApiKey)
|
|
||||||
|
|
||||||
req := openai.ChatRequest{
|
req := openai.ChatRequest{
|
||||||
Model: openai.ModelGpt0305Turbo,
|
Model: openai.ModelGpt0305Turbo,
|
||||||
Messages: []openai.ChatMessage{
|
Messages: []openai.ChatMessage{
|
||||||
|
@ -93,67 +85,27 @@ func handleTranslateBtn(c tele.Context) error {
|
||||||
},
|
},
|
||||||
Temperature: lo.ToPtr(0.2),
|
Temperature: lo.ToPtr(0.2),
|
||||||
}
|
}
|
||||||
logger.Debugf("Openai chat request: %#+v", req)
|
|
||||||
|
|
||||||
resp, err := ai.ChatCompletionStream(req)
|
actionCh := setTyping(c)
|
||||||
|
err := assistantStreamedResponse(req, func(text string, finished bool) error {
|
||||||
|
var err error
|
||||||
|
<-actionCh
|
||||||
|
if finished {
|
||||||
|
retryBtn := translateBtnRetry
|
||||||
|
retryBtn.Data = targetLang
|
||||||
|
respMenu := &tele.ReplyMarkup{}
|
||||||
|
respMenu.Inline(respMenu.Row(retryBtn))
|
||||||
|
|
||||||
|
msg, err = c.Bot().Edit(msg, text, respMenu)
|
||||||
|
} else {
|
||||||
|
msg, err = c.Bot().Edit(msg, text)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed to translate: req: %#+v, err: %v", req, err)
|
logger.Errorf("failed to translate: req: %#+v, err: %v", req, err)
|
||||||
_, _ = c.Bot().Reply(origMsg, stickerFromID(stickerPanic), tele.Silent)
|
_, _ = c.Bot().Reply(origMsg, stickerFromID(stickerPanic), tele.Silent)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
respBuilder := strings.Builder{}
|
|
||||||
minWait := time.After(1 * time.Second)
|
|
||||||
for {
|
|
||||||
var (
|
|
||||||
nNewChunk int
|
|
||||||
finished bool
|
|
||||||
minWaitSatisfied bool
|
|
||||||
)
|
|
||||||
Drain:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case chunk, ok := <-resp.Stream:
|
|
||||||
if !ok {
|
|
||||||
finished = true
|
|
||||||
break Drain
|
|
||||||
}
|
|
||||||
nNewChunk += 1
|
|
||||||
respBuilder.WriteString(chunk)
|
|
||||||
default:
|
|
||||||
if minWaitSatisfied {
|
|
||||||
break Drain
|
|
||||||
}
|
|
||||||
<-minWait
|
|
||||||
minWaitSatisfied = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nNewChunk == 0 {
|
|
||||||
if chunk, ok := <-resp.Stream; !ok {
|
|
||||||
finished = true
|
|
||||||
} else {
|
|
||||||
respBuilder.WriteString(chunk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if finished {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
respoText := respBuilder.String() + assistantWritingSign
|
|
||||||
minWait = time.After(691 * time.Millisecond) // renew the timer
|
|
||||||
if msg, err = c.Bot().Edit(msg, respoText, tele.Silent); err != nil {
|
|
||||||
logger.Warnf("failed to edit the temporary message: %v", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
logger.Debugf("... message edited")
|
|
||||||
}
|
|
||||||
|
|
||||||
respText := respBuilder.String()
|
|
||||||
retryBtn := translateBtnRetry
|
|
||||||
retryBtn.Data = targetLang
|
|
||||||
respMenu := &tele.ReplyMarkup{}
|
|
||||||
respMenu.Inline(respMenu.Row(retryBtn))
|
|
||||||
_, err = c.Bot().Edit(msg, respText, tele.Silent, respMenu)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue