refactor(llm): use Registry pattern for framework registry#1802
refactor(llm): use Registry pattern for framework registry#1802Pouyanpi wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
488bee2 to
e3381bc
Compare
06486f4 to
1682183
Compare
e3381bc to
4ebe621
Compare
1682183 to
06486f4
Compare
Greptile SummaryThis PR replaces the ad-hoc module-level dict and globals in
|
| Filename | Overview |
|---|---|
| nemoguardrails/llm/frameworks/registry.py | Refactors framework state into LLMFrameworkRegistry(Registry) singleton with validate(), lazy factory get(), and active property; one dead guard remains after validate() was added. |
| nemoguardrails/llm/frameworks/init.py | Removes private reset helpers from public all and re-exports only the four stable public functions; clean and minimal. |
| tests/conftest.py | Updates langchain_framework fixture to import _reset_frameworks from the internal registry module instead of the public package; straightforward and correct. |
| tests/llm/frameworks/test_registry.py | Adds register_provider/get_provider_names to all fake framework stubs, updates error-message assertions, removes obsolete _FrameworkWithoutReset tests, and adds TestRegisterFrameworkProtocolEnforcement for the new validate() path. |
Sequence Diagram
sequenceDiagram
participant C as Caller
participant RF as register_framework()
participant GF as get_framework()
participant R as LLMFrameworkRegistry (Singleton)
participant V as validate()
C->>RF: register_framework("custom", fw)
RF->>R: .add("custom", fw)
R->>V: validate("custom", fw)
V-->>R: ✓ isinstance(LLMFramework) + async reset
R-->>RF: stored in items
C->>GF: get_framework("langchain")
GF->>R: .get("langchain")
R->>R: "langchain" not in items but in _factories
R->>R: _langchain_factory() → LangChainFramework()
R->>V: validate("langchain", fw)
V-->>R: ✓
R-->>GF: LangChainFramework instance
C->>C: await _areset_frameworks()
C->>R: items.values() → snapshot
loop each framework
C->>C: await fw.reset()
end
C->>R: registry.reset() → items cleared
C->>R: _active_name = env or "default"
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
nemoguardrails/llm/frameworks/registry.py:92-99
**Dead guard code after protocol enforcement was added**
The `reset is None` guard (and the `getattr` that feeds it) is now unreachable. `validate()` already rejects any framework whose `reset` attribute is absent or synchronous before it enters `self.items`, so the path where `reset` could be `None` at teardown time no longer exists.
```suggestion
try:
for fw in frameworks_to_close:
try:
await fw.reset()
except Exception as exc:
log.warning("Error resetting framework %r: %s", fw, exc)
```
Reviews (5): Last reviewed commit: "refactor(llm): use Registry pattern for ..." | Re-trigger Greptile
06486f4 to
c7573db
Compare
ce98050 to
f9ff9ac
Compare
f9ff9ac to
0b532e8
Compare
540d83b to
4ed1167
Compare
📝 WalkthroughWalkthroughThe registry module refactors from module-level globals to an Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Review rate limit: 8/10 reviews remaining, refill in 7 minutes and 5 seconds. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
nemoguardrails/llm/frameworks/registry.py (1)
40-43: ⚡ Quick winMake
LLMFrameworkRegistry._factoriesimmutable to avoid shared-state drift.At Line 40,
_factoriesis a mutable class-level dict shared across the singleton. Free mutation from tests/extensions can silently alter built-in framework resolution for the whole process.Proposed patch
+from types import MappingProxyType -from typing import Any, Callable, Dict +from typing import Any, Callable, ClassVar, Mapping ... class LLMFrameworkRegistry(Registry): - _factories: Dict[str, Callable[[], LLMFramework]] = { + _factories: ClassVar[Mapping[str, Callable[[], LLMFramework]]] = MappingProxyType({ "default": _default_factory, "langchain": _langchain_factory, - } + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nemoguardrails/llm/frameworks/registry.py` around lines 40 - 43, LLMFrameworkRegistry currently defines a mutable class-level dict _factories that can be mutated by tests/extensions; make it immutable by replacing the plain dict with an immutable mapping (e.g., wrap the dict in types.MappingProxyType or use an immutable mapping type) so consumers still lookup by key but cannot change entries at runtime; update the declaration of _factories and any places that assume mutability (keep _default_factory and _langchain_factory as the callable values) and ensure any tests or code that previously mutated LLMFrameworkRegistry._factories instead use a provided registration API or patching helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@nemoguardrails/llm/frameworks/registry.py`:
- Around line 40-43: LLMFrameworkRegistry currently defines a mutable
class-level dict _factories that can be mutated by tests/extensions; make it
immutable by replacing the plain dict with an immutable mapping (e.g., wrap the
dict in types.MappingProxyType or use an immutable mapping type) so consumers
still lookup by key but cannot change entries at runtime; update the declaration
of _factories and any places that assume mutability (keep _default_factory and
_langchain_factory as the callable values) and ensure any tests or code that
previously mutated LLMFrameworkRegistry._factories instead use a provided
registration API or patching helper.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: d9df53c5-332e-404a-ba87-07f361718ff5
📒 Files selected for processing (2)
nemoguardrails/llm/frameworks/registry.pytests/llm/frameworks/test_registry.py
4ed1167 to
eddcbff
Compare
Migrates nemoguardrails/llm/frameworks.py from module-level dict + state globals to an LLMFrameworkRegistry(Registry) subclass, matching the established pattern used by LogAdapterRegistry and EmbeddingProviderRegistry. Changes: - LLMFrameworkRegistry inherits from Registry (with Singleton metaclass). Gets add/get/list/reset/__contains__/__iter__ for free. - validate() enforces the LLMFramework protocol at registration time. - Lazy factories (_factories) replace the hardcoded branches in get_framework: built-in "default" and "langchain" entries, extensible by simple dict update for future frameworks. - Active framework name becomes a class attribute (one source of truth) instead of a separate module global. - Public facade functions (register_framework, get_framework, set_default_framework, get_default_framework, _reset_frameworks) keep their signatures — behavior-compatible. - Test fake updated to include full protocol surface; error-message matchers updated to Registry wording.
eddcbff to
df27452
Compare
a6be550 to
c69efe5
Compare
Description
Migrates
nemoguardrails/llm/frameworks.pyfrom an ad-hoc module-level dict and state globals to anLLMFrameworkRegistry(Registry)subclass, matching the pattern used byLogAdapterRegistryandEmbeddingProviderRegistry.Summary by CodeRabbit
Refactor
Tests