Skip to content

Conversation

@ieaves
Copy link
Collaborator

@ieaves ieaves commented Dec 13, 2025

Summary by Sourcery

Improve type safety and robustness across CLI, engine, RAG, and common utilities while tightening port selection logic and test isolation.

Bug Fixes:

  • Ensure AddPathOrUrl argparse action is only used with list-valued arguments and raise a clear error otherwise.
  • Avoid exhausting all available ports by validating the exclusion list in compute_ports and raising a descriptive error when no ports remain.
  • Prevent accidental use of user configuration and Apple VM detection in config-related unit tests by isolating them with default mocks.
  • Fix potential crashes and undefined behavior in CLI error handling when args or parser may be unbound.

Enhancements:

  • Introduce typed argument protocols and dataclass conversions for serve/run and RAG-related commands to better structure CLI arguments.
  • Add and refine type annotations across CLI, engine, transports, and common utilities to improve static checking and code clarity.
  • Tighten container engine info handling by casting JSON output to typed structures and annotating helper functions for labels, logs, and inspection.
  • Refine RAG transport and engine interaction methods with explicit types for arguments, return values, and container health checks.
  • Simplify and clarify port computation logic by using explicit exclusion handling and deterministic range construction.

Tests:

  • Add an autouse fixture in config tests to mock config loading and Apple VM detection, ensuring deterministic behavior and isolation from user environment.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 13, 2025

Reviewer's Guide

Adds stricter typing across CLI, engine, RAG, and common utilities, introduces structured arg types for serve/run and RAG commands, tightens error handling and invariants, and fixes small behavioral issues (port computation, config finalization, tests isolation, and argument validation).

File-Level Changes

Change Details Files
Introduce typed argument protocols and dataclasses for serve/run and RAG commands, and thread them through RAG transport logic.
  • Define ServeRunArgsType protocol with explicit fields and derive ServeRunArgs dataclass via protocol_to_dataclass.
  • Define RagArgsType protocol extending ServeRunArgsType, make rag required, and derive RagArgs dataclass.
  • Type RagTransport methods to accept RagArgsType/ServeRunArgsType, adjust signatures (e.g., run/serve/_start_model/_handle_container_chat), and ensure RAG health checks use typed args.
ramalama/arg_types.py
ramalama/rag.py
Tighten CLI typing, validation, and error reporting, including argument helpers and exception handling paths.
  • Type argparse completer assignment and suppress mypy warnings with targeted type: ignore.
  • Refine map_https_to_transport return type to never be None.
  • Type add_network_argument default as optional string and add explicit dict typing for info object.
  • Guard AddPathOrUrl to only operate when values is a list (nargs='+') and raise a clear ValueError otherwise.
  • Type eprint parameters, ensure parser.print_help and args.model accesses are annotated/ignored where necessary, and refactor SafetensorModelNotSupported handling into a composed error message string before calling eprint.
ramalama/cli.py
Improve engine utilities typing and behavior, especially around container manager interaction and label handling.
  • Annotate BaseEngine flags (use_docker/use_podman) and exec_args list with concrete types and narrow add/add_label/add_name signatures.
  • Type info/inspect/logs/stop_container functions, casting JSON output to typed dicts and exposing more precise return types.
  • Ensure add uses extend with a Sequence of strings and add_labels accepts a typed callable for label application.
  • Clarify is_healthy model_name derivation using a typed cast from args.MODEL.
ramalama/engine.py
Strengthen common utility typing and behavior, especially for subprocess invocation and port computation.
  • Fully type run_cmd parameters and return value as subprocess.CompletedProcess, including cwd/stdout/env types.
  • Change compute_ports to build the excluded set explicitly when None, avoid set arithmetic on ranges, and raise a ValueError when the exclusion list exhausts the default port range.
ramalama/common.py
ramalama/transports/base.py
Harden configuration handling and tests, and simplify engine selection logic on macOS.
  • Remove unnecessary blank lines in inference spec/schema discovery and keep iteration over candidate files unchanged.
  • Refine _finalize_engine to directly check for podman on macOS and fall back to docker only when engine is unset and podman VM is unavailable.
  • Add an autouse pytest fixture to isolate tests from user config and apple_vm detection by mocking load_file_config and apple_vm.
ramalama/config.py
test/unit/test_config.py
Tighten typing for shortname resolution and chat operational arguments.
  • Type chat_operational_args in base transport as returning an optional ChatOperationalArgs.
  • Constrain ShortNameResolver.resolve to take and return strings, always returning at least the original model name.
ramalama/transports/base.py
ramalama/shortnames.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ieaves, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the codebase by integrating comprehensive type annotations throughout key modules, which enhances code readability, maintainability, and allows for more robust static analysis. It also includes critical bug fixes related to argument parsing and port allocation, alongside minor refactorings to streamline existing code and improve test isolation for future development.

Highlights

  • Enhanced Type Annotations: Introduced new type protocols (ServeRunArgsType, RagArgsType) and added extensive type hints across various modules (cli.py, common.py, engine.py, rag.py, shortnames.py, transports/base.py) to improve code clarity, maintainability, and enable better static analysis.
  • Bug Fixes and Robustness: Addressed a bug in AddPathOrUrl to ensure argument values are always lists and fixed an issue in compute_ports to prevent an infinite loop if all ports are excluded, now raising a ValueError.
  • Code Refactoring and Readability: Refactored parts of _list_models_from_store for more concise dictionary creation, simplified conditional logic in _finalize_engine, and improved error message formatting for SafetensorModelNotSupported.
  • Improved Test Isolation: Added an autouse pytest fixture isolate_config to test_config.py to mock configuration loading, ensuring tests are more isolated and reliable.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • In engine.info, the returned JSON is now cast to dict[str, Any] even though json.loads can return a list (and the function’s return type hint still includes list[Any]); consider preserving the actual type (Any or dict | list) or narrowing the annotation instead of forcing a dict cast.
  • The SafetensorModelNotSupported error message construction in cli.main lost the line breaks between commands when you switched to a single parenthesized string; consider adding explicit \n or using a multiline string to keep the output readable.
  • Several new # type: ignore[possibly-unbound] comments around parser and args.model in cli.main suggest real control-flow uncertainty; it may be cleaner to initialize these variables before the try or restructure the control flow so they are always defined instead of suppressing the type checker.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `engine.info`, the returned JSON is now cast to `dict[str, Any]` even though `json.loads` can return a list (and the function’s return type hint still includes `list[Any]`); consider preserving the actual type (`Any` or `dict | list`) or narrowing the annotation instead of forcing a dict cast.
- The SafetensorModelNotSupported error message construction in `cli.main` lost the line breaks between commands when you switched to a single parenthesized string; consider adding explicit `\n` or using a multiline string to keep the output readable.
- Several new `# type: ignore[possibly-unbound]` comments around `parser` and `args.model` in `cli.main` suggest real control-flow uncertainty; it may be cleaner to initialize these variables before the `try` or restructure the control flow so they are always defined instead of suppressing the type checker.

## Individual Comments

### Comment 1
<location> `ramalama/cli.py:317` </location>
<code_context>
     """Perform additional setup after parsing arguments."""

-    def map_https_to_transport(input: str) -> str | None:
+    def map_https_to_transport(input: str) -> str:
         if input.startswith("https://") or input.startswith("http://"):
             url = urlparse(input)
</code_context>

<issue_to_address>
**issue (bug_risk):** The return type annotation for `map_https_to_transport` is too narrow and does not match the implementation.

Because the function can reach the end without a `return` when `input` is not an HTTP(S) URL, it currently returns `None` despite the `-> str` annotation. This mismatch will be flagged by type checkers and can mislead callers into assuming a non-`None` result. Please either restore the `str | None` return type or add an explicit return for the non-URL case that matches the intended behavior.
</issue_to_address>

### Comment 2
<location> `ramalama/cli.py:1566-1572` </location>
<code_context>
-$ ramalama run <oci-name>
-""",
-            errno.ENOTSUP,
+        message = (
+            "Safetensor models are not supported. Please convert it to GGUF via:"
+            f"$ ramalama convert --gguf=<quantization> {args.model} <oci-name>"  # type: ignore[possibly-unbound]
+            "$ ramalama run <oci-name>"
         )
+        eprint(message, errno.ENOTSUP)
</code_context>

<issue_to_address>
**suggestion (bug_risk):** The Safetensor error message loses line breaks/spaces compared to the previous version.

The concatenated string will render as `...via:$ ramalama convert...$ ramalama run...` with no spaces or newlines, reducing readability compared to the previous triple-quoted version. Please add `"\n"` (or at least a space) between segments to preserve the intended formatting.

```suggestion
    except SafetensorModelNotSupported:
        message = (
            "Safetensor models are not supported. Please convert it to GGUF via:\n"
            f"$ ramalama convert --gguf=<quantization> {args.model} <oci-name>\n"  # type: ignore[possibly-unbound]
            "$ ramalama run <oci-name>\n"
        )
        eprint(message, errno.ENOTSUP)
```
</issue_to_address>

### Comment 3
<location> `ramalama/engine.py:333` </location>
<code_context>
         if output == "":
             return []
-        return json.loads(output)
+        return cast(dict[str, Any], json.loads(output))
     except FileNotFoundError as e:
         return str(e)
</code_context>

<issue_to_address>
**issue (bug_risk):** Casting `json.loads(output)` to `dict[str, Any]` is misleading given `info`'s actual return shape.

The function’s signature still allows `list[Any] | str | dict[str, Any]`, and `json.loads(output)` can return a list. Casting to `dict[str, Any]` misleads type checkers and callers into assuming a dict and can hide real type issues. Either keep the union return type without a cast, or enforce and validate that the result is always a dict before returning it.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

if output == "":
return []
return json.loads(output)
return cast(dict[str, Any], json.loads(output))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Casting json.loads(output) to dict[str, Any] is misleading given info's actual return shape.

The function’s signature still allows list[Any] | str | dict[str, Any], and json.loads(output) can return a list. Casting to dict[str, Any] misleads type checkers and callers into assuming a dict and can hide real type issues. Either keep the union return type without a cast, or enforce and validate that the result is always a dict before returning it.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant improvements to type safety and robustness across the codebase. The addition of typed argument protocols, refined type annotations, and bug fixes in areas like argument parsing and port computation are excellent. The test isolation for config-related tests is also a great enhancement. I've found a couple of minor areas for improvement, mainly related to code consistency and cleanup, which I've detailed in the comments.

eprint(message, errno.ENOTSUP)
except NoGGUFModelFileFound:
eprint(f"No GGUF model file found for downloaded model '{args.model}'", errno.ENOENT)
eprint(f"No GGUF model file found for downloaded model '{args.model}'", errno.ENOENT) # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with the type: ignore on line 1569 and for better clarity, it's good practice to specify the error code being ignored. In this case, args could be unbound if init_cli() fails.

Suggested change
eprint(f"No GGUF model file found for downloaded model '{args.model}'", errno.ENOENT) # type: ignore
eprint(f"No GGUF model file found for downloaded model '{args.model}'", errno.ENOENT) # type: ignore[possibly-unbound]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line exceeds 120 characters with the error specified

Comment on lines +18 to +27
@pytest.fixture(autouse=True)
def isolate_config():
"""
Isolate tests from user configuration files by mocking config loading.
This fixture is automatically used for all tests in this module.
Individual tests can override by explicitly patching if needed.
"""
with patch("ramalama.config.load_file_config", return_value={}):
with patch("ramalama.config.apple_vm", return_value=False):
yield
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This autouse fixture is a great addition for isolating tests from the user's environment. It simplifies the test setup significantly. As a follow-up, you could remove the now-redundant with patch(\"ramalama.config.load_file_config\", ...) context manager from test_correct_config_defaults to clean up the code.

Signed-off-by: Ian Eaves <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant