1.LLMManager的实现:

可以看到我们测试的时候要调用大模型的时候会很麻烦 所以现在我们实现一个管理模型的方法

    1.头文件的实现:

#pragma once
#include <map>
#include <memory>
#include <string>
#include <functional>
#include "LLMProvider.h"
namespace ai_chat_sdk{
            // LLM 管理
class LLMManager{
            public:
            // 注册 LLM 提供者
            bool registerProvider(const std::string& name,std::unique_ptr<LLMProvider> provider);
            // 初始化指定模型
            bool initModel(const std::string& modelName, const std::map<std::string,std::string>& modelParam);
            // 获取可⽤模型列表
            std::vector<ModelInfo> getAvailableModels()const;
            // 检查模型是否可⽤
            bool isModelAvailable(const std::string& modelName)const;
            // 发送消息到指定模型
            std::string sendMessage(const std::string& modelName,const std::vector<Message>& messages,const std::map<std::string, std::string>&requestParam);
            // 发送消息到指定模型,流式响应
            std::string sendMessageStream(const std::string& modelName,const std::vector<Message>& messages,const std::map<std::string, std::string>& requestParam,std::function<void(const std::string&, bool)> callback);
            private:
            //key 模型的名称 value 模型提供器
            std::map<std::string, std::unique_ptr<LLMProvider>> _providers;
            //key 模型的名称 value 模型信息
            std::map<std::string, ModelInfo> _modelInfos;
        };
} // end ai_chat_sdk

2.源文件的实现:

#include "../include/LLMManger.h"
#include"../include/util/myLog.h"
namespace ai_chat_sdk{
// 注册 LLM 提供者
bool LLMManager::registerProvider(const std::string& modelName,std::unique_ptr<LLMProvider> provider)
        {
            // 参数检测
            if(!provider){
            ERR("Cannot register null provider,modelName={}",modelName);
            return false;
            }
            // 注意,unique_ptr是防拷⻉的,此处只能通过move的⽅式将资源转移给当前对象
            // 因此,provider在设置的时候,设置成⼀个临时变量
            _providers[modelName] = std::move(provider);
            // 添加模型信息
            _modelInfos[modelName] = ModelInfo(modelName);
            // 模型注册成功
            INFO("Register LLM Provider,modelName : {}", modelName);
            return true;
        }
    // 初始化指定模型
    bool LLMManager::initModel(const std::string& modelName, const std::map<std::string, std::string>& modelParam)
    {
            // 检测模型是否注册
        auto it = _providers.find(modelName);
        if(it == _providers.end()){
            ERR("Model {} is not registered!!!", modelName);
            return false;
        }   
         // 模型已经注册过了,可以进⾏初始化
        bool isSuccess = it->second->initModel(modelParam);
        if(isSuccess){
        INFO("Model {} init success!!!", modelName);
        _modelInfos[modelName]._modelDesc = it->second->getModelDesc();
        _modelInfos[modelName]._isAvailable = true;
        }else{
        INFO("Model {} init Failed!!!", modelName);
        _modelInfos[modelName]._isAvailable = false;
        }
    return isSuccess;
    }
// 获取可⽤模型列表
std::vector<ModelInfo> LLMManager::getAvailableModels()const
{
        // 从注册的模型列表中筛选出所有可⽤的模型
        std::vector<ModelInfo> models;
        for(const auto& pair : _modelInfos){
            if(pair.second._isAvailable){
                models.push_back(pair.second);
            }
        }
        return models;
}

// 检查模型是否可⽤
bool LLMManager::isModelAvailable(const std::string& modelName)const
{
    auto it= _modelInfos.find(modelName);//
    return it != _modelInfos.end() && it->second._isAvailable;//返回模型是否可⽤的结果
}

//发送消息给指定模型 全量返回模型回复
//第一个参数:模型名称
//第二个参数:消息列表
//第三个参数:请求参数,如api key、max_tokens等
std::string LLMManager::sendMessage(const std::string& modelName,const std::vector<Message>& messages, const std::map<std::string, std::string>& requestParam)
{
    // 检测模型是否注册
    auto it = _providers.find(modelName);
    if(it == _providers.end()){
        ERR("Model {} is not registered!!!", modelName);
        return "";
    }

    // 检测模型是否可⽤
    if(!isModelAvailable(modelName)){
        ERR("Model {} is not available!!!", modelName);
        return "";
    }
    // 模型已注册,调⽤模型提供方发送消息
    return it->second->sendMessage(messages,requestParam);
}

// 发送消息到指定模型,流式响应
//第一个参数:模型名称
//第二个参数:消息列表
//第三个参数:请求参数,如api key、max_tokens等
//第四个参数:回调函数,用于处理流式响应
std::string LLMManager::sendMessageStream(const std::string& modelName,
                                        const std::vector<Message>& messages,
                                        const std::map<std::string, std::string>& requestParam,
                                        std::function<void(const std::string&, bool)> callback)
{
  // 检测模型是否注册
    auto it = _providers.find(modelName);
    if(it == _providers.end()){
        ERR("Model {} is not registered!!!", modelName);
        return "";
    }

    // 检测模型是否可⽤
    if(!isModelAvailable(modelName)){
        ERR("Model {} is not available!!!", modelName);
        return "";
    }
    return it->second->sendMessageStream(messages,requestParam,callback);//调⽤模型提供方发送消息
}



}//end namespace

其实这些实现就是封装一个大的类 如果你想调用其他的大模型 就指定调用哪个类就行了 没必要像测试那样一个个去调用。

2.会话管理:

      1.介绍会话:

因为我们要封装一个智能聊天助手 但是现在仅仅只靠这个大模型管理是远远不行的:

我们在最开始的common文件中就封装了这个会话结构

   2.会话管理的实现:

下面就是我们需要实现会话的方法内容:

    1.头文件的实现:

#pragma once
#include "common.h"
#include<unordered_map>
#include<mutex>
#include<atomic>
#include<memory>



namespace ai_chat_sdk{
    class SessionManager{
        public:
        // 创建会话 提供模型名称
        std::string createSession(const std::string& modelName);
        // 获取会话信息
        std::shared_ptr<SessionInfo> getSession(const std::string& sessionId)const;
        //往某个会话添加消息
        void addMessage(const std::string& sessionId,const Message& message);
        //获取某个会话的历史消息
        std::vector<Message> getHistoryMessages(const std::string& sessionId)const;
        //更新会话的时间戳
        void updateSessionTimestamp(const std::string& sessionId);
        //获取所有的会话列表
        std::vector<std::string> getSessionLists()const;
        //删除某个会话
        bool deleteSession(const std::string& sessionId);
        //清空所有会话
        void clearAllSessions();
        //获取会话总数
        size_t getSessionCount()const;
        private:
           std::string generateSessionId();//生成会话id
           std::string generateMessageId(size_t messageCounter);//生成消息id 当前会话的消息计数
        private:
             // 会话管理器,用于管理所有会话  key:会话id  value:会话信息
            std::unordered_map<std::string, std::shared_ptr<SessionInfo>> _sessions;
            mutable std::mutex _mutex;// 互斥锁,用于保护会话映射的访问 mutable 用于在const成员中修改会话映射 保护会话映射的线程安全访问
            std::atomic<int64_t> _sessionCounter={0};// 记录所有会话的总数  原子操作,确保线程安全
    };
}

2.源文件的实现:

   1.消息id和会话id的实现:

  2.创建会话:

根据这个结构来创建会话

因为考虑到会话可能比较大 我们用智能指针封装 让他在堆上:

3.往某个会话添加消息:

//往某个会话添加消息
    bool SessionManager::addMessage(const std::string& sessionId,const Message& message){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时添加消息
        //获取对应的会话信息
        auto it = _sessions.find(sessionId);//通过会话id查找会话
        if(it == _sessions.end()){
            return false;
        }
        //创建消息
        Message msg(message._role,message._content);
        //获得历史消息
        msg._messageid = generateMessageId(it->second->_messages.size());//根据当前会话历史消息的大小生成消息id
        // 添加消息到会话历史消息
        it->second->_messages.push_back(msg);
        // 更新会话时间戳
        it->second->_updatedAt = std::time(nullptr);
        // 会话更新成功
        INFO("add message success, session id: {}, message.content: {}",sessionId,msg._content);//打印添加的消息内容 和会话id
        // 返回true
        return true;
    }

4.获取某个会话的历史消息:

//获取某个会话的历史消息
    std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId)const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话历史消息
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            return {};
        }
        // 返回会话历史消息
        return it->second->_messages;
    }
5.更新会话的时间戳:
//更新会话的时间戳
    void SessionManager::updateSessionTimestamp(const std::string& sessionId){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时更新会话时间戳
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            ERR("Session {} not found!!!",sessionId);
            return;
        }
        // 更新会话时间戳
        it->second->_updatedAt = std::time(nullptr);
        // 会话更新成功
        INFO("update session timestamp success, session id: {}",sessionId);//打印会话id
    }
6.获取所有会话的列表:

我们用string存储的原因是 其实实际返回的是所有会话的会话id

其实返回指针地址没有用 你返回id  如果不是在同一台电脑使用的话 返回地址没有用

那能否直接把这个会话对象返回呢?

还要注意返回的这些会话id中,最好按照时间戳进行降序排序 拿到最新的会话

//获取所有的会话列表
    std::vector<std::string> SessionManager::getSessionLists()const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话列表
        //构建一个临时对话列表,将其内容的会话按照更新的时间降序排序
        std::vector<std::pair<std::time_t,std::shared_ptr<SessionInfo>>> temp;//临时会话列表中存储会话时间戳和会话对象
        temp.reserve(_sessions.size());//分配内存  避免重复分配内存

        //将会话添加到临时会话列表中
        for(const auto& pair : _sessions){
            temp.emplace_back(pair.second->_updatedAt,pair.second);//将会话时间戳和会话对象添加到临时会话列表中   
        }
        
        // 对临时会话列表进行排序                                  auto转化前的类std::pair<std::time_t,std::shared_ptr<SessionInfo>>
        std::sort(temp.begin(),temp.end(),[](const auto& a,const auto& b){//利用lambda表达式对临时会话列表进行排序
            return a.first > b.first;//按照会话时间戳降序排序
        });
        
        std::vector<std::string> sessionIds;//存储排序后的会话id
        sessionIds.reserve(_sessions.size());//分配内存  避免重复分配内存
        // 提取排序后的会话id
        for(const auto& pair : temp){
            sessionIds.push_back(pair.second->_sessionid);
        }
        // 返回排序后的会话id
        return sessionIds;
    }

7.其他功能的实现:

8.整个代码预览:

#include "../include/SessionManager.h"
#include "../include/util/myLog.h"
#include<sstream>
#include<iomanip>

namespace ai_chat_sdk{
    //生成会话id 会话id的格式:session_时间戳_会话计数器
    std::string SessionManager::generateSessionId(){
        //会话计数器自增
        _sessionCounter.fetch_add(1);
        //获取当前时间戳
        std::time_t time = std::time(nullptr);
        //拼接在一起生成会话id  
        std::ostringstream os;
        os << "session_" << time << "_" << std::setw(8)  << _sessionCounter;//setw(8) 表示会话计数器占8个字符宽度,不足8个字符用0填充
        std::string sessionId = os.str();//将ostringstream中的内容转换为字符串
        //返回会话id
        return sessionId;
    }
    //生成消息id 消息id的格式:消息计数器_时间戳_会话id_消息计数器
    std::string SessionManager::generateMessageId(size_t messageCounter){
        messageCounter++;//消息计数器自增
        std::time_t time = std::time(nullptr);
        
        //构造消息内容
        std::ostringstream os;
        os << "msg_" << time << "_" << std::setw(8)  << _sessionCounter;//消息计数器_时间戳_会话id_消息计数器
        std::string messageId = os.str();//将ostringstream中的内容转换为字符串
        //返回消息id
        return messageId;
    }
    //创建会话
    std::string SessionManager::createSession(const std::string& modelName){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时创建会话
        // 生成新的会话id
        std::string sessionId = generateSessionId();
        // 创建新的会话 
        auto session = std::make_shared<SessionInfo>(modelName);//初始化会话消息
        session->_sessionid = sessionId;
       
        //加入到会话列表
        _sessions[sessionId] = session;//将会话信息加入到会话管理器中
        return sessionId;
    }
    //获取会话信息
    std::shared_ptr<SessionInfo> SessionManager::getSession(const std::string& sessionId)const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话信息
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);//通过会话id查找会话
        if(it == _sessions.end()){
            return nullptr;
        }
        // 返回会话信息
        return it->second;//返回unordered_map中的会话信息指针
    }
    //往某个会话添加消息
    bool SessionManager::addMessage(const std::string& sessionId,const Message& message){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时添加消息
        //获取对应的会话信息
        auto it = _sessions.find(sessionId);//通过会话id查找会话
        if(it == _sessions.end()){
            return false;
        }
        //创建消息
        Message msg(message._role,message._content);
        //获得历史消息
        msg._messageid = generateMessageId(it->second->_messages.size());//根据当前会话历史消息的大小生成消息id
        // 添加消息到会话历史消息
        it->second->_messages.push_back(msg);
        // 更新会话时间戳
        it->second->_updatedAt = std::time(nullptr);
        // 会话更新成功
        INFO("add message success, session id: {}, message.content: {}",sessionId,msg._content);//打印添加的消息内容 和会话id
        // 返回true
        return true;
    }
    //获取某个会话的历史消息
    std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId)const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话历史消息
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            return {};
        }
        // 返回会话历史消息
        return it->second->_messages;
    }
    //更新会话的时间戳
    void SessionManager::updateSessionTimestamp(const std::string& sessionId){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时更新会话时间戳
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            ERR("Session {} not found!!!",sessionId);
            return;
        }
        // 更新会话时间戳
        it->second->_updatedAt = std::time(nullptr);
        // 会话更新成功
        INFO("update session timestamp success, session id: {}",sessionId);//打印会话id
    }
    //获取所有的会话列表
    std::vector<std::string> SessionManager::getSessionLists()const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话列表
        //构建一个临时对话列表,将其内容的会话按照更新的时间降序排序
        std::vector<std::pair<std::time_t,std::shared_ptr<SessionInfo>>> temp;//临时会话列表中存储会话时间戳和会话对象
        temp.reserve(_sessions.size());//分配内存  避免重复分配内存

        //将会话添加到临时会话列表中
        for(const auto& pair : _sessions){
            temp.emplace_back(pair.second->_updatedAt,pair.second);//将会话时间戳和会话对象添加到临时会话列表中   
        }
        
        // 对临时会话列表进行排序                                  auto转化前的类std::pair<std::time_t,std::shared_ptr<SessionInfo>>
        std::sort(temp.begin(),temp.end(),[](const auto& a,const auto& b){//利用lambda表达式对临时会话列表进行排序
            return a.first > b.first;//按照会话时间戳降序排序
        });
        
        std::vector<std::string> sessionIds;//存储排序后的会话id
        sessionIds.reserve(_sessions.size());//分配内存  避免重复分配内存
        // 提取排序后的会话id
        for(const auto& pair : temp){
            sessionIds.push_back(pair.second->_sessionid);
        }
        // 返回排序后的会话id
        return sessionIds;
    }
    //删除某个会话
    bool SessionManager::deleteSession(const std::string& sessionId){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时删除会话
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            ERR("Session {} not found!!!",sessionId);
            return false;
        }
        // 删除会话
        _sessions.erase(it);
        // 会话删除成功
        INFO("Session {} deleted!!!",sessionId);
        // 返回true
        return true;
    }
    //清空所有会话
    void SessionManager::clearAllSessions(){
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时清空会话
        // 清空有会话
        _sessions.clear();//map提供了clear()方法  清空所有元素  会自动调用析构函数  释放内存
        // 所有会话已清空
        INFO("All sessions cleared!!!");
    }
    //获取会话总数
    size_t SessionManager::getSessionCount()const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话总数
        // 返回会话总数
        return _sessions.size();
    }
     
}//end namespace ai_chat_sdk

3 sqlite:

     上面封装好了模型管理文件后  如果不封装数据库 这些信息就存储在内存当中 而当年一旦关机重启 这些会话信息就找不到了 所以我们要将这些会话信息放入到数据库中。

我们这里使用sqlite即可 不需要mysql这种支持高并发的库 这个项目没有这么大的项目逻辑,只需要一个轻量化的数据库即可。

保姆级使用教程:SQLite 数据类型 | 菜鸟教程

安装完成后 就可以在终端执行了 这个跟mysql是差不多的支持标准的sql语句

上面是命令行的操作方式

代码连接库的方式 上面是编译时连接静态库 第二个通过修改CMake文件来添加连接方式

详细介绍 可转到菜鸟教程

4.数据管理的实现:

   我们要创建两个表来存储会话和消息的信息

     1.头文件的编写:

#pragma once
#include <sqlite3.h>
#include <string>
#include <mutex>
#include "common.h"
#include <memory>


namespace ai_chat_sdk{


class DataManager{
public:
        DataManager(const std::string& dbName);//构造函数
        ~DataManager();//析构函数

        //Session相关操作

        //插入Session会话
        bool insertSession(const SessionInfo& session);

        //获取指定会话信息
        std::shared_ptr<SessionInfo> getSession(const std::string& sessionId)const;
        //更新指定会话的时间戳
        bool updateSessionTimestamp(const std::string& sessionId, const std::string& timestamp);
        //删除指定会话 删除会话时也需要删除创建的这个会话中的所有消息
        bool deleteSession(const std::string& sessionId);
        //获取所有会话ID
        std::vector<std::string> getAllSessionIds()const;
        //获取所有的会话信息
        std::vector<std::shared_ptr<SessionInfo>> getAllSessions()const;
        //获取会话总数
        int getSessionCount()const;




        //Message相关操作
        //插入消息 插入消息那么整个会话就变了 还需要更新会话的时间戳 所以还要传会话ID
        bool insertMessage(const std::string& sessionId, const Message& message);
        //获取指定会话的历史消息
        std::vector<std::shared_ptr<Message>> getSessionMessages(const std::string& sessionId)const;
        //获取指定会话的消息总数
        int getSessionMessageCount(const std::string& sessionId)const;
        //删除指定会话的所有消息
        bool deleteSessionMessages(const std::string& sessionId);
        
private:
        //初始化数据库
        bool initDataBase();
        //执行SQL语句的工具函数
        bool execSQL(const std::string& sql);
private:
        sqlite3* _db=nullptr;//数据库指针
        std::string _dbName;//数据库名称
        mutable std::mutex _mutex;//互斥锁 mutable可以修改const成员变量
    };

}//end namespace ai_chat_sdk

2.源文件的编写:

      1.构造函数和析构函数:

      2.初始化数据库

创建会话表时要根据定义的会话格式来进行创建

消息列表我们要单独存放另一张表上的。

会话列表的创建

根据创建的结构体来进行建表只是多了以一个会话id这样好和会话列表连接在一起

3.执行sql语句的函数编写:

3.会话部分的函数功能实现

    1.插入会话的实现:

这????就是占位符的意思填的时候就根据上面的格式来的

      2.获取指定会话信息的实现:

reinterpret_cast将sqlite3_column_text返回的const char*转换为std::string的构造函数

这里取去调用了消息相关操作的获取指定会话的历史消息的这个函数  因为本函数一开始已经加锁了所有要注意在是实现这个getSessionMessages时不能加锁 不然就死锁了。

     3.更新会话的时间戳的实现:

     4.删除指定会话:

     5.获取所有会话ID

    6.获取所有会话信息:

      7.获取会话总数

4.消息部分的函数功能实现:

     1.插入新消息的实现:

       

2.获取指定会话的历史消息

3.删除指定会话的所有消息的实现:

5.会话管理和数据管理相结合:

接着往下走的话还需要把会话管理和数据管理结合起来 我们需要把会话管理的数据放到数据去。

这样的话我们就可以跟大模型开启多轮聊天了,每次聊天就相当于会话的开始。

这里数据管理和会话管理是分开的,我们需要把他关联起来。当你的会话一更新你的数据库就得更新。

我们需要把会话管理中加上一个数据管理的对象

再加上用数据库名称构造会话管理器。

这里析构我们让编译器调用自己的析构函数。

      1.构造函数的实现:

生成会话id和消息id不需要修改:

2.创建会话的重写:

我们要在创建会话时把数据插入到数据库中去

这里insertSession插入的时候也有锁,而这里创建会话的时候也有锁

我们这里手动加锁和解锁

3.获取会话的重写:

没有数据库的实现时 我们先在内存中去查找。

我们获取会话的实现是const类型 但是这里我们要添加查找到的会话到会话列表 所以我们要把const去掉

4.往某个会话添加消息的重写:

以前的

获取会话已经写好了 所以不管你这个会话存不存在 他都拿到我们的内存上了。

  5.获取某个会话的历史消息:

6.更新会话的时间戳重写:

因为会话的时间戳重写了 所以数据库中的数据库也要进行重写

7.获取所有的会话列表的重写:

获取所有会话时如果数据库的会话不在内存中 就把数据库的会话列表也添加进来。

8.删除某个会话的重写:

这里我们还需要加上一个方法删除数据库会话的方法

这里我们只需要去删会话即可,不需要去删除消息 因为构建消息列表时加了外键约束和级联

一旦你会话列表删除了,消息列表也要被删除。

9.获取会话总数

这个不用重写当我们服务器启动我们一开始就拿到了所有会话

10.重写后的代码:

#include "../include/SessionManager.h"
#include "../include/util/myLog.h"
#include<sstream>
#include<iomanip>

namespace ai_chat_sdk{
    // 构造函数
        SessionManager::SessionManager(const std::string& dbName)
             :_dbManager(dbName)
        {
            //通过数据库名称构造会话管理器
            //获取所有会话信息
            auto sessions = _dbManager.getAllSessions();
            for(auto& session : sessions){
                //将会话信息加入到会话管理器中
                _sessions[session->_sessionid] = session; //key:会话id  value:会话信息指针
            }
        }
    //生成会话id 会话id的格式:session_时间戳_会话计数器
    std::string SessionManager::generateSessionId(){
        //会话计数器自增
        _sessionCounter.fetch_add(1);
        //获取当前时间戳
        std::time_t time = std::time(nullptr);
        //拼接在一起生成会话id  
        std::ostringstream os;
        os << "session_" << time << "_" << std::setw(8)  << _sessionCounter;//setw(8) 表示会话计数器占8个字符宽度,不足8个字符用0填充
        std::string sessionId = os.str();//将ostringstream中的内容转换为字符串
        //返回会话id
        return sessionId;
    }
    //生成消息id 消息id的格式:消息计数器_时间戳_会话id_消息计数器
    std::string SessionManager::generateMessageId(size_t messageCounter){
        messageCounter++;//消息计数器自增
        std::time_t time = std::time(nullptr);
        
        //构造消息内容
        std::ostringstream os;
        os << "msg_" << time << "_" << std::setw(8)  << _sessionCounter;//消息计数器_时间戳_会话id_消息计数器
        std::string messageId = os.str();//将ostringstream中的内容转换为字符串
        //返回消息id
        return messageId;
    }
    //创建会话
    std::string SessionManager::createSession(const std::string& modelName){
        _mutex.lock();//加锁  防止其他线程同时创建会话
        // 生成新的会话id
        std::string sessionId = generateSessionId();
        // 创建新的会话 
        auto session = std::make_shared<SessionInfo>(modelName);//初始化会话消息
        session->_sessionid = sessionId;
        session->_createdAt = session->_updatedAt=std::time(nullptr);//创建会话时创建时间与更新时间相同  当前时间戳
       
        //加入到会话列表
        _sessions[sessionId] = session;//将会话信息加入到会话管理器中
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息加入到数据库中
        //将会话保存到数据库
        _dbManager.insertSession(*session);//我们需要session的引用  因为insertSession方法需要一个SessionInfo对象的引用
        return sessionId;
    }
    //获取会话信息
    std::shared_ptr<SessionInfo> SessionManager::getSession(const std::string& sessionId){
        //先在内存中查找会话信息
        _mutex.lock();//加锁  防止其他线程同时获取会话信息
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);//通过会话id查找会话
        if(it != _sessions.end()){
            //如果会话存在
            //解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
            _mutex.unlock();
            //将这个会话的历史消息从数据库中获取
            it->second->_messages = _dbManager.getSessionMessages(sessionId);
            //返回会话信息
            return it->second;
        }


        //内存中没找到就要去数据库中查找
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        auto session = _dbManager.getSession(sessionId);//通过会话id查找会话
        //如果数据库中存在会话
        //将会话信息加入到内存中
        if(session){
            _mutex.lock();//加锁  防止其他线程同时获取会话信息
            auto it = _sessions.find(sessionId);//通过会话id查找会话
            if(it == _sessions.end()){
                //内存中没有找到  说明会话不存在
                //将会话信息加入到会话管理器中
                _sessions[sessionId] = session;//key:会话id  value:会话信息指针
            }
            _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
            //如果会话不在内存中 我们就要从数据库中获取会话信息并加入到内存中的话管理器中
            //将这个会话的历史消息从数据库中获取
            session->_messages = _dbManager.getSessionMessages(sessionId);//将数据库中的消息赋值给会话信息
            //返回会话信息
            return session;
        }
        WARN("Session {} not found!!!", sessionId);
        return nullptr;//返回nullptr表示会话不存在
    }
    //往某个会话添加消息
    bool SessionManager::addMessage(const std::string& sessionId,const Message& message){
        _mutex.lock();//加锁  防止其他线程同时添加消息
        //获取对应的会话信息
        auto it = _sessions.find(sessionId);//通过会话id查找会话
        if(it == _sessions.end()){
            ERR("Session {} not found!!!",sessionId);
            _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
            return false;
        }
        //创建消息
        Message msg(message._role,message._content);
        //获得历史消息
        msg._messageid = generateMessageId(it->second->_messages.size());//根据当前会话历史消息的大小生成消息id
        // 添加消息到会话历史消息
        it->second->_messages.push_back(msg);
        // 更新会话时间戳
        it->second->_updatedAt = std::time(nullptr);
        // 会话更新成功
        INFO("add message success, session id: {}, message.content: {}",sessionId,msg._content);//打印添加的消息内容 和会话id
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        //将会消息保存到数据库
        _dbManager.insertMessage(sessionId,msg);//我们需要msg的引用  因为insertMessage方法需要一个Message对象的引用
        // 返回true
        return true;
    }
    //获取某个会话的历史消息
    std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId)const{
        _mutex.lock();//加锁  防止其他线程同时获取会话历史消息
        // 检查会话是否存在
        //先在内存中查找会话信息 如果内存中获取不到就去数据库中查找
        auto it = _sessions.find(sessionId);
        if(it != _sessions.end()){
            //如果会话存在
            //解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
            _mutex.unlock();
            //返回会话历史消息
            return it->second->_messages;
        }
        //解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        _mutex.unlock();
        //内存中没有找到 去数据库中查找会话信息
        return _dbManager.getSessionMessages(sessionId);
    }
    //更新会话的时间戳
    void SessionManager::updateSessionTimestamp(const std::string& sessionId){
        _mutex.lock();//加锁  防止其他线程同时更新会话时间戳
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it != _sessions.end()){
            // 找到了 更新会话时间戳
            it->second->_updatedAt = std::time(nullptr);
        }
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        
        //更新会话时间戳到数据库
        _dbManager.updateSessionTimestamp(sessionId,it->second->_updatedAt);
        
        // 会话更新成功
        INFO("update session timestamp success, session id: {}",sessionId);//打印会话id
    }
    //获取所有的会话列表
    std::vector<std::string> SessionManager::getSessionLists()const{
        auto sessions = _dbManager.getAllSessions();//从数据库中获取所有的会话信息
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话列表
        //构建一个临时对话列表,将其内容的会话按照更新的时间降序排序
        std::vector<std::pair<std::time_t,std::shared_ptr<SessionInfo>>> temp;//临时会话列表中存储会话时间戳和会话对象
        temp.reserve(_sessions.size());//分配内存  避免重复分配内存

        //将会话添加到临时会话列表中
        for(const auto& pair : _sessions){
            temp.emplace_back(pair.second->_updatedAt,pair.second);//将会话时间戳和会话对象添加到临时会话列表中   
        }
        // 将数据库中获取到的会话添加到临时会话列表中
        for(const auto& session : sessions){
            if(_sessions.find(session->_sessionid) == _sessions.end()){
                // 如果内存中没有找到  则添加到临时会话列表中
                temp.emplace_back(session->_updatedAt,session);//将会话时间戳和会话对象添加到临时会话列表中   
            }
        }
        
        // 对临时会话列表进行排序                                  auto转化前的类std::pair<std::time_t,std::shared_ptr<SessionInfo>>
        std::sort(temp.begin(),temp.end(),[](const auto& a,const auto& b){//利用lambda表达式对临时会话列表进行排序
            return a.first > b.first;//按照会话时间戳降序排序
        });
        
        std::vector<std::string> sessionIds;//存储排序后的会话id
        sessionIds.reserve(_sessions.size());//分配内存  避免重复分配内存
        // 提取排序后的会话id
        for(const auto& pair : temp){
            sessionIds.push_back(pair.second->_sessionid);
        }
        // 返回排序后的会话id
        return sessionIds;
    }
    //删除某个会话
    bool SessionManager::deleteSession(const std::string& sessionId){
        _mutex.lock();//加锁  防止其他线程同时删除会话
        // 检查会话是否存在
        auto it = _sessions.find(sessionId);
        if(it == _sessions.end()){
            ERR("Session {} not found!!!",sessionId);
            _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
            return false;
        }
        // 删除会话
        _sessions.erase(it);
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        //删除数据库中的会话
        _dbManager.deleteSession(sessionId);

        // 会话删除成功
        INFO("Session {} deleted!!!",sessionId);
        // 返回true
        return true;
    }
    //清空所有会话
    void SessionManager::clearAllSessions(){
        _mutex.lock();//加锁  防止其他线程同时清空会话
        // 清空有会话
        //清理内存中所有的会话对象
        _sessions.clear();//map提供了clear()方法  清空所有元素  会自动调用析构函数  释放内存
        _mutex.unlock();//解锁  允许其他线程访问会话管理器 这样就可以把会话信息从数据库中获取
        // 清空数据库中的所有会话
        _dbManager.clearAllSessions();

        // 所有会话已清空
        INFO("All sessions cleared!!!");
    }
    //获取会话总数
    size_t SessionManager::getSessionCount()const{
        std::lock_guard<std::mutex> lock(_mutex);//加锁  防止其他线程同时获取会话总数
        // 返回会话总数
        return _sessions.size();
    }
     
}//end namespace ai_chat_sdk

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐