fix:(llmrails): pre-initialize LLMRails.kb to prevent AttributeError on init failure#1823
fix:(llmrails): pre-initialize LLMRails.kb to prevent AttributeError on init failure#1823octo-patch wants to merge 1 commit intoNVIDIA-NeMo:developfrom
Conversation
…lure (fixes NVIDIA-NeMo#1665) When `_init_kb()` runs in a background thread via `asyncio.run()`, any failure before the coroutine body executes leaves `self.kb` unset. Subsequent access to `self.kb` then raises AttributeError instead of a meaningful error. Pre-initialize `self.kb = None` before the thread starts so the attribute always exists, regardless of thread execution outcome. This is consistent with how `_init_kb()` itself begins by setting `self.kb = None` before any other logic. Co-Authored-By: Octopus <liyuan851277048@icloud.com> Signed-off-by: octo-patch <octo-patch@github.com>
Greptile SummaryThis PR adds a single defensive line
|
| Filename | Overview |
|---|---|
| nemoguardrails/rails/llm/llmrails.py | Adds self.kb = None pre-initialization before the background thread launch to prevent AttributeError when the thread exits before setting the attribute; change is minimal, correct, and mirrors the first line of _init_kb(). |
Sequence Diagram
sequenceDiagram
participant C as Caller
participant R as LLMRails.__init__
participant T as Background Thread
participant K as _init_kb()
C->>R: LLMRails(config, ...)
R->>R: register_actions(llm_generation_actions)
Note over R: self.kb = None ← NEW (pre-init)
R->>T: Thread(target=asyncio.run, args=(_init_kb(),)).start()
T->>K: asyncio.run(_init_kb())
alt _init_kb succeeds
K->>K: self.kb = None
K->>K: self.kb = KnowledgeBase(...)
K->>K: kb.init() / kb.build()
K-->>T: coroutine completes
T-->>R: join() returns
R->>R: register_action_param("kb", self.kb) ← kb is KnowledgeBase
else _init_kb fails
K--xT: exception swallowed by thread
T-->>R: join() returns (silently)
R->>R: register_action_param("kb", self.kb) ← kb is None (no AttributeError)
end
R-->>C: LLMRails instance returned
Reviews (1): Last reviewed commit: "fix: pre-initialize LLMRails.kb to preve..." | Re-trigger Greptile
📝 WalkthroughWalkthroughThe change initializes Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Pouyanpi
left a comment
There was a problem hiding this comment.
Thank you @octo-patch for fixing the issue👍🏻
a6be550 to
c69efe5
Compare
Fixes #1665
Problem
When
LLMRailsis initialized, it calls_init_kb()in a background thread viaasyncio.run(). If the background thread fails (e.g.,asyncio.run()raises aRuntimeErrorbefore the coroutine body starts), the thread exits silently —threading.Thread.join()does not propagate thread exceptions. As a result,self.kbis never set on theLLMRailsinstance.Any subsequent access to
self.kb(includingself.runtime.register_action_param("kb", self.kb)at the end of__init__) then raises:This has been reported for Python 3.12+ in LangGraph/LangChain integration scenarios.
Solution
Pre-initialize
self.kb = Noneimmediately before launching the background thread. This ensures:LLMRailsinstance, even if_init_kb()never runs._init_kb()itself, which already setsself.kb = Noneas its very first statement before any conditional logic.Testing
_init_kb()already does.Signed-off-by: Octopus liyuan851277048@icloud.com
Summary by CodeRabbit