fix(provider_state): change Lock to RLock to prevent re-entrant deadlock
TIER-3 READ AGENTS.md + conductor/code_styleguides/error_handling.md + src/provider_state.py + src/ai_client.py:2148-2220 before provider-state-rlock-fix.
Tier 2's 25a22057 commit re-bound the 14 module globals in src/ai_client.py as
aliases to provider_state.get_history(...) instances. The ProviderHistory dunder
methods (__bool__, __len__, __iter__, __getitem__) all use \with self.lock:\.
The dunders are non-reentrant: \ hreading.Lock\ blocks if the lock is already
held. The call site in src/ai_client.py:2210-2217 acquires the lock via
\with _deepseek_history_lock:\ (alias to ProviderHistory.lock), then calls
_rerepair_deepseek_history(_deepseek_history) which does \history[-1]\
(acquires the lock again -> DEADLOCK). This caused
tests/test_deepseek_provider.py::test_deepseek_completion_logic to hang
with a 30s timeout.
Fix: change \ hreading.Lock\ to \ hreading.RLock\ in ProviderHistory.
The dunders can now be safely called while the lock is already held.
Also removed:
- Duplicate @dataclass decorator on ProviderHistory (line 25-26)
- Duplicate _PROVIDER_HISTORIES dict declaration (lines 64-71 and 74-81)
Acceptance: test_deepseek_provider (7/7) + test_provider_state + test_ai_client_result + test_ai_client_tool_loop all pass.
This commit is contained in:
+1
-12
@@ -22,11 +22,10 @@ from dataclasses import dataclass, field
|
|||||||
from src.type_aliases import HistoryMessage, Metadata
|
from src.type_aliases import HistoryMessage, Metadata
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProviderHistory:
|
class ProviderHistory:
|
||||||
messages: list[HistoryMessage] = field(default_factory=list)
|
messages: list[HistoryMessage] = field(default_factory=list)
|
||||||
lock: threading.Lock = field(default_factory=threading.Lock)
|
lock: threading.RLock = field(default_factory=threading.RLock)
|
||||||
|
|
||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
@@ -71,16 +70,6 @@ _PROVIDER_HISTORIES: dict[str, ProviderHistory] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_PROVIDER_HISTORIES: dict[str, ProviderHistory] = {
|
|
||||||
"anthropic": ProviderHistory(),
|
|
||||||
"deepseek": ProviderHistory(),
|
|
||||||
"minimax": ProviderHistory(),
|
|
||||||
"qwen": ProviderHistory(),
|
|
||||||
"grok": ProviderHistory(),
|
|
||||||
"llama": ProviderHistory(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_history(provider: str) -> ProviderHistory:
|
def get_history(provider: str) -> ProviderHistory:
|
||||||
if provider not in _PROVIDER_HISTORIES:
|
if provider not in _PROVIDER_HISTORIES:
|
||||||
raise KeyError(f"Unknown provider: {provider!r}")
|
raise KeyError(f"Unknown provider: {provider!r}")
|
||||||
|
|||||||
Reference in New Issue
Block a user