clarifai 9.8.1__py3-none-any.whl → 9.9.0__py3-none-any.whl
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/client/app.py +115 -14
- clarifai/client/base.py +11 -4
- clarifai/client/dataset.py +8 -3
- clarifai/client/input.py +34 -28
- clarifai/client/model.py +71 -2
- clarifai/client/module.py +4 -2
- clarifai/client/runner.py +161 -0
- clarifai/client/search.py +173 -0
- clarifai/client/user.py +110 -4
- clarifai/client/workflow.py +27 -2
- clarifai/constants/search.py +2 -0
- clarifai/datasets/upload/loaders/xview_detection.py +1 -1
- clarifai/models/model_serving/README.md +3 -3
- clarifai/models/model_serving/cli/deploy_cli.py +2 -3
- clarifai/models/model_serving/cli/repository.py +3 -5
- clarifai/models/model_serving/constants.py +1 -5
- clarifai/models/model_serving/docs/custom_config.md +5 -6
- clarifai/models/model_serving/docs/dependencies.md +5 -10
- clarifai/models/model_serving/examples/image_classification/age_vit/requirements.txt +1 -0
- clarifai/models/model_serving/examples/text_classification/xlm-roberta/requirements.txt +1 -0
- clarifai/models/model_serving/examples/text_to_image/sd-v1.5/requirements.txt +1 -0
- clarifai/models/model_serving/examples/text_to_text/bart-summarize/requirements.txt +1 -0
- clarifai/models/model_serving/examples/visual_detection/yolov5x/requirements.txt +1 -1
- clarifai/models/model_serving/examples/visual_embedding/vit-base/requirements.txt +1 -0
- clarifai/models/model_serving/examples/visual_segmentation/segformer-b2/requirements.txt +1 -0
- clarifai/models/model_serving/model_config/__init__.py +2 -0
- clarifai/models/model_serving/model_config/config.py +298 -0
- clarifai/models/model_serving/model_config/model_types_config/text-classifier.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/text-embedder.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/text-to-image.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/text-to-text.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/visual-classifier.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/visual-detector.yaml +28 -0
- clarifai/models/model_serving/model_config/model_types_config/visual-embedder.yaml +18 -0
- clarifai/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +18 -0
- clarifai/models/model_serving/model_config/serializer.py +1 -1
- clarifai/models/model_serving/models/default_test.py +22 -21
- clarifai/models/model_serving/models/output.py +2 -2
- clarifai/models/model_serving/pb_model_repository.py +2 -5
- clarifai/runners/__init__.py +0 -0
- clarifai/runners/example.py +33 -0
- clarifai/schema/search.py +60 -0
- clarifai/utils/logging.py +53 -3
- clarifai/versions.py +1 -1
- clarifai/workflows/__init__.py +0 -0
- clarifai/workflows/export.py +68 -0
- clarifai/workflows/utils.py +59 -0
- clarifai/workflows/validate.py +67 -0
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/METADATA +20 -2
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/RECORD +102 -86
- clarifai_utils/client/app.py +115 -14
- clarifai_utils/client/base.py +11 -4
- clarifai_utils/client/dataset.py +8 -3
- clarifai_utils/client/input.py +34 -28
- clarifai_utils/client/model.py +71 -2
- clarifai_utils/client/module.py +4 -2
- clarifai_utils/client/runner.py +161 -0
- clarifai_utils/client/search.py +173 -0
- clarifai_utils/client/user.py +110 -4
- clarifai_utils/client/workflow.py +27 -2
- clarifai_utils/constants/search.py +2 -0
- clarifai_utils/datasets/upload/loaders/xview_detection.py +1 -1
- clarifai_utils/models/model_serving/README.md +3 -3
- clarifai_utils/models/model_serving/cli/deploy_cli.py +2 -3
- clarifai_utils/models/model_serving/cli/repository.py +3 -5
- clarifai_utils/models/model_serving/constants.py +1 -5
- clarifai_utils/models/model_serving/docs/custom_config.md +5 -6
- clarifai_utils/models/model_serving/docs/dependencies.md +5 -10
- clarifai_utils/models/model_serving/examples/image_classification/age_vit/requirements.txt +1 -0
- clarifai_utils/models/model_serving/examples/text_classification/xlm-roberta/requirements.txt +1 -0
- clarifai_utils/models/model_serving/examples/text_to_image/sd-v1.5/requirements.txt +1 -0
- clarifai_utils/models/model_serving/examples/text_to_text/bart-summarize/requirements.txt +1 -0
- clarifai_utils/models/model_serving/examples/visual_detection/yolov5x/requirements.txt +1 -1
- clarifai_utils/models/model_serving/examples/visual_embedding/vit-base/requirements.txt +1 -0
- clarifai_utils/models/model_serving/examples/visual_segmentation/segformer-b2/requirements.txt +1 -0
- clarifai_utils/models/model_serving/model_config/__init__.py +2 -0
- clarifai_utils/models/model_serving/model_config/config.py +298 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/text-classifier.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/text-embedder.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/text-to-image.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/text-to-text.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/visual-classifier.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/visual-detector.yaml +28 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/visual-embedder.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +18 -0
- clarifai_utils/models/model_serving/model_config/serializer.py +1 -1
- clarifai_utils/models/model_serving/models/default_test.py +22 -21
- clarifai_utils/models/model_serving/models/output.py +2 -2
- clarifai_utils/models/model_serving/pb_model_repository.py +2 -5
- clarifai_utils/runners/__init__.py +0 -0
- clarifai_utils/runners/example.py +33 -0
- clarifai_utils/schema/search.py +60 -0
- clarifai_utils/utils/logging.py +53 -3
- clarifai_utils/versions.py +1 -1
- clarifai_utils/workflows/__init__.py +0 -0
- clarifai_utils/workflows/export.py +68 -0
- clarifai_utils/workflows/utils.py +59 -0
- clarifai_utils/workflows/validate.py +67 -0
- clarifai/models/model_serving/envs/triton_conda-cp3.8-torch1.13.1-19f97078.yaml +0 -35
- clarifai/models/model_serving/envs/triton_conda-cp3.8-torch2.0.0-ce980f28.yaml +0 -51
- clarifai/models/model_serving/examples/image_classification/age_vit/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/text_classification/xlm-roberta/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/text_to_image/sd-v1.5/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/text_to_text/bart-summarize/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/visual_detection/yolov5x/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/visual_embedding/vit-base/triton_conda.yaml +0 -1
- clarifai/models/model_serving/examples/visual_segmentation/segformer-b2/triton_conda.yaml +0 -1
- clarifai/models/model_serving/model_config/deploy.py +0 -75
- clarifai/models/model_serving/model_config/triton_config.py +0 -226
- clarifai_utils/models/model_serving/envs/triton_conda-cp3.8-torch1.13.1-19f97078.yaml +0 -35
- clarifai_utils/models/model_serving/envs/triton_conda-cp3.8-torch2.0.0-ce980f28.yaml +0 -51
- clarifai_utils/models/model_serving/examples/image_classification/age_vit/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/text_classification/xlm-roberta/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/text_to_image/sd-v1.5/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/text_to_text/bart-summarize/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/visual_detection/yolov5x/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/visual_embedding/vit-base/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/examples/visual_segmentation/segformer-b2/triton_conda.yaml +0 -1
- clarifai_utils/models/model_serving/model_config/deploy.py +0 -75
- clarifai_utils/models/model_serving/model_config/triton_config.py +0 -226
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/LICENSE +0 -0
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/WHEEL +0 -0
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/entry_points.txt +0 -0
- {clarifai-9.8.1.dist-info → clarifai-9.9.0.dist-info}/top_level.txt +0 -0
|
@@ -19,8 +19,7 @@ import os
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
from typing import Callable, Type
|
|
21
21
|
|
|
22
|
-
from .model_config
|
|
23
|
-
from .model_config.triton_config import TritonModelConfig
|
|
22
|
+
from .model_config import Serializer, TritonModelConfig
|
|
24
23
|
from .models import inference, pb_model, test
|
|
25
24
|
|
|
26
25
|
|
|
@@ -79,11 +78,9 @@ class TritonModelRepository:
|
|
|
79
78
|
pass
|
|
80
79
|
else:
|
|
81
80
|
continue
|
|
82
|
-
# gen requirements
|
|
81
|
+
# gen requirements
|
|
83
82
|
with open(os.path.join(repository_path, "requirements.txt"), "w") as f:
|
|
84
83
|
f.write("clarifai>9.5.3\ntritonclient[all]") # for model upload utils
|
|
85
|
-
with open(os.path.join(repository_path, "triton_conda.yaml"), "w") as conda_env:
|
|
86
|
-
conda_env.write("name: triton_conda-cp3.8-torch1.13.1-19f97078")
|
|
87
84
|
|
|
88
85
|
if not os.path.isdir(model_version_path):
|
|
89
86
|
os.mkdir(model_version_path)
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from clarifai_grpc.grpc.api import resources_pb2
|
|
2
|
+
|
|
3
|
+
from clarifai.client.runner import Runner
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MyRunner(Runner):
|
|
7
|
+
"""A custom runner that adds "Hello World" to the end of the text and replaces the domain of the
|
|
8
|
+
image URL as an example.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def run_input(self, input: resources_pb2.Input) -> resources_pb2.Output:
|
|
12
|
+
"""This is the method that will be called when the runner is run. It takes in an input and
|
|
13
|
+
returns an output.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
output = resources_pb2.Output()
|
|
17
|
+
|
|
18
|
+
data = input.data
|
|
19
|
+
|
|
20
|
+
if data.text.raw != "":
|
|
21
|
+
output.data.text.raw = data.text.raw + "Hello World"
|
|
22
|
+
if data.image.url != "":
|
|
23
|
+
output.data.text.raw = data.image.url.replace("samples.clarifai.com", "newdomain.com")
|
|
24
|
+
return output
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if __name__ == '__main__':
|
|
28
|
+
# Make sure you set these env vars before running the example.
|
|
29
|
+
# CLARIFAI_PAT
|
|
30
|
+
# CLARIFAI_USER_ID
|
|
31
|
+
|
|
32
|
+
# You need to first create a runner in the Clarifai API and then use the ID here.
|
|
33
|
+
MyRunner(runner_id="sdk-test-runner").start()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from schema import And, Optional, Regex, Schema
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def get_schema() -> Schema:
|
|
5
|
+
"""Initialize the schema for rank and filter.
|
|
6
|
+
|
|
7
|
+
This schema validates:
|
|
8
|
+
|
|
9
|
+
- Rank and filter must be a list
|
|
10
|
+
- Each item in the list must be a dict
|
|
11
|
+
- The dict can contain these optional keys:
|
|
12
|
+
- 'image_url': Valid URL string
|
|
13
|
+
- 'text_raw': Non-empty string
|
|
14
|
+
- 'metadata': Dict
|
|
15
|
+
- 'image_bytes': Bytes
|
|
16
|
+
- 'geo_point': Dict with 'longitude', 'latitude' and 'geo_limit' as float, float and int respectively
|
|
17
|
+
- 'concepts': List where each item is a concept dict
|
|
18
|
+
- Concept dict requires at least one of:
|
|
19
|
+
- 'name': Non-empty string with dashes/underscores
|
|
20
|
+
- 'id': Non-empty string
|
|
21
|
+
- 'language': Non-empty string
|
|
22
|
+
- 'value': 0 or 1 integer
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Schema: The schema for rank and filter.
|
|
26
|
+
"""
|
|
27
|
+
# Schema for a single concept
|
|
28
|
+
concept_schema = Schema({
|
|
29
|
+
Optional('value'):
|
|
30
|
+
And(int, lambda x: x in [0, 1]),
|
|
31
|
+
Optional('id'):
|
|
32
|
+
And(str, len),
|
|
33
|
+
Optional('language'):
|
|
34
|
+
And(str, len),
|
|
35
|
+
# Non-empty strings with internal dashes and underscores.
|
|
36
|
+
Optional('name'):
|
|
37
|
+
And(str, len, Regex(r'^[0-9A-Za-z]+([-_][0-9A-Za-z]+)*$'))
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
# Schema for a rank or filter item
|
|
41
|
+
rank_filter_item_schema = Schema({
|
|
42
|
+
Optional('image_url'):
|
|
43
|
+
And(str, Regex(r'^https?://')),
|
|
44
|
+
Optional('text_raw'):
|
|
45
|
+
And(str, len),
|
|
46
|
+
Optional('metadata'):
|
|
47
|
+
dict,
|
|
48
|
+
Optional('image_bytes'):
|
|
49
|
+
bytes,
|
|
50
|
+
Optional('geo_point'): {
|
|
51
|
+
'longitude': float,
|
|
52
|
+
'latitude': float,
|
|
53
|
+
'geo_limit': int
|
|
54
|
+
},
|
|
55
|
+
Optional("concepts"):
|
|
56
|
+
And(list, lambda x: all(concept_schema.is_valid(item) and len(item) > 0 for item in x)),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
# Schema for rank and filter args
|
|
60
|
+
return Schema([rank_filter_item_schema])
|
clarifai/utils/logging.py
CHANGED
|
@@ -1,16 +1,66 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from typing import Dict, List, Optional
|
|
3
4
|
|
|
5
|
+
from rich import print as rprint
|
|
4
6
|
from rich.logging import RichHandler
|
|
5
7
|
from rich.table import Table
|
|
6
8
|
from rich.traceback import install
|
|
9
|
+
from rich.tree import Tree
|
|
7
10
|
|
|
8
11
|
install()
|
|
9
12
|
|
|
10
13
|
|
|
11
|
-
def
|
|
14
|
+
def display_workflow_tree(nodes_data: List[Dict]) -> None:
|
|
15
|
+
"""Displays a tree of the workflow nodes."""
|
|
16
|
+
# Create a mapping of node_id to the list of node_ids that are connected to it.
|
|
17
|
+
node_adj_mapping = defaultdict(list)
|
|
18
|
+
# Create a mapping of node_id to the node data info.
|
|
19
|
+
nodes_data_dict = {}
|
|
20
|
+
for node in nodes_data:
|
|
21
|
+
nodes_data_dict[node["id"]] = node
|
|
22
|
+
if node.get("node_inputs", "") == "":
|
|
23
|
+
node_adj_mapping["Input"].append(node["id"])
|
|
24
|
+
else:
|
|
25
|
+
for node_input in node["node_inputs"]:
|
|
26
|
+
node_adj_mapping[node_input["node_id"]].append(node["id"])
|
|
27
|
+
|
|
28
|
+
# Get all leaf nodes.
|
|
29
|
+
leaf_node_ids = set()
|
|
30
|
+
for node_id in list(nodes_data_dict.keys()):
|
|
31
|
+
if node_adj_mapping.get(node_id, "") == "":
|
|
32
|
+
leaf_node_ids.add(node_id)
|
|
33
|
+
|
|
34
|
+
def build_node_tree(node_id="Input"):
|
|
35
|
+
"""Recursively builds a rich tree of the workflow nodes."""
|
|
36
|
+
# Set the style of the current node.
|
|
37
|
+
style_str = "green" if node_id in leaf_node_ids else "white"
|
|
38
|
+
|
|
39
|
+
# Create a Tree object for the current node.
|
|
40
|
+
if node_id != "Input":
|
|
41
|
+
node_table = table_from_dict(
|
|
42
|
+
[nodes_data_dict[node_id]["model"]],
|
|
43
|
+
column_names=["id", "model_type_id", "app_id", "user_id"],
|
|
44
|
+
title="Node: " + node_id)
|
|
45
|
+
|
|
46
|
+
tree = Tree(node_table, style=style_str, guide_style="underline2 white")
|
|
47
|
+
else:
|
|
48
|
+
tree = Tree(f"[green] {node_id}", style=style_str, guide_style="underline2 white")
|
|
49
|
+
|
|
50
|
+
# Recursively add the child nodes of the current node to the tree.
|
|
51
|
+
for child in node_adj_mapping.get(node_id, []):
|
|
52
|
+
tree.add(build_node_tree(child))
|
|
53
|
+
|
|
54
|
+
# Return the tree.
|
|
55
|
+
return tree
|
|
56
|
+
|
|
57
|
+
tree = build_node_tree("Input")
|
|
58
|
+
rprint(tree)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def table_from_dict(data: List[Dict], column_names: List[str], title: str = "") -> Table:
|
|
12
62
|
"""Use this function for printing tables from a list of dicts."""
|
|
13
|
-
table = Table(title=title, show_header=True, header_style="
|
|
63
|
+
table = Table(title=title, show_lines=False, show_header=True, header_style="blue")
|
|
14
64
|
for column_name in column_names:
|
|
15
65
|
table.add_column(column_name)
|
|
16
66
|
for row in data:
|
clarifai/versions.py
CHANGED
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
import yaml
|
|
4
|
+
from google.protobuf.json_format import MessageToDict
|
|
5
|
+
|
|
6
|
+
VALID_YAML_KEYS = ["workflow", "id", "nodes", "node_inputs", "node_id", "model"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def clean_up_unused_keys(wf: dict):
|
|
10
|
+
"""Removes unused keys from dict before exporting to yaml. Supports nested dicts."""
|
|
11
|
+
new_wf = dict()
|
|
12
|
+
for key, val in wf.items():
|
|
13
|
+
if key not in VALID_YAML_KEYS:
|
|
14
|
+
continue
|
|
15
|
+
if key == "model":
|
|
16
|
+
new_wf["model"] = {
|
|
17
|
+
"model_id": wf["model"]["id"],
|
|
18
|
+
"model_version_id": wf["model"]["model_version"]["id"]
|
|
19
|
+
}
|
|
20
|
+
# If the model is not from clarifai main, add the app_id and user_id to the model dict.
|
|
21
|
+
if wf["model"]["user_id"] != "clarifai" and wf["model"]["app_id"] != "main":
|
|
22
|
+
new_wf["model"].update({
|
|
23
|
+
"app_id": wf["model"]["app_id"],
|
|
24
|
+
"user_id": wf["model"]["user_id"]
|
|
25
|
+
})
|
|
26
|
+
elif isinstance(val, dict):
|
|
27
|
+
new_wf[key] = clean_up_unused_keys(val)
|
|
28
|
+
elif isinstance(val, list):
|
|
29
|
+
new_list = []
|
|
30
|
+
for i in val:
|
|
31
|
+
new_list.append(clean_up_unused_keys(i))
|
|
32
|
+
new_wf[key] = new_list
|
|
33
|
+
else:
|
|
34
|
+
new_wf[key] = val
|
|
35
|
+
return new_wf
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Exporter:
|
|
39
|
+
|
|
40
|
+
def __init__(self, workflow):
|
|
41
|
+
self.wf = workflow
|
|
42
|
+
|
|
43
|
+
def __enter__(self):
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
def parse(self) -> Dict[str, Any]:
|
|
47
|
+
"""Reads a resources_pb2.Workflow object (e.g. from a GetWorkflow response)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
dict: A dict representation of the workflow.
|
|
51
|
+
"""
|
|
52
|
+
if isinstance(self.wf, list):
|
|
53
|
+
self.wf = self.wf[0]
|
|
54
|
+
wf = {"workflow": MessageToDict(self.wf, preserving_proto_field_name=True)}
|
|
55
|
+
clean_wf = clean_up_unused_keys(wf)
|
|
56
|
+
self.wf_dict = clean_wf
|
|
57
|
+
return clean_wf
|
|
58
|
+
|
|
59
|
+
def export(self, out_path):
|
|
60
|
+
with open(out_path, 'w') as out_file:
|
|
61
|
+
yaml.dump(self.wf_dict["workflow"], out_file, default_flow_style=False)
|
|
62
|
+
|
|
63
|
+
def __exit__(self, *args):
|
|
64
|
+
self.close()
|
|
65
|
+
|
|
66
|
+
def close(self):
|
|
67
|
+
del self.wf
|
|
68
|
+
del self.wf_dict
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import Dict, Optional, Set
|
|
2
|
+
|
|
3
|
+
from clarifai_grpc.grpc.api import resources_pb2
|
|
4
|
+
from google.protobuf import struct_pb2
|
|
5
|
+
from google.protobuf.json_format import MessageToDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_yaml_output_info_proto(yaml_model_output_info: Dict) -> Optional[resources_pb2.OutputInfo]:
|
|
9
|
+
"""Converts a yaml model output info to an api model output info."""
|
|
10
|
+
if not yaml_model_output_info:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
return resources_pb2.OutputInfo(
|
|
14
|
+
params=convert_yaml_params_to_api_params(yaml_model_output_info.get('params')))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def convert_yaml_params_to_api_params(yaml_params: Dict) -> Optional[struct_pb2.Struct]:
|
|
18
|
+
"""Converts a yaml model output info params to an api model output info params."""
|
|
19
|
+
if not yaml_params:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
s = struct_pb2.Struct()
|
|
23
|
+
s.update(yaml_params)
|
|
24
|
+
|
|
25
|
+
return s
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_same_yaml_model(api_model: resources_pb2.Model, yaml_model: Dict) -> bool:
|
|
29
|
+
"""Compares a model from the API with a model from a yaml file."""
|
|
30
|
+
api_model = MessageToDict(api_model, preserving_proto_field_name=True)
|
|
31
|
+
|
|
32
|
+
yaml_model_from_api = dict()
|
|
33
|
+
for k, _ in yaml_model.items():
|
|
34
|
+
if k == "output_info" and api_model["model_version"].get("output_info", "") != "":
|
|
35
|
+
yaml_model_from_api[k] = dict(params=api_model["model_version"]["output_info"].get("params"))
|
|
36
|
+
else:
|
|
37
|
+
yaml_model_from_api[k] = api_model.get(k)
|
|
38
|
+
yaml_model_from_api.update({"model_id": api_model.get("id")})
|
|
39
|
+
|
|
40
|
+
ignore_keys = {}
|
|
41
|
+
|
|
42
|
+
return is_dict_in_dict(yaml_model, yaml_model_from_api, ignore_keys)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def is_dict_in_dict(d1: Dict, d2: Dict, ignore_keys: Set = None) -> bool:
|
|
46
|
+
"""Compares two dicts recursively."""
|
|
47
|
+
for k, v in d1.items():
|
|
48
|
+
if ignore_keys and k in ignore_keys:
|
|
49
|
+
continue
|
|
50
|
+
if k not in d2:
|
|
51
|
+
return False
|
|
52
|
+
if isinstance(v, dict):
|
|
53
|
+
if not isinstance(d2[k], dict):
|
|
54
|
+
return False
|
|
55
|
+
return is_dict_in_dict(d1[k], d2[k], None)
|
|
56
|
+
elif v != d2[k]:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
return True
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from schema import And, Optional, Regex, Schema, SchemaError, Use
|
|
2
|
+
|
|
3
|
+
# Non-empty, up to 32-character ASCII strings with internal dashes and underscores.
|
|
4
|
+
_id_validator = And(str, lambda s: 0 < len(s) <= 48, Regex(r'^[0-9A-Za-z]+([-_][0-9A-Za-z]+)*$'))
|
|
5
|
+
|
|
6
|
+
# 32-character hex string, converted to lower-case.
|
|
7
|
+
_hex_id_validator = And(str, Use(str.lower), Regex(r'^[0-9a-f]{32}'))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _model_does_not_have_model_version_id_and_other_fields(m):
|
|
11
|
+
""" Validate that model does not have model_version_id and other model fields."""
|
|
12
|
+
if ('model_version_id' in m) and _model_has_other_fields(m):
|
|
13
|
+
raise SchemaError(f"model should not set model_version_id and other model fields: {m};"
|
|
14
|
+
f" please remove model_version_id or other model fields.")
|
|
15
|
+
return True
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _model_has_other_fields(m):
|
|
19
|
+
return any(k not in ['model_id', 'model_version_id'] for k in m.keys())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _workflow_nodes_have_valid_dependencies(nodes):
|
|
23
|
+
"""Validate that all inputs to a node are declared before it."""
|
|
24
|
+
node_ids = set()
|
|
25
|
+
for node in nodes:
|
|
26
|
+
for node_input in node.get("node_inputs", []):
|
|
27
|
+
if node_input["node_id"] not in node_ids:
|
|
28
|
+
raise SchemaError(f"missing input '{node_input['node_id']}' for node '{node['id']}'")
|
|
29
|
+
node_ids.add(node["id"])
|
|
30
|
+
|
|
31
|
+
return True
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_data_schema = Schema({
|
|
35
|
+
"workflow": {
|
|
36
|
+
"id":
|
|
37
|
+
_id_validator,
|
|
38
|
+
"nodes":
|
|
39
|
+
And(
|
|
40
|
+
len,
|
|
41
|
+
[{
|
|
42
|
+
"id":
|
|
43
|
+
And(str, len), # Node IDs are not validated as IDs by the API.
|
|
44
|
+
"model":
|
|
45
|
+
And({
|
|
46
|
+
"model_id": _id_validator,
|
|
47
|
+
Optional("app_id"): _id_validator,
|
|
48
|
+
Optional("user_id"): _id_validator,
|
|
49
|
+
Optional("model_version_id"): _hex_id_validator,
|
|
50
|
+
Optional("model_type_id"): _id_validator,
|
|
51
|
+
Optional("description"): str,
|
|
52
|
+
Optional("output_info"): {
|
|
53
|
+
Optional("params"): dict,
|
|
54
|
+
},
|
|
55
|
+
}, _model_does_not_have_model_version_id_and_other_fields),
|
|
56
|
+
Optional("node_inputs"):
|
|
57
|
+
And(len, [{
|
|
58
|
+
"node_id": And(str, len),
|
|
59
|
+
}]),
|
|
60
|
+
}],
|
|
61
|
+
_workflow_nodes_have_valid_dependencies),
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def validate(data):
|
|
67
|
+
return _data_schema.validate(data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: clarifai
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.9.0
|
|
4
4
|
Summary: Clarifai Python SDK
|
|
5
5
|
Home-page: https://github.com/Clarifai/clarifai-python
|
|
6
6
|
Author: Clarifai
|
|
@@ -18,6 +18,8 @@ Requires-Dist: tritonclient (==2.34.0)
|
|
|
18
18
|
Requires-Dist: packaging
|
|
19
19
|
Requires-Dist: tqdm (==4.64.1)
|
|
20
20
|
Requires-Dist: rich (==13.4.2)
|
|
21
|
+
Requires-Dist: PyYAML (==6.0.1)
|
|
22
|
+
Requires-Dist: schema (==0.7.5)
|
|
21
23
|
|
|
22
24
|
<h1 align="center">
|
|
23
25
|
<a href="https://www.clarifai.com/"><img alt="Clarifai" title="Clarifai" src="https://upload.wikimedia.org/wikipedia/commons/b/bc/Clarifai_Logo_FC_Web.png"></a>
|
|
@@ -51,7 +53,7 @@ This is the official Python client for interacting with our powerful [API](https
|
|
|
51
53
|
|
|
52
54
|
**Clarifai Community**: [https://clarifai.com/explore](https://clarifai.com/explore)
|
|
53
55
|
|
|
54
|
-
**Python SDK Docs**: [https://clarifai
|
|
56
|
+
**Python SDK Docs**: [https://docs.clarifai.com/python-sdk/api-reference](https://docs.clarifai.com/python-sdk/api-reference)
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
---
|
|
@@ -216,7 +218,23 @@ all_workflow = app.list_workflow()
|
|
|
216
218
|
# List all workflow in community filtered by description
|
|
217
219
|
all_face_community_workflows = App().list_workflows(filter_by={"query": "face"}, only_in_app=False) # Get all face related workflows
|
|
218
220
|
```
|
|
221
|
+
#### Workflow Create
|
|
222
|
+
Create a new workflow specified by a yaml config file.
|
|
223
|
+
```python
|
|
224
|
+
# Note: CLARIFAI_PAT must be set as env variable.
|
|
225
|
+
from clarifai.client.app import App
|
|
226
|
+
app = App(app_id="app_id", user_id="user_id")
|
|
227
|
+
workflow = app.create_workflow(config_filepath="config.yml")
|
|
228
|
+
```
|
|
219
229
|
|
|
230
|
+
#### Workflow Export
|
|
231
|
+
Export an existing workflow from Clarifai as a local yaml file.
|
|
232
|
+
```python
|
|
233
|
+
# Note: CLARIFAI_PAT must be set as env variable.
|
|
234
|
+
from clarifai.client.workflow import Workflow
|
|
235
|
+
workflow = Workflow("https://clarifai.com/clarifai/main/workflows/Demographics")
|
|
236
|
+
workflow.export('demographics_workflow.yml')
|
|
237
|
+
```
|
|
220
238
|
|
|
221
239
|
## More Examples
|
|
222
240
|
See many more code examples in this [repo](https://github.com/Clarifai/examples).
|