2024-11-19 02:22:39 +08:00
|
|
|
|
package llm_factory
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"catface/app/global/errcode"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/yankeguo/zhipu"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// INFO 维护 GLM Client 与用户之间的客户端消息队列,也就是在 "github.com/yankeguo/zhipu" 的基础上实现一层封装。
|
|
|
|
|
|
|
|
|
|
type GlmClientHub struct {
|
2024-11-20 08:50:22 +08:00
|
|
|
|
Idle int // 最大连接数
|
|
|
|
|
Active int
|
2024-11-19 02:22:39 +08:00
|
|
|
|
ApiKey string
|
|
|
|
|
DefaultModelName string
|
|
|
|
|
InitPrompt string
|
|
|
|
|
Clients map[string]*ClientInfo
|
|
|
|
|
LifeTime time.Duration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ClientInfo struct {
|
2024-11-19 03:21:28 +08:00
|
|
|
|
Client *zhipu.ChatCompletionService
|
|
|
|
|
UserQuerys []string
|
|
|
|
|
LastUsed time.Time
|
2024-11-19 02:22:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func InitGlmClientHub(maxIdle, maxActive, lifetime int, apiKey, defaultModelName, initPrompt string) *GlmClientHub {
|
|
|
|
|
hub := &GlmClientHub{
|
2024-11-20 08:50:22 +08:00
|
|
|
|
Idle: maxIdle,
|
|
|
|
|
Active: maxActive,
|
2024-11-19 02:22:39 +08:00
|
|
|
|
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 {*}
|
|
|
|
|
*/
|
2024-11-19 03:21:28 +08:00
|
|
|
|
func (g *GlmClientHub) GetOneGlmClientInfo(token string, mode int) (clientInfo *ClientInfo, code int) {
|
2024-11-19 02:22:39 +08:00
|
|
|
|
if info, ok := g.Clients[token]; ok {
|
|
|
|
|
info.LastUsed = time.Now() // INFO 刷新生命周期
|
2024-11-19 03:21:28 +08:00
|
|
|
|
return info, 0
|
2024-11-19 02:22:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 空闲数检查
|
2024-11-20 08:50:22 +08:00
|
|
|
|
if g.Idle > 0 {
|
|
|
|
|
g.Idle -= 1
|
2024-11-19 02:22:39 +08:00
|
|
|
|
} else {
|
|
|
|
|
code = errcode.ErrGlmBusy
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Client Init
|
|
|
|
|
preClient, err := zhipu.NewClient(zhipu.WithAPIKey(g.ApiKey))
|
|
|
|
|
if err != nil {
|
|
|
|
|
code = errcode.ErrGlmNewClientFail
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-11-19 03:21:28 +08:00
|
|
|
|
client := preClient.ChatCompletion(g.DefaultModelName)
|
2024-11-19 02:22:39 +08:00
|
|
|
|
|
|
|
|
|
if mode == GlmModeKnowledgeHub {
|
|
|
|
|
client.AddMessage(zhipu.ChatCompletionMessage{
|
|
|
|
|
Role: zhipu.RoleSystem, // TIP 使用 System 角色来初始化对话
|
|
|
|
|
Content: g.InitPrompt,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-19 03:21:28 +08:00
|
|
|
|
clientInfo = &ClientInfo{
|
2024-11-19 02:22:39 +08:00
|
|
|
|
Client: client,
|
|
|
|
|
LastUsed: time.Now(),
|
|
|
|
|
}
|
2024-11-19 03:21:28 +08:00
|
|
|
|
g.Clients[token] = clientInfo
|
2024-11-19 02:22:39 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-19 03:21:28 +08:00
|
|
|
|
/**
|
|
|
|
|
* @description: 获取并返回 ClientInfo 的 Client 和 code。
|
|
|
|
|
* @param {string} token
|
|
|
|
|
* @param {int} mode
|
|
|
|
|
* @return {(*zhipu.ChatCompletionService, int)}
|
|
|
|
|
*/
|
|
|
|
|
func (g *GlmClientHub) GetOneGlmClient(token string, mode int) (*zhipu.ChatCompletionService, int) {
|
|
|
|
|
clientInfo, code := g.GetOneGlmClientInfo(token, mode)
|
|
|
|
|
if clientInfo == nil || code != 0 {
|
|
|
|
|
return nil, code
|
|
|
|
|
}
|
|
|
|
|
return clientInfo.Client, code
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-19 02:22:39 +08:00
|
|
|
|
// 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)
|
2024-11-20 08:50:22 +08:00
|
|
|
|
g.Idle += 1
|
2024-11-19 02:22:39 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @description: 显式地释放资源。
|
|
|
|
|
* @param {string} token
|
|
|
|
|
* @return {*}
|
|
|
|
|
*/
|
2024-11-20 08:50:22 +08:00
|
|
|
|
func (g *GlmClientHub) ReleaseOneGlmClient(token string) bool {
|
|
|
|
|
if _, exists := g.Clients[token]; exists {
|
|
|
|
|
delete(g.Clients, token)
|
|
|
|
|
g.Idle += 1
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
2024-11-19 02:22:39 +08:00
|
|
|
|
}
|
2024-11-19 03:21:28 +08:00
|
|
|
|
|
|
|
|
|
// TAG ClientInfo
|
|
|
|
|
func (c *ClientInfo) AddQuery(query string) {
|
|
|
|
|
c.UserQuerys = append(c.UserQuerys, query)
|
|
|
|
|
}
|