fix(discord): preserve transport owner for thread renames
(cherry picked from commit b3e23293ed8b0f6597d442579304ea5095a9029c)
This commit is contained in:
committed by
Teknium
parent
8c58e4f976
commit
fa787be2bd
@@ -435,6 +435,14 @@ class GatewayTopicThreadsMixin:
|
||||
copied_source = source
|
||||
with suppress(Exception):
|
||||
copied_source = dataclasses.replace(source)
|
||||
# ``dataclasses.replace`` intentionally copies only declared fields.
|
||||
# Preserve the in-process transport-owner ref that build_source()
|
||||
# stamps on live inbound events; multiplex routed profiles need it
|
||||
# for side effects such as Discord thread rename, because the
|
||||
# runtime profile may not own the Discord adapter/token.
|
||||
transport_ref = getattr(source, "_transport_adapter_ref", None)
|
||||
if transport_ref is not None:
|
||||
setattr(copied_source, "_transport_adapter_ref", transport_ref)
|
||||
future = safe_schedule_threadsafe(
|
||||
make_coro(copied_source), loop, logger=logger, log_message=f"{label} failed to schedule",
|
||||
)
|
||||
|
||||
@@ -10,10 +10,12 @@ ten minutes, so the throwaway can be the one that survives.
|
||||
from __future__ import annotations
|
||||
|
||||
import types
|
||||
import weakref
|
||||
|
||||
import pytest
|
||||
|
||||
from gateway.config import Platform
|
||||
from gateway.session import SessionSource
|
||||
from gateway.run import GatewayRunner
|
||||
from gateway.run_turn_runner import TurnRunner
|
||||
|
||||
@@ -56,7 +58,7 @@ def test_the_rename_waits_for_the_model_title(lane):
|
||||
assert renames == ["Fix flaky auth test"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.anyio
|
||||
async def test_native_thread_rename_passes_only_the_initial_name_guard():
|
||||
"""The shared rename lane must honor the strict native adapter contract."""
|
||||
calls: list[tuple[str, str, str | None]] = []
|
||||
@@ -102,3 +104,53 @@ async def test_native_thread_rename_passes_only_the_initial_name_guard():
|
||||
)
|
||||
|
||||
assert calls == [("999", "Semantic Session Title", "Initial words")]
|
||||
|
||||
|
||||
def test_title_thread_copy_preserves_transport_adapter_ref(monkeypatch):
|
||||
"""Multiplex-routed sources must keep their transport owner for side effects."""
|
||||
captured_sources = []
|
||||
|
||||
class Adapter:
|
||||
pass
|
||||
|
||||
adapter = Adapter()
|
||||
|
||||
async def noop():
|
||||
return None
|
||||
|
||||
def fake_schedule(coro, loop, logger=None, log_message=None):
|
||||
# The test only inspects the source passed into make_coro; close the
|
||||
# coroutine so pytest does not warn about an unawaited object.
|
||||
coro.close()
|
||||
return None
|
||||
|
||||
monkeypatch.setattr("gateway.run.safe_schedule_threadsafe", fake_schedule)
|
||||
|
||||
source = SessionSource(
|
||||
platform=Platform.DISCORD,
|
||||
chat_id="thread-1",
|
||||
chat_type="thread",
|
||||
thread_id="thread-1",
|
||||
profile="runtime-profile",
|
||||
auto_thread_created=True,
|
||||
auto_thread_initial_name="Initial words",
|
||||
)
|
||||
source._transport_adapter_ref = weakref.ref(adapter)
|
||||
|
||||
runner = types.SimpleNamespace(
|
||||
_gateway_loop=types.SimpleNamespace(is_closed=lambda: False),
|
||||
_schedule_rename_from_title_thread=GatewayRunner._schedule_rename_from_title_thread,
|
||||
)
|
||||
|
||||
runner._schedule_rename_from_title_thread(
|
||||
runner,
|
||||
source,
|
||||
lambda copied: captured_sources.append(copied) or noop(),
|
||||
"Discord semantic thread rename",
|
||||
)
|
||||
|
||||
assert len(captured_sources) == 1
|
||||
copied = captured_sources[0]
|
||||
assert copied is not source
|
||||
assert copied.profile == "runtime-profile"
|
||||
assert copied._transport_adapter_ref() is adapter
|
||||
|
||||
Reference in New Issue
Block a user