ZeroBot-Plugin/plugin/aichat/main.go
2026-01-03 23:36:49 +08:00

194 lines
5.5 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package aichat 大模型聊天和Agent
package aichat
import (
"encoding/json"
"math/rand"
"strings"
"github.com/fumiama/deepinfra"
goba "github.com/fumiama/go-onebot-agent"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/single"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/airecord"
"github.com/FloatTech/floatbox/process"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/chat"
"github.com/FloatTech/zbputils/control"
)
var (
// en data [8 temp] [8 rate] LSB
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("aichat"),
Brief: "大模型聊天和Agent",
Help: "- (随意聊天, 概率匹配)",
PrivateDataFolder: "aichat",
}).ApplySingle(single.New(
single.WithKeyFn(func(ctx *zero.Ctx) int64 {
if ctx.Event.GroupID == 0 {
return -ctx.Event.UserID
}
return ctx.Event.GroupID
}),
// no post option, silently quit
))
)
var (
fastfailnorecord = false
)
func init() {
en.OnMessage(chat.EnsureConfig, func(ctx *zero.Ctx) bool {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
stor, ok := ctx.State[zero.StateKeyPrefixKeep+"aichatcfg_stor__"].(chat.Storage)
if !ok {
logrus.Warnln("ERROR: cannot get stor")
return false
}
if !(ctx.ExtractPlainText() != "" &&
(!stor.NoReplyAt() || (stor.NoReplyAt() && !ctx.Event.IsToMe))) {
return false
}
rate := stor.Rate()
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
return false
}
if chat.AC.Key == "" {
logrus.Warnln("ERROR: get extra err: empty key")
return false
}
if ctx.Event.IsToMe {
ctx.Block()
}
return true
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
stor := ctx.State[zero.StateKeyPrefixKeep+"aichatcfg_stor__"].(chat.Storage)
temperature := stor.Temp()
topp, maxn := chat.AC.MParams()
logrus.Debugln("[aichat] agent mode test: noagent", stor.NoAgent(), "hasapi", chat.AC.AgentAPI != "", "hasmodel", chat.AC.AgentModelName != "")
if !stor.NoAgent() && chat.AC.AgentAPI != "" && chat.AC.AgentModelName != "" {
logrus.Debugln("[aichat] enter agent mode")
x := deepinfra.NewAPI(chat.AC.AgentAPI, string(chat.AC.AgentKey))
mod, err := chat.AC.Type.Protocol(chat.AC.AgentModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
role := goba.PermRoleUser
if zero.AdminPermission(ctx) {
role = goba.PermRoleAdmin
if zero.SuperUserPermission(ctx) {
role = goba.PermRoleOwner
}
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
logrus.Warnln("ERROR: cannot get ctrl mamager")
}
ag := chat.AgentOf(ctx.Event.SelfID, c.Service)
logrus.Debugln("[aichat] got agent")
if chat.AC.ImageAPI != "" && !ag.CanViewImage() {
mod, err := chat.AC.ImageType.Protocol(chat.AC.ImageModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
ag.SetViewImageAPI(deepinfra.NewAPI(chat.AC.ImageAPI, string(chat.AC.ImageKey)), mod)
logrus.Debugln("[aichat] agent set img")
}
ctx.NoTimeout()
logrus.Debugln("[aichat] agent set no timeout")
hasresp := false
for i := 0; i < 8; i++ { // 最大运行 8 轮因为问答上下文只有 16
reqs := chat.CallAgent(ag, zero.SuperUserPermission(ctx), x, mod, gid, role)
if len(reqs) == 0 {
logrus.Debugln("[aichat] agent call got empty response")
break
}
hasresp = true
for _, req := range reqs {
if req.Action == goba.SVM { // is a fake action
continue
}
resp := ctx.CallAction(req.Action, req.Params)
logrus.Infoln("[aichat] agent get resp:", reqs)
ag.AddResponse(gid, &goba.APIResponse{
Status: resp.Status,
Data: json.RawMessage(resp.Data.Raw),
Message: resp.Message,
Wording: resp.Wording,
RetCode: resp.RetCode,
})
}
}
if hasresp {
return
}
// no response, fall back to normal chat
logrus.Debugln("[aichat] agent fell back to normal chat")
}
x := deepinfra.NewAPI(chat.AC.API, string(chat.AC.Key))
mod, err := chat.AC.Type.Protocol(chat.AC.ModelName, temperature, topp, maxn)
if err != nil {
logrus.Warnln("ERROR: ", err)
return
}
data, err := x.Request(chat.GetChatContext(mod, gid, chat.AC.SystemP, bool(chat.AC.NoSystemP)))
if err != nil {
logrus.Warnln("[aichat] post err:", err)
return
}
txt := chat.Sanitize(strings.Trim(data, "\n  "))
if len(txt) > 0 {
chat.AddChatReply(gid, txt)
nick := zero.BotConfig.NickName[rand.Intn(len(zero.BotConfig.NickName))]
txt = strings.ReplaceAll(txt, "{name}", ctx.CardOrNickName(ctx.Event.UserID))
txt = strings.ReplaceAll(txt, "{me}", nick)
id := any(nil)
if ctx.Event.IsToMe {
id = ctx.Event.MessageID
}
for _, t := range strings.Split(txt, "{segment}") {
if t == "" {
continue
}
logrus.Infoln("[aichat] 回复内容:", t)
recCfg := airecord.GetConfig()
record := ""
if !fastfailnorecord && !stor.NoRecord() {
record = ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, t)
if record != "" {
ctx.SendChain(message.Record(record))
continue
}
fastfailnorecord = true
}
if id != nil {
id = ctx.SendChain(message.Reply(id), message.Text(t))
} else {
id = ctx.SendChain(message.Text(t))
}
process.SleepAbout1sTo2s()
}
}
})
}