Version: 0.4.1.dev.260304
feat: 💬 新增对话创建与上下文记忆机制 * 新增对话的创建与使用功能,实现会话级上下文隔离 * 实现上下文保存与传递机制,使模型具备持续对话记忆能力 * 引入滑动窗口策略控制上下文规模 * 当前窗口大小限制为 20 条消息,超过后自动丢弃最早消息以控制上下文长度 docs: 📝 更新示例配置文件 * 更新示例配置文件,新增 `agent` 相关配置信息 * 明确 Agent 模块运行所需参数,方便本地部署与环境初始化 undo: ⚠️ Agent 上下文读取性能待优化 * 当前测试中模型响应速度偏慢 * 计划后续将上下文暂存至缓存层,以减少读取与拼接开销并提升响应速度
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/cloudwego/eino-ext/components/model/ark"
|
"github.com/cloudwego/eino-ext/components/model/ark"
|
||||||
"github.com/cloudwego/eino/schema"
|
"github.com/cloudwego/eino/schema"
|
||||||
@@ -42,35 +43,44 @@ func ToOpenAIStream(chunk *schema.Message) (string, error) {
|
|||||||
return string(jsonBytes), nil
|
return string(jsonBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StreamChat(ctx context.Context, llm *ark.ChatModel, userInput string, outChan chan<- string) error {
|
func StreamChat(ctx context.Context, llm *ark.ChatModel, userInput string, chatHistory []*schema.Message, outChan chan<- string) (string, error) {
|
||||||
// 1. 组装消息
|
// 1. 组装消息
|
||||||
messages := []*schema.Message{
|
messages := make([]*schema.Message, 0)
|
||||||
schema.SystemMessage("你是一位时间管理大师兼日程安排专家兼个人助理,协助用户高效安排日程,优化时间利用率。"),
|
// A. 塞入 System Message (人设)
|
||||||
schema.UserMessage(userInput),
|
messages = append(messages, schema.SystemMessage(SystemPrompt))
|
||||||
|
// B. 塞入历史记录 (上下文)
|
||||||
|
if len(chatHistory) > 0 {
|
||||||
|
messages = append(messages, chatHistory...)
|
||||||
}
|
}
|
||||||
|
// C. 塞入用户当前的消息 (当前需求)
|
||||||
|
messages = append(messages, schema.UserMessage(userInput))
|
||||||
// 2. 调用流式接口
|
// 2. 调用流式接口
|
||||||
reader, err := llm.Stream(ctx, messages)
|
reader, err := llm.Stream(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer reader.Close() // 记得关闭 Reader
|
defer reader.Close() // 记得关闭 Reader
|
||||||
|
|
||||||
// 3. 循环读取直到结束
|
// 3. 循环读取直到结束
|
||||||
|
var fullText strings.Builder
|
||||||
for {
|
for {
|
||||||
chunk, err := reader.Recv()
|
chunk, err := reader.Recv()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break // 读取完成
|
break // 读取完成
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
if chunk.Content == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fullText.WriteString(chunk.Content)
|
||||||
// 将内容发送到通道中供前端消费
|
// 将内容发送到通道中供前端消费
|
||||||
retChuck, err := ToOpenAIStream(chunk)
|
retChuck, err := ToOpenAIStream(chunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
outChan <- retChuck
|
outChan <- retChuck
|
||||||
}
|
}
|
||||||
return nil
|
return fullText.String(), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,33 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SystemPrompt 全局系统人设:定义 SmartFlow 的基本调性
|
||||||
|
SystemPrompt = `你叫 SmartFlow,是专为重邮(CQUPT)学子打造的智能排程专家。
|
||||||
|
你的回复应当专业、干练,偶尔可以带一点程序员式的冷幽默。`
|
||||||
|
|
||||||
|
// SmartAssistantPrompt 合并了分诊与对话能力的超级提示词
|
||||||
|
SmartAssistantPrompt = `你叫 SmartFlow,是专为重邮(CQUPT)学子打造的智能排程专家。
|
||||||
|
### 你的双重职责:
|
||||||
|
1. **直接对话**:如果用户是闲聊、查询简单信息或进行通用问答,请直接以专业且幽默的口吻回复。
|
||||||
|
2. **决策路由**:如果用户提出需要“安排日程”、“解决冲突”或涉及“3D Atomic TimeGrid”的操作,请在回复中明确你的计划,并准备调用相应的排程工具。
|
||||||
|
### 核心约束:
|
||||||
|
- 始终保持对“稳扎稳打(Steady)模式”的敬畏,压缩率不得超过 15%。
|
||||||
|
- 针对重邮场景(如:红岩网校、南山教学楼)提供有温度的建议。
|
||||||
|
### 输出格式:
|
||||||
|
- 如果涉及排程工具调用,请先简要说明你的调整思路,再执行动作。`
|
||||||
|
|
||||||
|
// SchedulerPromptTemplate 排程专家 (Scheduler):核心算法 Agent
|
||||||
|
// 这里注入 3D Grid 和 Steady 模式的约束
|
||||||
|
SchedulerPromptTemplate = `你是一位精通“三维原子时间网格(3D Atomic TimeGrid)”的顶级排程架构师。
|
||||||
|
在处理用户的排程请求时,你必须遵循以下硬性逻辑约束:
|
||||||
|
1. 稳扎稳打(Steady)模式:任务步长(Step)的动态分配必须保守,压缩率严禁超过原始时长的 15%。
|
||||||
|
2. 逻辑空间投影(Logical Space Mapping):当发生时空重叠时,优先尝试在逻辑向量维度平移,而非直接删除冲突任务。
|
||||||
|
3. 冲突自愈:若发现网格冲突,请主动提出“缩放任务块”或“重新锚定时间点”的自愈方案。
|
||||||
|
|
||||||
|
请以极其严谨的态度处理每一秒钟的分配。`
|
||||||
|
|
||||||
|
// DefaultPromptTemplate 通用助手 (Assistant):也就是你之前占位的那个
|
||||||
|
DefaultPromptTemplate = `你是一位时间管理大师、日程安排专家兼个人助理。
|
||||||
|
你的目标是协助用户高效安排日程。请确保你的回答简洁明了,直接针对用户的需求进行回复。
|
||||||
|
如果用户提到重邮(CQUPT)相关内容(如:南山、红岩网校、卓越工程师班),请表现出你的亲切感。`
|
||||||
|
)
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ func (api *AgentHandler) ChatAgent(c *gin.Context) {
|
|||||||
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
c.JSON(http.StatusBadRequest, respond.WrongParamType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
userID := c.GetInt("user_id") // 从上下文中获取用户 ID
|
||||||
// 3. 调用 Service 层的聊天方法,获取输出通道和错误通道
|
// 3. 调用 Service 层的聊天方法,获取输出通道和错误通道
|
||||||
outChan, errChan := api.svc.AgentChat(c.Request.Context(), req.Message)
|
outChan, errChan := api.svc.AgentChat(c.Request.Context(), req.Message, userID, req.ConversationID)
|
||||||
// 4. 循环转发消息/错误
|
// 4. 循环转发消息/错误
|
||||||
c.Stream(func(w io.Writer) bool {
|
c.Stream(func(w io.Writer) bool {
|
||||||
select {
|
select {
|
||||||
|
|||||||
@@ -60,13 +60,14 @@ func Start() {
|
|||||||
taskClassRepo := dao.NewTaskClassDAO(db)
|
taskClassRepo := dao.NewTaskClassDAO(db)
|
||||||
scheduleRepo := dao.NewScheduleDAO(db)
|
scheduleRepo := dao.NewScheduleDAO(db)
|
||||||
manager := dao.NewManager(db)
|
manager := dao.NewManager(db)
|
||||||
|
agentRepo := dao.NewAgentDAO(db)
|
||||||
//service 层
|
//service 层
|
||||||
userService := service.NewUserService(userRepo, cacheRepo)
|
userService := service.NewUserService(userRepo, cacheRepo)
|
||||||
taskSv := service.NewTaskService(taskRepo, cacheRepo)
|
taskSv := service.NewTaskService(taskRepo, cacheRepo)
|
||||||
courseService := service.NewCourseService(courseRepo, scheduleRepo)
|
courseService := service.NewCourseService(courseRepo, scheduleRepo)
|
||||||
taskClassService := service.NewTaskClassService(taskClassRepo, cacheRepo, scheduleRepo, manager)
|
taskClassService := service.NewTaskClassService(taskClassRepo, cacheRepo, scheduleRepo, manager)
|
||||||
scheduleService := service.NewScheduleService(scheduleRepo, userRepo, taskClassRepo, manager, cacheRepo)
|
scheduleService := service.NewScheduleService(scheduleRepo, userRepo, taskClassRepo, manager, cacheRepo)
|
||||||
agentService := service.NewAgentService(aiHub)
|
agentService := service.NewAgentService(aiHub, agentRepo)
|
||||||
//api 层
|
//api 层
|
||||||
userApi := api.NewUserHandler(userService)
|
userApi := api.NewUserHandler(userService)
|
||||||
taskApi := api.NewTaskHandler(taskSv)
|
taskApi := api.NewTaskHandler(taskSv)
|
||||||
|
|||||||
@@ -36,4 +36,9 @@ redis:
|
|||||||
time:
|
time:
|
||||||
zone: "Asia/Shanghai"
|
zone: "Asia/Shanghai"
|
||||||
semesterStartDate: "2026-03-02" #学期开始日期,一定要设定为周一,以便于计算周数
|
semesterStartDate: "2026-03-02" #学期开始日期,一定要设定为周一,以便于计算周数
|
||||||
semesterEndDate: "2026-07-19" #学期结束日期,一定要设定为周日,确保最后一周完整
|
semesterEndDate: "2026-07-19" #学期结束日期,一定要设定为周日,确保最后一周完整
|
||||||
|
|
||||||
|
agent:
|
||||||
|
workerModel: "doubao-seed-1-6-lite-251015" # 智能体使用的Worker模型,需根据实际情况调整
|
||||||
|
strategistModel: "deepseek-v3-2-251201" # 策略师使用的Worker模型,需根据实际情况调整
|
||||||
|
baseURL: "https://ark.cn-beijing.volces.com/api/v3" # Worker服务的基础URL,需根据实际情况调整
|
||||||
27
backend/conv/agent.go
Normal file
27
backend/conv/agent.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package conv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/LoveLosita/smartflow/backend/model"
|
||||||
|
"github.com/cloudwego/eino/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToEinoMessages 将数据库模型转换为 Eino 模型
|
||||||
|
func ToEinoMessages(dbMsgs []model.ChatHistory) []*schema.Message {
|
||||||
|
res := make([]*schema.Message, 0)
|
||||||
|
for _, m := range dbMsgs {
|
||||||
|
var role schema.RoleType
|
||||||
|
switch *m.Role {
|
||||||
|
case "user":
|
||||||
|
role = schema.User
|
||||||
|
case "assistant":
|
||||||
|
role = schema.Assistant
|
||||||
|
default:
|
||||||
|
role = schema.System
|
||||||
|
}
|
||||||
|
res = append(res, &schema.Message{
|
||||||
|
Role: role,
|
||||||
|
Content: *m.MessageContent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
64
backend/dao/agent.go
Normal file
64
backend/dao/agent.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package dao
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/LoveLosita/smartflow/backend/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AgentDAO struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentDAO(db *gorm.DB) *AgentDAO {
|
||||||
|
return &AgentDAO{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AgentDAO) SaveChatHistory(ctx context.Context, userID int, conversationID string, role, message string) error {
|
||||||
|
userChat := model.ChatHistory{
|
||||||
|
UserID: userID,
|
||||||
|
MessageContent: &message,
|
||||||
|
Role: &role,
|
||||||
|
ChatID: conversationID,
|
||||||
|
}
|
||||||
|
if err := a.db.WithContext(ctx).Create(&userChat).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AgentDAO) CreateNewChat(userID int, chatID string) (int64, error) {
|
||||||
|
chat := model.AgentChat{
|
||||||
|
ChatID: chatID,
|
||||||
|
UserID: userID,
|
||||||
|
MessageCount: 0,
|
||||||
|
LastMessageAt: nil,
|
||||||
|
}
|
||||||
|
if err := a.db.Create(&chat).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return chat.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AgentDAO) GetUserChatHistories(ctx context.Context, userID, limit int, chatID string) ([]model.ChatHistory, error) {
|
||||||
|
var histories []model.ChatHistory
|
||||||
|
err := a.db.WithContext(ctx).Where("user_id = ? AND chat_id = ?", userID, chatID).Order("created_at desc").Limit(limit).Find(&histories).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return histories, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AgentDAO) IfChatExists(ctx context.Context, userID int, chatID string) (bool, error) {
|
||||||
|
var chat model.AgentChat
|
||||||
|
err := a.db.WithContext(ctx).Where("user_id = ? AND chat_id = ?", userID, chatID).First(&chat).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return false, nil // 没有找到记录,表示会话不存在
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type UserSendMessageRequest struct {
|
type UserSendMessageRequest struct {
|
||||||
ConversationID int `json:"conversation_id,omitempty"` // 可选,指定对话 ID
|
ConversationID string `json:"conversation_id,omitempty"` // 可选,指定对话 ID
|
||||||
Message string `json:"message" binding:"required"`
|
Message string `json:"message" binding:"required"`
|
||||||
Model string `json:"model,omitempty"` // 可选,指定使用的模型
|
Model string `json:"model,omitempty"` // 可选,指定使用的模型
|
||||||
}
|
}
|
||||||
@@ -17,3 +19,38 @@ type SSEMessageData struct {
|
|||||||
Step int `json:"step,omitempty"`
|
Step int `json:"step,omitempty"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AgentChat struct {
|
||||||
|
ID int64 `gorm:"column:id;primaryKey;autoIncrement;comment:自增ID"`
|
||||||
|
ChatID string `gorm:"column:chat_id;type:varchar(36);not null;uniqueIndex:uk_chat_id;comment:会话ID,UUID格式"`
|
||||||
|
UserID int `gorm:"column:user_id;not null;index:idx_user_last,priority:1;index:idx_user_status,priority:1;comment:所属用户"`
|
||||||
|
Title *string `gorm:"column:title;type:varchar(255);comment:会话标题"`
|
||||||
|
SystemPrompt *string `gorm:"column:system_prompt;type:text;comment:可选:系统提示词/会话级上下文"`
|
||||||
|
Model *string `gorm:"column:model;type:varchar(100);comment:可选:使用的模型标识"`
|
||||||
|
MessageCount int `gorm:"column:message_count;not null;default:0;comment:消息数(可冗余)"`
|
||||||
|
TokensTotal int `gorm:"column:tokens_total;not null;default:0;comment:累计消耗(可冗余)"`
|
||||||
|
LastMessageAt *time.Time `gorm:"column:last_message_at;comment:最后一条消息时间"`
|
||||||
|
Status string `gorm:"column:status;type:varchar(32);not null;default:active;index:idx_user_status,priority:2;comment:active/archived"`
|
||||||
|
CreatedAt *time.Time `gorm:"column:created_at;autoCreateTime"`
|
||||||
|
UpdatedAt *time.Time `gorm:"column:updated_at;autoUpdateTime"`
|
||||||
|
DeletedAt *time.Time `gorm:"column:deleted_at;comment:软删除"`
|
||||||
|
// 关联:一个会话有多条消息
|
||||||
|
Chats []ChatHistory `gorm:"foreignKey:ChatID;references:ChatID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AgentChat) TableName() string { return "agent_chats" }
|
||||||
|
|
||||||
|
type ChatHistory struct {
|
||||||
|
ID int `gorm:"column:id;primaryKey;autoIncrement"`
|
||||||
|
ChatID string `gorm:"column:chat_id;type:varchar(36);not null;index:idx_user_chat,priority:2;index:idx_chat_id;comment:对话UUID"`
|
||||||
|
UserID int `gorm:"column:user_id;not null;index:idx_user_chat,priority:1"`
|
||||||
|
MessageContent *string `gorm:"column:message_content;type:text;comment:用户或AI的话"`
|
||||||
|
Role *string `gorm:"column:role;type:varchar(32);comment:user / assistant"`
|
||||||
|
TokensConsumed int `gorm:"column:tokens_consumed;not null;default:0;comment:单次消耗,用于累加到 users 表"`
|
||||||
|
CreatedAt *time.Time `gorm:"column:created_at;autoCreateTime"`
|
||||||
|
|
||||||
|
// 可选:回挂会话(按 chat_id -> chat_histories.chat_id)
|
||||||
|
ChatHistory AgentChat `gorm:"foreignKey:ChatID;references:ChatID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ChatHistory) TableName() string { return "chat_histories" }
|
||||||
|
|||||||
@@ -4,28 +4,77 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/LoveLosita/smartflow/backend/agent"
|
"github.com/LoveLosita/smartflow/backend/agent"
|
||||||
|
"github.com/LoveLosita/smartflow/backend/conv"
|
||||||
|
"github.com/LoveLosita/smartflow/backend/dao"
|
||||||
"github.com/LoveLosita/smartflow/backend/inits"
|
"github.com/LoveLosita/smartflow/backend/inits"
|
||||||
|
"github.com/cloudwego/eino/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AgentService struct {
|
type AgentService struct {
|
||||||
AIHub *inits.AIHub
|
AIHub *inits.AIHub
|
||||||
|
repo *dao.AgentDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgentService(aiHub *inits.AIHub) *AgentService {
|
func NewAgentService(aiHub *inits.AIHub, repo *dao.AgentDAO) *AgentService {
|
||||||
return &AgentService{
|
return &AgentService{
|
||||||
AIHub: aiHub,
|
AIHub: aiHub,
|
||||||
|
repo: repo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *AgentService) AgentChat(ctx context.Context, userMessage string) (<-chan string, <-chan error) {
|
func (s *AgentService) AgentChat(ctx context.Context, userMessage string, userID int, chatID string) (<-chan string, <-chan error) {
|
||||||
//1. 创建一个输出通道
|
//1. 创建一个输出通道
|
||||||
outChan := make(chan string, 5)
|
outChan := make(chan string, 5)
|
||||||
errChan := make(chan error)
|
errChan := make(chan error, 1)
|
||||||
//2. 启动一个 goroutine 来处理聊天逻辑
|
//2. 先确保这个会话存在(如果不存在就创建一个新的)
|
||||||
|
result, err := s.repo.IfChatExists(ctx, userID, chatID)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
close(outChan)
|
||||||
|
close(errChan)
|
||||||
|
return outChan, errChan
|
||||||
|
}
|
||||||
|
var chatHistory []*schema.Message
|
||||||
|
if result {
|
||||||
|
//4. 提取出历史消息,构建上下文
|
||||||
|
//先从数据库拿到历史消息
|
||||||
|
histories, err := s.repo.GetUserChatHistories(ctx, userID, 20, chatID)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
close(outChan)
|
||||||
|
close(errChan)
|
||||||
|
return outChan, errChan
|
||||||
|
}
|
||||||
|
//再转换成 Eino 的消息格式
|
||||||
|
chatHistory = conv.ToEinoMessages(histories)
|
||||||
|
} else {
|
||||||
|
//如果会话不存在,先创建一个新的会话
|
||||||
|
_, err := s.repo.CreateNewChat(userID, chatID)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
close(outChan)
|
||||||
|
close(errChan)
|
||||||
|
return outChan, errChan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//3. 将用户消息落库
|
||||||
|
err = s.repo.SaveChatHistory(ctx, userID, chatID, "user", userMessage)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
close(outChan)
|
||||||
|
close(errChan)
|
||||||
|
return outChan, errChan
|
||||||
|
}
|
||||||
|
//5. 启动一个 goroutine 来处理聊天逻辑
|
||||||
go func() {
|
go func() {
|
||||||
defer close(outChan) // 确保在函数结束时关闭通道
|
defer close(outChan) // 确保在函数结束时关闭通道
|
||||||
//3. 调用 StreamChat 函数进行流式聊天
|
//3. 调用 StreamChat 函数进行流式聊天
|
||||||
err := agent.StreamChat(ctx, s.AIHub.Worker, userMessage, outChan)
|
fullText, err := agent.StreamChat(ctx, s.AIHub.Worker, userMessage, chatHistory, outChan)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.repo.SaveChatHistory(ctx, userID, chatID, "assistant", fullText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
return
|
return
|
||||||
@@ -33,3 +82,7 @@ func (s *AgentService) AgentChat(ctx context.Context, userMessage string) (<-cha
|
|||||||
}()
|
}()
|
||||||
return outChan, errChan
|
return outChan, errChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *AgentService) CreateNewChat(userID int, chatID string) (int64, error) {
|
||||||
|
return s.repo.CreateNewChat(userID, chatID)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user