Skip to content

Commit 85b26e5

Browse files
[PR-1160]: Removed check for local usage (#911)
1 parent b95179c commit 85b26e5

File tree

10 files changed

+78
-21
lines changed

10 files changed

+78
-21
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,3 +581,12 @@ Also see the official [Python SDK docs](https://clarifai-python.readthedocs.io/e
581581

582582
Examples for uploading models and runners have been moved to this [repo](https://github.com/Clarifai/runners-examples).
583583
Find our official documentation at [docs.clarifai.com/compute/models/upload](https://docs.clarifai.com/compute/models/upload).
584+
585+
## Versioning
586+
587+
This project uses [CalVer](https://calver.org/) with the format **`YY.MM.PATCH`**:
588+
- **YY** — two-digit year (e.g. `12` for 2026)
589+
- **MM** — month number, not zero-padded (e.g. `1` for January, `12` for December)
590+
- **PATCH** — incremental release within that month, starting at `0`
591+
592+
Git tags use the same format without a `v` prefix (e.g. `12.2.0`). The version is defined in `clarifai/__init__.py`.

clarifai/cli/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,7 +1212,7 @@ def local_runner(ctx, model_path, pool_size, suppress_toolkit_logs, mode, keep_i
12121212
model_path = os.path.abspath(model_path)
12131213
_ensure_hf_token(ctx, model_path)
12141214
builder = ModelBuilder(model_path, download_validation_only=True)
1215-
manager = ModelRunLocally(model_path)
1215+
manager = ModelRunLocally(model_path, model_builder=builder)
12161216

12171217
port = 8080
12181218
if mode == "env":
@@ -1675,7 +1675,7 @@ def print_code_snippet():
16751675
else:
16761676
print_code_snippet()
16771677
# This reads the config.yaml from the model_path so we alter it above first.
1678-
server = ModelServer(model_path=model_path, model_runner_local=None)
1678+
server = ModelServer(model_path=model_path, model_runner_local=None, model_builder=builder)
16791679
server.serve(**serving_args)
16801680

16811681

clarifai/runners/models/model_builder.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def __init__(
175175
platform: Optional[str] = None,
176176
pat: Optional[str] = None,
177177
base_url: Optional[str] = None,
178+
compute_info_required: bool = False,
178179
):
179180
"""
180181
:param folder: The folder containing the model.py, config.yaml, requirements.txt and
@@ -188,6 +189,7 @@ def __init__(
188189
:param platform: Target platform(s) for Docker image build (e.g., "linux/amd64" or "linux/amd64,linux/arm64"). This overrides the platform specified in config.yaml.
189190
:param pat: Personal access token for authentication. If None, will use environment variables.
190191
:param base_url: Base URL for the API. If None, will use environment variables.
192+
:param compute_info_required: Whether inference compute info is required. This affects certain validation and behavior.
191193
"""
192194
assert app_not_found_action in ["auto_create", "prompt", "error"], ValueError(
193195
f"Expected one of {['auto_create', 'prompt', 'error']}, got {app_not_found_action=}"
@@ -208,7 +210,9 @@ def __init__(
208210
self.model_proto = self._get_model_proto()
209211
self.model_id = self.model_proto.id
210212
self.model_version_id = None
211-
self.inference_compute_info = self._get_inference_compute_info()
213+
self.inference_compute_info = self._get_inference_compute_info(
214+
compute_info_required=compute_info_required
215+
)
212216
self.is_v3 = True # Do model build for v3
213217

214218
def create_model_instance(self, load_model=True, mocking=False) -> ModelClass:
@@ -937,11 +941,12 @@ def _get_model_proto(self):
937941

938942
return model_proto
939943

940-
def _get_inference_compute_info(self):
941-
assert "inference_compute_info" in self.config, (
942-
"inference_compute_info not found in the config file"
943-
)
944-
inference_compute_info = self.config.get('inference_compute_info')
944+
def _get_inference_compute_info(self, compute_info_required=False):
945+
if compute_info_required:
946+
assert "inference_compute_info" in self.config, (
947+
"inference_compute_info not found in the config file"
948+
)
949+
inference_compute_info = self.config.get('inference_compute_info') or {}
945950
# Ensure cpu_limit is a string if it exists and is an int
946951
if 'cpu_limit' in inference_compute_info and isinstance(
947952
inference_compute_info['cpu_limit'], int
@@ -1949,7 +1954,12 @@ def upload_model(
19491954
:param base_url: Base URL for the API. If None, will use environment variables.
19501955
"""
19511956
builder = ModelBuilder(
1952-
folder, app_not_found_action="prompt", platform=platform, pat=pat, base_url=base_url
1957+
folder,
1958+
app_not_found_action="prompt",
1959+
platform=platform,
1960+
pat=pat,
1961+
base_url=base_url,
1962+
compute_info_required=True,
19531963
)
19541964
builder.download_checkpoints(stage=stage)
19551965

clarifai/runners/models/model_run_locally.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,31 @@
1818

1919

2020
class ModelRunLocally:
21-
def __init__(self, model_path):
21+
def __init__(self, model_path, model_builder: ModelBuilder = None):
22+
"""
23+
Initialize a helper to run a Clarifai model locally in an isolated environment.
24+
25+
Parameters
26+
----------
27+
model_path : str
28+
Filesystem path to the root directory of the model. This directory is expected
29+
to contain the model code and a ``requirements.txt`` file describing its
30+
Python dependencies.
31+
model_builder : ModelBuilder, optional
32+
An existing :class:`ModelBuilder` instance to use for interacting with the
33+
model. Pass this when you already have a configured builder you want to
34+
reuse. If ``None`` (the default), a new ``ModelBuilder`` is created for
35+
``model_path`` with ``download_validation_only=True``.
36+
"""
2237
self.model_path = os.path.abspath(model_path)
2338
self.requirements_file = os.path.join(self.model_path, "requirements.txt")
2439

2540
# ModelBuilder contains multiple useful methods to interact with the model
26-
self.builder = ModelBuilder(self.model_path, download_validation_only=True)
41+
self.builder = (
42+
model_builder
43+
if model_builder
44+
else ModelBuilder(self.model_path, download_validation_only=True)
45+
)
2746
self.config = self.builder.config
2847

2948
def _get_method_signatures(self):

clarifai/runners/models/model_runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ def __init__(
9191
HealthProbeRequestHandler.is_startup = True
9292

9393
start_health_server_thread(port=health_check_port, address='')
94+
if health_check_port is not None:
95+
start_health_server_thread(port=health_check_port, address='')
9496

9597
def get_runner_item_output_for_status(
9698
self, status: status_pb2.Status

clarifai/runners/server.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,12 @@ def main():
141141

142142

143143
class ModelServer:
144-
def __init__(self, model_path, model_runner_local: ModelRunLocally = None):
144+
def __init__(
145+
self,
146+
model_path,
147+
model_runner_local: ModelRunLocally = None,
148+
model_builder: ModelBuilder = None,
149+
):
145150
"""Initialize the ModelServer.
146151
Args:
147152
model_path: Path to the model directory
@@ -158,7 +163,11 @@ def __init__(self, model_path, model_runner_local: ModelRunLocally = None):
158163
self._initialize_secrets_system()
159164

160165
# Build model after secrets are loaded
161-
self._builder = ModelBuilder(model_path, download_validation_only=True)
166+
self._builder = (
167+
model_builder
168+
if model_builder
169+
else ModelBuilder(model_path, download_validation_only=True)
170+
)
162171
self._current_model = self._builder.create_model_instance()
163172

164173
logger.info("ModelServer initialized successfully")

clarifai/runners/utils/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def handles_list(self):
2121

2222
def is_repeated_field(field_name):
2323
descriptor = resources_pb2.Data.DESCRIPTOR.fields_by_name.get(field_name)
24-
return descriptor and descriptor.label == descriptor.LABEL_REPEATED
24+
return descriptor and descriptor.is_repeated
2525

2626

2727
class AtomicFieldSerializer(Serializer):

clarifai/utils/protobuf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def dict_to_protobuf(pb_obj: Message, data: dict) -> None:
3737

3838
try:
3939
# Handle repeated fields (lists)
40-
if field_descriptor.label == FieldDescriptor.LABEL_REPEATED:
40+
if field_descriptor.is_repeated:
4141
_handle_repeated_field(pb_obj, field_descriptor, field, value)
4242

4343
# Handle message fields (nested messages)

tests/cli/test_local_runner_cli.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ def test_local_runner_creates_resources_when_missing(
145145
# Mock ModelBuilder
146146
mock_builder = MagicMock()
147147
mock_builder.config = {"model": {"model_type_id": "multimodal-to-text"}, "toolkit": {}}
148-
mock_builder.get_method_signatures.return_value = {"predict": "mock_signature"}
148+
mock_method_sig = MagicMock()
149+
mock_method_sig.name = "predict"
150+
mock_builder.get_method_signatures.return_value = [mock_method_sig]
149151
mock_builder_class.return_value = mock_builder
150152
mock_builder_class._load_config.return_value = mock_builder.config
151153

@@ -266,7 +268,9 @@ def test_local_runner_uses_existing_resources(
266268
# Mock ModelBuilder
267269
mock_builder = MagicMock()
268270
mock_builder.config = {"model": {"model_type_id": "multimodal-to-text"}, "toolkit": {}}
269-
mock_builder.get_method_signatures.return_value = {"predict": "mock_signature"}
271+
mock_method_sig = MagicMock()
272+
mock_method_sig.name = "predict"
273+
mock_builder.get_method_signatures.return_value = [mock_method_sig]
270274
mock_builder_class.return_value = mock_builder
271275
mock_builder_class._load_config.return_value = mock_builder.config
272276

@@ -398,7 +402,9 @@ def test_local_runner_with_pool_size_parameter(
398402
# Mock ModelBuilder
399403
mock_builder = MagicMock()
400404
mock_builder.config = {"model": {"model_type_id": "multimodal-to-text"}, "toolkit": {}}
401-
mock_builder.get_method_signatures.return_value = {"predict": "mock_signature"}
405+
mock_method_sig = MagicMock()
406+
mock_method_sig.name = "predict"
407+
mock_builder.get_method_signatures.return_value = [mock_method_sig]
402408
mock_builder_class.return_value = mock_builder
403409
mock_builder_class._load_config.return_value = mock_builder.config
404410

@@ -534,7 +540,9 @@ def test_local_runner_model_serving(
534540
# Mock ModelBuilder
535541
mock_builder = MagicMock()
536542
mock_builder.config = {"model": {"model_type_id": "multimodal-to-text"}, "toolkit": {}}
537-
mock_builder.get_method_signatures.return_value = {"predict": "mock_signature"}
543+
mock_method_sig = MagicMock()
544+
mock_method_sig.name = "predict"
545+
mock_builder.get_method_signatures.return_value = [mock_method_sig]
538546
mock_builder_class.return_value = mock_builder
539547
mock_builder_class._load_config.return_value = mock_builder.config
540548

@@ -625,7 +633,7 @@ def validate_ctx_mock(ctx):
625633

626634
# Verify ModelServer was instantiated with the correct model path
627635
mock_server_class.assert_called_once_with(
628-
model_path=str(dummy_model_dir), model_runner_local=None
636+
model_path=str(dummy_model_dir), model_runner_local=None, model_builder=mock_builder
629637
)
630638

631639
# Verify serve method was called with correct parameters for local runner

tests/runners/test_model_upload.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def test_model_uploader_flow(dummy_models_path, client):
152152
7. Delete the deployment
153153
"""
154154
# Initialize
155-
builder = ModelBuilder(folder=str(dummy_models_path))
155+
builder = ModelBuilder(folder=str(dummy_models_path), compute_info_required=True)
156156
assert builder.folder == str(dummy_models_path), "Uploader folder mismatch"
157157

158158
# Basic checks on config

0 commit comments

Comments
 (0)