-
Couldn't load subscription status.
- Fork 554
chore(types): Type-clean /cli (37 errors) #1380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Thank you @tgasser-nv . I also tested it against #1339 and all tests pass. Some of the changes might require further test cases, for example response type handling changes in chat.py:117-144. The existing tests only mock generate_async() to return simple dict responses, but don't test the new type-checking logic for: if type(response) == Tuple[Dict, Dict]:
elif type(response) == GenerationResponse: # untested
elif type(response) == Dict: # untested fallback logic |
I agree we need to add test-cases to cover these new options. Should we do this after #1339 is merged? |
|
Converting to draft while I rebase on the latest changes to develop. |
506155e to
89b9860
Compare
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Rebased this onto develop branch and re-ran the test plan. This is ready for review now cc @Pouyanpi @trebedea @cparisien |
added test cases for this and it looks good. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, thank you Tim.
Yep I agree. In general our test line coverage will go down after adding type-checking since we now explicitly check types on multiple if branches. This isn't a regression in test coverage. I'll add test cases to get these in a follow-up PR. I created a JIRA (NGUARD-496) so this doesn't get forgotten. |
89b9860 to
21d48a8
Compare
* Cleaned nemoguardrails/cli * Address Pouyan's feedback * Add nemoguardrails/cli to pyright pre-commit checking
* Cleaned nemoguardrails/cli * Address Pouyan's feedback * Add nemoguardrails/cli to pyright pre-commit checking
Description
Clean up type errors in the nemoguards/cli directory
Type-cleaning summary
This report summarizes the type-safety fixes applied to the
nemoguardrailsproject. The changes are categorized into high, medium, and low-risk buckets based on their potential to impact existing functionality.🔴 High-Risk Changes
This category includes changes to core logic that handle variable return types. While necessary for type safety, an incorrect assumption about the data structure could disrupt the primary chat functionality.
Handling Multiple Return Types from
generate_asyncThe
generate_asyncmethod can return several different types. The original implementation did not account for this, leading to potential runtime errors. The fix introduces a series ofisinstancechecks to correctly parse the response.nemoguardrails/cli/chat.pyTypeErrororAttributeErrorby assuming the return value fromgenerate_asyncis always adictwith a'content'key.responseis atuple,GenerationResponseobject, ordict, and correctly extracts the bot message from each structure. This makes the chat response handling significantly more robust. A safe.get()method is also used later to access the message content, preventingKeyError.responseis aGenerationResponseobject, itsresponseattribute will be a list containing the desired message dictionary. It also assumes that ifresponseis a tuple, the bot message is the first element.generate_asyncto always return a consistent object (e.g., aGenerationResponsedataclass). However, that would be a more invasive change. The current fix correctly handles the existing behavior without modifying the called function.🟡 Medium-Risk Changes
These changes involve data type conversions that rely on structural assumptions. They are medium risk because a mismatch between the expected and actual data structure would lead to a runtime
TypeError.1. Converting State Between Dataclass and Dictionary
The
process_events_asyncmethod expects the session state as a dictionary, but it was being stored as aStatedataclass. The fix converts the dataclass to a dictionary before passing it and converts the returned dictionary back into a dataclass.nemoguardrails/cli/chat.pyTypeErrorbecauseprocess_events_asyncexpected aDictbut received aStateobject. The return value was also aDictthat needed to be converted back.asdictutility fromdataclassesis used to convert thechat_state.stateobject into a dictionary. After the call, the returnedoutput_statedictionary is unpacked using**to initialize a newStatedataclass object. Thecastfunction informs the static type checker of this conversion.output_statedictionary perfectly match the field names of theStatedataclass. Any discrepancy (missing or extra keys) would raise aTypeErrorat runtime.process_events_asyncto directly accept and returnStateobjects. This would provide better end-to-end type safety but is a more significant refactoring.2. Normalizing
SpecObject in DebuggerIn the debugger, an AST node's
specattribute could be either aSpecobject or a dictionary. The code now normalizes this attribute into aSpecobject before use.nemoguardrails/cli/debugger.pyAttributeErrorfrom trying to access attributes (e.g.,spec.spec_type) on adict.specis already aSpecinstance. If not, it assumes it's a dictionary and unpacks it into theSpecconstructor to create a proper object.dictfound inhead_element_spec_op.spechas the necessary keys to construct aSpecobject.3. Using Enum for CLI Argument Types
The
convertcommand'sfrom-versionoption was a free-form string, replaced now by atypersupportedEnum.nemoguardrails/cli/__init__.py--from-version, potentially causing errors if the value was not one of the supported versions ("1.0"or"2.0-alpha").ColangVersionsenum was created to represent the valid choices.typerautomatically uses this to validate and parse the command-line argument. The enum's stringvalueis then extracted for use in themigratefunction, which expects aLiteralstring type.migratefunction is correctly typed to expect aLiteralstring, making the.valueaccess necessary.Enumapproach provides stronger static typing and allowstyperto generate more helpful CLI error messages automatically.🟢 Low-Risk Changes
These fixes are defensive additions, such as
Nonechecks and safer data access patterns, that increase robustness without altering logic. They are highly unlikely to introduce any new bugs.Null-Safety Checks and Defensive Programming
Numerous checks were added across the
climodule to handleOptionaltypes and preventTypeErrororAttributeErrorwhen dealing with values that could beNone.cli/chat.py,cli/debugger.py,cli/migration.pyTypeErrorfrom operating onNone(e.g.,None * int,function(None)where a string is expected) orAttributeError(e.g.,None.group(1)).if config_path is None: raise RuntimeError(...)to ensure critical variables are present. (cli/chat.py:64)next_line or ""andsample_conv_indent or 0to safely handleOptionalvariables. (cli/migration.py:259,cli/migration.py:1082)leading_whitespace_match.group(1) if leading_whitespace_match else "". (cli/migration.py:234)setattr(api.app, "rails_config_path", ...)to explicitly set dynamic attributes on theFastAPIapp instance, which silences static analysis warnings. (cli/__init__.py:146)if chat_state is not None:blocks in the debugger to prevent operations on aNoneglobal state. (cli/debugger.py:61)Test plan
Type-checking
$ poetry run pre-commit run --all-files check yaml...............................................................Passed fix end of files.........................................................Passed trim trailing whitespace.................................................Passed isort (python)...........................................................Passed black....................................................................Passed Insert license in comments...............................................Passed pyright..................................................................PassedUnit-tests
Chat local integration test
Checklist