clarifai 11.8.2__tar.gz → 11.8.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {clarifai-11.8.2/clarifai.egg-info → clarifai-11.8.3}/PKG-INFO +1 -1
- clarifai-11.8.3/clarifai/__init__.py +1 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/model.py +62 -24
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/pipeline.py +84 -6
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/model.py +2 -1
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/pipeline_steps/pipeline_step_builder.py +97 -1
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/pipelines/pipeline_builder.py +196 -34
- clarifai-11.8.3/clarifai/utils/hashing.py +117 -0
- {clarifai-11.8.2 → clarifai-11.8.3/clarifai.egg-info}/PKG-INFO +1 -1
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai.egg-info/SOURCES.txt +1 -0
- clarifai-11.8.2/clarifai/__init__.py +0 -1
- {clarifai-11.8.2 → clarifai-11.8.3}/LICENSE +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/MANIFEST.in +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/README.md +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/README.md +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/__main__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/base.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/compute_cluster.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/deployment.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/nodepool.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/pipeline_step.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/templates/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/templates/model_templates.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/templates/pipeline_step_templates.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli/templates/pipeline_templates.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/cli.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/app.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/auth/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/auth/helper.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/auth/register.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/auth/stub.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/base.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/compute_cluster.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/dataset.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/deployment.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/input.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/lister.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/model_client.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/module.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/nodepool.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/pipeline.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/pipeline_step.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/runner.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/search.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/user.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/client/workflow.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/base.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/dataset.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/input.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/model.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/rag.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/search.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/constants/workflow.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/export/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/export/inputs_annotations.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/base.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/features.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/image.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/README.md +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/coco_captions.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/coco_detection.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/imagenet_classification.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/loaders/xview_detection.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/multimodal.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/text.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/datasets/upload/utils.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/errors.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/models/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/models/api.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/modules/README.md +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/modules/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/modules/css.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/modules/pages.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/modules/style.css +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/rag/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/rag/rag.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/rag/utils.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/dockerfile_template/Dockerfile.template +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/dummy_openai_model.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/mcp_class.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/model_builder.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/model_class.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/model_run_locally.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/model_runner.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/model_servicer.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/openai_class.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/visual_classifier_class.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/models/visual_detector_class.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/pipeline_steps/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/pipelines/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/server.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/code_script.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/const.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/data_types/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/data_types/data_types.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/data_utils.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/loader.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/method_signatures.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/model_utils.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/openai_convertor.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/pipeline_validation.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/serializers.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/utils/url_fetcher.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/schema/search.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/urls/helper.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/cli.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/config.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/constants.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/evaluation/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/evaluation/helpers.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/evaluation/main.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/evaluation/testset_annotation_parser.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/logging.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/misc.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/model_train.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/protobuf.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/utils/secrets.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/versions.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/workflows/__init__.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/workflows/export.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/workflows/utils.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai/workflows/validate.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai.egg-info/dependency_links.txt +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai.egg-info/entry_points.txt +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai.egg-info/requires.txt +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/clarifai.egg-info/top_level.txt +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/pyproject.toml +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/requirements.txt +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/setup.cfg +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/setup.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_app.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_async_stub.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_auth.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_data_upload.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_eval.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_list_models.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_misc.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_model_predict.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_model_train.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_modules.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_pipeline_client.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_rag.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_search.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_secrets.py +0 -0
- {clarifai-11.8.2 → clarifai-11.8.3}/tests/test_stub.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "11.8.3"
|
@@ -231,6 +231,44 @@ def init(
|
|
231
231
|
repo_url = format_github_repo_url(github_url)
|
232
232
|
repo_url = f"https://github.com/{owner}/{repo}"
|
233
233
|
|
234
|
+
try:
|
235
|
+
# Create a temporary directory for cloning
|
236
|
+
with tempfile.TemporaryDirectory(prefix="clarifai_model_") as clone_dir:
|
237
|
+
# Clone the repository with explicit branch parameter
|
238
|
+
if not clone_github_repo(repo_url, clone_dir, github_pat, branch):
|
239
|
+
logger.error(f"Failed to clone repository from {repo_url}")
|
240
|
+
github_url = None # Fall back to template mode
|
241
|
+
|
242
|
+
else:
|
243
|
+
# Copy the entire repository content to target directory (excluding .git)
|
244
|
+
for item in os.listdir(clone_dir):
|
245
|
+
if item == '.git':
|
246
|
+
continue
|
247
|
+
|
248
|
+
source_path = os.path.join(clone_dir, item)
|
249
|
+
target_path = os.path.join(model_path, item)
|
250
|
+
|
251
|
+
if os.path.isdir(source_path):
|
252
|
+
shutil.copytree(source_path, target_path, dirs_exist_ok=True)
|
253
|
+
else:
|
254
|
+
shutil.copy2(source_path, target_path)
|
255
|
+
|
256
|
+
logger.info(f"Successfully cloned repository to {model_path}")
|
257
|
+
logger.info(
|
258
|
+
"Model initialization complete with GitHub repository clone"
|
259
|
+
)
|
260
|
+
logger.info("Next steps:")
|
261
|
+
logger.info("1. Review the model configuration")
|
262
|
+
logger.info("2. Install any required dependencies manually")
|
263
|
+
logger.info(
|
264
|
+
"3. Test the model locally using 'clarifai model local-test'"
|
265
|
+
)
|
266
|
+
return
|
267
|
+
|
268
|
+
except Exception as e:
|
269
|
+
logger.error(f"Failed to clone GitHub repository: {e}")
|
270
|
+
github_url = None # Fall back to template mode
|
271
|
+
|
234
272
|
if toolkit:
|
235
273
|
logger.info(f"Initializing model from GitHub repository: {github_url}")
|
236
274
|
|
@@ -240,31 +278,31 @@ def init(
|
|
240
278
|
else:
|
241
279
|
repo_url = format_github_repo_url(github_url)
|
242
280
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
else:
|
252
|
-
# Copy the entire repository content to target directory (excluding .git)
|
253
|
-
for item in os.listdir(clone_dir):
|
254
|
-
if item == '.git':
|
255
|
-
continue
|
256
|
-
|
257
|
-
source_path = os.path.join(clone_dir, item)
|
258
|
-
target_path = os.path.join(model_path, item)
|
259
|
-
|
260
|
-
if os.path.isdir(source_path):
|
261
|
-
shutil.copytree(source_path, target_path, dirs_exist_ok=True)
|
262
|
-
else:
|
263
|
-
shutil.copy2(source_path, target_path)
|
281
|
+
try:
|
282
|
+
# Create a temporary directory for cloning
|
283
|
+
with tempfile.TemporaryDirectory(prefix="clarifai_model_") as clone_dir:
|
284
|
+
# Clone the repository with explicit branch parameter
|
285
|
+
if not clone_github_repo(repo_url, clone_dir, github_pat, branch):
|
286
|
+
logger.error(f"Failed to clone repository from {repo_url}")
|
287
|
+
github_url = None # Fall back to template mode
|
264
288
|
|
265
|
-
|
266
|
-
|
267
|
-
|
289
|
+
else:
|
290
|
+
# Copy the entire repository content to target directory (excluding .git)
|
291
|
+
for item in os.listdir(clone_dir):
|
292
|
+
if item == '.git':
|
293
|
+
continue
|
294
|
+
|
295
|
+
source_path = os.path.join(clone_dir, item)
|
296
|
+
target_path = os.path.join(model_path, item)
|
297
|
+
|
298
|
+
if os.path.isdir(source_path):
|
299
|
+
shutil.copytree(source_path, target_path, dirs_exist_ok=True)
|
300
|
+
else:
|
301
|
+
shutil.copy2(source_path, target_path)
|
302
|
+
|
303
|
+
except Exception as e:
|
304
|
+
logger.error(f"Failed to clone GitHub repository: {e}")
|
305
|
+
github_url = None
|
268
306
|
|
269
307
|
if (model_name or port or context_length) and (toolkit == 'ollama'):
|
270
308
|
customize_ollama_model(model_path, model_name, port, context_length)
|
@@ -26,14 +26,19 @@ def pipeline():
|
|
26
26
|
|
27
27
|
@pipeline.command()
|
28
28
|
@click.argument("path", type=click.Path(exists=True), required=False, default=".")
|
29
|
-
|
29
|
+
@click.option(
|
30
|
+
'--no-lockfile',
|
31
|
+
is_flag=True,
|
32
|
+
help='Skip creating config-lock.yaml file.',
|
33
|
+
)
|
34
|
+
def upload(path, no_lockfile):
|
30
35
|
"""Upload a pipeline with associated pipeline steps to Clarifai.
|
31
36
|
|
32
37
|
PATH: Path to the pipeline configuration file or directory containing config.yaml. If not specified, the current directory is used by default.
|
33
38
|
"""
|
34
39
|
from clarifai.runners.pipelines.pipeline_builder import upload_pipeline
|
35
40
|
|
36
|
-
upload_pipeline(path)
|
41
|
+
upload_pipeline(path, no_lockfile=no_lockfile)
|
37
42
|
|
38
43
|
|
39
44
|
@pipeline.command()
|
@@ -106,15 +111,32 @@ def run(
|
|
106
111
|
|
107
112
|
validate_context(ctx)
|
108
113
|
|
114
|
+
# Try to load from config-lock.yaml first if no config is specified
|
115
|
+
lockfile_path = os.path.join(os.getcwd(), "config-lock.yaml")
|
116
|
+
if not config and os.path.exists(lockfile_path):
|
117
|
+
logger.info("Found config-lock.yaml, using it as default config source")
|
118
|
+
config = lockfile_path
|
119
|
+
|
109
120
|
if config:
|
110
121
|
config_data = from_yaml(config)
|
111
|
-
|
112
|
-
|
122
|
+
|
123
|
+
# Handle both regular config format and lockfile format
|
124
|
+
if 'pipeline' in config_data and isinstance(config_data['pipeline'], dict):
|
125
|
+
pipeline_config = config_data['pipeline']
|
126
|
+
pipeline_id = pipeline_config.get('id', pipeline_id)
|
127
|
+
pipeline_version_id = pipeline_config.get('version_id', pipeline_version_id)
|
128
|
+
user_id = pipeline_config.get('user_id', user_id)
|
129
|
+
app_id = pipeline_config.get('app_id', app_id)
|
130
|
+
else:
|
131
|
+
# Fallback to flat config structure
|
132
|
+
pipeline_id = config_data.get('pipeline_id', pipeline_id)
|
133
|
+
pipeline_version_id = config_data.get('pipeline_version_id', pipeline_version_id)
|
134
|
+
user_id = config_data.get('user_id', user_id)
|
135
|
+
app_id = config_data.get('app_id', app_id)
|
136
|
+
|
113
137
|
pipeline_version_run_id = config_data.get(
|
114
138
|
'pipeline_version_run_id', pipeline_version_run_id
|
115
139
|
)
|
116
|
-
user_id = config_data.get('user_id', user_id)
|
117
|
-
app_id = config_data.get('app_id', app_id)
|
118
140
|
nodepool_id = config_data.get('nodepool_id', nodepool_id)
|
119
141
|
compute_cluster_id = config_data.get('compute_cluster_id', compute_cluster_id)
|
120
142
|
pipeline_url = config_data.get('pipeline_url', pipeline_url)
|
@@ -319,6 +341,62 @@ def init(pipeline_path):
|
|
319
341
|
logger.info("3. Run 'clarifai pipeline upload config.yaml' to upload your pipeline")
|
320
342
|
|
321
343
|
|
344
|
+
@pipeline.command()
|
345
|
+
@click.argument(
|
346
|
+
"lockfile_path", type=click.Path(exists=True), required=False, default="config-lock.yaml"
|
347
|
+
)
|
348
|
+
def validate_lock(lockfile_path):
|
349
|
+
"""Validate a config-lock.yaml file for schema and reference consistency.
|
350
|
+
|
351
|
+
LOCKFILE_PATH: Path to the config-lock.yaml file. If not specified, looks for config-lock.yaml in current directory.
|
352
|
+
"""
|
353
|
+
from clarifai.runners.utils.pipeline_validation import PipelineConfigValidator
|
354
|
+
from clarifai.utils.cli import from_yaml
|
355
|
+
|
356
|
+
try:
|
357
|
+
# Load the lockfile
|
358
|
+
lockfile_data = from_yaml(lockfile_path)
|
359
|
+
|
360
|
+
# Validate required fields
|
361
|
+
if "pipeline" not in lockfile_data:
|
362
|
+
raise ValueError("'pipeline' section not found in lockfile")
|
363
|
+
|
364
|
+
pipeline = lockfile_data["pipeline"]
|
365
|
+
required_fields = ["id", "user_id", "app_id", "version_id"]
|
366
|
+
|
367
|
+
for field in required_fields:
|
368
|
+
if field not in pipeline:
|
369
|
+
raise ValueError(f"Required field '{field}' not found in pipeline section")
|
370
|
+
if not pipeline[field]:
|
371
|
+
raise ValueError(f"Required field '{field}' cannot be empty")
|
372
|
+
|
373
|
+
# Validate orchestration spec if present
|
374
|
+
if "orchestration_spec" in pipeline:
|
375
|
+
# Create a temporary config structure for validation
|
376
|
+
temp_config = {
|
377
|
+
"pipeline": {
|
378
|
+
"id": pipeline["id"],
|
379
|
+
"user_id": pipeline["user_id"],
|
380
|
+
"app_id": pipeline["app_id"],
|
381
|
+
"orchestration_spec": pipeline["orchestration_spec"],
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
# Use existing validator to check orchestration spec
|
386
|
+
validator = PipelineConfigValidator()
|
387
|
+
validator._validate_orchestration_spec(temp_config)
|
388
|
+
|
389
|
+
logger.info(f"✅ Lockfile {lockfile_path} is valid")
|
390
|
+
logger.info(f"Pipeline: {pipeline['id']}")
|
391
|
+
logger.info(f"User: {pipeline['user_id']}")
|
392
|
+
logger.info(f"App: {pipeline['app_id']}")
|
393
|
+
logger.info(f"Version: {pipeline['version_id']}")
|
394
|
+
|
395
|
+
except Exception as e:
|
396
|
+
logger.error(f"❌ Lockfile validation failed: {e}")
|
397
|
+
raise click.Abort()
|
398
|
+
|
399
|
+
|
322
400
|
@pipeline.command(['ls'])
|
323
401
|
@click.option('--page_no', required=False, help='Page number to list.', default=1)
|
324
402
|
@click.option('--per_page', required=False, help='Number of items per page.', default=16)
|
@@ -522,8 +522,9 @@ class Model(Lister, BaseClient):
|
|
522
522
|
model=self.model_info,
|
523
523
|
runner_selector=self._runner_selector,
|
524
524
|
)
|
525
|
+
# Pass in None for async stub will create it.
|
525
526
|
self._client = ModelClient(
|
526
|
-
stub=self.STUB, async_stub=
|
527
|
+
stub=self.STUB, async_stub=None, request_template=request_template
|
527
528
|
)
|
528
529
|
return self._client
|
529
530
|
|
{clarifai-11.8.2 → clarifai-11.8.3}/clarifai/runners/pipeline_steps/pipeline_step_builder.py
RENAMED
@@ -4,6 +4,7 @@ import sys
|
|
4
4
|
import tarfile
|
5
5
|
import time
|
6
6
|
from string import Template
|
7
|
+
from typing import List, Optional
|
7
8
|
|
8
9
|
import yaml
|
9
10
|
from clarifai_grpc.grpc.api import resources_pb2, service_pb2
|
@@ -11,6 +12,7 @@ from clarifai_grpc.grpc.api.status import status_code_pb2
|
|
11
12
|
from google.protobuf import json_format
|
12
13
|
|
13
14
|
from clarifai.client.base import BaseClient
|
15
|
+
from clarifai.utils.hashing import hash_directory
|
14
16
|
from clarifai.utils.logging import logger
|
15
17
|
from clarifai.utils.misc import get_uuid
|
16
18
|
from clarifai.versions import CLIENT_VERSION
|
@@ -22,12 +24,13 @@ UPLOAD_CHUNK_SIZE = 14 * 1024 * 1024
|
|
22
24
|
class PipelineStepBuilder:
|
23
25
|
"""Pipeline Step Builder class for managing pipeline step upload to Clarifai."""
|
24
26
|
|
25
|
-
def __init__(self, folder: str):
|
27
|
+
def __init__(self, folder: str, hash_exclusions: Optional[List[str]] = None):
|
26
28
|
"""
|
27
29
|
Initialize PipelineStepBuilder.
|
28
30
|
|
29
31
|
:param folder: The folder containing the pipeline step files (config.yaml, requirements.txt,
|
30
32
|
dockerfile, and pipeline_step.py in 1/ subdirectory)
|
33
|
+
:param hash_exclusions: List of file names to exclude from hash calculation (defaults to ['config-lock.yaml'])
|
31
34
|
"""
|
32
35
|
self._client = None
|
33
36
|
self.folder = self._validate_folder(folder)
|
@@ -37,6 +40,10 @@ class PipelineStepBuilder:
|
|
37
40
|
self.pipeline_step_id = self.pipeline_step_proto.id
|
38
41
|
self.pipeline_step_version_id = None
|
39
42
|
self.pipeline_step_compute_info = self._get_pipeline_step_compute_info()
|
43
|
+
# Configure files to exclude from hash calculation
|
44
|
+
self.hash_exclusions = (
|
45
|
+
hash_exclusions if hash_exclusions is not None else ['config-lock.yaml']
|
46
|
+
)
|
40
47
|
|
41
48
|
@property
|
42
49
|
def client(self):
|
@@ -490,6 +497,95 @@ COPY --link=true requirements.txt config.yaml /home/nonroot/main/
|
|
490
497
|
|
491
498
|
raise TimeoutError("Pipeline step build did not finish in time")
|
492
499
|
|
500
|
+
def load_config_lock(self):
|
501
|
+
"""
|
502
|
+
Load existing config-lock.yaml if it exists.
|
503
|
+
|
504
|
+
:return: Dictionary with config-lock data or None if file doesn't exist
|
505
|
+
"""
|
506
|
+
config_lock_path = os.path.join(self.folder, "config-lock.yaml")
|
507
|
+
if os.path.exists(config_lock_path):
|
508
|
+
try:
|
509
|
+
with open(config_lock_path, 'r', encoding='utf-8') as f:
|
510
|
+
return yaml.safe_load(f)
|
511
|
+
except Exception as e:
|
512
|
+
logger.warning(f"Failed to load config-lock.yaml: {e}")
|
513
|
+
return None
|
514
|
+
return None
|
515
|
+
|
516
|
+
def should_upload_step(self, algo="md5"):
|
517
|
+
"""
|
518
|
+
Check if the pipeline step should be uploaded based on hash comparison.
|
519
|
+
|
520
|
+
:param algo: Hash algorithm to use
|
521
|
+
:return: True if step should be uploaded, False otherwise
|
522
|
+
"""
|
523
|
+
config_lock = self.load_config_lock()
|
524
|
+
|
525
|
+
# If no config-lock.yaml exists, upload the step (first time upload)
|
526
|
+
if config_lock is None:
|
527
|
+
logger.info("No config-lock.yaml found, will upload pipeline step")
|
528
|
+
return True
|
529
|
+
|
530
|
+
# Compare stored hash with freshly computed one
|
531
|
+
current_hash = hash_directory(self.folder, algo=algo, exclude_files=self.hash_exclusions)
|
532
|
+
stored_hash_info = config_lock.get("hash", {})
|
533
|
+
stored_hash = stored_hash_info.get("value", "")
|
534
|
+
stored_algo = stored_hash_info.get("algo", "md5")
|
535
|
+
|
536
|
+
# If algorithm changed, re-upload to update hash
|
537
|
+
if stored_algo != algo:
|
538
|
+
logger.info(
|
539
|
+
f"Hash algorithm changed from {stored_algo} to {algo}, will upload pipeline step"
|
540
|
+
)
|
541
|
+
return True
|
542
|
+
|
543
|
+
# If hash changed, upload
|
544
|
+
if current_hash != stored_hash:
|
545
|
+
logger.info(
|
546
|
+
f"Hash changed (was: {stored_hash}, now: {current_hash}), will upload pipeline step"
|
547
|
+
)
|
548
|
+
return True
|
549
|
+
|
550
|
+
logger.info(f"Hash unchanged ({current_hash}), skipping pipeline step upload")
|
551
|
+
return False
|
552
|
+
|
553
|
+
def generate_config_lock(self, version_id, algo="md5"):
|
554
|
+
"""
|
555
|
+
Generate config-lock.yaml content for the pipeline step.
|
556
|
+
|
557
|
+
:param version_id: Pipeline step version ID
|
558
|
+
:param algo: Hash algorithm used
|
559
|
+
:return: Dictionary with config-lock data
|
560
|
+
"""
|
561
|
+
# Compute hash
|
562
|
+
hash_value = hash_directory(self.folder, algo=algo, exclude_files=self.hash_exclusions)
|
563
|
+
|
564
|
+
# Create config-lock structure
|
565
|
+
config_lock = {"id": version_id, "hash": {"algo": algo, "value": hash_value}}
|
566
|
+
|
567
|
+
# Append the original config.yaml contents
|
568
|
+
config_lock.update(self.config)
|
569
|
+
|
570
|
+
return config_lock
|
571
|
+
|
572
|
+
def save_config_lock(self, version_id, algo="md5"):
|
573
|
+
"""
|
574
|
+
Save config-lock.yaml file with pipeline step metadata.
|
575
|
+
|
576
|
+
:param version_id: Pipeline step version ID
|
577
|
+
:param algo: Hash algorithm used
|
578
|
+
"""
|
579
|
+
config_lock_data = self.generate_config_lock(version_id, algo)
|
580
|
+
config_lock_path = os.path.join(self.folder, "config-lock.yaml")
|
581
|
+
|
582
|
+
try:
|
583
|
+
with open(config_lock_path, 'w', encoding='utf-8') as f:
|
584
|
+
yaml.dump(config_lock_data, f, default_flow_style=False, allow_unicode=True)
|
585
|
+
logger.info(f"Generated config-lock.yaml at {config_lock_path}")
|
586
|
+
except Exception as e:
|
587
|
+
logger.error(f"Failed to save config-lock.yaml: {e}")
|
588
|
+
|
493
589
|
|
494
590
|
def upload_pipeline_step(folder, skip_dockerfile=False):
|
495
591
|
"""
|