feat(aichat): add OLLaMA & GenAI support

This commit is contained in:
源文雨
2025-03-30 21:36:31 +09:00
parent a29f4cb1f9
commit 2df52161e5
7 changed files with 119 additions and 42 deletions

View File

@@ -6,8 +6,6 @@ import (
"os"
"strconv"
"strings"
"sync/atomic"
"unsafe"
"github.com/fumiama/deepinfra"
"github.com/fumiama/deepinfra/model"
@@ -24,13 +22,15 @@ import (
)
var (
api *deepinfra.API
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
// en data [4 type] [8 temp] [8 rate] LSB
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("aichat"),
Brief: "OpenAI聊天",
Help: "- 设置AI聊天触发概率10\n" +
"- 设置AI聊天温度80\n" +
"- 设置AI聊天接口类型[OpenAI|OLLaMA|GenAI]\n" +
"- 设置AI聊天接口地址https://xxx\n" +
"- 设置AI聊天密钥xxx\n" +
"- 设置AI聊天模型名xxx\n" +
"- 重置AI聊天系统提示词\n" +
@@ -44,14 +44,22 @@ var (
var (
modelname = model.ModelDeepDeek
systemprompt = chat.SystemPrompt
api = deepinfra.OpenAIDeepInfra
sepstr = ""
noreplyat = false
)
var apitypes = map[string]uint8{
"OpenAI": 0,
"OLLaMA": 1,
"GenAI": 2,
}
func init() {
mf := en.DataFolder() + "model.txt"
sf := en.DataFolder() + "system.txt"
pf := en.DataFolder() + "sep.txt"
af := en.DataFolder() + "api.txt"
nf := en.DataFolder() + "NoReplyAT"
if file.IsExist(mf) {
data, err := os.ReadFile(mf)
@@ -77,6 +85,14 @@ func init() {
sepstr = string(data)
}
}
if file.IsExist(af) {
data, err := os.ReadFile(af)
if err != nil {
logrus.Warnln("read api", err)
} else {
api = string(data)
}
}
noreplyat = file.IsExist(nf)
en.OnMessage(func(ctx *zero.Ctx) bool {
@@ -92,6 +108,7 @@ func init() {
}
rate := c.GetData(gid)
temp := (rate >> 8) & 0xff
typ := (rate >> 16) & 0x0f
rate &= 0xff
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
return
@@ -109,14 +126,7 @@ func init() {
logrus.Warnln("ERROR: get extra err: empty key")
return
}
var x deepinfra.API
y := &x
if api == nil {
x = deepinfra.NewAPI(deepinfra.APIDeepInfra, key)
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&api)), unsafe.Pointer(&x))
} else {
y = api
}
if temp <= 0 {
temp = 70 // default setting
}
@@ -124,14 +134,39 @@ func init() {
temp = 100
}
data, err := y.Request(chat.Ask(model.NewOpenAI(
modelname, sepstr,
float32(temp)/100, 0.9, 4096,
), gid, systemprompt))
if err != nil {
logrus.Warnln("[niniqun] post err:", err)
var x deepinfra.API
var mod model.Protocol
switch typ {
case 0:
x = deepinfra.NewAPI(api, key)
mod = model.NewOpenAI(
modelname, sepstr,
float32(temp)/100, 0.9, 4096,
)
case 1:
x = deepinfra.NewAPI(api, key)
mod = model.NewOLLaMA(
modelname, sepstr,
float32(temp)/100, 0.9, 4096,
)
case 2:
x = deepinfra.NewAPI(api, key)
mod = model.NewGenAI(
modelname,
float32(temp)/100, 0.9, 4096,
)
default:
logrus.Warnln("[aichat] unsupported AI type", typ)
return
}
data, err := x.Request(chat.Ask(mod, gid, systemprompt))
if err != nil {
logrus.Warnln("[aichat] post err:", err)
return
}
txt := chat.Sanitize(strings.Trim(data, "\n  "))
if len(txt) > 0 {
chat.Reply(gid, txt)
@@ -221,6 +256,48 @@ func init() {
}
ctx.SendChain(message.Text("成功"))
})
en.OnPrefix("设置AI聊天接口类型", zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
if !ok {
ctx.SendChain(message.Text("ERROR: no such plugin"))
return
}
typ, ok := apitypes[args]
if !ok {
ctx.SendChain(message.Text("ERROR: 未知类型 ", args))
return
}
gid := ctx.Event.GroupID
if gid == 0 {
gid = -ctx.Event.UserID
}
val := c.GetData(gid) & (^0x0f0000)
err := c.SetData(gid, val|(int64(typ&0x0f)<<16))
if err != nil {
ctx.SendChain(message.Text("ERROR: set data err: ", err))
return
}
ctx.SendChain(message.Text("成功"))
})
en.OnPrefix("设置AI聊天接口地址", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {
ctx.SendChain(message.Text("ERROR: empty args"))
return
}
api = args
err := os.WriteFile(af, []byte(args), 0644)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("成功"))
})
en.OnPrefix("设置AI聊天密钥", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
args := strings.TrimSpace(ctx.State["args"].(string))
if args == "" {

View File

@@ -156,7 +156,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text("全员自闭结束~"))
})
// 禁言
engine.OnMessage(zero.NewPattern().Text("^禁言").At().Text("(\\d+)\\s*(.*)").AsRule(), zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
engine.OnMessage(zero.NewPattern(nil).Text("^禁言").At().Text("(\\d+)\\s*(.*)").AsRule(), zero.OnlyGroup, zero.AdminPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
parsed := ctx.State[zero.KeyPattern].([]zero.PatternParsed)
duration := math.Str2Int64(parsed[2].Text()[1])
@@ -650,7 +650,7 @@ func init() { // 插件主体
if rsp.RetCode == 0 {
ctx.SendChain(message.Text(option, "成功"))
} else {
ctx.SendChain(message.Text(option, "失败, 信息: ", rsp.Msg, "解释: ", rsp.Wording))
ctx.SendChain(message.Text(option, "失败, 信息: ", rsp.Message, "解释: ", rsp.Wording))
}
})
engine.OnCommand("精华列表", zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
@@ -699,7 +699,7 @@ func init() { // 插件主体
if rsp.RetCode == 0 {
ctx.SendChain(message.Text("取消成功"))
} else {
ctx.SendChain(message.Text("取消失败, 信息: ", rsp.Msg, "解释: ", rsp.Wording))
ctx.SendChain(message.Text("取消失败, 信息: ", rsp.Message, "解释: ", rsp.Wording))
}
})
}

View File

@@ -308,7 +308,7 @@ func init() {
}
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg))
})
en.OnMessage(zero.NewPattern().Text(`^(?:.*使用(.*))??jj`).At().AsRule(),
en.OnMessage(zero.NewPattern(nil).Text(`^(?:.*使用(.*))??jj`).At().AsRule(),
zero.OnlyGroup).SetBlock(true).Limit(func(ctx *zero.Ctx) *rate.Limiter {
lt := jjLimiter.Load(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID))
ctx.State["jj_last_touch"] = lt.LastTouch()

View File

@@ -32,7 +32,7 @@ type favorability struct {
func init() {
// 好感度系统
engine.OnMessage(zero.NewPattern().Text(`^查好感度`).At().AsRule(), zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
engine.OnMessage(zero.NewPattern(nil).Text(`^查好感度`).At().AsRule(), zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
patternParsed := ctx.State[zero.KeyPattern].([]zero.PatternParsed)
fiancee, _ := strconv.ParseInt(patternParsed[1].At(), 10, 64)
@@ -49,7 +49,7 @@ func init() {
)
})
// 礼物系统
engine.OnMessage(zero.NewPattern().Text(`^买礼物给`).At().AsRule(), zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
engine.OnMessage(zero.NewPattern(nil).Text(`^买礼物给`).At().AsRule(), zero.OnlyGroup, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
uid := ctx.Event.UserID

View File

@@ -93,7 +93,7 @@ func init() {
ctx.SendChain(message.Text("设置成功"))
})
// 单身技能
engine.OnMessage(zero.NewPattern().Text(`^(娶|嫁)`).At().AsRule(), zero.OnlyGroup, getdb, checkSingleDog).SetBlock(true).Limit(ctxext.LimitByUser).
engine.OnMessage(zero.NewPattern(nil).Text(`^(娶|嫁)`).At().AsRule(), zero.OnlyGroup, getdb, checkSingleDog).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
uid := ctx.Event.UserID
@@ -168,7 +168,7 @@ func init() {
)
})
// NTR技能
engine.OnMessage(zero.NewPattern().Text(`^当`).At().Text(`的小三`).AsRule(), zero.OnlyGroup, getdb, checkMistress).SetBlock(true).Limit(ctxext.LimitByUser).
engine.OnMessage(zero.NewPattern(nil).Text(`^当`).At().Text(`的小三`).AsRule(), zero.OnlyGroup, getdb, checkMistress).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
uid := ctx.Event.UserID
@@ -254,7 +254,7 @@ func init() {
)
})
// 做媒技能
engine.OnMessage(zero.NewPattern().Text(`做媒`).At().At().AsRule(), zero.OnlyGroup, zero.AdminPermission, getdb, checkMatchmaker).SetBlock(true).Limit(ctxext.LimitByUser).
engine.OnMessage(zero.NewPattern(nil).Text(`做媒`).At().At().AsRule(), zero.OnlyGroup, zero.AdminPermission, getdb, checkMatchmaker).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
gid := ctx.Event.GroupID
uid := ctx.Event.UserID