Skip to content

Commit b0687e2

Browse files
committed
fix runtime error
1 parent 7875c9c commit b0687e2

File tree

6 files changed

+81
-30
lines changed

6 files changed

+81
-30
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agentscope-runtime"
3-
version = "1.0.3a3"
3+
version = "1.0.3a4"
44
description = "A production-ready runtime framework for agent applications, providing secure sandboxed execution environments and scalable deployment solutions with multi-framework support."
55
readme = "README.md"
66
requires-python = ">=3.10"

src/agentscope_runtime/engine/services/agent_state/redis_state_service.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,8 @@ async def export_state(
159159
if self._ttl_seconds is not None:
160160
await self._redis.expire(key, self._ttl_seconds)
161161

162-
return json.loads(state_json)
162+
try:
163+
return json.loads(state_json)
164+
except (json.JSONDecodeError, ValueError):
165+
# Return None for corrupted state data instead of raising exception
166+
return None

src/agentscope_runtime/engine/services/memory/redis_memory_service.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,14 @@ async def add_memory(
133133
if self._ttl_seconds is not None:
134134
await self._redis.expire(key, self._ttl_seconds)
135135

136-
async def search_memory(
136+
async def search_memory( # pylint: disable=too-many-branches
137137
self,
138138
user_id: str,
139139
messages: list,
140140
filters: Optional[Dict[str, Any]] = None,
141141
) -> list:
142+
if not self._redis:
143+
raise RuntimeError("Redis connection is not available")
142144
key = self._user_key(user_id)
143145
if (
144146
not messages
@@ -172,7 +174,11 @@ async def search_memory(
172174
msgs_json = await self._redis.hget(key, session_id)
173175
if not msgs_json:
174176
continue
175-
msgs = self._deserialize(msgs_json)
177+
try:
178+
msgs = self._deserialize(msgs_json)
179+
except Exception:
180+
# Skip corrupted message data
181+
continue
176182

177183
# Match messages in this session
178184
for msg in msgs:
@@ -192,9 +198,8 @@ async def search_memory(
192198

193199
# Refresh TTL on read to extend lifetime of actively used data,
194200
# if a TTL is configured and there is existing data for this key.
195-
ttl_seconds = getattr(self, "_ttl", None)
196-
if ttl_seconds and hash_keys:
197-
await self._redis.expire(key, ttl_seconds)
201+
if self._ttl_seconds is not None and hash_keys:
202+
await self._redis.expire(key, self._ttl_seconds)
198203

199204
return result
200205

@@ -211,6 +216,8 @@ async def list_memory(
211216
user_id: str,
212217
filters: Optional[Dict[str, Any]] = None,
213218
) -> list:
219+
if not self._redis:
220+
raise RuntimeError("Redis connection is not available")
214221
key = self._user_key(user_id)
215222
page_num = filters.get("page_num", 1) if filters else 1
216223
page_size = filters.get("page_size", 10) if filters else 10
@@ -226,8 +233,12 @@ async def list_memory(
226233
for session_id in sorted(hash_keys):
227234
msgs_json = await self._redis.hget(key, session_id)
228235
if msgs_json:
229-
msgs = self._deserialize(msgs_json)
230-
all_msgs.extend(msgs)
236+
try:
237+
msgs = self._deserialize(msgs_json)
238+
all_msgs.extend(msgs)
239+
except Exception:
240+
# Skip corrupted message data
241+
continue
231242

232243
# Early exit optimization: if we've loaded enough messages
233244
# to cover the requested page, we can stop (but this assumes
@@ -236,7 +247,7 @@ async def list_memory(
236247

237248
# Refresh TTL on active use to keep memory alive,
238249
# mirroring get_session behavior
239-
if getattr(self, "_ttl_seconds", None):
250+
if self._ttl_seconds is not None and hash_keys:
240251
await self._redis.expire(key, self._ttl_seconds)
241252
return all_msgs[start_index:end_index]
242253

@@ -245,6 +256,8 @@ async def delete_memory(
245256
user_id: str,
246257
session_id: Optional[str] = None,
247258
) -> None:
259+
if not self._redis:
260+
raise RuntimeError("Redis connection is not available")
248261
key = self._user_key(user_id)
249262
if session_id:
250263
await self._redis.hdel(key, session_id)

src/agentscope_runtime/engine/services/session_history/redis_session_history_service.py

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ async def create_session(
106106
user_id: str,
107107
session_id: Optional[str] = None,
108108
) -> Session:
109+
if not self._redis:
110+
raise RuntimeError("Redis connection is not available")
109111
if session_id and session_id.strip():
110112
sid = session_id.strip()
111113
else:
@@ -127,12 +129,18 @@ async def get_session(
127129
user_id: str,
128130
session_id: str,
129131
) -> Optional[Session]:
132+
if not self._redis:
133+
raise RuntimeError("Redis connection is not available")
130134
key = self._session_key(user_id, session_id)
131135
session_json = await self._redis.get(key)
132136
if session_json is None:
133137
return None
134138

135-
session = self._session_from_json(session_json)
139+
try:
140+
session = self._session_from_json(session_json)
141+
except Exception:
142+
# Return None for corrupted session data
143+
return None
136144

137145
# Refresh TTL when accessing the session
138146
if self._ttl_seconds is not None:
@@ -141,6 +149,8 @@ async def get_session(
141149
return session
142150

143151
async def delete_session(self, user_id: str, session_id: str):
152+
if not self._redis:
153+
raise RuntimeError("Redis connection is not available")
144154
key = self._session_key(user_id, session_id)
145155
await self._redis.delete(key)
146156

@@ -150,6 +160,8 @@ async def list_sessions(self, user_id: str) -> list[Session]:
150160
Uses SCAN to find all session:{user_id}:* keys. Expired sessions
151161
naturally disappear as their keys expire, avoiding stale entries.
152162
"""
163+
if not self._redis:
164+
raise RuntimeError("Redis connection is not available")
153165
pattern = self._session_pattern(user_id)
154166
sessions = []
155167
cursor = 0
@@ -163,9 +175,13 @@ async def list_sessions(self, user_id: str) -> list[Session]:
163175
for key in keys:
164176
session_json = await self._redis.get(key)
165177
if session_json:
166-
session = self._session_from_json(session_json)
167-
session.messages = []
168-
sessions.append(session)
178+
try:
179+
session = self._session_from_json(session_json)
180+
session.messages = []
181+
sessions.append(session)
182+
except Exception:
183+
# Skip corrupted session data
184+
continue
169185

170186
if cursor == 0:
171187
break
@@ -182,6 +198,8 @@ async def append_message(
182198
List[Dict[str, Any]],
183199
],
184200
):
201+
if not self._redis:
202+
raise RuntimeError("Redis connection is not available")
185203
if not isinstance(message, list):
186204
message = [message]
187205
norm_message = []
@@ -199,14 +217,24 @@ async def append_message(
199217

200218
session_json = await self._redis.get(key)
201219
if session_json is None:
202-
raise RuntimeError(
203-
f"Session {session_id} not found or has expired for user "
204-
f"{user_id}. Previous memory/state has been lost. "
205-
f"Please create a new session.",
220+
# Session expired or not found, treat as a new session
221+
# Create a new session with the current messages
222+
stored_session = Session(
223+
id=session_id,
224+
user_id=user_id,
225+
messages=norm_message.copy(),
206226
)
207-
208-
stored_session = self._session_from_json(session_json)
209-
stored_session.messages.extend(norm_message)
227+
else:
228+
try:
229+
stored_session = self._session_from_json(session_json)
230+
stored_session.messages.extend(norm_message)
231+
except Exception:
232+
# Session data corrupted, treat as a new session
233+
stored_session = Session(
234+
id=session_id,
235+
user_id=user_id,
236+
messages=norm_message.copy(),
237+
)
210238

211239
# Limit the number of messages per session to prevent memory issues
212240
if self._max_messages_per_session is not None:

src/agentscope_runtime/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# -*- coding: utf-8 -*-
2-
__version__ = "v1.0.3a3"
2+
__version__ = "v1.0.3a4"

tests/unit/test_redis_session_history_service.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,19 +264,25 @@ async def test_append_message(
264264
for i, msg in enumerate(stored_session.messages[2:]):
265265
assert msg.content == messages3[i].get("content")
266266

267-
# Test appending to a non-existent session (should raise RuntimeError)
267+
# Test appending to a non-existent session (should create new session)
268268
non_existent_session = Session(
269269
id="non_existent",
270270
user_id=user_id,
271271
messages=[],
272272
)
273-
# This should raise a RuntimeError indicating
274-
# the session is missing/expired
275-
with pytest.raises(RuntimeError, match="not found or has expired"):
276-
await session_history_service.append_message(
277-
non_existent_session,
278-
message1,
279-
)
273+
# This should not raise an error, but create a new session
274+
await session_history_service.append_message(
275+
non_existent_session,
276+
message1,
277+
)
278+
# Verify the session was created with the message
279+
retrieved_session = await session_history_service.get_session(
280+
user_id,
281+
"non_existent",
282+
)
283+
assert retrieved_session is not None
284+
assert len(retrieved_session.messages) == 1
285+
assert retrieved_session.messages[0].content == message1.get("content")
280286

281287

282288
@pytest.mark.asyncio

0 commit comments

Comments
 (0)