wisruntime 0.4.2__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wisruntime-0.4.2/wisruntime.egg-info → wisruntime-0.5.0}/PKG-INFO +57 -6
- {wisruntime-0.4.2 → wisruntime-0.5.0}/README.md +56 -5
- {wisruntime-0.4.2 → wisruntime-0.5.0}/pyproject.toml +1 -1
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/__init__.py +1 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/langgraph/__init__.py +9 -0
- wisruntime-0.5.0/wisruntime/adapters/langgraph/_utils.py +34 -0
- wisruntime-0.5.0/wisruntime/adapters/langgraph/memory_middleware.py +223 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/langgraph/wrap_agent.py +18 -0
- wisruntime-0.5.0/wisruntime/adapters/memory/__init__.py +35 -0
- wisruntime-0.5.0/wisruntime/memory/__init__.py +17 -0
- wisruntime-0.5.0/wisruntime/memory/client.py +108 -0
- wisruntime-0.5.0/wisruntime/memory/formatter.py +67 -0
- wisruntime-0.5.0/wisruntime/memory/model.py +45 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0/wisruntime.egg-info}/PKG-INFO +57 -6
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime.egg-info/SOURCES.txt +7 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/MANIFEST.in +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/setup.cfg +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/__init__.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/langgraph/builtin.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/langgraph/converter.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/adapters/langgraph/tool_adapter.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/config.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/conversation/__init__.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/conversation/client.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/conversation/model.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/conversation/recorder.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/exceptions.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/model.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/server/__init__.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/server/dify_handler.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/server/invoker.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/server/protocol.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/server/server.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/skill/__init__.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/skill/client.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/skill/model.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/tool/__init__.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/tool/client.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/tool/model.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime/tool/protocol.py +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime.egg-info/dependency_links.txt +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime.egg-info/requires.txt +0 -0
- {wisruntime-0.4.2 → wisruntime-0.5.0}/wisruntime.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wisruntime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: 企业级 Agent Runtime SDK — 让 AI 工程师专注 Agent 能力开发,无需关心底层基础设施
|
|
5
5
|
Author: WIS Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -713,6 +713,30 @@ def create_server(
|
|
|
713
713
|
└─────────────────────────────────────────────────────────────────────┘
|
|
714
714
|
|
|
715
715
|
故事 6 ──────────────────────────────────────────────────────────────
|
|
716
|
+
│ 用户故事: 作为 AI 工程师,我可以用 WishubMemoryMiddleware 为我的 │
|
|
717
|
+
│ DeepAgents Agent 添加长期记忆能力,自动检索用户历史对话记忆 │
|
|
718
|
+
│ │
|
|
719
|
+
│ 验收标准: │
|
|
720
|
+
│ from deepagents import create_deep_agent │
|
|
721
|
+
│ from wisruntime.adapters.langgraph import WishubMemoryMiddleware │
|
|
722
|
+
│ from wisruntime.memory import WishubMemoryClient │
|
|
723
|
+
│ │
|
|
724
|
+
│ memory_client = WishubMemoryClient( │
|
|
725
|
+
│ base_url="https://wishub.example.com", │
|
|
726
|
+
│ api_key="your-api-key" │
|
|
727
|
+
│ ) │
|
|
728
|
+
│ middleware = WishubMemoryMiddleware( │
|
|
729
|
+
│ client=memory_client, │
|
|
730
|
+
│ namespaces=["user_profile", "preferences"], │
|
|
731
|
+
│ top_k=5 │
|
|
732
|
+
│ ) │
|
|
733
|
+
│ agent = create_deep_agent(model=model, middlewares=[middleware]) │
|
|
734
|
+
│ # → Agent 在每次对话前自动检索相关历史记忆 │
|
|
735
|
+
│ # → 记忆以结构化 XML 格式注入系统提示词 │
|
|
736
|
+
│ # → 支持优雅降级: API 超时/失败时返回空列表,不影响对话 │
|
|
737
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
738
|
+
|
|
739
|
+
故事 7 ──────────────────────────────────────────────────────────────
|
|
716
740
|
│ 用户故事: 作为 AI 工程师,我的 Agent 对话历史可以自动持久化到 │
|
|
717
741
|
│ WIS Hub Checkpoint 服务,支持断点续传 │
|
|
718
742
|
│ │
|
|
@@ -782,16 +806,36 @@ Phase 3: 平台能力接入 (MCP ✅ 已完成 — v0.3)
|
|
|
782
806
|
分页发现 / 详情 / 302 预签名下载 / 严格 SemVer 版本, create_skill_sources() deepagents 集成
|
|
783
807
|
□ SandboxClient + Sandbox 数据模型
|
|
784
808
|
|
|
809
|
+
Phase 3.5: 记忆能力接入 (✅ 已完成 — v0.4)
|
|
810
|
+
═══════════════════════════════
|
|
811
|
+
目标: 用户故事 6 — WishubMemoryMiddleware 长期记忆
|
|
812
|
+
|
|
813
|
+
✅ memory/ 模块: MemoryItem, RetrieveRequest, RetrieveResponse 数据模型
|
|
814
|
+
✅ WishubMemoryClient — 异步 HTTP 客户端,对接 Wishub Memory API
|
|
815
|
+
- 优雅降级: 超时/错误返回空列表,不影响对话
|
|
816
|
+
- 完整日志: debug/info/warning/error 四级
|
|
817
|
+
✅ MemoryPromptFormatter — 记忆列表 → 结构化 XML 提示词片段
|
|
818
|
+
- 支持相关度评分显示
|
|
819
|
+
- 空记忆占位符
|
|
820
|
+
- 可选使用指南
|
|
821
|
+
✅ WishubMemoryMiddleware — DeepAgents 中间件
|
|
822
|
+
- abefore_agent: 检索记忆并存入 state
|
|
823
|
+
- modify_request: 注入记忆到系统提示词
|
|
824
|
+
- 跳过重复检索
|
|
825
|
+
- user_id/query 缺失时优雅处理
|
|
826
|
+
✅ wrap_agent config 注入 — user_id, thread_id 传递到 LangGraph config.configurable
|
|
827
|
+
✅ 完整测试覆盖 (39 个测试用例全部通过)
|
|
828
|
+
|
|
785
829
|
Phase 4: 高级特性
|
|
786
830
|
════════════════
|
|
787
|
-
目标: 用户故事
|
|
831
|
+
目标: 用户故事 7 — Checkpoint、多协议
|
|
788
832
|
|
|
789
833
|
□ CheckpointClient + 自动持久化
|
|
790
834
|
□ AGUIHandler
|
|
791
835
|
□ Stop endpoint
|
|
792
836
|
□ ApiKeyMiddleware
|
|
793
837
|
|
|
794
|
-
Phase
|
|
838
|
+
Phase 5: 多框架 + 多协议
|
|
795
839
|
══════════════════════
|
|
796
840
|
目标: 验证可扩展性
|
|
797
841
|
|
|
@@ -817,12 +861,19 @@ wisruntime/
|
|
|
817
861
|
│ └── client.py # MCPClient(ToolClient) — 薄封装标准 mcp 包
|
|
818
862
|
│
|
|
819
863
|
├── adapters/ # Adapter + Capability Bridge
|
|
864
|
+
│ ├── memory/ # Memory 记忆检索适配器 (✅ v0.4)
|
|
865
|
+
│ │ ├── __init__.py # 导出 WishubMemoryClient, MemoryPromptFormatter, MemoryItem
|
|
866
|
+
│ │ ├── model.py # MemoryItem, RetrieveRequest, RetrieveResponse (Pydantic 模型)
|
|
867
|
+
│ │ ├── client.py # WishubMemoryClient — 异步 HTTP 客户端,优雅降级
|
|
868
|
+
│ │ └── formatter.py # MemoryPromptFormatter — 记忆列表 → XML 提示词片段
|
|
869
|
+
│ │
|
|
820
870
|
│ └── langgraph/ # LangGraph/LangChain/DeepAgents 框架 (✅ v0.3)
|
|
821
|
-
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools
|
|
871
|
+
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools, WishubMemoryMiddleware
|
|
822
872
|
│ ├── converter.py # LangGraphEventConverter (astream_events → CanonicalEvent)
|
|
823
|
-
│ ├── wrap_agent.py # wrap_agent() 糖函数
|
|
873
|
+
│ ├── wrap_agent.py # wrap_agent() 糖函数 (注入 config: user_id, thread_id)
|
|
824
874
|
│ ├── tool_adapter.py # LangChainToolAdapter (ToolInfo → StructuredTool)
|
|
825
|
-
│
|
|
875
|
+
│ ├── builtin.py # create_model() / create_mcp_tools() 一行接入
|
|
876
|
+
│ └── memory_middleware.py # WishubMemoryMiddleware — DeepAgents 记忆中间件
|
|
826
877
|
│
|
|
827
878
|
├── server/ # Server 层
|
|
828
879
|
│ ├── __init__.py
|
|
@@ -681,6 +681,30 @@ def create_server(
|
|
|
681
681
|
└─────────────────────────────────────────────────────────────────────┘
|
|
682
682
|
|
|
683
683
|
故事 6 ──────────────────────────────────────────────────────────────
|
|
684
|
+
│ 用户故事: 作为 AI 工程师,我可以用 WishubMemoryMiddleware 为我的 │
|
|
685
|
+
│ DeepAgents Agent 添加长期记忆能力,自动检索用户历史对话记忆 │
|
|
686
|
+
│ │
|
|
687
|
+
│ 验收标准: │
|
|
688
|
+
│ from deepagents import create_deep_agent │
|
|
689
|
+
│ from wisruntime.adapters.langgraph import WishubMemoryMiddleware │
|
|
690
|
+
│ from wisruntime.memory import WishubMemoryClient │
|
|
691
|
+
│ │
|
|
692
|
+
│ memory_client = WishubMemoryClient( │
|
|
693
|
+
│ base_url="https://wishub.example.com", │
|
|
694
|
+
│ api_key="your-api-key" │
|
|
695
|
+
│ ) │
|
|
696
|
+
│ middleware = WishubMemoryMiddleware( │
|
|
697
|
+
│ client=memory_client, │
|
|
698
|
+
│ namespaces=["user_profile", "preferences"], │
|
|
699
|
+
│ top_k=5 │
|
|
700
|
+
│ ) │
|
|
701
|
+
│ agent = create_deep_agent(model=model, middlewares=[middleware]) │
|
|
702
|
+
│ # → Agent 在每次对话前自动检索相关历史记忆 │
|
|
703
|
+
│ # → 记忆以结构化 XML 格式注入系统提示词 │
|
|
704
|
+
│ # → 支持优雅降级: API 超时/失败时返回空列表,不影响对话 │
|
|
705
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
706
|
+
|
|
707
|
+
故事 7 ──────────────────────────────────────────────────────────────
|
|
684
708
|
│ 用户故事: 作为 AI 工程师,我的 Agent 对话历史可以自动持久化到 │
|
|
685
709
|
│ WIS Hub Checkpoint 服务,支持断点续传 │
|
|
686
710
|
│ │
|
|
@@ -750,16 +774,36 @@ Phase 3: 平台能力接入 (MCP ✅ 已完成 — v0.3)
|
|
|
750
774
|
分页发现 / 详情 / 302 预签名下载 / 严格 SemVer 版本, create_skill_sources() deepagents 集成
|
|
751
775
|
□ SandboxClient + Sandbox 数据模型
|
|
752
776
|
|
|
777
|
+
Phase 3.5: 记忆能力接入 (✅ 已完成 — v0.4)
|
|
778
|
+
═══════════════════════════════
|
|
779
|
+
目标: 用户故事 6 — WishubMemoryMiddleware 长期记忆
|
|
780
|
+
|
|
781
|
+
✅ memory/ 模块: MemoryItem, RetrieveRequest, RetrieveResponse 数据模型
|
|
782
|
+
✅ WishubMemoryClient — 异步 HTTP 客户端,对接 Wishub Memory API
|
|
783
|
+
- 优雅降级: 超时/错误返回空列表,不影响对话
|
|
784
|
+
- 完整日志: debug/info/warning/error 四级
|
|
785
|
+
✅ MemoryPromptFormatter — 记忆列表 → 结构化 XML 提示词片段
|
|
786
|
+
- 支持相关度评分显示
|
|
787
|
+
- 空记忆占位符
|
|
788
|
+
- 可选使用指南
|
|
789
|
+
✅ WishubMemoryMiddleware — DeepAgents 中间件
|
|
790
|
+
- abefore_agent: 检索记忆并存入 state
|
|
791
|
+
- modify_request: 注入记忆到系统提示词
|
|
792
|
+
- 跳过重复检索
|
|
793
|
+
- user_id/query 缺失时优雅处理
|
|
794
|
+
✅ wrap_agent config 注入 — user_id, thread_id 传递到 LangGraph config.configurable
|
|
795
|
+
✅ 完整测试覆盖 (39 个测试用例全部通过)
|
|
796
|
+
|
|
753
797
|
Phase 4: 高级特性
|
|
754
798
|
════════════════
|
|
755
|
-
目标: 用户故事
|
|
799
|
+
目标: 用户故事 7 — Checkpoint、多协议
|
|
756
800
|
|
|
757
801
|
□ CheckpointClient + 自动持久化
|
|
758
802
|
□ AGUIHandler
|
|
759
803
|
□ Stop endpoint
|
|
760
804
|
□ ApiKeyMiddleware
|
|
761
805
|
|
|
762
|
-
Phase
|
|
806
|
+
Phase 5: 多框架 + 多协议
|
|
763
807
|
══════════════════════
|
|
764
808
|
目标: 验证可扩展性
|
|
765
809
|
|
|
@@ -785,12 +829,19 @@ wisruntime/
|
|
|
785
829
|
│ └── client.py # MCPClient(ToolClient) — 薄封装标准 mcp 包
|
|
786
830
|
│
|
|
787
831
|
├── adapters/ # Adapter + Capability Bridge
|
|
832
|
+
│ ├── memory/ # Memory 记忆检索适配器 (✅ v0.4)
|
|
833
|
+
│ │ ├── __init__.py # 导出 WishubMemoryClient, MemoryPromptFormatter, MemoryItem
|
|
834
|
+
│ │ ├── model.py # MemoryItem, RetrieveRequest, RetrieveResponse (Pydantic 模型)
|
|
835
|
+
│ │ ├── client.py # WishubMemoryClient — 异步 HTTP 客户端,优雅降级
|
|
836
|
+
│ │ └── formatter.py # MemoryPromptFormatter — 记忆列表 → XML 提示词片段
|
|
837
|
+
│ │
|
|
788
838
|
│ └── langgraph/ # LangGraph/LangChain/DeepAgents 框架 (✅ v0.3)
|
|
789
|
-
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools
|
|
839
|
+
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools, WishubMemoryMiddleware
|
|
790
840
|
│ ├── converter.py # LangGraphEventConverter (astream_events → CanonicalEvent)
|
|
791
|
-
│ ├── wrap_agent.py # wrap_agent() 糖函数
|
|
841
|
+
│ ├── wrap_agent.py # wrap_agent() 糖函数 (注入 config: user_id, thread_id)
|
|
792
842
|
│ ├── tool_adapter.py # LangChainToolAdapter (ToolInfo → StructuredTool)
|
|
793
|
-
│
|
|
843
|
+
│ ├── builtin.py # create_model() / create_mcp_tools() 一行接入
|
|
844
|
+
│ └── memory_middleware.py # WishubMemoryMiddleware — DeepAgents 记忆中间件
|
|
794
845
|
│
|
|
795
846
|
├── server/ # Server 层
|
|
796
847
|
│ ├── __init__.py
|
|
@@ -6,10 +6,16 @@
|
|
|
6
6
|
- LangChainToolAdapter: ToolClient → StructuredTool 适配器
|
|
7
7
|
- create_model: 快速创建 LangChain 模型实例
|
|
8
8
|
- create_mcp_tools: 一行获取 MCP 工具列表
|
|
9
|
+
- WishubMemoryMiddleware: Wishub 记忆检索中间件
|
|
9
10
|
"""
|
|
10
11
|
|
|
11
12
|
from wisruntime.adapters.langgraph.builtin import create_mcp_tools, create_model
|
|
12
13
|
from wisruntime.adapters.langgraph.converter import LangGraphEventConverter
|
|
14
|
+
from wisruntime.adapters.langgraph.memory_middleware import (
|
|
15
|
+
WishubMemoryMiddleware,
|
|
16
|
+
WishubMemoryState,
|
|
17
|
+
WishubMemoryStateUpdate,
|
|
18
|
+
)
|
|
13
19
|
from wisruntime.adapters.langgraph.tool_adapter import LangChainToolAdapter
|
|
14
20
|
from wisruntime.adapters.langgraph.wrap_agent import wrap_agent
|
|
15
21
|
|
|
@@ -19,4 +25,7 @@ __all__ = [
|
|
|
19
25
|
"LangChainToolAdapter",
|
|
20
26
|
"create_model",
|
|
21
27
|
"create_mcp_tools",
|
|
28
|
+
"WishubMemoryMiddleware",
|
|
29
|
+
"WishubMemoryState",
|
|
30
|
+
"WishubMemoryStateUpdate",
|
|
22
31
|
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Utility functions for LangGraph middleware."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from langchain_core.messages import SystemMessage
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def append_to_system_message(
|
|
9
|
+
system_message: Optional[SystemMessage], content: str
|
|
10
|
+
) -> SystemMessage:
|
|
11
|
+
"""Append content to an existing system message or create a new one.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
system_message: Existing system message or None
|
|
15
|
+
content: Content to append
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
SystemMessage with appended content
|
|
19
|
+
"""
|
|
20
|
+
if system_message is None:
|
|
21
|
+
return SystemMessage(content=content)
|
|
22
|
+
|
|
23
|
+
# Get existing content
|
|
24
|
+
existing_content = system_message.content
|
|
25
|
+
if isinstance(existing_content, str):
|
|
26
|
+
new_content = existing_content + "\n\n" + content
|
|
27
|
+
elif isinstance(existing_content, list):
|
|
28
|
+
# Handle list of content blocks
|
|
29
|
+
new_content = existing_content + [{"type": "text", "text": content}]
|
|
30
|
+
else:
|
|
31
|
+
new_content = str(existing_content) + "\n\n" + content
|
|
32
|
+
|
|
33
|
+
# Create new message with combined content
|
|
34
|
+
return SystemMessage(content=new_content)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""Wishub Memory Middleware for DeepAgents。"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Annotated, NotRequired, TypedDict, Optional
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from collections.abc import Awaitable, Callable
|
|
8
|
+
from langchain_core.runnables import RunnableConfig
|
|
9
|
+
from langgraph.runtime import Runtime
|
|
10
|
+
|
|
11
|
+
from langchain.agents.middleware.types import (
|
|
12
|
+
AgentMiddleware,
|
|
13
|
+
AgentState,
|
|
14
|
+
ContextT,
|
|
15
|
+
ModelRequest,
|
|
16
|
+
ModelResponse,
|
|
17
|
+
PrivateStateAttr,
|
|
18
|
+
ResponseT,
|
|
19
|
+
TracePolicy,
|
|
20
|
+
omit_payload,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
from wisruntime.memory import WishubMemoryClient, MemoryPromptFormatter, MemoryItem
|
|
24
|
+
from wisruntime.adapters.langgraph._utils import append_to_system_message
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WishubMemoryState(AgentState):
|
|
30
|
+
"""Wishub Memory Middleware 的状态 schema。"""
|
|
31
|
+
|
|
32
|
+
retrieved_memories: NotRequired[Annotated[list[MemoryItem], PrivateStateAttr]]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class WishubMemoryStateUpdate(TypedDict):
|
|
36
|
+
"""状态更新。"""
|
|
37
|
+
|
|
38
|
+
retrieved_memories: list[MemoryItem]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WishubMemoryMiddleware(AgentMiddleware[WishubMemoryState, ContextT, ResponseT]):
|
|
42
|
+
"""Wishub 记忆检索中间件(DeepAgents)。
|
|
43
|
+
|
|
44
|
+
在 Agent 执行前,根据用户查询检索相关记忆,并注入到系统提示词中。
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
>>> from wisruntime.adapters.langgraph import WishubMemoryMiddleware
|
|
48
|
+
>>> from deepagents import create_deep_agent
|
|
49
|
+
>>>
|
|
50
|
+
>>> middleware = WishubMemoryMiddleware(
|
|
51
|
+
... base_url="https://aip-open-api-dev.infinitus.com.cn",
|
|
52
|
+
... api_key="your-api-key",
|
|
53
|
+
... namespaces=["preference", "history"],
|
|
54
|
+
... top_k=5,
|
|
55
|
+
... )
|
|
56
|
+
>>>
|
|
57
|
+
>>> agent = create_deep_agent(
|
|
58
|
+
... model=model,
|
|
59
|
+
... middleware=[middleware],
|
|
60
|
+
... )
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
trace_policy = TracePolicy(process_inputs=omit_payload)
|
|
64
|
+
state_schema = WishubMemoryState
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
*,
|
|
69
|
+
base_url: str,
|
|
70
|
+
api_key: str,
|
|
71
|
+
namespaces: Optional[list[str]] = None,
|
|
72
|
+
top_k: int = 5,
|
|
73
|
+
timeout: float = 5.0,
|
|
74
|
+
include_guidelines: bool = True,
|
|
75
|
+
):
|
|
76
|
+
"""初始化 Wishub Memory 中间件。
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
base_url: Wishub Memory API 基础地址
|
|
80
|
+
api_key: API Key (智能体通道鉴权)
|
|
81
|
+
namespaces: 可选的命名空间过滤列表
|
|
82
|
+
top_k: 检索记忆数量上限(默认5)
|
|
83
|
+
timeout: API 请求超时时间(秒)
|
|
84
|
+
include_guidelines: 是否在提示词中包含使用指南
|
|
85
|
+
"""
|
|
86
|
+
self.client = WishubMemoryClient(
|
|
87
|
+
base_url=base_url,
|
|
88
|
+
api_key=api_key,
|
|
89
|
+
timeout=timeout,
|
|
90
|
+
)
|
|
91
|
+
self.namespaces = namespaces
|
|
92
|
+
self.top_k = top_k
|
|
93
|
+
self.include_guidelines = include_guidelines
|
|
94
|
+
self.formatter = MemoryPromptFormatter()
|
|
95
|
+
|
|
96
|
+
def _extract_user_query(self, state: WishubMemoryState) -> Optional[str]:
|
|
97
|
+
"""从状态中提取用户最后一条消息作为检索查询。
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
state: Agent 状态
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
用户查询文本,如果没有则返回 None
|
|
104
|
+
"""
|
|
105
|
+
messages = state.get("messages", [])
|
|
106
|
+
|
|
107
|
+
# 从后往前找第一条用户消息
|
|
108
|
+
for msg in reversed(messages):
|
|
109
|
+
# LangChain 消息对象
|
|
110
|
+
if hasattr(msg, "type") and msg.type == "human":
|
|
111
|
+
content = msg.content
|
|
112
|
+
if isinstance(content, str):
|
|
113
|
+
return content.strip()
|
|
114
|
+
# 字典格式消息
|
|
115
|
+
elif isinstance(msg, dict) and msg.get("role") == "user":
|
|
116
|
+
content = msg.get("content", "")
|
|
117
|
+
if isinstance(content, str):
|
|
118
|
+
return content.strip()
|
|
119
|
+
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
def _get_user_id(self, state: WishubMemoryState, config: "RunnableConfig") -> Optional[str]:
|
|
123
|
+
"""获取 user_id,支持多种来源(按优先级)。
|
|
124
|
+
|
|
125
|
+
优先级:
|
|
126
|
+
1. config.configurable.user_id(标准方式,来自 wrap_agent)
|
|
127
|
+
2. state.user_id(兼容方案,如果有人直接在 state 里传)
|
|
128
|
+
3. None(无法获取,跳过检索)
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
state: Agent 状态
|
|
132
|
+
config: Runnable 配置
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
user_id 或 None
|
|
136
|
+
"""
|
|
137
|
+
# 方案1:从 config 读取(推荐)
|
|
138
|
+
user_id = config.get("configurable", {}).get("user_id")
|
|
139
|
+
if user_id:
|
|
140
|
+
return user_id
|
|
141
|
+
|
|
142
|
+
# 方案2:从 state 读取(兼容)
|
|
143
|
+
user_id = state.get("user_id")
|
|
144
|
+
if user_id:
|
|
145
|
+
logger.debug("user_id found in state (fallback), consider using config.configurable")
|
|
146
|
+
return user_id
|
|
147
|
+
|
|
148
|
+
# 方案3:无法获取
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
async def abefore_agent(
|
|
152
|
+
self,
|
|
153
|
+
state: WishubMemoryState,
|
|
154
|
+
runtime: "Runtime",
|
|
155
|
+
config: "RunnableConfig",
|
|
156
|
+
) -> Optional[WishubMemoryStateUpdate]:
|
|
157
|
+
"""在 Agent 执行前检索记忆。"""
|
|
158
|
+
|
|
159
|
+
# 跳过重复检索
|
|
160
|
+
if "retrieved_memories" in state:
|
|
161
|
+
logger.debug("Memories already retrieved, skipping")
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
# 提取用户查询
|
|
165
|
+
query = self._extract_user_query(state)
|
|
166
|
+
if not query:
|
|
167
|
+
logger.debug("No user query found, skipping memory retrieval")
|
|
168
|
+
return WishubMemoryStateUpdate(retrieved_memories=[])
|
|
169
|
+
|
|
170
|
+
# 提取 userId
|
|
171
|
+
user_id = self._get_user_id(state, config)
|
|
172
|
+
if not user_id:
|
|
173
|
+
logger.warning(
|
|
174
|
+
"Cannot retrieve memories: user_id not found. "
|
|
175
|
+
"Check that wrap_agent is passing request.user to config.configurable"
|
|
176
|
+
)
|
|
177
|
+
return WishubMemoryStateUpdate(retrieved_memories=[])
|
|
178
|
+
|
|
179
|
+
# 调用检索接口
|
|
180
|
+
logger.debug(f"Retrieving memories for user={user_id}, query_len={len(query)}")
|
|
181
|
+
memories = await self.client.retrieve_memories(
|
|
182
|
+
query=query,
|
|
183
|
+
user_id=user_id,
|
|
184
|
+
namespaces=self.namespaces,
|
|
185
|
+
top_k=self.top_k,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
logger.info(f"Retrieved {len(memories)} memories for user {user_id}")
|
|
189
|
+
return WishubMemoryStateUpdate(retrieved_memories=memories)
|
|
190
|
+
|
|
191
|
+
def modify_request(
|
|
192
|
+
self,
|
|
193
|
+
request: ModelRequest[ContextT],
|
|
194
|
+
) -> ModelRequest[ContextT]:
|
|
195
|
+
"""将检索到的记忆注入系统提示词。"""
|
|
196
|
+
|
|
197
|
+
memories = request.state.get("retrieved_memories", [])
|
|
198
|
+
|
|
199
|
+
# 格式化记忆提示词
|
|
200
|
+
memory_prompt = self.formatter.format_memories_prompt(
|
|
201
|
+
memories,
|
|
202
|
+
include_guidelines=self.include_guidelines,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# 注入到系统消息
|
|
206
|
+
new_system_message = append_to_system_message(
|
|
207
|
+
request.system_message,
|
|
208
|
+
memory_prompt,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if new_system_message is request.system_message:
|
|
212
|
+
return request
|
|
213
|
+
|
|
214
|
+
return request.override(system_message=new_system_message)
|
|
215
|
+
|
|
216
|
+
async def awrap_model_call(
|
|
217
|
+
self,
|
|
218
|
+
request: ModelRequest[ContextT],
|
|
219
|
+
handler: "Callable[[ModelRequest[ContextT]], Awaitable[ModelResponse[ResponseT]]]",
|
|
220
|
+
) -> ModelResponse[ResponseT]:
|
|
221
|
+
"""异步包装模型调用。"""
|
|
222
|
+
modified_request = self.modify_request(request)
|
|
223
|
+
return await handler(modified_request)
|
|
@@ -70,8 +70,17 @@ def _build_astream_events_invoker(agent_graph):
|
|
|
70
70
|
messages = _agent_request_to_messages(request)
|
|
71
71
|
converter = LangGraphEventConverter()
|
|
72
72
|
|
|
73
|
+
# 构建 config,将 AgentRequest 元信息注入 configurable
|
|
74
|
+
config = {
|
|
75
|
+
"configurable": {
|
|
76
|
+
"thread_id": request.conversation_id or "default",
|
|
77
|
+
"user_id": request.user_id,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
async for event in agent_graph.astream_events(
|
|
74
82
|
{"messages": messages},
|
|
83
|
+
config=config,
|
|
75
84
|
version="v2",
|
|
76
85
|
):
|
|
77
86
|
for item in converter.convert(event):
|
|
@@ -87,9 +96,18 @@ def _build_astream_invoker(agent_graph, stream_mode: str):
|
|
|
87
96
|
messages = _agent_request_to_messages(request)
|
|
88
97
|
converter = LangGraphEventConverter()
|
|
89
98
|
|
|
99
|
+
# 构建 config,将 AgentRequest 元信息注入 configurable
|
|
100
|
+
config = {
|
|
101
|
+
"configurable": {
|
|
102
|
+
"thread_id": request.conversation_id or "default",
|
|
103
|
+
"user_id": request.user_id,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
90
107
|
async for event in agent_graph.astream(
|
|
91
108
|
{"messages": messages},
|
|
92
109
|
stream_mode=stream_mode,
|
|
110
|
+
config=config,
|
|
93
111
|
):
|
|
94
112
|
for item in converter.convert(event):
|
|
95
113
|
yield item
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deprecated: This module has moved to wisruntime.memory
|
|
3
|
+
|
|
4
|
+
This compatibility shim will be removed in a future version.
|
|
5
|
+
Please update your imports:
|
|
6
|
+
from wisruntime.adapters.memory import X # Old (deprecated)
|
|
7
|
+
from wisruntime.memory import X # New
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
|
|
12
|
+
# Issue deprecation warning
|
|
13
|
+
warnings.warn(
|
|
14
|
+
"wisruntime.adapters.memory is deprecated and will be removed in a future version. "
|
|
15
|
+
"Please use wisruntime.memory instead.",
|
|
16
|
+
DeprecationWarning,
|
|
17
|
+
stacklevel=2,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Re-export from new location for backward compatibility
|
|
21
|
+
from wisruntime.memory import (
|
|
22
|
+
WishubMemoryClient,
|
|
23
|
+
MemoryPromptFormatter,
|
|
24
|
+
MemoryItem,
|
|
25
|
+
RetrieveRequest,
|
|
26
|
+
RetrieveResponse,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"WishubMemoryClient",
|
|
31
|
+
"MemoryPromptFormatter",
|
|
32
|
+
"MemoryItem",
|
|
33
|
+
"RetrieveRequest",
|
|
34
|
+
"RetrieveResponse",
|
|
35
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Wisruntime Memory Module
|
|
3
|
+
|
|
4
|
+
Provides client and utilities for interacting with Wishub Memory API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .client import WishubMemoryClient
|
|
8
|
+
from .formatter import MemoryPromptFormatter
|
|
9
|
+
from .model import MemoryItem, RetrieveRequest, RetrieveResponse
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"WishubMemoryClient",
|
|
13
|
+
"MemoryPromptFormatter",
|
|
14
|
+
"MemoryItem",
|
|
15
|
+
"RetrieveRequest",
|
|
16
|
+
"RetrieveResponse",
|
|
17
|
+
]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Wishub Memory API 客户端(框架无关)。"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from wisruntime.memory.model import (
|
|
9
|
+
MemoryItem,
|
|
10
|
+
RetrieveResponse,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WishubMemoryClient:
|
|
17
|
+
"""Wishub 记忆检索客户端。"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
base_url: str,
|
|
23
|
+
api_key: str,
|
|
24
|
+
timeout: float = 5.0,
|
|
25
|
+
):
|
|
26
|
+
"""初始化记忆客户端。
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
base_url: API 基础地址,如 "https://aip-open-api-dev.infinitus.com.cn"
|
|
30
|
+
api_key: API Key (智能体通道鉴权)
|
|
31
|
+
timeout: 请求超时时间(秒)
|
|
32
|
+
"""
|
|
33
|
+
self.base_url = base_url.rstrip("/")
|
|
34
|
+
self.api_key = api_key
|
|
35
|
+
self.timeout = timeout
|
|
36
|
+
self._endpoint = f"{self.base_url}/wishub-memory/openapi/v1/memories/retrieve"
|
|
37
|
+
|
|
38
|
+
async def retrieve_memories(
|
|
39
|
+
self,
|
|
40
|
+
*,
|
|
41
|
+
query: str,
|
|
42
|
+
user_id: str,
|
|
43
|
+
namespaces: Optional[list[str]] = None,
|
|
44
|
+
top_k: int = 5,
|
|
45
|
+
) -> list[MemoryItem]:
|
|
46
|
+
"""检索相关记忆。
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
query: 用户查询文本(用于语义检索)
|
|
50
|
+
user_id: 用户ID(智能体通道必填)
|
|
51
|
+
namespaces: 可选的命名空间过滤
|
|
52
|
+
top_k: 返回记忆数量上限(默认5,最大20)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
记忆列表。降级或失败时返回空列表(不抛异常)
|
|
56
|
+
"""
|
|
57
|
+
headers = {
|
|
58
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
59
|
+
"Content-Type": "application/json",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
payload = {
|
|
63
|
+
"query": query,
|
|
64
|
+
"userId": user_id,
|
|
65
|
+
"topK": min(top_k, 20),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if namespaces:
|
|
69
|
+
payload["namespaces"] = namespaces
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
73
|
+
response = await client.post(
|
|
74
|
+
self._endpoint,
|
|
75
|
+
json=payload,
|
|
76
|
+
headers=headers,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
logger.debug(
|
|
80
|
+
f"Memory retrieval: user={user_id}, query_len={len(query)}, "
|
|
81
|
+
f"status={response.status_code}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if response.status_code != 200:
|
|
85
|
+
logger.warning(
|
|
86
|
+
f"Memory API returned {response.status_code}: {response.text[:200]}"
|
|
87
|
+
)
|
|
88
|
+
return []
|
|
89
|
+
|
|
90
|
+
result = RetrieveResponse(**response.json())
|
|
91
|
+
|
|
92
|
+
if not result.success:
|
|
93
|
+
logger.warning(f"Memory retrieval failed: {result.code} - {result.msg}")
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
if result.data.degraded:
|
|
97
|
+
logger.warning("Memory service degraded, returning empty list")
|
|
98
|
+
return []
|
|
99
|
+
|
|
100
|
+
logger.info(f"Retrieved {len(result.data.memories)} memories for user {user_id}")
|
|
101
|
+
return result.data.memories
|
|
102
|
+
|
|
103
|
+
except httpx.TimeoutException:
|
|
104
|
+
logger.warning(f"Memory retrieval timeout after {self.timeout}s")
|
|
105
|
+
return []
|
|
106
|
+
except Exception as e:
|
|
107
|
+
logger.error(f"Memory retrieval error: {e}", exc_info=True)
|
|
108
|
+
return []
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""记忆提示词格式化器(框架无关)。"""
|
|
2
|
+
|
|
3
|
+
from wisruntime.memory.model import MemoryItem
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MemoryPromptFormatter:
|
|
7
|
+
"""将检索到的记忆格式化为提示词片段。"""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def format_memories_prompt(
|
|
11
|
+
memories: list[MemoryItem],
|
|
12
|
+
*,
|
|
13
|
+
include_guidelines: bool = True,
|
|
14
|
+
) -> str:
|
|
15
|
+
"""格式化记忆为系统提示词片段。
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
memories: 记忆列表
|
|
19
|
+
include_guidelines: 是否包含使用指南
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
格式化的 XML 提示词片段
|
|
23
|
+
"""
|
|
24
|
+
if not memories:
|
|
25
|
+
return MemoryPromptFormatter._empty_memory_prompt(include_guidelines)
|
|
26
|
+
|
|
27
|
+
# 构建记忆内容
|
|
28
|
+
memory_items = []
|
|
29
|
+
for i, mem in enumerate(memories, 1):
|
|
30
|
+
memory_items.append(
|
|
31
|
+
f"记忆 {i} (相关度: {mem.score:.2f}):\n{mem.content.strip()}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
memories_text = "\n\n".join(memory_items)
|
|
35
|
+
|
|
36
|
+
prompt = f"""<user_memory>
|
|
37
|
+
以下是根据用户问题检索到的相关记忆:
|
|
38
|
+
|
|
39
|
+
{memories_text}
|
|
40
|
+
</user_memory>"""
|
|
41
|
+
|
|
42
|
+
if include_guidelines:
|
|
43
|
+
prompt += """
|
|
44
|
+
|
|
45
|
+
<memory_usage_guidelines>
|
|
46
|
+
- 上述记忆是从历史对话中检索出的相关信息
|
|
47
|
+
- 相关度分数仅供参考,不同记忆引擎的分数体系不可比
|
|
48
|
+
- 请结合当前问题上下文合理使用这些记忆
|
|
49
|
+
- 如果记忆内容与用户当前问题不符,请以用户当前的明确表述为准
|
|
50
|
+
- 记忆可能包含过时信息,请在回答时注意时效性
|
|
51
|
+
</memory_usage_guidelines>"""
|
|
52
|
+
|
|
53
|
+
return prompt
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def _empty_memory_prompt(include_guidelines: bool) -> str:
|
|
57
|
+
"""无记忆时的占位提示。"""
|
|
58
|
+
prompt = "<user_memory>\n(未检索到相关历史记忆)\n</user_memory>"
|
|
59
|
+
|
|
60
|
+
if include_guidelines:
|
|
61
|
+
prompt += """
|
|
62
|
+
|
|
63
|
+
<memory_usage_guidelines>
|
|
64
|
+
- 当前未检索到相关历史记忆,请基于用户当前问题直接回答
|
|
65
|
+
</memory_usage_guidelines>"""
|
|
66
|
+
|
|
67
|
+
return prompt
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""记忆检索数据模型(框架无关)。"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MemoryItem(BaseModel):
|
|
9
|
+
"""单条记忆项。"""
|
|
10
|
+
|
|
11
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
12
|
+
|
|
13
|
+
memory_id: str = Field(alias="memoryId")
|
|
14
|
+
content: str
|
|
15
|
+
score: float
|
|
16
|
+
metadata: Optional[dict] = None
|
|
17
|
+
user_id: str = Field(alias="userId")
|
|
18
|
+
app_code: Optional[str] = Field(None, alias="appCode")
|
|
19
|
+
run_id: Optional[str] = Field(None, alias="runId")
|
|
20
|
+
created_at: str = Field(alias="createdAt")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RetrieveRequest(BaseModel):
|
|
24
|
+
"""记忆检索请求。"""
|
|
25
|
+
|
|
26
|
+
query: str
|
|
27
|
+
user_id: str = Field(alias="userId")
|
|
28
|
+
namespaces: Optional[list[str]] = None
|
|
29
|
+
top_k: int = Field(5, alias="topK")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RetrieveResponseData(BaseModel):
|
|
33
|
+
"""检索响应数据。"""
|
|
34
|
+
|
|
35
|
+
memories: list[MemoryItem]
|
|
36
|
+
degraded: bool = False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RetrieveResponse(BaseModel):
|
|
40
|
+
"""记忆检索响应。"""
|
|
41
|
+
|
|
42
|
+
success: bool
|
|
43
|
+
code: str
|
|
44
|
+
msg: str
|
|
45
|
+
data: RetrieveResponseData
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wisruntime
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: 企业级 Agent Runtime SDK — 让 AI 工程师专注 Agent 能力开发,无需关心底层基础设施
|
|
5
5
|
Author: WIS Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -713,6 +713,30 @@ def create_server(
|
|
|
713
713
|
└─────────────────────────────────────────────────────────────────────┘
|
|
714
714
|
|
|
715
715
|
故事 6 ──────────────────────────────────────────────────────────────
|
|
716
|
+
│ 用户故事: 作为 AI 工程师,我可以用 WishubMemoryMiddleware 为我的 │
|
|
717
|
+
│ DeepAgents Agent 添加长期记忆能力,自动检索用户历史对话记忆 │
|
|
718
|
+
│ │
|
|
719
|
+
│ 验收标准: │
|
|
720
|
+
│ from deepagents import create_deep_agent │
|
|
721
|
+
│ from wisruntime.adapters.langgraph import WishubMemoryMiddleware │
|
|
722
|
+
│ from wisruntime.memory import WishubMemoryClient │
|
|
723
|
+
│ │
|
|
724
|
+
│ memory_client = WishubMemoryClient( │
|
|
725
|
+
│ base_url="https://wishub.example.com", │
|
|
726
|
+
│ api_key="your-api-key" │
|
|
727
|
+
│ ) │
|
|
728
|
+
│ middleware = WishubMemoryMiddleware( │
|
|
729
|
+
│ client=memory_client, │
|
|
730
|
+
│ namespaces=["user_profile", "preferences"], │
|
|
731
|
+
│ top_k=5 │
|
|
732
|
+
│ ) │
|
|
733
|
+
│ agent = create_deep_agent(model=model, middlewares=[middleware]) │
|
|
734
|
+
│ # → Agent 在每次对话前自动检索相关历史记忆 │
|
|
735
|
+
│ # → 记忆以结构化 XML 格式注入系统提示词 │
|
|
736
|
+
│ # → 支持优雅降级: API 超时/失败时返回空列表,不影响对话 │
|
|
737
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
738
|
+
|
|
739
|
+
故事 7 ──────────────────────────────────────────────────────────────
|
|
716
740
|
│ 用户故事: 作为 AI 工程师,我的 Agent 对话历史可以自动持久化到 │
|
|
717
741
|
│ WIS Hub Checkpoint 服务,支持断点续传 │
|
|
718
742
|
│ │
|
|
@@ -782,16 +806,36 @@ Phase 3: 平台能力接入 (MCP ✅ 已完成 — v0.3)
|
|
|
782
806
|
分页发现 / 详情 / 302 预签名下载 / 严格 SemVer 版本, create_skill_sources() deepagents 集成
|
|
783
807
|
□ SandboxClient + Sandbox 数据模型
|
|
784
808
|
|
|
809
|
+
Phase 3.5: 记忆能力接入 (✅ 已完成 — v0.4)
|
|
810
|
+
═══════════════════════════════
|
|
811
|
+
目标: 用户故事 6 — WishubMemoryMiddleware 长期记忆
|
|
812
|
+
|
|
813
|
+
✅ memory/ 模块: MemoryItem, RetrieveRequest, RetrieveResponse 数据模型
|
|
814
|
+
✅ WishubMemoryClient — 异步 HTTP 客户端,对接 Wishub Memory API
|
|
815
|
+
- 优雅降级: 超时/错误返回空列表,不影响对话
|
|
816
|
+
- 完整日志: debug/info/warning/error 四级
|
|
817
|
+
✅ MemoryPromptFormatter — 记忆列表 → 结构化 XML 提示词片段
|
|
818
|
+
- 支持相关度评分显示
|
|
819
|
+
- 空记忆占位符
|
|
820
|
+
- 可选使用指南
|
|
821
|
+
✅ WishubMemoryMiddleware — DeepAgents 中间件
|
|
822
|
+
- abefore_agent: 检索记忆并存入 state
|
|
823
|
+
- modify_request: 注入记忆到系统提示词
|
|
824
|
+
- 跳过重复检索
|
|
825
|
+
- user_id/query 缺失时优雅处理
|
|
826
|
+
✅ wrap_agent config 注入 — user_id, thread_id 传递到 LangGraph config.configurable
|
|
827
|
+
✅ 完整测试覆盖 (39 个测试用例全部通过)
|
|
828
|
+
|
|
785
829
|
Phase 4: 高级特性
|
|
786
830
|
════════════════
|
|
787
|
-
目标: 用户故事
|
|
831
|
+
目标: 用户故事 7 — Checkpoint、多协议
|
|
788
832
|
|
|
789
833
|
□ CheckpointClient + 自动持久化
|
|
790
834
|
□ AGUIHandler
|
|
791
835
|
□ Stop endpoint
|
|
792
836
|
□ ApiKeyMiddleware
|
|
793
837
|
|
|
794
|
-
Phase
|
|
838
|
+
Phase 5: 多框架 + 多协议
|
|
795
839
|
══════════════════════
|
|
796
840
|
目标: 验证可扩展性
|
|
797
841
|
|
|
@@ -817,12 +861,19 @@ wisruntime/
|
|
|
817
861
|
│ └── client.py # MCPClient(ToolClient) — 薄封装标准 mcp 包
|
|
818
862
|
│
|
|
819
863
|
├── adapters/ # Adapter + Capability Bridge
|
|
864
|
+
│ ├── memory/ # Memory 记忆检索适配器 (✅ v0.4)
|
|
865
|
+
│ │ ├── __init__.py # 导出 WishubMemoryClient, MemoryPromptFormatter, MemoryItem
|
|
866
|
+
│ │ ├── model.py # MemoryItem, RetrieveRequest, RetrieveResponse (Pydantic 模型)
|
|
867
|
+
│ │ ├── client.py # WishubMemoryClient — 异步 HTTP 客户端,优雅降级
|
|
868
|
+
│ │ └── formatter.py # MemoryPromptFormatter — 记忆列表 → XML 提示词片段
|
|
869
|
+
│ │
|
|
820
870
|
│ └── langgraph/ # LangGraph/LangChain/DeepAgents 框架 (✅ v0.3)
|
|
821
|
-
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools
|
|
871
|
+
│ ├── __init__.py # 导出 LangGraphEventConverter, wrap_agent, LangChainToolAdapter, create_model, create_mcp_tools, WishubMemoryMiddleware
|
|
822
872
|
│ ├── converter.py # LangGraphEventConverter (astream_events → CanonicalEvent)
|
|
823
|
-
│ ├── wrap_agent.py # wrap_agent() 糖函数
|
|
873
|
+
│ ├── wrap_agent.py # wrap_agent() 糖函数 (注入 config: user_id, thread_id)
|
|
824
874
|
│ ├── tool_adapter.py # LangChainToolAdapter (ToolInfo → StructuredTool)
|
|
825
|
-
│
|
|
875
|
+
│ ├── builtin.py # create_model() / create_mcp_tools() 一行接入
|
|
876
|
+
│ └── memory_middleware.py # WishubMemoryMiddleware — DeepAgents 记忆中间件
|
|
826
877
|
│
|
|
827
878
|
├── server/ # Server 层
|
|
828
879
|
│ ├── __init__.py
|
|
@@ -12,14 +12,21 @@ wisruntime.egg-info/requires.txt
|
|
|
12
12
|
wisruntime.egg-info/top_level.txt
|
|
13
13
|
wisruntime/adapters/__init__.py
|
|
14
14
|
wisruntime/adapters/langgraph/__init__.py
|
|
15
|
+
wisruntime/adapters/langgraph/_utils.py
|
|
15
16
|
wisruntime/adapters/langgraph/builtin.py
|
|
16
17
|
wisruntime/adapters/langgraph/converter.py
|
|
18
|
+
wisruntime/adapters/langgraph/memory_middleware.py
|
|
17
19
|
wisruntime/adapters/langgraph/tool_adapter.py
|
|
18
20
|
wisruntime/adapters/langgraph/wrap_agent.py
|
|
21
|
+
wisruntime/adapters/memory/__init__.py
|
|
19
22
|
wisruntime/conversation/__init__.py
|
|
20
23
|
wisruntime/conversation/client.py
|
|
21
24
|
wisruntime/conversation/model.py
|
|
22
25
|
wisruntime/conversation/recorder.py
|
|
26
|
+
wisruntime/memory/__init__.py
|
|
27
|
+
wisruntime/memory/client.py
|
|
28
|
+
wisruntime/memory/formatter.py
|
|
29
|
+
wisruntime/memory/model.py
|
|
23
30
|
wisruntime/server/__init__.py
|
|
24
31
|
wisruntime/server/dify_handler.py
|
|
25
32
|
wisruntime/server/invoker.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|