Skip to content

Typing and bug squashes #1764

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

Merged
merged 1 commit into from
Aug 1, 2025
Merged

Conversation

ieaves
Copy link
Collaborator

@ieaves ieaves commented Jul 28, 2025

Summary by Sourcery

Introduce comprehensive static typing to core modules, convert ModelBase to an abstract ABC, tighten ModelFactory initialization, and resolve GGUF parsing bugs with accompanying tests

Bug Fixes:

  • Raise ParseError for unknown GGUF value types and fix tensor type handling in GGUF parsing and serialization

Enhancements:

  • Add type annotations across modules using TypeAlias, TypedDict, and explicit casts
  • Convert ModelBase to an abstract ABC with @AbstractMethod declarations
  • Enforce non-null engine argument in ModelFactory and update function signatures

Tests:

  • Add unit test for GGUFModelInfo serialization when tensor.type is a string

Copy link
Contributor

sourcery-ai bot commented Jul 28, 2025

Reviewer's Guide

This PR systematically enhances type safety and enforces interface contracts while squashing minor bugs and normalizing code style across the codebase.

Class diagram for ModelBase and Model with typing and ABC changes

classDiagram
    class ModelBase {
        <<abstract>>
        str model
        str type
        __not_implemented_error(param)
        pull(args)
        push(source_model, args)
        <<abstract>> remove(args)
        <<abstract>> bench(args)
        <<abstract>> run(args)
        <<abstract>> perplexity(args)
        <<abstract>> serve(args)
        <<abstract>> exists() bool
        <<abstract>> inspect(args)
    }
    class Model {
        str model
        str type = "Model"
        str directory
        str filename
        str _model_name
        str _model_tag
        ...
        Model(model: str, model_store_path: str)
    }
    Model --|> ModelBase
Loading

Class diagram for Rag class with improved typing

classDiagram
    class Rag {
        str model = ""
        str target = ""
        list~str~ urls = []
        Rag(target: str)
        build(source: str, target: str, args)
        _handle_paths(path: str)
        generate(args)
    }
Loading

Class diagram for ModelFactory and New function with type aliasing

classDiagram
    class ModelFactory {
        str model
        str store_path
        str transport
        bool ignore_stderr
        ModelFactory(model: str, args: StoreArgType, transport: str, ignore_stderr: bool)
        detect_model_model_type() Tuple[type[CLASS_MODEL_TYPES], Callable[[], CLASS_MODEL_TYPES]]
        create()
        create_huggingface()
        create_ollama()
        create_oci()
        create_url()
        create_modelscope()
    }
    class New {
        <<function>>
        New(name, args, transport: str | None = None) -> Union[Huggingface | ModelScope | Ollama | OCI | URL]
    }
Loading

Class diagram for AccelType, GPUEnvVar, AccelEnvVar TypeAliases

classDiagram
    class AccelType {
        <<typealias>>
        Literal["asahi", "cuda", "cann", "hip", "intel", "musa"]
    }
    class GPUEnvVar {
        <<typealias>>
        Literal["ASAHI_VISIBLE_DEVICES", "ASCEND_VISIBLE_DEVICES", "CUDA_VISIBLE_DEVICES", "HIP_VISIBLE_DEVICES", "INTEL_VISIBLE_DEVICES", "MUSA_VISIBLE_DEVICES"]
    }
    class AccelEnvVar {
        <<typealias>>
        Literal["CUDA_LAUNCH_BLOCKING", "HSA_VISIBLE_DEVICES", "HSA_OVERRIDE_GFX_VERSION", "MUSA_VISIBLE_DEVICES"]
    }
Loading

Class diagram for file loaders with updated load method signature

classDiagram
    class BaseFileManager {
        <<abstract>>
        _get_loader(file: str) base.BaseFileLoader
        <<abstract>> load(*args, **kwargs)
        supported_extensions()
    }
    class FileManager {
        load(file_path: str) list~dict~
        text_manager
        image_manager
    }
Loading

Class diagram for StoreFile, RefJSONFile, and StoreFileType with typing fixes

classDiagram
    class StoreFileType {
        GGUF_MODEL = "gguf"
        MMPROJ = "mmproj"
        CHAT_TEMPLATE = "chat_template"
    }
    class StoreFile {
        hash: str
        name: str
        type: StoreFileType
    }
    class RefJSONFile {
        hash: str
        path: str
        files: list~StoreFile~
    }
Loading

Class diagram for updated config type aliases

classDiagram
    class PathStr {
        <<typealias>>
        str
    }
    class SUPPORTED_ENGINES {
        <<typealias>>
        Literal["podman", "docker"] | PathStr
    }
    class SUPPORTED_RUNTIMES {
        <<typealias>>
        Literal["llama.cpp", "vllm", "mlx"]
    }
    class COLOR_OPTIONS {
        <<typealias>>
        Literal["auto", "always", "never"]
    }
Loading

File-Level Changes

Change Details Files
Introduce static typing throughout core modules to improve type safety.
  • Defined TypeAlias and TypedDict in config, common, and factory layers
  • Annotated function signatures, class properties, and return types
  • Replaced untyped lists and dicts with typed equivalents
  • Enforced non-null engine in ModelFactory constructor
ramalama/config.py
ramalama/common.py
ramalama/model_factory.py
ramalama/rag.py
ramalama/file_loaders/file_manager.py
ramalama/chat.py
ramalama/hf_style_repo_base.py
Refactor ModelBase to use an abstract base class with typed attributes.
  • Made ModelBase inherit from ABC
  • Declared core methods as @AbstractMethod
  • Added type annotations for model and type properties
ramalama/model.py
Standardize list literal usage and f-string formatting for clarity and consistency.
  • Unified multi-line engine.add and exec_args.extend calls to bracketed lists
  • Adjusted spacing in f-strings around arithmetic in logger.debug
  • Removed extraneous blank lines
ramalama/model.py
Enhance GGUF parser with explicit casts, refined return types, and error checks.
  • Applied cast() to numeric reads to satisfy typing
  • Specified precise return unions for read_value
  • Added ParseError for unknown types and cleaned up control flow
ramalama/model_inspect/gguf_parser.py
Improve CDI YAML loader and environment variable helpers with TypedDicts and comprehensions.
  • Introduced CDI_DEVICE and CDI_RETURN_TYPE TypedDicts
  • Typed load_cdi_yaml and load_cdi_config return types
  • Simplified env var extraction with comprehensions and inline assignments
ramalama/common.py
Fix model store snapshot operations by guarding optional refs and tightening types.
  • Changed snapshot file handlers to accept Sequence instead of list
  • Checked for None before modifying or writing RefJSONFile
  • Fixed ModelFile instantiation to use file.name
  • Corrected symlink path logic and refcounts computation
ramalama/model_store/store.py
ramalama/model_store/reffile.py
ramalama/model_store/snapshot_file.py
ramalama/model_store/global_store.py
Clean up go2jinja parser edge cases and expand unit tests for new behaviors.
  • Added cast imports and type ignores for scope nodes
  • Removed stray blank lines after key functions
  • Extended unit tests for GGUFModelInfo and model_store to catch regressions
ramalama/model_store/go2jinja.py
ramalama/model_inspect/gguf_info.py
test/unit/test_model_store.py
test/unit/test_gguf_info.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

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.

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 primarily focuses on a comprehensive effort to improve the codebase's type safety and address several identified bugs. The changes aim to make the system more robust, easier to understand, and more maintainable for future development. It also includes minor refactorings to enhance code clarity and consistency.

Highlights

  • Enhanced Type Safety: Introduced extensive type hints across the codebase, utilizing TypeAlias, TypedDict, Sequence, and explicit annotations for variables, function parameters, and return types. This significantly improves code readability, maintainability, and enables more robust static analysis.
  • Critical Bug Fixes: Addressed several bugs, including incorrect attribute access in GGUF model inspection, potential None dereferences in model store operations, and missing validation for engine configuration, enhancing the stability and reliability of the application.
  • Code Structure and Readability Improvements: Refactored ModelBase into an Abstract Base Class (ABC) with abstract methods to enforce consistent interfaces. Minor code style adjustments, such as using the walrus operator and reformatting argument lists, were also applied to improve overall readability and conciseness.
  • New Unit Test: Added a new unit test for GGUFModelInfo serialization to ensure the correctness of recent type-related fixes in model inspection.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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 or fill out our survey to provide feedback.

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 @ieaves - I've reviewed your changes - here's some feedback:

  • ModelBase defines several interface methods that raise NotImplementedError but aren’t decorated with @AbstractMethod (e.g. pull, push); consider marking them abstract so subclasses must implement them.
  • In check_nvidia you cast the result of zip(*parsed_lines) directly to list[list[str]], but zip yields tuples—unpack into indices, uuids and convert each to list explicitly to satisfy the declared type.
  • GGUF parsing now stores Tensor.type as a string but gguf_info.serialize still expects an enum with .name; unify the Tensor.type field across parser and serializer to avoid attribute errors.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- ModelBase defines several interface methods that raise NotImplementedError but aren’t decorated with @abstractmethod (e.g. pull, push); consider marking them abstract so subclasses must implement them.
- In check_nvidia you cast the result of zip(*parsed_lines) directly to list[list[str]], but zip yields tuples—unpack into indices, uuids and convert each to list explicitly to satisfy the declared type.
- GGUF parsing now stores Tensor.type as a string but gguf_info.serialize still expects an enum with .name; unify the Tensor.type field across parser and serializer to avoid attribute errors.

## Individual Comments

### Comment 1
<location> `ramalama/common.py:343` </location>
<code_context>
         return None

-    indices, uuids = zip(*parsed_lines) if parsed_lines else (tuple(), tuple())
+    indices, uuids = cast(list[list[str]], zip(*parsed_lines))
     # Get the list of devices specified by CUDA_VISIBLE_DEVICES, if any
     cuda_visible_devices = os.environ.get("CUDA_VISIBLE_DEVICES", "")
</code_context>

<issue_to_address>
Use of cast with zip may not be type safe.

Instead of casting, use map(list, zip(*parsed_lines)) to ensure type safety, as zip returns tuples, not lists.
</issue_to_address>

### Comment 2
<location> `ramalama/model_store/go2jinja.py:375` </location>
<code_context>
                     children=[],
                     artificial=True,
                 )
+                for_node.next = cast(Node, for_node.next)
                 for_node.next.prev = initial_set_node
                 for_node.next = initial_set_node
</code_context>

<issue_to_address>
Use of cast may hide potential NoneType errors.

Add a check or assertion to ensure for_node.next is not None before casting.
</issue_to_address>

### Comment 3
<location> `test/unit/test_model_store.py:1` </location>
<code_context>
+import pytest
+from ramalama.model_inspect.gguf_info import GGUFModelInfo
+from ramalama.model_inspect.base_info import Tensor
</code_context>

<issue_to_address>
No new or updated tests for recent changes in model_store/store.py and related files.

Please add or update tests to cover:
- get_ref_file returning None in update_ref_file, get_cached_files, and _download_snapshot_files
- Handling of HTTPStatus.NOT_FOUND in _download_snapshot_files when ref_file is None
- The new Sequence type annotations for update_snapshot and validate_snapshot_files
This will help ensure robustness and proper edge case handling.
</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.

Comment on lines 1 to 6
import pytest

from ramalama.model_store.snapshot_file import SnapshotFile, SnapshotFileType, validate_snapshot_files
from ramalama.model_store.global_store import ModelFile
from ramalama.model_store.reffile import StoreFile, StoreFileType

chat_template = SnapshotFile(name="chat-template", hash="", header={}, type=SnapshotFileType.ChatTemplate, url="")
model_file = SnapshotFile(name="model", hash="", header={}, type=SnapshotFileType.Model, url="")
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): No new or updated tests for recent changes in model_store/store.py and related files.

Please add or update tests to cover:

  • get_ref_file returning None in update_ref_file, get_cached_files, and _download_snapshot_files
  • Handling of HTTPStatus.NOT_FOUND in _download_snapshot_files when ref_file is None
  • The new Sequence type annotations for update_snapshot and validate_snapshot_files
    This will help ensure robustness and proper edge case handling.

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 does a great job of improving type safety and fixing bugs. The introduction of TypedDict, TypeAlias, and converting ModelBase to an ABC are all excellent changes that enhance maintainability.

I've pointed out a few areas for improvement, mainly around strengthening type hints and handling potential None values more robustly to prevent runtime errors. Overall, this is a solid contribution.

@rhatdan
Copy link
Member

rhatdan commented Jul 31, 2025

Please squash your commits.

Copy link
Member

@rhatdan rhatdan left a comment

Choose a reason for hiding this comment

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

LGTM

@rhatdan rhatdan merged commit eb43ed3 into containers:main Aug 1, 2025
54 of 61 checks passed
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.

3 participants