Skip to content

Commit f83ef6a

Browse files
raychen911weimch
authored andcommitted
fix(storage): 优化 SQL 存储兼容性
- 新增通用的 Content 清理逻辑,在 SQL Session 和 Memory 存储读写时过滤空的 Content Part。 - 修复 MySQL 下 DynamicPickleType 写入 LONGBLOB 时未序列化导致的类型错误。 - 支持通过 sessionmaker_kwargs 向 SqlStorage 传递 sessionmaker 配置,例如 expire_on_commit。 - 将 SQL Session 中显式更新 update_time 的逻辑改为使用 datetime.now(),避免 func.now() 带来的 ORM 状态问题。 - 统一通过当前事件循环创建 Session 和 Memory 服务的清理任务。 - 优化 SQL Session 示例输出,在助手回复前打印用户问题。 - 在 SQL Memory/Session 示例中关闭 thinking 输出,使演示结果更清晰。 - 增加 SQL Content 清理和 MySQL DynamicPickleType 序列化相关测试。
1 parent ca82991 commit f83ef6a

25 files changed

Lines changed: 210 additions & 189 deletions

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ tRPC-Agent-Python provides an end-to-end foundation for agent building, orchestr
1616

1717
- **Multi-paradigm agent orchestration**: Built-in orchestration supports `ChainAgent` / `ParallelAgent` / `CycleAgent` / `TransferAgent`, with `GraphAgent` for graph-based orchestration.
1818
- **Graph orchestration capability (`GraphAgent`)**: Use DSL to orchestrate `Agent` / `Tool` / `MCP` / `Knowledge` / `CodeExecutor` in one unified flow.
19-
- **Efficient integration with Python AI ecosystems**: Agent ecosystem extensions (`claude-agent-sdk` / `LangGraph`, etc.) / Tool ecosystem extensions (`mcp`, etc.) / Knowledge ecosystem extensions (`LangChain`, etc.) / Model ecosystem extensions (`LiteLLM`, etc.) / Memory ecosystem extensions (`Mem0`, etc.).
19+
- **Efficient integration with Python AI ecosystems**: Agent ecosystem extensions (`claude-agent-sdk` / `LangGraph`, etc.) / Tool ecosystem extensions (`mcp`, etc.) / Knowledge ecosystem extensions (`LangChain`, etc.) / Model ecosystem extensions (`LiteLLM`, etc.) / Memory ecosystem extensions (`Mem0`, `Mempalace`, etc.).
2020
- **Agent ecosystem extensions**: Supports `LangGraphAgent` / `ClaudeAgent` / `TeamAgent` (Agno-Like).
2121
- **Tool ecosystem extensions**: `FunctionTool` / File tools / `MCPToolset` / LangChain Tool / Agent-as-Tool.
22-
- **Complete memory capability (`Session` / `Memory`)**: `Session` manages messages and state within a single session, while `Memory` manages cross-session long-term memory and personalization. Persistence supports `InMemory` / `Redis` / `SQL`; `Memory` also supports `Mem0`.
22+
- **Complete memory capability (`Session` / `Memory`)**: `Session` manages messages and state within a single session, while `Memory` manages cross-session long-term memory and personalization. Persistence supports `InMemory` / `Redis` / `SQL`; `Memory` also supports `Mem0``Mempalace`.
2323
- **Production-grade knowledge capability**: Built on LangChain components with first-class RAG support.
2424
- **CodeExecutor extension capability**: Supports local / container executors for code execution and task grounding.
2525
- **Skills extension capability**: Supports `SKILL.md`-based skill systems for reusable capabilities and dynamic tooling.
@@ -84,7 +84,7 @@ pip install trpc-agent-py
8484
Install optional capabilities as needed:
8585

8686
```bash
87-
pip install trpc-agent-py[a2a,ag-ui,knowledge,agent-claude,mem0,langfuse]
87+
pip install trpc-agent-py[a2a,ag-ui,knowledge,agent-claude,mem0, Mempalace, langfuse]
8888
```
8989

9090
### Develop Weather Agent
@@ -457,7 +457,7 @@ Related docs:
457457
This group helps you:
458458

459459
- Session: manage per-session messages, summaries, and state
460-
- Memory: manage cross-session long-term memory (including Mem0)
460+
- Memory: manage cross-session long-term memory (including Mem0, Mempalace)
461461
- Knowledge: cover document loading, retrieval, RAG, and prompt templates
462462

463463
### 10. Serving and Protocols
@@ -545,12 +545,12 @@ The framework is organized in an event-driven architecture where each layer can
545545
- **Runner layer**: Unified execution entry, coordinating Session / Memory / Artifact services
546546
- **Tool layer**: FunctionTool / file tools / MCPToolset / Skill tools
547547
- **Model layer**: OpenAIModel / AnthropicModel / LiteLLMModel
548-
- **Memory layer**: SessionService / MemoryService / SessionSummarizer / Mem0MemoryService
548+
- **Memory layer**: SessionService / MemoryService / SessionSummarizer / Mem0MemoryService / MempalaceMemoryService
549549
- **Knowledge layer**: Production-grade LangChain-based knowledge and RAG capability
550550
- **Execution and skill layer**: CodeExecutor (local / container) / Skills
551551
- **Service layer**: FastAPI / A2A / AG-UI
552552
- **Observability layer**: OpenTelemetry tracing/metrics, integrable with platforms like Langfuse
553-
- **Ecosystem adapter layer**: claude-agent-sdk / mcp / LangChain / LiteLLM / Mem0 plugged into the main chain through model/tool/memory adapters
553+
- **Ecosystem adapter layer**: claude-agent-sdk / mcp / LangChain / LiteLLM / Mem0 / Mempalace plugged into the main chain through model/tool/memory adapters
554554

555555
Key packages:
556556

README.zh_CN.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ tRPC-Agent-Python 提供从 Agent 构建、编排、工具接入、会话记忆
1616

1717
- **多范式 Agent 编排**:预设编排支持 ChainAgent / ParallelAgent / CycleAgent / TransferAgent,同时支持 GraphAgent 图编排
1818
- **图编排能力(GraphAgent)**:通过 DSL 统一编排 Agent / Tool / MCP / Knowledge / CodeExecutor
19-
- **高效接入 Python AI 生态扩展**:Agent 生态扩展(claude-agent-sdk / LangGraph 等)/ 工具生态扩展(mcp 等)/ 知识库生态扩展(LangChain 等)/ 模型生态扩展(LiteLLM 等)/ 记忆生态扩展(Mem0
19+
- **高效接入 Python AI 生态扩展**:Agent 生态扩展(claude-agent-sdk / LangGraph 等)/ 工具生态扩展(mcp 等)/ 知识库生态扩展(LangChain 等)/ 模型生态扩展(LiteLLM 等)/ 记忆生态扩展(Mem0、Mempalace等
2020
- **Agent 生态扩展**:支持 LangGraphAgent / ClaudeAgent / TeamAgent(Agno-Like)
2121
- **Tool 生态扩展**:FunctionTool / 文件工具 / MCPToolset / LangChain Tool / Agent-as-Tool
22-
- **完善的记忆能力(Session / Memory)**:Session 负责单会话内的消息与状态管理,Memory 负责跨会话长期记忆与个性化信息沉淀。持久化支持 InMemory / Redis / SQL,Memory 还支持 Mem0
22+
- **完善的记忆能力(Session / Memory)**:Session 负责单会话内的消息与状态管理,Memory 负责跨会话长期记忆与个性化信息沉淀。持久化支持 InMemory / Redis / SQL,Memory 还支持 Mem0、Mempalace
2323
- **生产级知识库能力**:知识库能力基于 LangChain 组件构建,支持 RAG 场景
2424
- **CodeExecutor 扩展能力**:支持本地 / 容器执行器,用于支持 Agent 的代码执行与任务落地能力
2525
- **Skills 扩展能力**:支持 SKILL.md 技能体系,用于支持 Agent 的技能复用与动态工具化能力
@@ -84,7 +84,7 @@ pip install trpc-agent-py
8484
按需安装扩展能力:
8585

8686
```bash
87-
pip install trpc-agent-py[a2a,ag-ui,knowledge,agent-claude,mem0,langfuse]
87+
pip install trpc-agent-py[a2a,ag-ui,knowledge,agent-claude,mem0, Mempalace, langfuse]
8888
```
8989

9090

@@ -458,7 +458,7 @@ skill_tool_set = SkillToolSet(repository=repository, run_tool_kwargs=tool_kwargs
458458
这组示例可以帮你:
459459

460460
- Session:管理单会话的消息、摘要与状态
461-
- Memory:管理跨会话长期记忆(含 Mem0)
461+
- Memory:管理跨会话长期记忆(含 Mem0, Mempalace
462462
- Knowledge:覆盖文档加载、检索、RAG、提示模板等链路
463463

464464
### 10. 服务化与协议
@@ -546,12 +546,12 @@ skill_tool_set = SkillToolSet(repository=repository, run_tool_kwargs=tool_kwargs
546546
- **Runner 层**:统一执行入口,负责 Session/Memory/Artifact 等服务协同
547547
- **Tool 层**:FunctionTool / 文件工具 / MCPToolset / Skill 工具
548548
- **Model 层**:OpenAIModel / AnthropicModel / LiteLLMModel
549-
- **Memory 层**:SessionService / MemoryService / SessionSummarizer / Mem0MemoryService
549+
- **Memory 层**:SessionService / MemoryService / SessionSummarizer / Mem0MemoryService / MempalaceMemoryService
550550
- **Knowledge 层**:基于 LangChain 的生产级知识库能力(RAG)
551551
- **执行与技能层**:CodeExecutor(本地/容器)/ Skills
552552
- **服务层**:FastAPI / A2A / AG-UI
553553
- **观测层**:OpenTelemetry tracing/metrics,可对接 Langfuse 等平台
554-
- **生态适配层**:claude-agent-sdk / mcp / LangChain / LiteLLM / Mem0,通过模型/工具/记忆适配器接入主链路
554+
- **生态适配层**:claude-agent-sdk / mcp / LangChain / LiteLLM / Mem0 / MemoryService,通过模型/工具/记忆适配器接入主链路
555555

556556
关键包一览:
557557

examples/memory_service_with_sql/agent/agent.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from trpc_agent_sdk.models import OpenAIModel
1111
from trpc_agent_sdk.tools import FunctionTool
1212
from trpc_agent_sdk.tools import load_memory_tool
13+
from trpc_agent_sdk.types import GenerateContentConfig
14+
from trpc_agent_sdk.types import HttpOptions
1315

1416
from .config import get_model_config
1517
from .prompts import INSTRUCTION
@@ -25,12 +27,18 @@ def _create_model() -> LLMModel:
2527

2628
def create_agent() -> LlmAgent:
2729
""" Create an agent"""
30+
generate_content_config = GenerateContentConfig(
31+
http_options=HttpOptions(extra_body={"chat_template_kwargs": {
32+
"enable_thinking": False
33+
}}),
34+
)
2835
agent = LlmAgent(
2936
name="assistant",
3037
description="A helpful assistant for conversation",
3138
model=_create_model(), # You can change this to your preferred model
3239
instruction=INSTRUCTION,
3340
tools=[FunctionTool(get_weather_report), load_memory_tool],
41+
generate_content_config=generate_content_config,
3442
)
3543
return agent
3644

examples/session_service_with_sql/agent/agent.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from trpc_agent_sdk.models import LLMModel
1010
from trpc_agent_sdk.models import OpenAIModel
1111
from trpc_agent_sdk.tools import FunctionTool
12+
from trpc_agent_sdk.types import GenerateContentConfig
13+
from trpc_agent_sdk.types import HttpOptions
1214

1315
from .config import get_model_config
1416
from .prompts import INSTRUCTION
@@ -24,12 +26,18 @@ def _create_model() -> LLMModel:
2426

2527
def create_agent() -> LlmAgent:
2628
""" Create an agent"""
29+
generate_content_config = GenerateContentConfig(
30+
http_options=HttpOptions(extra_body={"chat_template_kwargs": {
31+
"enable_thinking": False
32+
}}),
33+
)
2734
agent = LlmAgent(
2835
name="assistant",
2936
description="A helpful assistant for conversation",
3037
model=_create_model(), # You can change this to your preferred model
3138
instruction=INSTRUCTION,
3239
tools=[FunctionTool(get_weather_report)],
40+
generate_content_config=generate_content_config,
3341
)
3442
return agent
3543

examples/session_service_with_sql/run_agent.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ async def run_weather_agent():
7676
]
7777

7878
for query in demo_queries:
79-
# Use a new session for each query
80-
8179
user_content = Content(parts=[Part.from_text(text=query)])
8280

81+
print(f"👤 User: {query}")
8382
print("🤖 Assistant: ", end="", flush=True)
8483
async for event in runner.run_async(user_id=user_id, session_id=current_session_id, new_message=user_content):
8584
# Check if event.content exists

tests/common/test_compatible.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
Covers:
99
- PY_310 version flag
10-
- checkenum() with standard enums, IntEnum, and fallback path
10+
- check_enum() with standard enums, IntEnum, and fallback path
1111
- OSDetector: platform detection, properties, get_os_name, get_os_info, __str__
1212
- OS_DETECTOR module-level singleton
1313
"""
@@ -21,7 +21,7 @@
2121

2222
import pytest
2323

24-
from trpc_agent_sdk.common._compatible import OS_DETECTOR, OSDetector, PY_310, checkenum
24+
from trpc_agent_sdk.common._compatible import OS_DETECTOR, OSDetector, PY_310, check_enum
2525

2626

2727
# ---------------------------------------------------------------------------
@@ -40,7 +40,7 @@ def test_py310_matches_runtime(self):
4040

4141

4242
# ---------------------------------------------------------------------------
43-
# checkenum
43+
# check_enum
4444
# ---------------------------------------------------------------------------
4545

4646

@@ -63,32 +63,32 @@ class _FlagEnum(enum.Flag):
6363

6464

6565
class TestCheckenum:
66-
"""Tests for checkenum()."""
66+
"""Tests for check_enum()."""
6767

6868
def test_valid_enum_member(self):
69-
assert checkenum(_Color.RED, _Color) is True
69+
assert check_enum(_Color.RED, _Color) is True
7070

7171
def test_invalid_enum_member(self):
72-
assert checkenum("yellow", _Color) is False
72+
assert check_enum("yellow", _Color) is False
7373

7474
def test_valid_int_enum_member(self):
75-
assert checkenum(_Priority.HIGH, _Priority) is True
75+
assert check_enum(_Priority.HIGH, _Priority) is True
7676

7777
def test_invalid_int_enum_member(self):
78-
assert checkenum(99, _Priority) is False
78+
assert check_enum(99, _Priority) is False
7979

8080
def test_valid_flag_enum_member(self):
81-
assert checkenum(_FlagEnum.READ, _FlagEnum) is True
81+
assert check_enum(_FlagEnum.READ, _FlagEnum) is True
8282

8383
def test_string_value_is_found_by_value(self):
8484
# Python 3.12+ enum __contains__ matches by value
85-
assert checkenum("red", _Color) is True
85+
assert check_enum("red", _Color) is True
8686

8787
def test_string_not_matching_any_value(self):
88-
assert checkenum("magenta", _Color) is False
88+
assert check_enum("magenta", _Color) is False
8989

9090
def test_none_is_not_member(self):
91-
assert checkenum(None, _Color) is False
91+
assert check_enum(None, _Color) is False
9292

9393
def test_fallback_to_members_values(self):
9494
"""When ``in`` raises, falls back to __members__.values()."""
@@ -108,8 +108,8 @@ def __contains__(self, item):
108108
def __iter__(self):
109109
raise TypeError("broken __iter__")
110110

111-
assert checkenum("a", _BadContains()) is True
112-
assert checkenum("c", _BadContains()) is False
111+
assert check_enum("a", _BadContains()) is True
112+
assert check_enum("c", _BadContains()) is False
113113

114114

115115
# ---------------------------------------------------------------------------

tests/common/test_init.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@
1111
from __future__ import annotations
1212

1313
import trpc_agent_sdk.common as common_mod
14-
from trpc_agent_sdk.common import OS_DETECTOR, OSDetector, checkenum
14+
from trpc_agent_sdk.common import OS_DETECTOR, OSDetector, check_enum
1515
from trpc_agent_sdk.common._compatible import (
1616
OS_DETECTOR as _ORIG_OS_DETECTOR,
1717
OSDetector as _OrigOSDetector,
18-
checkenum as _orig_checkenum,
18+
check_enum as _orig_check_enum,
1919
)
2020

2121

2222
class TestPublicExports:
2323
"""Ensure __init__.py re-exports the right objects."""
2424

2525
def test_all_contains_expected_names(self):
26-
assert set(common_mod.__all__) == {"OSDetector", "OS_DETECTOR", "checkenum"}
26+
assert set(common_mod.__all__) == {"OSDetector", "OS_DETECTOR", "check_enum"}
2727

2828
def test_os_detector_class_is_same_object(self):
2929
assert OSDetector is _OrigOSDetector
@@ -32,4 +32,4 @@ def test_os_detector_instance_is_same_object(self):
3232
assert OS_DETECTOR is _ORIG_OS_DETECTOR
3333

3434
def test_checkenum_is_same_function(self):
35-
assert checkenum is _orig_checkenum
35+
assert check_enum is _orig_check_enum

tests/memory/test_sql_memory_service.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -278,17 +278,6 @@ async def test_store_skips_events_without_content(self):
278278
svc._sql_storage.add.assert_not_called()
279279
svc._sql_storage.commit.assert_not_called()
280280

281-
async def test_store_raises_on_non_session(self):
282-
svc = SqlMemoryService.__new__(SqlMemoryService)
283-
svc._memory_service_config = _make_config_no_ttl()
284-
svc._sql_storage = _patch_sql_storage()
285-
svc._SqlMemoryService__cleanup_task = None
286-
svc._SqlMemoryService__cleanup_stop_event = None
287-
288-
with pytest.raises(TypeError, match="Content must be a Session"):
289-
await svc.store_session("not a session")
290-
291-
292281
# ---------------------------------------------------------------------------
293282
# SqlMemoryService — search_memory
294283
# ---------------------------------------------------------------------------

tests/models/test_anthropic_model_ext.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -519,14 +519,5 @@ def test_part_with_inline_data_is_valid(self):
519519
request = LlmRequest(contents=[Content(parts=[part], role="user")])
520520
model.validate_request(request)
521521

522-
def test_part_with_no_content_raises(self):
523-
"""Part with no meaningful content raises ValueError."""
524-
model = _model()
525-
part = Part()
526-
request = LlmRequest(contents=[Content(parts=[part], role="user")])
527-
with pytest.raises(ValueError, match="Content parts must have"):
528-
model.validate_request(request)
529-
530-
531522
if __name__ == "__main__":
532523
pytest.main([__file__, "-v"])

tests/models/test_openai_model.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,6 @@ def test_validate_request_with_multiple_contents(self):
148148
# Should not raise
149149
model.validate_request(request)
150150

151-
def test_validate_request_with_parts_without_content(self):
152-
"""Test validating request with parts that have no actual content raises ValueError."""
153-
model = OpenAIModel(model_name="gpt-4", api_key="test_key")
154-
155-
# Create a part with all None fields
156-
part = Part()
157-
content = Content(parts=[part], role="user")
158-
request = LlmRequest(contents=[content], config=None, tools_dict={})
159-
160-
with pytest.raises(ValueError, match="Content parts must have"):
161-
model.validate_request(request)
162-
163151
def test_properties_and_config(self):
164152
"""Test model properties and config."""
165153
model = OpenAIModel(

0 commit comments

Comments
 (0)