From 0bff0b1ffaef07a2e675b6d5dbca13eb8a46aaf2 Mon Sep 17 00:00:00 2001 From: Deepak Nagaraj Date: Thu, 21 Aug 2025 13:24:26 -0700 Subject: [PATCH 1/2] adjust truss hash calculation for buildless --- pyproject.toml | 2 +- .../image_builder/serving_image_builder.py | 28 +++++++++++++++++-- uv.lock | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4621a6ac8..d8997316b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "truss" -version = "0.10.9rc1" +version = "0.10.9rc520" description = "A seamless bridge from model development to model delivery" authors = [ { name = "Pankaj Gupta", email = "no-reply@baseten.co" }, diff --git a/truss/contexts/image_builder/serving_image_builder.py b/truss/contexts/image_builder/serving_image_builder.py index 3835a4d48..73ebb040d 100644 --- a/truss/contexts/image_builder/serving_image_builder.py +++ b/truss/contexts/image_builder/serving_image_builder.py @@ -809,9 +809,8 @@ def _render_dockerfile( data_dir_exists=data_dir.exists(), model_dir_exists=model_dir.exists(), bundled_packages_dir_exists=bundled_packages_dir.exists(), - truss_hash=directory_content_hash( - self._truss_dir, self._spec.hash_ignore_patterns - ), + # Create a sanitized copy of the truss directory for hash calculation + truss_hash=self._calculate_sanitized_truss_hash(build_dir), models=model_files, use_hf_secret=use_hf_secret, cached_files=cached_files, @@ -832,3 +831,26 @@ def _render_dockerfile( ).strip() docker_file_path = build_dir / MODEL_DOCKERFILE_NAME docker_file_path.write_text(dockerfile_contents) + + def _calculate_sanitized_truss_hash(self, build_dir: Path) -> str: + """Calculate hash from a sanitized copy of the truss directory with runtime fields cleared.""" + sanitized_truss_dir = build_dir / "build_model_scaffold" + + # Remove existing copy if it exists + if sanitized_truss_dir.exists(): + shutil.rmtree(sanitized_truss_dir) + + # Copy the entire truss directory + shutil.copytree(self._truss_dir, sanitized_truss_dir) + + # Clear runtime fields from the config + config_file_path = sanitized_truss_dir / "config.yaml" + if config_file_path.exists(): + truss_config = TrussConfig.from_yaml(config_file_path) + truss_config.clear_runtime_fields() + truss_config.write_to_yaml_file(config_file_path) + + # Calculate hash from the sanitized directory + return directory_content_hash( + sanitized_truss_dir, self._spec.hash_ignore_patterns + ) diff --git a/uv.lock b/uv.lock index d98a74fbb..dba98f535 100644 --- a/uv.lock +++ b/uv.lock @@ -3312,7 +3312,7 @@ wheels = [ [[package]] name = "truss" -version = "0.10.9rc1" +version = "0.10.9rc520" source = { editable = "." } dependencies = [ { name = "aiofiles" }, From bfd86422c497e77e7bf5de2ad1857a8ebdead655 Mon Sep 17 00:00:00 2001 From: Deepak Nagaraj Date: Thu, 21 Aug 2025 13:25:08 -0700 Subject: [PATCH 2/2] debug prints and lint --- .../image_builder/serving_image_builder.py | 8 ++++---- truss/truss_handle/patch/hash.py | 14 +++++++++++++- truss/truss_handle/truss_handle.py | 3 +++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/truss/contexts/image_builder/serving_image_builder.py b/truss/contexts/image_builder/serving_image_builder.py index 73ebb040d..515278800 100644 --- a/truss/contexts/image_builder/serving_image_builder.py +++ b/truss/contexts/image_builder/serving_image_builder.py @@ -835,21 +835,21 @@ def _render_dockerfile( def _calculate_sanitized_truss_hash(self, build_dir: Path) -> str: """Calculate hash from a sanitized copy of the truss directory with runtime fields cleared.""" sanitized_truss_dir = build_dir / "build_model_scaffold" - + # Remove existing copy if it exists if sanitized_truss_dir.exists(): shutil.rmtree(sanitized_truss_dir) - + # Copy the entire truss directory shutil.copytree(self._truss_dir, sanitized_truss_dir) - + # Clear runtime fields from the config config_file_path = sanitized_truss_dir / "config.yaml" if config_file_path.exists(): truss_config = TrussConfig.from_yaml(config_file_path) truss_config.clear_runtime_fields() truss_config.write_to_yaml_file(config_file_path) - + # Calculate hash from the sanitized directory return directory_content_hash( sanitized_truss_dir, self._spec.hash_ignore_patterns diff --git a/truss/truss_handle/patch/hash.py b/truss/truss_handle/patch/hash.py index 7fe35e87e..201c282d8 100644 --- a/truss/truss_handle/patch/hash.py +++ b/truss/truss_handle/patch/hash.py @@ -1,3 +1,4 @@ +import traceback from pathlib import Path from typing import Any, List, Optional @@ -19,6 +20,15 @@ def directory_content_hash( underneath. The (root) Directory will have the same hash, even if renamed. """ hasher = blake3() + print(f"Calculating hash for {root}, ignore_patterns: {ignore_patterns}") + + # Print caller chain + caller_chain = traceback.extract_stack() + if len(caller_chain) > 1: + print("Caller chain:") + for i, frame in enumerate(caller_chain[:-1]): # Skip the current function + print(f" {i + 1}. {frame.filename}:{frame.lineno} in {frame.name}") + paths = list(get_unignored_relative_paths_from_root(root, ignore_patterns)) paths.sort() for path in paths: @@ -26,7 +36,9 @@ def directory_content_hash( absolute_path = root / path if absolute_path.is_file(): hasher.update(file_content_hash(absolute_path)) - return hasher.hexdigest() + hash_str = hasher.hexdigest() + print(f"Hash for {root}: {hash_str}") + return hash_str def file_content_hash(file: Path) -> bytes: diff --git a/truss/truss_handle/truss_handle.py b/truss/truss_handle/truss_handle.py index c0710df61..89671396e 100644 --- a/truss/truss_handle/truss_handle.py +++ b/truss/truss_handle/truss_handle.py @@ -938,6 +938,9 @@ def _build_image( def _update_config(self, **fields_to_update): config = self._spec.config.model_copy(update=fields_to_update) + print( + f"Updating config: {fields_to_update}, writing to {self._spec.config_path}" + ) config.write_to_yaml_file(self._spec.config_path) self._spec = TrussSpec(self._truss_dir) # Reload.