* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* Fix/interface home (#182)
* [fix]Fix the interface for statistics of recent activities and applications
* [changes]Modify the code based on the AI review
1.Use the boolean auxiliary methods provided by SQLAlchemy instead of using == True in the is_active filter.
2.The calculation of the "PROJECT_ROOT" has now been hardcoded with five levels of nested os.path.dirname calls.
* [fix]Fix the interface for statistics of recent activities and applications
* [changes]Modify the code based on the AI review
1.Use the boolean auxiliary methods provided by SQLAlchemy instead of using == True in the is_active filter.
2.The calculation of the "PROJECT_ROOT" has now been hardcoded with five levels of nested os.path.dirname calls.
* Fix/optimize inerface (#183)
* [changes]Optimize the time consumption of the "/end_users" interface
* [fix]Optimize the time consumption of the "/hot_memory_tags" interface
* [changes]Optimize the time consumption of the "/end_users" interface
* [fix]Optimize the time consumption of the "/hot_memory_tags" interface
* [changes]Improve the code based on AI review
* Fix/memory mcp2 1 (#184)
* 优化快速检索的回复内容
* 优化快速检索的回复内容
* Fix/memory mcp2 1 (#185)
* 优化快速检索的回复内容
* 优化快速检索的回复内容
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* Fix/memory mcp2 1 (#188)
* 优化快速检索的回复内容
* 优化快速检索的回复内容
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* LLM生存缺少config_id认证,修复BUG
* LLM生存缺少config_id认证,修复BUG
* LLM生存缺少config_id认证,修复BUG
* 解决冲突
* 解决冲突
* feat(home page): version description update
* Fix/memory mcp2 1 (#190)
* 优化快速检索的回复内容
* 优化快速检索的回复内容
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* 路径的BUG修复
* LLM生存缺少config_id认证,修复BUG
* LLM生存缺少config_id认证,修复BUG
* LLM生存缺少config_id认证,修复BUG
* 深度检索优化,搜索不到数据/提问的概念过于蘑菇,以引导的方式继续提问
* 深度检索优化,搜索不到数据/提问的概念过于蘑菇,以引导的方式继续提问
* 深度检索优化,搜索不到数据/提问的概念过于蘑菇,以引导的方式继续提问
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* feat(web): memory related interface parameter transfer adjustment
* 感知meta_data字段BUG修复
* Fix/memory bug fix (#171)
* feat(sandbox): add Python 3 code execution sandbox support
* feat(workflow): emit SSE events for node exception output
* perf(sandbox): optimize code encryption handling
* perf(workflow): update standard node output structure
* [add] migration script
* [modify] migration script
* feat(web): add workflow runtime info
* fix(web): handleSSE bugfix
* fix(sandbox): prevent imports from being blocked when network is disabled
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* Fix/memory bug fix (#199)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* user_id->显示为config_id_old传输
* feat(web): update read_all_config select valueKey
* user_id->显示为config_id_old传输
* feat(workflow): Add a new node for executing code
* fix(web): KnowledgeConfigModal bugfix
* fix(web): iteration's variable add parameter-extractor node
* fix(sandbox): treat non-zero exit codes as errors instead of relying only on stderr
* Fix/memory bug fix (#200)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* Refactor/benchmark test (#196)
* [changes]refactor locomo_test
* [fix]Fix the circular import of ModelParameters
* [changes]The benchmark test can run stably.
* [fix]Complete end-to-end LoCoMo repair
* [fix]Complete the end-to-end longmemeval and memsciqa fixes
* [changes]Complete the benchmark test description document to ensure that the configuration parameters take effect.
* [changes]refactor locomo_test
* [fix]Fix the circular import of ModelParameters
* [changes]The benchmark test can run stably.
* [fix]Complete end-to-end LoCoMo repair
* [fix]Complete the end-to-end longmemeval and memsciqa fixes
* [changes]Complete the benchmark test description document to ensure that the configuration parameters take effect.
* [changes]Benchmark test adaptation for end_user_id
* [changes]refactor locomo_test
* [fix]Fix the circular import of ModelParameters
* [changes]The benchmark test can run stably.
* [fix]Complete end-to-end LoCoMo repair
* [fix]Complete the end-to-end longmemeval and memsciqa fixes
* [changes]Complete the benchmark test description document to ensure that the configuration parameters take effect.
* [fix]Complete the end-to-end longmemeval and memsciqa fixes
* [changes]Complete the benchmark test description document to ensure that the configuration parameters take effect.
* [changes]Benchmark test adaptation for end_user_id
* [modify] migration script
* delete benchmark-test (#204)
* Refactor: Move evaluation folder to redbear-mem-benchmark submodule
* [changes]Restore .gitmodules
* feat(web): workflow add code node
* 检查需要更改的格式问题
* Fix/redbear benchmark (#205)
* Refactor: Move evaluation folder to redbear-mem-benchmark submodule
* [changes]Update submodule reference
* Refactor: Move evaluation folder to redbear-mem-benchmark submodule
* [changes]Update submodule reference
* Remove duplicate evaluation submodule, use redbear-mem-benchmark instead
* Fix/memory bug fix (#207)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* fix(web): remove URI decode and encode
* [add] plugin system and base sso module
* 修复宿主列表获取memory_config_idBUG
* Fix/memory bug fix (#209)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
* 修复宿主列表获取memory_config_idBUG
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* [modify] file local server url
* [add] migration script
* fix(workflow): fix activation and branch control issues in streaming output
* fix(workflow): fix function cache not taking effect and potential list index overflow
* style(workflow): enforce PEP8 style and remove redundant imports
* fix(workflow): fix streaming output error when variable is not a string
* [fix]remove aspose-slides
* perf(workflow): enhance streaming output node activation performance
* feat(workflow): store token usage in message table
* feat(web): add PageEmpty component
* feat(web): add PageTabs component
* perf(workflow): make memory configuration backward compatible
* feat(web): update model management
* config_id做映射
* config_id做映射
* Fix/memory bug fix (#211)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
* 修复宿主列表获取memory_config_idBUG
* config_id做映射
* config_id做映射
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* feat(web): getModelListUrl add is_active param
* config_id做映射+1
* config_id做映射+1
* config_id做映射+1
* feat(web): remove file url replace
* Fix/memory bug fix (#212)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
* 修复宿主列表获取memory_config_idBUG
* config_id做映射
* config_id做映射
* config_id做映射+1
* config_id做映射+1
* config_id做映射+1
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* feat(model and app statistic): 1. Optimize the model list; 2. Increase the model combination; 3. Add a model square; 4. Add application management statistics
* feat(web): model logo update
* 应用层memory_content->memory_config
* fix(web): correct spelling
* 应用层memory_content->memory_config
* 应用层memory_content->memory_config
* Fix/memory bug fix (#215)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
* 修复宿主列表获取memory_config_idBUG
* config_id做映射
* config_id做映射
* config_id做映射+1
* config_id做映射+1
* config_id做映射+1
* 应用层memory_content->memory_config
* 应用层memory_content->memory_config
* 应用层memory_content->memory_config
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* feat(model and app statistic): 1. Optimize the model list; 2. Increase the model combination; 3. Add a model square; 4. Add application management statistics
* fix(web): model loading update
* 统一字段为config_id_old
* 统一字段为config_id_old
* feat(model and app statistic): 1. Optimize the model list; 2. Increase the model combination; 3. Add a model square; 4. Add application management statistics
* 统一字段为config_id_old
* 统一字段为config_id_old
* memory_content暂时不修改
* memory_content暂时不修改
* Fix/memory bug fix (#217)
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 图谱数据量限制数量去掉
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化1.0(优化隐私输出、时间检索)
* 反思优化测试接口
* 反思优化测试接口
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 读取接口内层嵌套BUG修复
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 新增中翻英功能(记忆时间线)(用户摘要)(兴趣分布接口)(查询核心档案)(记忆洞察)-接口添加翻译字段
* 把group_id替换end_user_id
* 把group_id替换end_user_id_
* 把group_id替换end_user_id_
* config_config替换成memory_config
* config_config替换成memory_config
* [fix]Fix the memory interface to use end_user_id.
* config_config替换成memory_config
* config_config替换成memory_config
* config_config替换成memory_config
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID
* config_id字段改成UUID,与develop校对恢复
* 检查项目,修复group_id的遗留问题
* 检查项目,修复group_id的遗留问题
* 解决冲突
* 解决冲突
* end_user_id清理干净
* end_user_id清理干净
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 修复遗留合并BUG
* 感知meta_data字段BUG修复
* user_id->现实为config_id_old
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* user_id->显示为config_id_old传输
* 检查需要更改的格式问题
* 修复宿主列表获取memory_config_idBUG
* config_id做映射
* config_id做映射
* config_id做映射+1
* config_id做映射+1
* config_id做映射+1
* 应用层memory_content->memory_config
* 应用层memory_content->memory_config
* 应用层memory_content->memory_config
* 统一字段为config_id_old
* 统一字段为config_id_old
* 统一字段为config_id_old
* 统一字段为config_id_old
* memory_content暂时不修改
* memory_content暂时不修改
---------
Co-authored-by: lanceyq <1982376970@qq.com>
* feat(web): add app statistics
* fix(workflow): fix streaming output issues with multi-output End nodes
End nodes with multiple output segments could cause cursor errors or leave some
segments inactive, resulting in incorrect final outputs.
Unified _emit_active_chunks and _update_scope_activate to ensure all segments
are activated in order and streamed correctly.
* feat(web): add apps statistics api
* fix(web): agent's knowledge_bases bugfix
* Revert "feat(web): update read_all_config select valueKey"
This reverts commit 46f0f3cee9.
* [add] migrations script
* perf(workflow): make memory write node backward-compatible and defer config validation
* 旧数据兼容
* 旧数据兼容
* 旧数据兼容
* 旧数据兼容
* fix(web): model bugfix
* fix(web): model bugfix
* 提交遗漏 (#228)
* [fix] chat api for workflow
* [fix] web search set for v1 api
* fix(web): model bugfix
* fix(web): model list remove is_active
* fix(model): bug fix
* [add]migration script
* [fix] api
* [fix] api
* fix(web): model bugfix
* fix(model): the model type does not allow modification, delete tts and speech2text type
* fix(model): bug fix
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* Add/develop memory (#239)
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* feat(web): model ui update
* feat(web): model ui update
* Add/develop memory (#243)
* 遗漏的历史映射
* 遗漏的历史映射
* fix(model): bug fix
* feat(web): model ui update
* Add/develop memory (#247)
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* 遗漏的历史映射
* [modify] migration script
* [add] migration script
* fix(web): change form message
* fix(web): the memoryContent field is compatible with numbers and strings
* feat(web): code node hidden
* fix(model):
1. create a basic model to check if the name and provider are duplicated.
2. The result shows error models because the provider created API Keys for all matching models.
---------
Co-authored-by: lixinyue <2569494688@qq.com>
Co-authored-by: lanceyq <1982376970@qq.com>
Co-authored-by: yujiangping <yujiangping@taofen8.com>
Co-authored-by: 乐力齐 <162269739+lanceyq@users.noreply.github.com>
Co-authored-by: lixinyue11 <94037597+lixinyue11@users.noreply.github.com>
Co-authored-by: yingzhao <zhaoyingyz@126.com>
Co-authored-by: Timebomb2018 <18868801967@163.com>
Co-authored-by: Mark <zhuwenhui5566@163.com>
Co-authored-by: zhaoying <yzhao96@best-inc.com>
Co-authored-by: Eternity <1533512157@qq.com>
Co-authored-by: lixiangcheng1 <lixiangcheng1@wanda.cn>
722 lines
26 KiB
Python
722 lines
26 KiB
Python
"""
|
||
遗忘引擎服务层模块
|
||
|
||
本模块提供遗忘引擎的业务逻辑实现,包括:
|
||
1. 遗忘周期执行
|
||
2. 配置管理
|
||
3. 统计信息查询
|
||
4. 遗忘曲线生成
|
||
|
||
所有业务逻辑从控制器层分离到此服务层。
|
||
"""
|
||
|
||
from typing import Optional, Dict, Any, Tuple
|
||
from datetime import datetime, timezone
|
||
from uuid import UUID
|
||
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.logging_config import get_api_logger
|
||
from app.core.memory.storage_services.forgetting_engine.actr_calculator import ACTRCalculator
|
||
from app.core.memory.storage_services.forgetting_engine.forgetting_strategy import ForgettingStrategy
|
||
from app.core.memory.storage_services.forgetting_engine.forgetting_scheduler import ForgettingScheduler
|
||
from app.core.memory.storage_services.forgetting_engine.config_utils import (
|
||
load_actr_config_from_db,
|
||
)
|
||
from app.repositories.neo4j.neo4j_connector import Neo4jConnector
|
||
from app.repositories.memory_config_repository import MemoryConfigRepository
|
||
from app.repositories.forgetting_cycle_history_repository import ForgettingCycleHistoryRepository
|
||
|
||
|
||
# 获取API专用日志器
|
||
api_logger = get_api_logger()
|
||
|
||
|
||
def convert_neo4j_datetime_to_python(value: Any) -> Optional[datetime]:
|
||
"""
|
||
将 Neo4j DateTime 对象转换为 Python datetime 对象
|
||
|
||
Args:
|
||
value: Neo4j DateTime 对象、Python datetime 对象或字符串
|
||
|
||
Returns:
|
||
Python datetime 对象或 None
|
||
"""
|
||
if value is None:
|
||
return None
|
||
|
||
try:
|
||
# Neo4j DateTime 对象
|
||
if hasattr(value, 'to_native'):
|
||
return value.to_native()
|
||
# Python datetime 对象
|
||
elif isinstance(value, datetime):
|
||
return value
|
||
# 字符串格式
|
||
elif isinstance(value, str):
|
||
if value.endswith('Z'):
|
||
return datetime.fromisoformat(value.replace('Z', '+00:00'))
|
||
else:
|
||
return datetime.fromisoformat(value)
|
||
# 其他类型,尝试转换为字符串
|
||
else:
|
||
return datetime.fromisoformat(str(value).replace('Z', '+00:00'))
|
||
except Exception as e:
|
||
api_logger.warning(f"转换时间失败: {value} (类型: {type(value).__name__}), 错误: {e}")
|
||
return None
|
||
|
||
|
||
class MemoryForgetService:
|
||
"""遗忘引擎服务类"""
|
||
|
||
def __init__(self):
|
||
"""初始化服务"""
|
||
self.config_repository = MemoryConfigRepository()
|
||
self.history_repository = ForgettingCycleHistoryRepository()
|
||
|
||
def _get_neo4j_connector(self) -> Neo4jConnector:
|
||
"""
|
||
获取 Neo4j 连接器实例
|
||
|
||
Returns:
|
||
Neo4jConnector: Neo4j 连接器实例
|
||
"""
|
||
# 这里应该从配置或依赖注入获取连接器
|
||
# 暂时创建新实例(实际应该使用单例或连接池)
|
||
return Neo4jConnector()
|
||
|
||
async def _get_forgetting_components(
|
||
self,
|
||
db: Session,
|
||
config_id: Optional[UUID] = None
|
||
) -> Tuple[ACTRCalculator, ForgettingStrategy, ForgettingScheduler, Dict[str, Any]]:
|
||
"""
|
||
获取遗忘引擎组件(计算器、策略、调度器)
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
config_id: 配置ID(可选)
|
||
|
||
Returns:
|
||
tuple: (actr_calculator, forgetting_strategy, forgetting_scheduler, config)
|
||
"""
|
||
# 加载配置
|
||
config = load_actr_config_from_db(db, config_id)
|
||
|
||
# 创建 ACT-R 计算器
|
||
actr_calculator = ACTRCalculator(
|
||
decay_constant=config['decay_constant'],
|
||
forgetting_rate=config['forgetting_rate'],
|
||
offset=config['offset'],
|
||
max_history_length=config['max_history_length']
|
||
)
|
||
|
||
# 获取 Neo4j 连接器
|
||
connector = self._get_neo4j_connector()
|
||
|
||
# 创建遗忘策略执行器
|
||
forgetting_strategy = ForgettingStrategy(
|
||
connector=connector,
|
||
actr_calculator=actr_calculator,
|
||
forgetting_threshold=config['forgetting_threshold'],
|
||
enable_llm_summary=config['enable_llm_summary']
|
||
)
|
||
|
||
# 创建遗忘调度器
|
||
forgetting_scheduler = ForgettingScheduler(
|
||
forgetting_strategy=forgetting_strategy,
|
||
connector=connector
|
||
)
|
||
|
||
return actr_calculator, forgetting_strategy, forgetting_scheduler, config
|
||
|
||
async def _get_knowledge_stats(
|
||
self,
|
||
connector: Neo4jConnector,
|
||
end_user_id: Optional[str] = None,
|
||
forgetting_threshold: float = 0.3
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
获取知识层统计信息
|
||
|
||
Args:
|
||
connector: Neo4j 连接器
|
||
end_user_id: 组ID(可选)
|
||
forgetting_threshold: 遗忘阈值
|
||
|
||
Returns:
|
||
dict: 统计信息字典
|
||
"""
|
||
# 构建查询
|
||
query = """
|
||
MATCH (n)
|
||
WHERE (n:Statement OR n:ExtractedEntity OR n:MemorySummary)
|
||
"""
|
||
|
||
if end_user_id:
|
||
query += " AND n.end_user_id = $end_user_id"
|
||
|
||
query += """
|
||
WITH n,
|
||
CASE
|
||
WHEN n:Statement THEN 'statement'
|
||
WHEN n:ExtractedEntity THEN 'entity'
|
||
WHEN n:MemorySummary THEN 'summary'
|
||
END as node_type
|
||
RETURN
|
||
count(n) as total_nodes,
|
||
sum(CASE WHEN node_type = 'statement' THEN 1 ELSE 0 END) as statement_count,
|
||
sum(CASE WHEN node_type = 'entity' THEN 1 ELSE 0 END) as entity_count,
|
||
sum(CASE WHEN node_type = 'summary' THEN 1 ELSE 0 END) as summary_count,
|
||
avg(n.activation_value) as average_activation,
|
||
sum(CASE WHEN n.activation_value IS NOT NULL AND n.activation_value < $threshold THEN 1 ELSE 0 END) as low_activation_nodes
|
||
"""
|
||
|
||
params = {'threshold': forgetting_threshold}
|
||
if end_user_id:
|
||
params['end_user_id'] = end_user_id
|
||
|
||
results = await connector.execute_query(query, **params)
|
||
|
||
if results:
|
||
result = results[0]
|
||
return {
|
||
'total_nodes': result['total_nodes'] or 0,
|
||
'statement_count': result['statement_count'] or 0,
|
||
'entity_count': result['entity_count'] or 0,
|
||
'summary_count': result['summary_count'] or 0,
|
||
'average_activation': result['average_activation'],
|
||
'low_activation_nodes': result['low_activation_nodes'] or 0
|
||
}
|
||
|
||
return {
|
||
'total_nodes': 0,
|
||
'statement_count': 0,
|
||
'entity_count': 0,
|
||
'summary_count': 0,
|
||
'average_activation': None,
|
||
'low_activation_nodes': 0
|
||
}
|
||
|
||
async def _get_pending_forgetting_nodes(
|
||
self,
|
||
connector: Neo4jConnector,
|
||
end_user_id: str,
|
||
forgetting_threshold: float,
|
||
min_days_since_access: int,
|
||
limit: int = 20
|
||
) -> list[Dict[str, Any]]:
|
||
"""
|
||
获取待遗忘节点列表
|
||
|
||
查询满足遗忘条件的节点(激活值低于阈值且最后访问时间超过最小天数)
|
||
|
||
Args:
|
||
connector: Neo4j 连接器
|
||
end_user_id: 组ID
|
||
forgetting_threshold: 遗忘阈值
|
||
min_days_since_access: 最小未访问天数
|
||
limit: 返回节点数量限制
|
||
|
||
Returns:
|
||
list: 待遗忘节点列表
|
||
"""
|
||
from datetime import timedelta
|
||
|
||
# 计算最小访问时间(ISO 8601 格式字符串,使用 UTC 时区)
|
||
min_access_time = datetime.now(timezone.utc) - timedelta(days=min_days_since_access)
|
||
min_access_time_str = min_access_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
|
||
|
||
query = """
|
||
MATCH (n)
|
||
WHERE (n:Statement OR n:ExtractedEntity OR n:MemorySummary)
|
||
AND n.end_user_id = $end_user_id
|
||
AND n.activation_value IS NOT NULL
|
||
AND n.activation_value < $threshold
|
||
AND n.last_access_time IS NOT NULL
|
||
AND datetime(n.last_access_time) < datetime($min_access_time_str)
|
||
RETURN
|
||
elementId(n) as node_id,
|
||
labels(n)[0] as node_type,
|
||
CASE
|
||
WHEN n:Statement THEN n.statement
|
||
WHEN n:ExtractedEntity THEN n.name
|
||
WHEN n:MemorySummary THEN n.content
|
||
ELSE ''
|
||
END as content_summary,
|
||
n.activation_value as activation_value,
|
||
n.last_access_time as last_access_time
|
||
ORDER BY n.activation_value ASC
|
||
LIMIT $limit
|
||
"""
|
||
|
||
params = {
|
||
'end_user_id': end_user_id,
|
||
'threshold': forgetting_threshold,
|
||
'min_access_time_str': min_access_time_str,
|
||
'limit': limit
|
||
}
|
||
|
||
results = await connector.execute_query(query, **params)
|
||
|
||
pending_nodes = []
|
||
for result in results:
|
||
# 将节点类型标签转换为小写
|
||
node_type_label = result['node_type'].lower()
|
||
if node_type_label == 'extractedentity':
|
||
node_type_label = 'entity'
|
||
elif node_type_label == 'memorysummary':
|
||
node_type_label = 'summary'
|
||
|
||
# 将 Neo4j DateTime 对象转换为时间戳(毫秒)
|
||
last_access_time = result['last_access_time']
|
||
last_access_dt = convert_neo4j_datetime_to_python(last_access_time)
|
||
# 确保 datetime 带有时区信息(假定为 UTC),避免 naive datetime 导致的时区偏差
|
||
if last_access_dt:
|
||
if last_access_dt.tzinfo is None:
|
||
last_access_dt = last_access_dt.replace(tzinfo=timezone.utc)
|
||
last_access_timestamp = int(last_access_dt.timestamp() * 1000)
|
||
else:
|
||
last_access_timestamp = 0
|
||
|
||
pending_nodes.append({
|
||
'node_id': str(result['node_id']),
|
||
'node_type': node_type_label,
|
||
'content_summary': result['content_summary'] or '',
|
||
'activation_value': result['activation_value'],
|
||
'last_access_time': last_access_timestamp
|
||
})
|
||
|
||
return pending_nodes
|
||
|
||
async def trigger_forgetting_cycle(
|
||
self,
|
||
db: Session,
|
||
end_user_id: str,
|
||
max_merge_batch_size: Optional[int] = None,
|
||
min_days_since_access: Optional[int] = None,
|
||
config_id: Optional[UUID] = None
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
手动触发遗忘周期
|
||
|
||
执行一次完整的遗忘周期,识别并融合低激活值节点。
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
end_user_id: 组ID(即终端用户ID,必填)
|
||
max_merge_batch_size: 最大融合批次大小(可选)
|
||
min_days_since_access: 最小未访问天数(可选)
|
||
config_id: 配置ID(必填,由控制器层通过 end_user_id 获取)
|
||
|
||
Returns:
|
||
dict: 遗忘报告
|
||
"""
|
||
# 获取遗忘引擎组件
|
||
_, _, forgetting_scheduler, config = await self._get_forgetting_components(db, config_id)
|
||
|
||
# 记录执行开始时间
|
||
execution_time = datetime.now()
|
||
|
||
# 运行遗忘周期(LLM 客户端将在需要时由 forgetting_strategy 内部获取)
|
||
report = await forgetting_scheduler.run_forgetting_cycle(
|
||
end_user_id=end_user_id,
|
||
max_merge_batch_size=max_merge_batch_size,
|
||
min_days_since_access=min_days_since_access,
|
||
config_id=config_id,
|
||
db=db
|
||
)
|
||
|
||
api_logger.info(
|
||
f"遗忘周期完成: 融合 {report['merged_count']} 对节点, "
|
||
f"失败 {report['failed_count']} 对, "
|
||
f"耗时 {report['duration_seconds']:.2f} 秒"
|
||
)
|
||
|
||
# 获取当前的激活值统计(用于记录历史)
|
||
try:
|
||
connector = forgetting_scheduler.connector
|
||
stats_query = """
|
||
MATCH (n)
|
||
WHERE (n:Statement OR n:ExtractedEntity OR n:MemorySummary OR n:Chunk)
|
||
AND n.end_user_id = $end_user_id
|
||
RETURN
|
||
count(n) as total_nodes,
|
||
avg(n.activation_value) as average_activation,
|
||
sum(CASE WHEN n.activation_value IS NOT NULL AND n.activation_value < $threshold THEN 1 ELSE 0 END) as low_activation_nodes
|
||
"""
|
||
|
||
stats_results = await connector.execute_query(
|
||
stats_query,
|
||
end_user_id=end_user_id,
|
||
threshold=config['forgetting_threshold']
|
||
)
|
||
|
||
if stats_results:
|
||
stats = stats_results[0]
|
||
total_nodes = stats['total_nodes'] or 0
|
||
average_activation = stats['average_activation']
|
||
low_activation_nodes = stats['low_activation_nodes'] or 0
|
||
else:
|
||
total_nodes = 0
|
||
average_activation = None
|
||
low_activation_nodes = 0
|
||
|
||
# 保存历史记录到数据库
|
||
self.history_repository.create(
|
||
db=db,
|
||
end_user_id=end_user_id,
|
||
execution_time=execution_time,
|
||
merged_count=report['merged_count'],
|
||
failed_count=report['failed_count'],
|
||
average_activation_value=average_activation,
|
||
total_nodes=total_nodes,
|
||
low_activation_nodes=low_activation_nodes,
|
||
duration_seconds=report['duration_seconds'],
|
||
trigger_type='manual'
|
||
)
|
||
|
||
api_logger.info(
|
||
f"已保存遗忘周期历史记录: end_user_id={end_user_id}, "
|
||
f"merged_count={report['merged_count']}"
|
||
)
|
||
|
||
except Exception as e:
|
||
# 记录历史失败不应影响主流程
|
||
api_logger.error(f"保存遗忘周期历史记录失败: {str(e)}")
|
||
|
||
return report
|
||
|
||
def read_forgetting_config(
|
||
self,
|
||
db: Session,
|
||
config_id: UUID
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
获取遗忘引擎配置
|
||
|
||
读取指定配置ID的遗忘引擎参数。
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
config_id: 配置ID
|
||
|
||
Returns:
|
||
dict: 配置信息字典
|
||
"""
|
||
# 加载配置
|
||
config = load_actr_config_from_db(db, config_id)
|
||
|
||
# 添加 config_id 到返回结果
|
||
config['config_id'] = config_id
|
||
|
||
api_logger.info(f"成功读取遗忘引擎配置: config_id={config_id}")
|
||
|
||
return config
|
||
|
||
def update_forgetting_config(
|
||
self,
|
||
db: Session,
|
||
config_id: UUID,
|
||
update_fields: Dict[str, Any]
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
更新遗忘引擎配置
|
||
|
||
更新指定配置ID的遗忘引擎参数。
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
config_id: 配置ID
|
||
update_fields: 要更新的字段字典
|
||
|
||
Returns:
|
||
dict: 更新后的配置信息
|
||
|
||
Raises:
|
||
ValueError: 配置不存在
|
||
"""
|
||
# 检查配置是否存在
|
||
db_config = self.config_repository.get_by_id(db, config_id)
|
||
if db_config is None:
|
||
raise ValueError(f"配置不存在: {config_id}")
|
||
|
||
# 执行更新
|
||
if update_fields:
|
||
for key, value in update_fields.items():
|
||
if hasattr(db_config, key):
|
||
setattr(db_config, key, value)
|
||
|
||
db.commit()
|
||
db.refresh(db_config)
|
||
|
||
api_logger.info(
|
||
f"成功更新遗忘引擎配置: config_id={config_id}, "
|
||
f"更新字段: {list(update_fields.keys())}"
|
||
)
|
||
else:
|
||
api_logger.info(f"没有字段需要更新: config_id={config_id}")
|
||
|
||
# 重新加载配置并返回
|
||
config = load_actr_config_from_db(db, config_id)
|
||
config['config_id'] = config_id
|
||
|
||
return config
|
||
|
||
async def get_forgetting_stats(
|
||
self,
|
||
db: Session,
|
||
end_user_id: Optional[str] = None,
|
||
config_id: Optional[UUID] = None
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
获取遗忘引擎统计信息
|
||
|
||
返回知识层节点统计、激活值分布等信息。
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
end_user_id: 组ID(可选)
|
||
config_id: 配置ID(可选,用于获取遗忘阈值)
|
||
|
||
Returns:
|
||
dict: 统计信息字典
|
||
"""
|
||
# 获取遗忘引擎组件
|
||
_, _, forgetting_scheduler, config = await self._get_forgetting_components(db, config_id)
|
||
|
||
connector = forgetting_scheduler.connector
|
||
forgetting_threshold = config['forgetting_threshold']
|
||
|
||
# 收集激活值指标
|
||
activation_query = """
|
||
MATCH (n)
|
||
WHERE (n:Statement OR n:ExtractedEntity OR n:MemorySummary OR n:Chunk)
|
||
"""
|
||
|
||
if end_user_id:
|
||
activation_query += " AND n.end_user_id = $end_user_id"
|
||
|
||
activation_query += """
|
||
RETURN
|
||
count(n) as total_nodes,
|
||
sum(CASE WHEN n.activation_value IS NOT NULL THEN 1 ELSE 0 END) as nodes_with_activation,
|
||
sum(CASE WHEN n.activation_value IS NULL THEN 1 ELSE 0 END) as nodes_without_activation,
|
||
avg(n.activation_value) as average_activation,
|
||
sum(CASE WHEN n.activation_value IS NOT NULL AND n.activation_value < $threshold THEN 1 ELSE 0 END) as low_activation_nodes
|
||
"""
|
||
|
||
params = {'threshold': forgetting_threshold}
|
||
if end_user_id:
|
||
params['end_user_id'] = end_user_id
|
||
|
||
activation_results = await connector.execute_query(activation_query, **params)
|
||
|
||
if activation_results:
|
||
result = activation_results[0]
|
||
activation_metrics = {
|
||
'total_nodes': result['total_nodes'] or 0,
|
||
'nodes_with_activation': result['nodes_with_activation'] or 0,
|
||
'nodes_without_activation': result['nodes_without_activation'] or 0,
|
||
'average_activation_value': result['average_activation'],
|
||
'low_activation_nodes': result['low_activation_nodes'] or 0,
|
||
'forgetting_threshold': forgetting_threshold,
|
||
'timestamp': int(datetime.now().timestamp() * 1000)
|
||
}
|
||
else:
|
||
activation_metrics = {
|
||
'total_nodes': 0,
|
||
'nodes_with_activation': 0,
|
||
'nodes_without_activation': 0,
|
||
'average_activation_value': None,
|
||
'low_activation_nodes': 0,
|
||
'forgetting_threshold': forgetting_threshold,
|
||
'timestamp': int(datetime.now().timestamp() * 1000)
|
||
}
|
||
|
||
# 收集节点类型分布
|
||
distribution_query = """
|
||
MATCH (n)
|
||
WHERE (n:Statement OR n:ExtractedEntity OR n:MemorySummary OR n:Chunk)
|
||
"""
|
||
|
||
if end_user_id:
|
||
distribution_query += " AND n.end_user_id = $end_user_id"
|
||
|
||
distribution_query += """
|
||
WITH n,
|
||
CASE
|
||
WHEN n:Statement THEN 'statement'
|
||
WHEN n:ExtractedEntity THEN 'entity'
|
||
WHEN n:MemorySummary THEN 'summary'
|
||
WHEN n:Chunk THEN 'chunk'
|
||
END as node_type
|
||
RETURN
|
||
sum(CASE WHEN node_type = 'statement' THEN 1 ELSE 0 END) as statement_count,
|
||
sum(CASE WHEN node_type = 'entity' THEN 1 ELSE 0 END) as entity_count,
|
||
sum(CASE WHEN node_type = 'summary' THEN 1 ELSE 0 END) as summary_count,
|
||
sum(CASE WHEN node_type = 'chunk' THEN 1 ELSE 0 END) as chunk_count
|
||
"""
|
||
|
||
dist_params = {}
|
||
if end_user_id:
|
||
dist_params['end_user_id'] = end_user_id
|
||
|
||
distribution_results = await connector.execute_query(distribution_query, **dist_params)
|
||
|
||
if distribution_results:
|
||
result = distribution_results[0]
|
||
node_distribution = {
|
||
'statement_count': result['statement_count'] or 0,
|
||
'entity_count': result['entity_count'] or 0,
|
||
'summary_count': result['summary_count'] or 0,
|
||
'chunk_count': result['chunk_count'] or 0
|
||
}
|
||
else:
|
||
node_distribution = {
|
||
'statement_count': 0,
|
||
'entity_count': 0,
|
||
'summary_count': 0,
|
||
'chunk_count': 0
|
||
}
|
||
|
||
# 获取最近7个日期的历史趋势数据(每天取最后一次执行)
|
||
recent_trends = []
|
||
try:
|
||
if end_user_id:
|
||
# 查询所有历史记录
|
||
history_records = self.history_repository.get_recent_by_end_user(
|
||
db=db,
|
||
end_user_id=end_user_id
|
||
)
|
||
|
||
# 按日期分组(一天可能有多次执行,取最后一次)
|
||
from collections import OrderedDict
|
||
daily_records = OrderedDict()
|
||
|
||
# 遍历记录(已按时间降序),每个日期只保留第一次遇到的(即最后一次执行)
|
||
for record in history_records:
|
||
# 提取日期(格式: "1/1", "1/2")- 跨平台兼容
|
||
month = record.execution_time.month
|
||
day = record.execution_time.day
|
||
date_str = f"{month}/{day}"
|
||
|
||
# 如果这个日期还没有记录,添加它(这是该日期最后一次执行)
|
||
if date_str not in daily_records:
|
||
daily_records[date_str] = record
|
||
|
||
# 如果已经有7个不同的日期,停止
|
||
if len(daily_records) >= 7:
|
||
break
|
||
|
||
# 构建趋势数据点(按时间从旧到新排序)
|
||
sorted_dates = sorted(
|
||
daily_records.items(),
|
||
key=lambda x: x[1].execution_time
|
||
)
|
||
|
||
for date_str, record in sorted_dates:
|
||
recent_trends.append({
|
||
'date': date_str,
|
||
'merged_count': record.merged_count,
|
||
'average_activation': record.average_activation_value,
|
||
'total_nodes': record.total_nodes,
|
||
'execution_time': int(record.execution_time.timestamp() * 1000)
|
||
})
|
||
|
||
api_logger.info(f"成功获取最近 {len(recent_trends)} 个日期的历史趋势数据")
|
||
|
||
except Exception as e:
|
||
api_logger.error(f"获取历史趋势数据失败: {str(e)}")
|
||
# 失败时返回空列表,不影响主流程
|
||
|
||
# 获取待遗忘节点列表(前20个满足遗忘条件的节点)
|
||
pending_nodes = []
|
||
try:
|
||
if end_user_id:
|
||
# 验证 min_days_since_access 配置值
|
||
min_days = config.get('min_days_since_access')
|
||
if min_days is None or not isinstance(min_days, (int, float)) or min_days < 0:
|
||
api_logger.warning(
|
||
f"min_days_since_access 配置无效: {min_days}, 使用默认值 7"
|
||
)
|
||
min_days = 7
|
||
|
||
pending_nodes = await self._get_pending_forgetting_nodes(
|
||
connector=connector,
|
||
end_user_id=end_user_id,
|
||
forgetting_threshold=forgetting_threshold,
|
||
min_days_since_access=int(min_days),
|
||
limit=20
|
||
)
|
||
|
||
api_logger.info(f"成功获取 {len(pending_nodes)} 个待遗忘节点")
|
||
|
||
except Exception as e:
|
||
api_logger.error(f"获取待遗忘节点失败: {str(e)}")
|
||
# 失败时返回空列表,不影响主流程
|
||
|
||
# 构建统计信息
|
||
stats = {
|
||
'activation_metrics': activation_metrics,
|
||
'node_distribution': node_distribution,
|
||
'recent_trends': recent_trends,
|
||
'pending_nodes': pending_nodes,
|
||
'timestamp': int(datetime.now().timestamp() * 1000)
|
||
}
|
||
|
||
api_logger.info(
|
||
f"成功获取遗忘引擎统计: total_nodes={stats['activation_metrics']['total_nodes']}, "
|
||
f"low_activation_nodes={stats['activation_metrics']['low_activation_nodes']}, "
|
||
f"trend_days={len(recent_trends)}, pending_nodes={len(pending_nodes)}"
|
||
)
|
||
|
||
return stats
|
||
|
||
async def get_forgetting_curve(
|
||
self,
|
||
db: Session,
|
||
importance_score: float,
|
||
days: int,
|
||
config_id: Optional[UUID] = None
|
||
) -> Dict[str, Any]:
|
||
"""
|
||
获取遗忘曲线数据
|
||
|
||
生成遗忘曲线数据用于可视化,模拟记忆激活值随时间的衰减。
|
||
|
||
Args:
|
||
db: 数据库会话
|
||
importance_score: 重要性分数(0-1)
|
||
days: 模拟天数
|
||
config_id: 配置ID(可选)
|
||
|
||
Returns:
|
||
dict: 包含曲线数据和配置的字典
|
||
"""
|
||
# 获取 ACT-R 计算器
|
||
actr_calculator, _, _, config = await self._get_forgetting_components(db, config_id)
|
||
|
||
# 生成遗忘曲线数据
|
||
initial_time = datetime.now()
|
||
curve_data = actr_calculator.get_forgetting_curve(
|
||
initial_time=initial_time,
|
||
importance_score=importance_score,
|
||
days=days
|
||
)
|
||
|
||
api_logger.info(
|
||
f"成功生成遗忘曲线数据: {len(curve_data)} 个数据点"
|
||
)
|
||
|
||
return {
|
||
'curve_data': curve_data,
|
||
'config': {
|
||
'decay_constant': config['decay_constant'],
|
||
'forgetting_rate': config['forgetting_rate'],
|
||
'offset': config['offset'],
|
||
'importance_score': importance_score,
|
||
'days': days
|
||
}
|
||
}
|