Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions docs/mkdocs/en/session.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,18 @@ session = await session_service.get_session(

**List Sessions**:
```python
# Specify user_id: returns all sessions for that user (without events)
session_list = await session_service.list_sessions(
app_name="my_app",
user_id="user_001"
)
# Returns ListSessionsResponse, containing all sessions for the user (without events)

# user_id=None: returns sessions across all users for the app
all_session_list = await session_service.list_sessions(
app_name="my_app",
user_id=None
)
# Returns ListSessionsResponse, containing matching sessions (without events)
```

**Delete Session**:
Expand All @@ -128,7 +135,7 @@ await session_service.delete_session(
**Implementation Logic** (`_base_session_service.py`):
- `create_session`: Creates a session, separates and stores app/user/session state
- `get_session`: Retrieves a session, merges app/user/session state, applies event filtering
- `list_sessions`: Lists sessions (excludes events to reduce data transfer)
- `list_sessions`: Lists sessions (excludes events to reduce data transfer); passing `user_id=None` returns sessions across all users for the app
- `delete_session`: Deletes a session and its associated data

---
Expand Down Expand Up @@ -611,6 +618,7 @@ session = await session_service.get_session(
)

# List existing Sessions
# Specify user_id to return that user's sessions; user_id=None returns sessions across all users for the app
session_list = await session_service.list_sessions(
app_name=app_name,
user_id=user_id
Expand Down
12 changes: 10 additions & 2 deletions docs/mkdocs/zh/session.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,18 @@ session = await session_service.get_session(

**列出会话**:
```python
# 指定 user_id:返回该用户的所有会话(不含 events)
session_list = await session_service.list_sessions(
app_name="my_app",
user_id="user_001"
)
# 返回 ListSessionsResponse,包含该用户的所有会话(不含 events)

# user_id 为 None:返回该 app 下所有用户的会话
all_session_list = await session_service.list_sessions(
app_name="my_app",
user_id=None
)
# 返回 ListSessionsResponse,包含符合条件的所有会话(不含 events)
```

**删除会话**:
Expand All @@ -128,7 +135,7 @@ await session_service.delete_session(
**实现逻辑**(`_base_session_service.py`):
- `create_session`:创建会话,分离并存储 app/user/session 状态
- `get_session`:获取会话,合并 app/user/session 状态,应用事件过滤
- `list_sessions`:列出会话列表(不包含 events,减少数据传输)
- `list_sessions`:列出会话列表(不包含 events,减少数据传输);`user_id` 传 `None` 时返回该 app 下所有用户的会话
- `delete_session`:删除会话及其关联数据

---
Expand Down Expand Up @@ -611,6 +618,7 @@ session = await session_service.get_session(
)

# 列出存在的 Session
# 指定 user_id 返回该用户的会话;user_id=None 返回该 app 下所有用户的会话
session_list = await session_service.list_sessions(
app_name=app_name,
user_id=user_id
Expand Down
73 changes: 57 additions & 16 deletions tests/sessions/test_in_memory_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
def _make_session_config(ttl_seconds=0, cleanup_interval=0.0, enable_ttl=False, **kwargs):
config = SessionServiceConfig(**kwargs)
if enable_ttl:
config.ttl = SessionServiceConfig.create_ttl_config(
enable=True, ttl_seconds=ttl_seconds, cleanup_interval_seconds=cleanup_interval)
config.ttl = SessionServiceConfig.create_ttl_config(enable=True,
ttl_seconds=ttl_seconds,
cleanup_interval_seconds=cleanup_interval)
else:
config.clean_ttl_config()
return config
Expand All @@ -56,7 +57,9 @@ def _make_event(author="agent", text="hello", state_delta=None, partial=False):
# SessionWithTTL
# ---------------------------------------------------------------------------


class TestSessionWithTTL:

def test_update_and_get(self):
session = Session(id="s1", app_name="app", user_id="user", save_key="k")
wrapper = SessionWithTTL(session=session)
Expand Down Expand Up @@ -85,7 +88,9 @@ def test_get_non_expired(self):
# StateWithTTL
# ---------------------------------------------------------------------------


class TestStateWithTTL:

def test_update(self):
wrapper = StateWithTTL()
result = wrapper.update({"key": "value"})
Expand Down Expand Up @@ -119,7 +124,9 @@ def test_update_expired_resets_then_updates(self):
# InMemorySessionService — create_session
# ---------------------------------------------------------------------------


class TestInMemoryCreateSession:

async def test_create_basic_session(self):
svc = InMemorySessionService(session_config=_make_session_config())
session = await svc.create_session(app_name="app", user_id="user")
Expand All @@ -143,13 +150,13 @@ async def test_create_with_whitespace_id_generates_uuid(self):

async def test_create_with_state(self):
svc = InMemorySessionService(session_config=_make_session_config())
session = await svc.create_session(
app_name="app", user_id="user",
state={
"session_key": "session_val",
f"{State.APP_PREFIX}app_key": "app_val",
f"{State.USER_PREFIX}user_key": "user_val",
})
session = await svc.create_session(app_name="app",
user_id="user",
state={
"session_key": "session_val",
f"{State.APP_PREFIX}app_key": "app_val",
f"{State.USER_PREFIX}user_key": "user_val",
})
assert session.state["session_key"] == "session_val"
assert session.state[f"{State.APP_PREFIX}app_key"] == "app_val"
assert session.state[f"{State.USER_PREFIX}user_key"] == "user_val"
Expand All @@ -168,7 +175,9 @@ async def test_create_returns_deep_copy(self):
# InMemorySessionService — get_session
# ---------------------------------------------------------------------------


class TestInMemoryGetSession:

async def test_get_existing_session(self):
svc = InMemorySessionService(session_config=_make_session_config())
created = await svc.create_session(app_name="app", user_id="user", session_id="s1")
Expand All @@ -185,13 +194,14 @@ async def test_get_nonexistent_session(self):

async def test_get_returns_merged_state(self):
svc = InMemorySessionService(session_config=_make_session_config())
await svc.create_session(
app_name="app", user_id="user", session_id="s1",
state={
"sk": "sv",
f"{State.APP_PREFIX}ak": "av",
f"{State.USER_PREFIX}uk": "uv",
})
await svc.create_session(app_name="app",
user_id="user",
session_id="s1",
state={
"sk": "sv",
f"{State.APP_PREFIX}ak": "av",
f"{State.USER_PREFIX}uk": "uv",
})
result = await svc.get_session(app_name="app", user_id="user", session_id="s1")
assert result.state["sk"] == "sv"
assert result.state[f"{State.APP_PREFIX}ak"] == "av"
Expand All @@ -212,7 +222,9 @@ async def test_get_returns_deep_copy(self):
# InMemorySessionService — list_sessions
# ---------------------------------------------------------------------------


class TestInMemoryListSessions:

async def test_list_empty(self):
svc = InMemorySessionService(session_config=_make_session_config())
result = await svc.list_sessions(app_name="app", user_id="user")
Expand Down Expand Up @@ -251,12 +263,31 @@ async def test_list_nonexistent_user(self):
assert result.sessions == []
await svc.close()

async def test_list_all_users_when_user_id_none(self):
svc = InMemorySessionService(session_config=_make_session_config())
await svc.create_session(app_name="app", user_id="user1", session_id="s1")
await svc.create_session(app_name="app", user_id="user2", session_id="s2")
result = await svc.list_sessions(app_name="app", user_id=None)
ids = sorted(s.id for s in result.sessions)
assert ids == ["s1", "s2"]
await svc.close()

async def test_list_all_users_filtered_by_app(self):
svc = InMemorySessionService(session_config=_make_session_config())
await svc.create_session(app_name="app1", user_id="user1", session_id="s1")
await svc.create_session(app_name="app2", user_id="user1", session_id="s2")
result = await svc.list_sessions(app_name="app1", user_id=None)
assert [s.id for s in result.sessions] == ["s1"]
await svc.close()


# ---------------------------------------------------------------------------
# InMemorySessionService — delete_session
# ---------------------------------------------------------------------------


class TestInMemoryDeleteSession:

async def test_delete_existing(self):
svc = InMemorySessionService(session_config=_make_session_config())
await svc.create_session(app_name="app", user_id="user", session_id="s1")
Expand All @@ -275,7 +306,9 @@ async def test_delete_nonexistent(self):
# InMemorySessionService — append_event
# ---------------------------------------------------------------------------


class TestInMemoryAppendEvent:

async def test_append_basic(self):
svc = InMemorySessionService(session_config=_make_session_config())
session = await svc.create_session(app_name="app", user_id="user", session_id="s1")
Expand Down Expand Up @@ -351,7 +384,9 @@ async def test_append_updates_conversation_count(self):
# InMemorySessionService — update_session
# ---------------------------------------------------------------------------


class TestInMemoryUpdateSession:

async def test_update_existing(self):
svc = InMemorySessionService(session_config=_make_session_config())
session = await svc.create_session(app_name="app", user_id="user", session_id="s1")
Expand Down Expand Up @@ -384,7 +419,9 @@ async def test_update_nonexistent_session(self):
# InMemorySessionService — cleanup
# ---------------------------------------------------------------------------


class TestInMemoryCleanupExpired:

async def test_cleanup_removes_expired_sessions(self):
config = _make_session_config(enable_ttl=True, ttl_seconds=1, cleanup_interval=3600.0)
svc = InMemorySessionService(session_config=config)
Expand Down Expand Up @@ -436,7 +473,9 @@ async def test_cleanup_nothing_expired(self):
# InMemorySessionService — cleanup task lifecycle
# ---------------------------------------------------------------------------


class TestInMemoryCleanupTask:

def test_no_cleanup_task_when_ttl_disabled(self):
svc = InMemorySessionService(session_config=_make_session_config())
assert svc._InMemorySessionService__cleanup_task is None
Expand Down Expand Up @@ -490,7 +529,9 @@ async def test_cleanup_loop_handles_error(self):
# InMemorySessionService — internal helpers
# ---------------------------------------------------------------------------


class TestInMemoryInternalHelpers:

async def test_get_app_state_nonexistent(self):
svc = InMemorySessionService(session_config=_make_session_config())
assert svc._get_app_state("nonexistent") == {}
Expand Down
Loading
Loading