mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-19 16:12:30 +00:00
feat(feishu): add topic_isolation config switch
This commit is contained in:
parent
fd6887c274
commit
03b357b12d
@ -258,6 +258,7 @@ class FeishuConfig(Base):
|
||||
reply_to_message: bool = False # If True, bot replies quote the user's original message
|
||||
streaming: bool = True
|
||||
domain: Literal["feishu", "lark"] = "feishu" # Set to "lark" for international Lark
|
||||
topic_isolation: bool = True # If True, each topic in group chat gets its own session (isolation)
|
||||
|
||||
|
||||
_STREAM_ELEMENT_ID = "streaming_md"
|
||||
@ -1770,12 +1771,15 @@ class FeishuChannel(BaseChannel):
|
||||
if not content and not media_paths:
|
||||
return
|
||||
|
||||
# Build topic-scoped session key for conversation isolation.
|
||||
# Group chat: each topic gets its own session via root_id (replies
|
||||
# inside a topic) or message_id (top-level messages start a new topic).
|
||||
# Build session key for conversation isolation.
|
||||
# If topic_isolation is True: each topic gets its own session via root_id/message_id.
|
||||
# If topic_isolation is False: all messages in group share the same session.
|
||||
# Private chat: no override — same behavior as Telegram/Slack.
|
||||
if chat_type == "group":
|
||||
session_key = f"feishu:{chat_id}:{root_id or message_id}"
|
||||
if self.config.topic_isolation:
|
||||
session_key = f"feishu:{chat_id}:{root_id or message_id}"
|
||||
else:
|
||||
session_key = f"feishu:{chat_id}"
|
||||
else:
|
||||
session_key = None
|
||||
|
||||
|
||||
@ -912,3 +912,95 @@ async def test_on_message_ignores_unauthorized_sender_before_side_effects() -> N
|
||||
channel._download_and_save_media.assert_not_awaited()
|
||||
channel.transcribe_audio.assert_not_awaited()
|
||||
channel._handle_message.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_session_key_with_topic_isolation_true_uses_thread_scoped() -> None:
|
||||
"""When topic_isolation is True (default), group messages use thread-scoped session keys."""
|
||||
channel = _make_feishu_channel(group_policy="open")
|
||||
channel.config.topic_isolation = True
|
||||
bus_spy = []
|
||||
original_publish = channel.bus.publish_inbound
|
||||
|
||||
async def capture(msg):
|
||||
bus_spy.append(msg)
|
||||
await original_publish(msg)
|
||||
|
||||
channel.bus.publish_inbound = capture
|
||||
channel._download_and_save_media = AsyncMock(return_value=(None, ""))
|
||||
channel.transcribe_audio = AsyncMock(return_value="")
|
||||
channel._add_reaction = AsyncMock(return_value=None)
|
||||
|
||||
# Test with root_id
|
||||
event1 = _make_feishu_event(
|
||||
chat_type="group",
|
||||
content='{"text": "hello"}',
|
||||
root_id="om_root123",
|
||||
message_id="om_child456",
|
||||
)
|
||||
await channel._on_message(event1)
|
||||
|
||||
# Test without root_id
|
||||
event2 = _make_feishu_event(
|
||||
chat_type="group",
|
||||
content='{"text": "another"}',
|
||||
root_id=None,
|
||||
message_id="om_001",
|
||||
)
|
||||
await channel._on_message(event2)
|
||||
|
||||
assert len(bus_spy) == 2
|
||||
assert bus_spy[0].session_key_override == "feishu:oc_abc:om_root123"
|
||||
assert bus_spy[1].session_key_override == "feishu:oc_abc:om_001"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_session_key_with_topic_isolation_false_uses_group_scoped() -> None:
|
||||
"""When topic_isolation is False, all group messages share the same session key (no isolation)."""
|
||||
channel = _make_feishu_channel(group_policy="open")
|
||||
channel.config.topic_isolation = False
|
||||
bus_spy = []
|
||||
original_publish = channel.bus.publish_inbound
|
||||
|
||||
async def capture(msg):
|
||||
bus_spy.append(msg)
|
||||
await original_publish(msg)
|
||||
|
||||
channel.bus.publish_inbound = capture
|
||||
channel._download_and_save_media = AsyncMock(return_value=(None, ""))
|
||||
channel.transcribe_audio = AsyncMock(return_value="")
|
||||
channel._add_reaction = AsyncMock(return_value=None)
|
||||
|
||||
# Test with root_id
|
||||
event1 = _make_feishu_event(
|
||||
chat_type="group",
|
||||
content='{"text": "hello"}',
|
||||
root_id="om_root123",
|
||||
message_id="om_child456",
|
||||
)
|
||||
await channel._on_message(event1)
|
||||
|
||||
# Test without root_id
|
||||
event2 = _make_feishu_event(
|
||||
chat_type="group",
|
||||
content='{"text": "another"}',
|
||||
root_id=None,
|
||||
message_id="om_001",
|
||||
)
|
||||
await channel._on_message(event2)
|
||||
|
||||
# Private chat still works
|
||||
event3 = _make_feishu_event(
|
||||
chat_type="p2p",
|
||||
content='{"text": "private"}',
|
||||
root_id=None,
|
||||
message_id="om_private",
|
||||
)
|
||||
await channel._on_message(event3)
|
||||
|
||||
assert len(bus_spy) == 3
|
||||
# Group messages all share the same key
|
||||
assert bus_spy[0].session_key_override == "feishu:oc_abc"
|
||||
assert bus_spy[1].session_key_override == "feishu:oc_abc"
|
||||
# Private chat has no session key override
|
||||
assert bus_spy[2].session_key_override is None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user