117 lines
2.9 KiB
Go
117 lines
2.9 KiB
Go
package llm_factory
|
||
|
||
import (
|
||
"catface/app/global/errcode"
|
||
"time"
|
||
|
||
"github.com/yankeguo/zhipu"
|
||
)
|
||
|
||
// INFO 维护 GLM Client 与用户之间的客户端消息队列,也就是在 "github.com/yankeguo/zhipu" 的基础上实现一层封装。
|
||
|
||
type GlmClientHub struct {
|
||
MaxIdle int
|
||
MaxActive int
|
||
ApiKey string
|
||
DefaultModelName string
|
||
InitPrompt string
|
||
Clients map[string]*ClientInfo
|
||
LifeTime time.Duration
|
||
}
|
||
|
||
type ClientInfo struct {
|
||
Client *zhipu.ChatCompletionService
|
||
LastUsed time.Time
|
||
}
|
||
|
||
func InitGlmClientHub(maxIdle, maxActive, lifetime int, apiKey, defaultModelName, initPrompt string) *GlmClientHub {
|
||
hub := &GlmClientHub{
|
||
MaxIdle: maxIdle,
|
||
MaxActive: maxActive,
|
||
ApiKey: apiKey,
|
||
DefaultModelName: defaultModelName,
|
||
InitPrompt: initPrompt,
|
||
Clients: make(map[string]*ClientInfo),
|
||
LifeTime: time.Duration(lifetime) * time.Second,
|
||
}
|
||
go hub.cleanupRoutine() // 启动定时器清理过期会话。
|
||
return hub
|
||
}
|
||
|
||
const (
|
||
GlmModeSimple = iota
|
||
GlmModeKnowledgeHub
|
||
)
|
||
|
||
/**
|
||
* @description: 鉴权用户之后,根据其 ID 来从 map池 里获取之前的连接。
|
||
* // UPDATE 现在只是单用户单连接(也就是只支持“同时只有一个对话”),之后可以考虑扩展【消息队列】的封装方式。
|
||
* 默认启用的是 没有预设的 prompt 的空。
|
||
* @param {string} token: // TODO 如何在 token 中保存信息?
|
||
* @return {*}
|
||
*/
|
||
func (g *GlmClientHub) GetOneGlmClient(token string, mode int) (client *zhipu.ChatCompletionService, code int) {
|
||
if info, ok := g.Clients[token]; ok {
|
||
info.LastUsed = time.Now() // INFO 刷新生命周期
|
||
return info.Client, 0
|
||
}
|
||
|
||
// 空闲数检查
|
||
if g.MaxIdle > 0 {
|
||
g.MaxIdle -= 1
|
||
} else {
|
||
code = errcode.ErrGlmBusy
|
||
return
|
||
}
|
||
|
||
// Client Init
|
||
preClient, err := zhipu.NewClient(zhipu.WithAPIKey(g.ApiKey))
|
||
if err != nil {
|
||
code = errcode.ErrGlmNewClientFail
|
||
return
|
||
}
|
||
client = preClient.ChatCompletion(g.DefaultModelName)
|
||
|
||
if mode == GlmModeKnowledgeHub {
|
||
client.AddMessage(zhipu.ChatCompletionMessage{
|
||
Role: zhipu.RoleSystem, // TIP 使用 System 角色来初始化对话
|
||
Content: g.InitPrompt,
|
||
})
|
||
}
|
||
|
||
g.Clients[token] = &ClientInfo{
|
||
Client: client,
|
||
LastUsed: time.Now(),
|
||
}
|
||
return
|
||
}
|
||
|
||
// cleanupRoutine 定期检查并清理超过 1 小时未使用的 Client
|
||
func (g *GlmClientHub) cleanupRoutine() {
|
||
ticker := time.NewTicker(10 * time.Minute)
|
||
for range ticker.C {
|
||
g.cleanupClients()
|
||
}
|
||
}
|
||
|
||
// cleanupClients 清理超过 1 小时未使用的 Client
|
||
func (g *GlmClientHub) cleanupClients() {
|
||
now := time.Now()
|
||
for token, info := range g.Clients {
|
||
if now.Sub(info.LastUsed) > g.LifeTime {
|
||
delete(g.Clients, token)
|
||
g.MaxIdle += 1
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @description: 显式地释放资源。
|
||
* @param {string} token
|
||
* @return {*}
|
||
*/
|
||
func (g *GlmClientHub) ReleaseOneGlmClient(token string) {
|
||
delete(g.Clients, token)
|
||
g.MaxIdle += 1
|
||
}
|