* 用户详情优化
* 用户详情优化
* 用户详情优化
* 用户详情优化
* 读取的接口,去掉全局锁
* 输出数组
* 反思优化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>
946 lines
33 KiB
Python
946 lines
33 KiB
Python
"""
|
||
工作流服务层
|
||
"""
|
||
import datetime
|
||
import logging
|
||
import uuid
|
||
from typing import Any, Annotated, AsyncGenerator, Optional
|
||
|
||
from deprecated import deprecated
|
||
from fastapi import Depends
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.error_codes import BizCode
|
||
from app.core.exceptions import BusinessException
|
||
from app.core.workflow.validator import validate_workflow_config
|
||
from app.db import get_db
|
||
from app.models.workflow_model import WorkflowConfig, WorkflowExecution
|
||
from app.repositories.workflow_repository import (
|
||
WorkflowConfigRepository,
|
||
WorkflowExecutionRepository,
|
||
WorkflowNodeExecutionRepository
|
||
)
|
||
from app.schemas import DraftRunRequest
|
||
from app.services.conversation_service import ConversationService
|
||
from app.services.multi_agent_service import convert_uuids_to_str
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class WorkflowService:
|
||
"""工作流服务"""
|
||
|
||
def __init__(self, db: Session):
|
||
self.db = db
|
||
self.config_repo = WorkflowConfigRepository(db)
|
||
self.execution_repo = WorkflowExecutionRepository(db)
|
||
self.node_execution_repo = WorkflowNodeExecutionRepository(db)
|
||
self.conversation_service = ConversationService(db)
|
||
|
||
# ==================== 配置管理 ====================
|
||
|
||
def create_workflow_config(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
nodes: list[dict[str, Any]],
|
||
edges: list[dict[str, Any]],
|
||
variables: list[dict[str, Any]] | None = None,
|
||
execution_config: dict[str, Any] | None = None,
|
||
triggers: list[dict[str, Any]] | None = None,
|
||
validate: bool = True
|
||
) -> WorkflowConfig:
|
||
"""创建工作流配置
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
nodes: 节点列表
|
||
edges: 边列表
|
||
variables: 变量列表
|
||
execution_config: 执行配置
|
||
triggers: 触发器列表
|
||
validate: 是否验证配置
|
||
|
||
Returns:
|
||
工作流配置
|
||
|
||
Raises:
|
||
BusinessException: 配置无效时抛出
|
||
"""
|
||
# 构建配置字典
|
||
config_dict = {
|
||
"nodes": nodes,
|
||
"edges": edges,
|
||
"variables": variables or [],
|
||
"execution_config": execution_config or {},
|
||
"triggers": triggers or []
|
||
}
|
||
|
||
# 验证配置
|
||
if validate:
|
||
is_valid, errors = validate_workflow_config(config_dict, for_publish=False)
|
||
if not is_valid:
|
||
logger.warning(f"工作流配置验证失败: {errors}")
|
||
raise BusinessException(
|
||
code=BizCode.INVALID_PARAMETER,
|
||
message=f"工作流配置无效: {'; '.join(errors)}"
|
||
)
|
||
|
||
# 创建或更新配置
|
||
config = self.config_repo.create_or_update(
|
||
app_id=app_id,
|
||
nodes=nodes,
|
||
edges=edges,
|
||
variables=variables,
|
||
execution_config=execution_config,
|
||
triggers=triggers
|
||
)
|
||
|
||
logger.info(f"创建工作流配置成功: app_id={app_id}, config_id={config.id}")
|
||
return config
|
||
|
||
def get_workflow_config(self, app_id: uuid.UUID) -> WorkflowConfig | None:
|
||
"""获取工作流配置
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
|
||
Returns:
|
||
工作流配置或 None
|
||
"""
|
||
return self.config_repo.get_by_app_id(app_id)
|
||
|
||
def update_workflow_config(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
nodes: list[dict[str, Any]] | None = None,
|
||
edges: list[dict[str, Any]] | None = None,
|
||
variables: list[dict[str, Any]] | None = None,
|
||
execution_config: dict[str, Any] | None = None,
|
||
triggers: list[dict[str, Any]] | None = None,
|
||
validate: bool = True
|
||
) -> WorkflowConfig:
|
||
"""更新工作流配置
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
nodes: 节点列表
|
||
edges: 边列表
|
||
variables: 变量列表
|
||
execution_config: 执行配置
|
||
triggers: 触发器列表
|
||
validate: 是否验证配置
|
||
|
||
Returns:
|
||
工作流配置
|
||
|
||
Raises:
|
||
BusinessException: 配置不存在或无效时抛出
|
||
"""
|
||
# 获取现有配置
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
code=BizCode.NOT_FOUND,
|
||
message=f"工作流配置不存在: app_id={app_id}"
|
||
)
|
||
|
||
# 合并配置
|
||
updated_nodes = nodes if nodes is not None else config.nodes
|
||
updated_edges = edges if edges is not None else config.edges
|
||
updated_variables = variables if variables is not None else config.variables
|
||
updated_execution_config = execution_config if execution_config is not None else config.execution_config
|
||
updated_triggers = triggers if triggers is not None else config.triggers
|
||
|
||
# 构建配置字典
|
||
config_dict = {
|
||
"nodes": updated_nodes,
|
||
"edges": updated_edges,
|
||
"variables": updated_variables,
|
||
"execution_config": updated_execution_config,
|
||
"triggers": updated_triggers
|
||
}
|
||
|
||
# 验证配置
|
||
if validate:
|
||
is_valid, errors = validate_workflow_config(config_dict, for_publish=False)
|
||
if not is_valid:
|
||
logger.warning(f"工作流配置验证失败: {errors}")
|
||
raise BusinessException(
|
||
code=BizCode.INVALID_PARAMETER,
|
||
message=f"工作流配置无效: {'; '.join(errors)}"
|
||
)
|
||
|
||
# 更新配置
|
||
config = self.config_repo.create_or_update(
|
||
app_id=app_id,
|
||
nodes=updated_nodes,
|
||
edges=updated_edges,
|
||
variables=updated_variables,
|
||
execution_config=updated_execution_config,
|
||
triggers=updated_triggers
|
||
)
|
||
|
||
logger.info(f"更新工作流配置成功: app_id={app_id}, config_id={config.id}")
|
||
return config
|
||
|
||
def delete_workflow_config(self, app_id: uuid.UUID) -> bool:
|
||
"""删除工作流配置
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
|
||
Returns:
|
||
是否删除成功
|
||
"""
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
return False
|
||
config.is_active = False
|
||
logger.info(f"删除工作流配置成功: app_id={app_id}, config_id={config.id}")
|
||
return True
|
||
|
||
def check_config(self, app_id: uuid.UUID) -> WorkflowConfig:
|
||
"""检查工作流配置的完整性
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
|
||
Raises:
|
||
BusinessException: 配置不完整或不存在时抛出
|
||
"""
|
||
|
||
# 1. 检查多智能体配置是否存在
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
"工作流配置不存在,无法运行",
|
||
BizCode.CONFIG_MISSING
|
||
)
|
||
# validator 现在支持直接接受 Pydantic 模型
|
||
is_valid, errors = validate_workflow_config(config, for_publish=False)
|
||
if not is_valid:
|
||
logger.warning(f"工作流配置验证失败: {errors}")
|
||
raise BusinessException(
|
||
code=BizCode.INVALID_PARAMETER,
|
||
message=f"工作流配置无效: {'; '.join(errors)}"
|
||
)
|
||
return config
|
||
|
||
def validate_workflow_config_for_publish(
|
||
self,
|
||
app_id: uuid.UUID
|
||
) -> tuple[bool, list[str]]:
|
||
"""验证工作流配置是否可以发布
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
|
||
Returns:
|
||
(is_valid, errors): 是否有效和错误列表
|
||
|
||
Raises:
|
||
BusinessException: 配置不存在时抛出
|
||
"""
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
code=BizCode.NOT_FOUND,
|
||
message=f"工作流配置不存在: app_id={app_id}"
|
||
)
|
||
|
||
config_dict = {
|
||
"nodes": config.nodes,
|
||
"edges": config.edges,
|
||
"variables": config.variables,
|
||
"execution_config": config.execution_config,
|
||
"triggers": config.triggers
|
||
}
|
||
|
||
return validate_workflow_config(config_dict, for_publish=True)
|
||
|
||
# ==================== 执行管理 ====================
|
||
|
||
def create_execution(
|
||
self,
|
||
workflow_config_id: uuid.UUID,
|
||
app_id: uuid.UUID,
|
||
trigger_type: str,
|
||
release_id: uuid.UUID | None = None,
|
||
triggered_by: uuid.UUID | None = None,
|
||
conversation_id: uuid.UUID | None = None,
|
||
input_data: dict[str, Any] | None = None
|
||
) -> WorkflowExecution:
|
||
"""创建工作流执行记录
|
||
|
||
Args:
|
||
release_id: 应用发布 ID
|
||
workflow_config_id: 工作流配置 ID
|
||
app_id: 应用 ID
|
||
trigger_type: 触发类型
|
||
triggered_by: 触发用户 ID
|
||
conversation_id: 会话 ID
|
||
input_data: 输入数据
|
||
|
||
Returns:
|
||
执行记录
|
||
"""
|
||
# 生成执行 ID
|
||
execution_id = f"exec_{uuid.uuid4().hex[:16]}"
|
||
|
||
execution = WorkflowExecution(
|
||
workflow_config_id=workflow_config_id,
|
||
app_id=app_id,
|
||
release_id=release_id,
|
||
conversation_id=conversation_id,
|
||
execution_id=execution_id,
|
||
trigger_type=trigger_type,
|
||
triggered_by=triggered_by,
|
||
input_data=input_data or {},
|
||
status="pending"
|
||
)
|
||
|
||
self.db.add(execution)
|
||
self.db.commit()
|
||
self.db.refresh(execution)
|
||
|
||
logger.info(f"创建工作流执行记录: execution_id={execution_id}")
|
||
return execution
|
||
|
||
def get_execution(self, execution_id: str) -> WorkflowExecution | None:
|
||
"""获取执行记录
|
||
|
||
Args:
|
||
execution_id: 执行 ID
|
||
|
||
Returns:
|
||
执行记录或 None
|
||
"""
|
||
return self.execution_repo.get_by_execution_id(execution_id)
|
||
|
||
def get_executions_by_app(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
limit: int = 50,
|
||
offset: int = 0
|
||
) -> list[WorkflowExecution]:
|
||
"""获取应用的执行记录列表
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
limit: 返回数量限制
|
||
offset: 偏移量
|
||
|
||
Returns:
|
||
执行记录列表
|
||
"""
|
||
return self.execution_repo.get_by_app_id(app_id, limit, offset)
|
||
|
||
def update_execution_status(
|
||
self,
|
||
execution_id: str,
|
||
status: str,
|
||
token_usage: int | None = None,
|
||
output_data: dict[str, Any] | None = None,
|
||
error_message: str | None = None,
|
||
error_node_id: str | None = None
|
||
) -> WorkflowExecution:
|
||
"""更新执行状态
|
||
|
||
Args:
|
||
execution_id: 执行 ID
|
||
status: 状态
|
||
token_usage: token消耗
|
||
output_data: 输出数据
|
||
error_message: 错误信息
|
||
error_node_id: 出错节点 ID
|
||
|
||
Returns:
|
||
执行记录
|
||
|
||
Raises:
|
||
BusinessException: 执行记录不存在时抛出
|
||
"""
|
||
execution = self.get_execution(execution_id)
|
||
if not execution:
|
||
raise BusinessException(
|
||
code=BizCode.NOT_FOUND,
|
||
message=f"执行记录不存在: execution_id={execution_id}"
|
||
)
|
||
|
||
execution.status = status
|
||
if token_usage is not None:
|
||
execution.token_usage = token_usage
|
||
if output_data is not None:
|
||
execution.output_data = convert_uuids_to_str(output_data)
|
||
if error_message is not None:
|
||
execution.error_message = error_message
|
||
if error_node_id is not None:
|
||
execution.error_node_id = error_node_id
|
||
|
||
# 如果是完成状态,计算耗时
|
||
if status in ["completed", "failed", "cancelled", "timeout"]:
|
||
if not execution.completed_at:
|
||
execution.completed_at = datetime.datetime.now()
|
||
elapsed = (execution.completed_at - execution.started_at).total_seconds()
|
||
execution.elapsed_time = elapsed
|
||
|
||
self.db.commit()
|
||
self.db.refresh(execution)
|
||
|
||
logger.info(f"更新执行状态: execution_id={execution_id}, status={status}")
|
||
return execution
|
||
|
||
def get_execution_statistics(self, app_id: uuid.UUID) -> dict[str, Any]:
|
||
"""获取执行统计信息
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
|
||
Returns:
|
||
统计信息
|
||
"""
|
||
total = self.execution_repo.count_by_app_id(app_id)
|
||
completed = self.execution_repo.count_by_status(app_id, "completed")
|
||
failed = self.execution_repo.count_by_status(app_id, "failed")
|
||
running = self.execution_repo.count_by_status(app_id, "running")
|
||
|
||
return {
|
||
"total": total,
|
||
"completed": completed,
|
||
"failed": failed,
|
||
"running": running,
|
||
"success_rate": completed / total if total > 0 else 0
|
||
}
|
||
|
||
# ==================== 工作流执行 ====================
|
||
|
||
async def run(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
payload: DraftRunRequest,
|
||
config: WorkflowConfig,
|
||
workspace_id: uuid.UUID,
|
||
release_id: uuid.UUID | None = None,
|
||
):
|
||
"""运行工作流
|
||
|
||
Args:
|
||
release_id: 发布 ID
|
||
workspace_id:工作空间 ID
|
||
config: 配置
|
||
payload:
|
||
app_id: 应用 ID
|
||
|
||
Returns:
|
||
执行结果(非流式)
|
||
|
||
Raises:
|
||
BusinessException: 配置不存在或执行失败时抛出
|
||
"""
|
||
# 1. 获取工作流配置
|
||
if not config:
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
code=BizCode.CONFIG_MISSING,
|
||
message=f"工作流配置不存在: app_id={app_id}"
|
||
)
|
||
input_data = {"message": payload.message, "variables": payload.variables,
|
||
"conversation_id": payload.conversation_id}
|
||
|
||
# 转换 user_id 为 UUID
|
||
triggered_by_uuid = None
|
||
if payload.user_id:
|
||
try:
|
||
triggered_by_uuid = uuid.UUID(payload.user_id)
|
||
except (ValueError, AttributeError):
|
||
logger.warning(f"无效的 user_id 格式: {payload.user_id}")
|
||
|
||
# 转换 conversation_id 为 UUID
|
||
conversation_id_uuid = None
|
||
if payload.conversation_id:
|
||
try:
|
||
conversation_id_uuid = uuid.UUID(payload.conversation_id)
|
||
except (ValueError, AttributeError):
|
||
logger.warning(f"无效的 conversation_id 格式: {payload.conversation_id}")
|
||
|
||
# 2. 创建执行记录
|
||
execution = self.create_execution(
|
||
workflow_config_id=config.id,
|
||
app_id=app_id,
|
||
trigger_type="manual",
|
||
triggered_by=None,
|
||
conversation_id=conversation_id_uuid,
|
||
input_data=input_data,
|
||
release_id=release_id,
|
||
)
|
||
|
||
# 3. 构建工作流配置字典
|
||
workflow_config_dict = {
|
||
"nodes": config.nodes,
|
||
"edges": config.edges,
|
||
"variables": config.variables,
|
||
"execution_config": config.execution_config
|
||
}
|
||
|
||
# 4. 获取工作空间 ID(从 app 获取)
|
||
|
||
# 5. 执行工作流
|
||
from app.core.workflow.executor import execute_workflow
|
||
|
||
try:
|
||
# 更新状态为运行中
|
||
self.update_execution_status(execution.execution_id, "running")
|
||
|
||
executions = self.execution_repo.get_by_conversation_id(conversation_id=conversation_id_uuid)
|
||
|
||
for exec_res in executions:
|
||
if exec_res.status == "completed":
|
||
last_state = exec_res.output_data
|
||
if isinstance(last_state, dict):
|
||
variables = last_state.get("variables", {})
|
||
conv_vars = variables.get("conv", {})
|
||
input_data["conv"] = conv_vars
|
||
input_data["conv_messages"] = last_state.get("messages") or []
|
||
break
|
||
|
||
init_message_length = len(input_data.get("conv_messages", []))
|
||
|
||
result = await execute_workflow(
|
||
workflow_config=workflow_config_dict,
|
||
input_data=input_data,
|
||
execution_id=execution.execution_id,
|
||
workspace_id=str(workspace_id),
|
||
user_id=payload.user_id
|
||
)
|
||
|
||
# 更新执行结果
|
||
if result.get("status") == "completed":
|
||
token_usage = result.get("token_usage", {}) or {}
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"completed",
|
||
output_data=result,
|
||
token_usage=token_usage.get("total_tokens", None)
|
||
)
|
||
final_messages = result.get("messages", [])[init_message_length:]
|
||
for message in final_messages:
|
||
self.conversation_service.add_message(
|
||
conversation_id=conversation_id_uuid,
|
||
role=message["role"],
|
||
content=message["content"],
|
||
meta_data=None if message["role"] == "user" else {"usage": token_usage}
|
||
)
|
||
logger.info(f"Workflow Run Success, "
|
||
f"execution_id: {execution.execution_id}, message count: {len(final_messages)}")
|
||
else:
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
error_message=result.get("error")
|
||
)
|
||
|
||
# 返回增强的响应结构
|
||
return {
|
||
"execution_id": execution.execution_id,
|
||
"status": result.get("status"),
|
||
"variables": result.get("variables"),
|
||
"messages": result.get("messages"),
|
||
"output": result.get("output"), # 最终输出(字符串)
|
||
"output_data": result.get("node_outputs", {}), # 所有节点输出(详细数据)
|
||
"conversation_id": result.get("conversation_id"), # 所有节点输出(详细数据)payload., # 会话 ID
|
||
"error_message": result.get("error"),
|
||
"elapsed_time": result.get("elapsed_time"),
|
||
"token_usage": result.get("token_usage")
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"工作流执行失败: execution_id={execution.execution_id}, error={e}", exc_info=True)
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
error_message=str(e)
|
||
)
|
||
raise BusinessException(
|
||
code=BizCode.INTERNAL_ERROR,
|
||
message=f"工作流执行失败: {str(e)}"
|
||
)
|
||
|
||
async def run_stream(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
payload: DraftRunRequest,
|
||
config: WorkflowConfig,
|
||
workspace_id: uuid.UUID,
|
||
release_id: Optional[uuid.UUID] = None,
|
||
):
|
||
"""运行工作流(流式)
|
||
|
||
Args:
|
||
release_id: 发布id
|
||
workspace_id:
|
||
app_id: 应用 ID
|
||
payload: 请求对象(包含 message, variables, conversation_id 等)
|
||
config: 存储类型(可选)
|
||
|
||
Yields:
|
||
SSE 格式的流式事件
|
||
|
||
Raises:
|
||
BusinessException: 配置不存在或执行失败时抛出
|
||
"""
|
||
# 1. 获取工作流配置
|
||
if not config:
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
code=BizCode.CONFIG_MISSING,
|
||
message=f"工作流配置不存在: app_id={app_id}"
|
||
)
|
||
input_data = {"message": payload.message, "variables": payload.variables,
|
||
"conversation_id": payload.conversation_id}
|
||
|
||
# 转换 user_id 为 UUID
|
||
triggered_by_uuid = None
|
||
if payload.user_id:
|
||
try:
|
||
triggered_by_uuid = uuid.UUID(payload.user_id)
|
||
except (ValueError, AttributeError):
|
||
logger.warning(f"无效的 user_id 格式: {payload.user_id}")
|
||
|
||
# 转换 conversation_id 为 UUID
|
||
conversation_id_uuid = None
|
||
if payload.conversation_id:
|
||
try:
|
||
conversation_id_uuid = uuid.UUID(payload.conversation_id)
|
||
except (ValueError, AttributeError):
|
||
logger.warning(f"无效的 conversation_id 格式: {payload.conversation_id}")
|
||
|
||
# 2. 创建执行记录
|
||
execution = self.create_execution(
|
||
workflow_config_id=config.id,
|
||
app_id=app_id,
|
||
trigger_type="manual",
|
||
triggered_by=None,
|
||
conversation_id=conversation_id_uuid,
|
||
input_data=input_data,
|
||
release_id=release_id,
|
||
)
|
||
|
||
# 3. 构建工作流配置字典
|
||
workflow_config_dict = {
|
||
"nodes": config.nodes,
|
||
"edges": config.edges,
|
||
"variables": config.variables,
|
||
"execution_config": config.execution_config
|
||
}
|
||
|
||
# 4. 获取工作空间 ID(从 app 获取)
|
||
|
||
# 5. 流式执行工作流
|
||
|
||
try:
|
||
# 更新状态为运行中
|
||
self.update_execution_status(execution.execution_id, "running")
|
||
executions = self.execution_repo.get_by_conversation_id(conversation_id=conversation_id_uuid)
|
||
|
||
for exec_res in executions:
|
||
if exec_res.status == "completed":
|
||
last_state = exec_res.output_data
|
||
if isinstance(last_state, dict):
|
||
variables = last_state.get("variables", {})
|
||
conv_vars = variables.get("conv", {})
|
||
input_data["conv"] = conv_vars
|
||
input_data["conv_messages"] = last_state.get("messages") or []
|
||
break
|
||
init_message_length = len(input_data.get("conv_messages", []))
|
||
from app.core.workflow.executor import execute_workflow_stream
|
||
|
||
async for event in execute_workflow_stream(
|
||
workflow_config=workflow_config_dict,
|
||
input_data=input_data,
|
||
execution_id=execution.execution_id,
|
||
workspace_id=str(workspace_id),
|
||
user_id=payload.user_id
|
||
):
|
||
if event.get("event") == "workflow_end":
|
||
|
||
status = event.get("data", {}).get("status")
|
||
token_usage = event.get("data", {}).get("token_usage", {}) or {}
|
||
if status == "completed":
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"completed",
|
||
output_data=event.get("data"),
|
||
token_usage=token_usage.get("total_tokens", None)
|
||
)
|
||
final_messages = event.get("data", {}).get("messages", [])[init_message_length:]
|
||
for message in final_messages:
|
||
self.conversation_service.add_message(
|
||
conversation_id=conversation_id_uuid,
|
||
role=message["role"],
|
||
content=message["content"],
|
||
meta_data=None if message["role"] == "user" else {"usage": token_usage}
|
||
)
|
||
logger.info(f"Workflow Run Success, "
|
||
f"execution_id: {execution.execution_id}, message count: {len(final_messages)}")
|
||
elif status == "failed":
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
output_data=event.get("data")
|
||
)
|
||
else:
|
||
logger.error(f"unexpect workflow run status, status: {status}")
|
||
yield event
|
||
|
||
except Exception as e:
|
||
logger.error(f"工作流流式执行失败: execution_id={execution.execution_id}, error={e}", exc_info=True)
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
error_message=str(e)
|
||
)
|
||
# 发送错误事件
|
||
yield {
|
||
"event": "error",
|
||
"data": {
|
||
"execution_id": execution.execution_id,
|
||
"error": str(e)
|
||
}
|
||
}
|
||
|
||
@deprecated(reason="This method is deprecated. "
|
||
"Please use WorkflowService.run / run_stream instead.")
|
||
async def run_workflow(
|
||
self,
|
||
app_id: uuid.UUID,
|
||
input_data: dict[str, Any],
|
||
triggered_by: uuid.UUID,
|
||
conversation_id: uuid.UUID | None = None,
|
||
stream: bool = False
|
||
) -> AsyncGenerator | dict:
|
||
"""运行工作流
|
||
|
||
Args:
|
||
app_id: 应用 ID
|
||
input_data: 输入数据(包含 message 和 variables)
|
||
triggered_by: 触发用户 ID
|
||
conversation_id: 会话 ID(可选)
|
||
stream: 是否流式返回
|
||
|
||
Returns:
|
||
执行结果(非流式)或生成器(流式)
|
||
|
||
Raises:
|
||
BusinessException: 配置不存在或执行失败时抛出
|
||
"""
|
||
# 1. 获取工作流配置
|
||
config = self.get_workflow_config(app_id)
|
||
if not config:
|
||
raise BusinessException(
|
||
code=BizCode.NOT_FOUND,
|
||
message=f"工作流配置不存在: app_id={app_id}"
|
||
)
|
||
|
||
# 2. 创建执行记录
|
||
execution = self.create_execution(
|
||
workflow_config_id=config.id,
|
||
app_id=app_id,
|
||
trigger_type="manual",
|
||
triggered_by=triggered_by,
|
||
conversation_id=conversation_id,
|
||
input_data=input_data
|
||
)
|
||
|
||
# 3. 构建工作流配置字典
|
||
workflow_config_dict = {
|
||
"nodes": config.nodes,
|
||
"edges": config.edges,
|
||
"variables": config.variables,
|
||
"execution_config": config.execution_config
|
||
}
|
||
|
||
# 4. 获取工作空间 ID(从 app 获取)
|
||
from app.models import App
|
||
app = self.db.query(App).filter(
|
||
App.id == app_id,
|
||
App.is_active.is_(True)
|
||
).first()
|
||
if not app:
|
||
raise BusinessException(
|
||
code=BizCode.NOT_FOUND,
|
||
message=f"应用不存在: app_id={app_id}"
|
||
)
|
||
|
||
# 5. 执行工作流
|
||
from app.core.workflow.executor import execute_workflow
|
||
|
||
try:
|
||
# 更新状态为运行中
|
||
self.update_execution_status(execution.execution_id, "running")
|
||
|
||
if stream:
|
||
# 流式执行
|
||
return self._run_workflow_stream(
|
||
workflow_config_dict,
|
||
input_data,
|
||
execution.execution_id,
|
||
str(app.workspace_id),
|
||
str(triggered_by)
|
||
)
|
||
else:
|
||
# 非流式执行
|
||
result = await execute_workflow(
|
||
workflow_config=workflow_config_dict,
|
||
input_data=input_data,
|
||
execution_id=execution.execution_id,
|
||
workspace_id=str(app.workspace_id),
|
||
user_id=str(triggered_by)
|
||
)
|
||
|
||
# 更新执行结果
|
||
if result.get("status") == "completed":
|
||
token_usage = result.get("data").get("token_usage", {}) or {}
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"completed",
|
||
output_data=result.get("node_outputs", {}),
|
||
token_usage=token_usage.get("total_tokens", None)
|
||
)
|
||
else:
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
error_message=result.get("error")
|
||
)
|
||
|
||
# 返回增强的响应结构
|
||
return {
|
||
"execution_id": execution.execution_id,
|
||
"status": result.get("status"),
|
||
"output": result.get("output"), # 最终输出(字符串)
|
||
"output_data": result.get("node_outputs", {}), # 所有节点输出(详细数据)
|
||
"error_message": result.get("error"),
|
||
"elapsed_time": result.get("elapsed_time"),
|
||
"token_usage": result.get("token_usage")
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.error(f"工作流执行失败: execution_id={execution.execution_id}, error={e}", exc_info=True)
|
||
self.update_execution_status(
|
||
execution.execution_id,
|
||
"failed",
|
||
error_message=str(e)
|
||
)
|
||
raise BusinessException(
|
||
code=BizCode.INTERNAL_ERROR,
|
||
message=f"工作流执行失败: {str(e)}"
|
||
)
|
||
|
||
def _clean_event_for_json(self, event: dict[str, Any]) -> dict[str, Any]:
|
||
"""清理事件数据,移除不可序列化的对象
|
||
|
||
Args:
|
||
event: 原始事件数据
|
||
|
||
Returns:
|
||
可序列化的事件数据
|
||
"""
|
||
from langchain_core.messages import BaseMessage
|
||
|
||
def clean_value(value):
|
||
"""递归清理值"""
|
||
if isinstance(value, BaseMessage):
|
||
# 将 Message 对象转换为字典
|
||
return {
|
||
"type": value.__class__.__name__,
|
||
"content": value.content,
|
||
}
|
||
elif isinstance(value, dict):
|
||
return {k: clean_value(v) for k, v in value.items()}
|
||
elif isinstance(value, list):
|
||
return [clean_value(item) for item in value]
|
||
elif isinstance(value, (str, int, float, bool, type(None))):
|
||
return value
|
||
else:
|
||
# 其他不可序列化的对象转换为字符串
|
||
return str(value)
|
||
|
||
return clean_value(event)
|
||
|
||
@deprecated(reason="This method is deprecated. Please use WorkflowService.run_stream instead.")
|
||
async def _run_workflow_stream(
|
||
self,
|
||
workflow_config: dict[str, Any],
|
||
input_data: dict[str, Any],
|
||
execution_id: str,
|
||
workspace_id: str,
|
||
user_id: str):
|
||
"""运行工作流(流式,内部方法)
|
||
|
||
Args:
|
||
workflow_config: 工作流配置
|
||
input_data: 输入数据
|
||
execution_id: 执行 ID
|
||
workspace_id: 工作空间 ID
|
||
user_id: 用户 ID
|
||
|
||
Yields:
|
||
流式事件(格式:{"event": "<type>", "data": {...}})
|
||
"""
|
||
from app.core.workflow.executor import execute_workflow_stream
|
||
|
||
try:
|
||
async for event in execute_workflow_stream(
|
||
workflow_config=workflow_config,
|
||
input_data=input_data,
|
||
execution_id=execution_id,
|
||
workspace_id=workspace_id,
|
||
user_id=user_id
|
||
):
|
||
# 直接转发事件(executor 已经返回正确格式)
|
||
if event.get("event") == "workflow_end":
|
||
token_usage = event.get("data").get("token_usage", {}) or {}
|
||
status = event.get("data", {}).get("status")
|
||
if status == "completed":
|
||
self.update_execution_status(
|
||
execution_id,
|
||
"completed",
|
||
output_data=event.get("data"),
|
||
token_usage=token_usage.get("total_tokens", None)
|
||
)
|
||
elif status == "failed":
|
||
self.update_execution_status(
|
||
execution_id,
|
||
"failed",
|
||
output_data=event.get("data")
|
||
)
|
||
else:
|
||
logger.error(f"unexpect workflow run status, status: {status}")
|
||
yield event
|
||
|
||
except Exception as e:
|
||
logger.error(f"工作流流式执行失败: execution_id={execution_id}, error={e}", exc_info=True)
|
||
self.update_execution_status(
|
||
execution_id,
|
||
"failed",
|
||
error_message=str(e)
|
||
)
|
||
yield {
|
||
"event": "error",
|
||
"data": {
|
||
"execution_id": execution_id,
|
||
"error": str(e)
|
||
}
|
||
}
|
||
|
||
|
||
# ==================== 依赖注入函数 ====================
|
||
|
||
def get_workflow_service(
|
||
db: Annotated[Session, Depends(get_db)]
|
||
) -> WorkflowService:
|
||
"""获取工作流服务(依赖注入)"""
|
||
return WorkflowService(db)
|