ed
cc7993e53d
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.
2026-06-24 23:30:15 -04:00
ed
25a2205722
refactor(ai_client): 14 module globals → provider_state.get_history() pattern
2026-06-24 17:17:58 -04:00
ed
5bd416c3ca
feat(provider): add src/provider_state.py + tests (t3_2, t3_3)
...
Phase 3 of any_type_componentization_20260621 (PARTIAL). Adds the
ProviderHistory abstraction and 6-provider registry.
NEW src/provider_state.py (60 lines):
- ProviderHistory dataclass (messages: list[HistoryMessage], lock: Lock,
append / get_all / replace_all / clear methods)
- _PROVIDER_HISTORIES: dict[str, ProviderHistory] for anthropic / deepseek /
minimax / qwen / grok / llama
- get_history(provider) factory + clear_all() + providers()
- SDK client holders (_gemini_chat, _anthropic_client, etc.) NOT touched
per Pattern 3 (heterogeneous SDK types)
NEW tests/test_provider_state.py (12 tests, all pass):
- test_six_providers_registered
- test_get_history_returns_singleton_per_provider
- test_get_history_raises_for_unknown
- test_provider_history_starts_empty
- test_provider_history_append / get_all_returns_copy / replace_all /
replace_all_takes_copy / clear
- test_clear_all_resets_every_provider
- test_provider_history_thread_safety (10 threads x 100 messages)
- test_independent_locks_per_provider (lock on one doesn't block another)
DEFERRED:
- t3_4 (Remove 14 globals from ai_client.py:111-133)
- t3_5 through t3_13 (Update call sites in _send_<provider> functions)
- t3_14 (Run full regression suite on test_ai_client*.py)
These call-site updates require careful per-function refactoring of the
~27 sites in _send_anthropic, _send_deepseek, _send_minimax, _send_qwen,
_send_grok, _send_llama. The ai_client.py file is 3432 lines; a single
regex pass risks subtle indentation regressions in nested constructs
(see the 7
ot : orphan lines from a previous attempt).
The provider_state module is independently usable and tested. Future
track: provider_state_migration_2026MMDD to wire up the call sites
mechanically, OR integrate into a Phase 3 retry pass.
Verified:
uv run pytest tests/test_provider_state.py --timeout=30
12 passed in 2.99s
2026-06-22 00:59:50 -04:00
ed
751b94d4e8
Revert "merge: tier2/phase2_4_5_call_site_completion_20260621 (parent + follow-up + Phase 6e analysis)"
...
This reverts commit f914b2bcd4 , reversing
changes made to 7fef95cc87 .
2026-06-21 22:39:14 -04:00
ed
2ad4718c3c
feat(provider): add src/provider_state.py + tests (t3_2, t3_3)
...
Phase 3 of any_type_componentization_20260621 (PARTIAL). Adds the
ProviderHistory abstraction and 6-provider registry.
NEW src/provider_state.py (60 lines):
- ProviderHistory dataclass (messages: list[HistoryMessage], lock: Lock,
append / get_all / replace_all / clear methods)
- _PROVIDER_HISTORIES: dict[str, ProviderHistory] for anthropic / deepseek /
minimax / qwen / grok / llama
- get_history(provider) factory + clear_all() + providers()
- SDK client holders (_gemini_chat, _anthropic_client, etc.) NOT touched
per Pattern 3 (heterogeneous SDK types)
NEW tests/test_provider_state.py (12 tests, all pass):
- test_six_providers_registered
- test_get_history_returns_singleton_per_provider
- test_get_history_raises_for_unknown
- test_provider_history_starts_empty
- test_provider_history_append / get_all_returns_copy / replace_all /
replace_all_takes_copy / clear
- test_clear_all_resets_every_provider
- test_provider_history_thread_safety (10 threads x 100 messages)
- test_independent_locks_per_provider (lock on one doesn't block another)
DEFERRED:
- t3_4 (Remove 14 globals from ai_client.py:111-133)
- t3_5 through t3_13 (Update call sites in _send_<provider> functions)
- t3_14 (Run full regression suite on test_ai_client*.py)
These call-site updates require careful per-function refactoring of the
~27 sites in _send_anthropic, _send_deepseek, _send_minimax, _send_qwen,
_send_grok, _send_llama. The ai_client.py file is 3432 lines; a single
regex pass risks subtle indentation regressions in nested constructs
(see the 7
ot : orphan lines from a previous attempt).
The provider_state module is independently usable and tested. Future
track: provider_state_migration_2026MMDD to wire up the call sites
mechanically, OR integrate into a Phase 3 retry pass.
Verified:
uv run pytest tests/test_provider_state.py --timeout=30
12 passed in 2.99s
2026-06-21 16:43:42 -04:00