clarifai 11.6.4rc2__py3-none-any.whl → 11.6.6__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/__init__.py +1 -1
- clarifai/cli/README.md +39 -0
- clarifai/cli/base.py +119 -118
- clarifai/cli/model.py +116 -86
- clarifai/client/app.py +1 -1
- clarifai/client/auth/helper.py +7 -2
- clarifai/client/base.py +35 -8
- clarifai/client/compute_cluster.py +1 -1
- clarifai/client/model.py +3 -1
- clarifai/client/model_client.py +20 -1
- clarifai/client/nodepool.py +4 -2
- clarifai/client/user.py +4 -2
- clarifai/runners/models/model_runner.py +55 -0
- clarifai/runners/server.py +23 -1
- clarifai/runners/utils/model_utils.py +4 -4
- clarifai/runners/utils/url_fetcher.py +51 -12
- clarifai/utils/cli.py +148 -13
- clarifai/utils/constants.py +5 -0
- clarifai/utils/misc.py +47 -0
- {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/METADATA +2 -2
- clarifai-11.6.6.dist-info/RECORD +127 -0
- {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/WHEEL +1 -1
- clarifai/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/__pycache__/errors.cpython-311.pyc +0 -0
- clarifai/__pycache__/errors.cpython-39.pyc +0 -0
- clarifai/__pycache__/versions.cpython-311.pyc +0 -0
- clarifai/__pycache__/versions.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/base.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/compute_cluster.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/deployment.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/model.cpython-39.pyc +0 -0
- clarifai/cli/__pycache__/nodepool.cpython-39.pyc +0 -0
- clarifai/cli/model_templates.py +0 -243
- clarifai/cli/pipeline_step_templates.py +0 -64
- clarifai/client/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/app.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/app.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/base.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/base.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/compute_cluster.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/dataset.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/dataset.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/deployment.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/deployment.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/input.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/input.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/lister.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/lister.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/model.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/model.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/model_client.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/model_client.cpython-39.pyc +0 -0
- clarifai/client/__pycache__/module.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/nodepool.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/runner.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/search.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/user.cpython-311.pyc +0 -0
- clarifai/client/__pycache__/workflow.cpython-311.pyc +0 -0
- clarifai/client/auth/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/client/auth/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/client/auth/__pycache__/helper.cpython-311.pyc +0 -0
- clarifai/client/auth/__pycache__/helper.cpython-39.pyc +0 -0
- clarifai/client/auth/__pycache__/register.cpython-311.pyc +0 -0
- clarifai/client/auth/__pycache__/register.cpython-39.pyc +0 -0
- clarifai/client/auth/__pycache__/stub.cpython-311.pyc +0 -0
- clarifai/client/auth/__pycache__/stub.cpython-39.pyc +0 -0
- clarifai/constants/__pycache__/base.cpython-311.pyc +0 -0
- clarifai/constants/__pycache__/base.cpython-39.pyc +0 -0
- clarifai/constants/__pycache__/dataset.cpython-311.pyc +0 -0
- clarifai/constants/__pycache__/dataset.cpython-39.pyc +0 -0
- clarifai/constants/__pycache__/input.cpython-311.pyc +0 -0
- clarifai/constants/__pycache__/input.cpython-39.pyc +0 -0
- clarifai/constants/__pycache__/model.cpython-311.pyc +0 -0
- clarifai/constants/__pycache__/model.cpython-39.pyc +0 -0
- clarifai/constants/__pycache__/search.cpython-311.pyc +0 -0
- clarifai/constants/__pycache__/workflow.cpython-311.pyc +0 -0
- clarifai/datasets/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/datasets/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/datasets/export/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/datasets/export/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/datasets/export/__pycache__/inputs_annotations.cpython-311.pyc +0 -0
- clarifai/datasets/export/__pycache__/inputs_annotations.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/base.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/base.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/features.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/features.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/image.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/image.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/multimodal.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/multimodal.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/text.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/text.cpython-39.pyc +0 -0
- clarifai/datasets/upload/__pycache__/utils.cpython-311.pyc +0 -0
- clarifai/datasets/upload/__pycache__/utils.cpython-39.pyc +0 -0
- clarifai/models/model_serving/README.md +0 -158
- clarifai/models/model_serving/__init__.py +0 -14
- clarifai/models/model_serving/cli/__init__.py +0 -12
- clarifai/models/model_serving/cli/_utils.py +0 -53
- clarifai/models/model_serving/cli/base.py +0 -14
- clarifai/models/model_serving/cli/build.py +0 -79
- clarifai/models/model_serving/cli/clarifai_clis.py +0 -33
- clarifai/models/model_serving/cli/create.py +0 -171
- clarifai/models/model_serving/cli/example_cli.py +0 -34
- clarifai/models/model_serving/cli/login.py +0 -26
- clarifai/models/model_serving/cli/upload.py +0 -179
- clarifai/models/model_serving/constants.py +0 -21
- clarifai/models/model_serving/docs/cli.md +0 -161
- clarifai/models/model_serving/docs/concepts.md +0 -229
- clarifai/models/model_serving/docs/dependencies.md +0 -11
- clarifai/models/model_serving/docs/inference_parameters.md +0 -139
- clarifai/models/model_serving/docs/model_types.md +0 -19
- clarifai/models/model_serving/model_config/__init__.py +0 -16
- clarifai/models/model_serving/model_config/base.py +0 -369
- clarifai/models/model_serving/model_config/config.py +0 -312
- clarifai/models/model_serving/model_config/inference_parameter.py +0 -129
- clarifai/models/model_serving/model_config/model_types_config/multimodal-embedder.yaml +0 -25
- clarifai/models/model_serving/model_config/model_types_config/text-classifier.yaml +0 -19
- clarifai/models/model_serving/model_config/model_types_config/text-embedder.yaml +0 -20
- clarifai/models/model_serving/model_config/model_types_config/text-to-image.yaml +0 -19
- clarifai/models/model_serving/model_config/model_types_config/text-to-text.yaml +0 -19
- clarifai/models/model_serving/model_config/model_types_config/visual-classifier.yaml +0 -22
- clarifai/models/model_serving/model_config/model_types_config/visual-detector.yaml +0 -32
- clarifai/models/model_serving/model_config/model_types_config/visual-embedder.yaml +0 -19
- clarifai/models/model_serving/model_config/model_types_config/visual-segmenter.yaml +0 -19
- clarifai/models/model_serving/model_config/output.py +0 -133
- clarifai/models/model_serving/model_config/triton/__init__.py +0 -14
- clarifai/models/model_serving/model_config/triton/serializer.py +0 -136
- clarifai/models/model_serving/model_config/triton/triton_config.py +0 -182
- clarifai/models/model_serving/model_config/triton/wrappers.py +0 -281
- clarifai/models/model_serving/repo_build/__init__.py +0 -14
- clarifai/models/model_serving/repo_build/build.py +0 -198
- clarifai/models/model_serving/repo_build/static_files/_requirements.txt +0 -2
- clarifai/models/model_serving/repo_build/static_files/base_test.py +0 -169
- clarifai/models/model_serving/repo_build/static_files/inference.py +0 -26
- clarifai/models/model_serving/repo_build/static_files/sample_clarifai_config.yaml +0 -25
- clarifai/models/model_serving/repo_build/static_files/test.py +0 -40
- clarifai/models/model_serving/repo_build/static_files/triton/model.py +0 -75
- clarifai/models/model_serving/utils.py +0 -23
- clarifai/runners/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/runners/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/runners/models/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/runners/models/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/runners/models/__pycache__/mcp_class.cpython-311.pyc +0 -0
- clarifai/runners/models/__pycache__/model_builder.cpython-311.pyc +0 -0
- clarifai/runners/models/__pycache__/model_builder.cpython-39.pyc +0 -0
- clarifai/runners/models/__pycache__/model_class.cpython-311.pyc +0 -0
- clarifai/runners/models/__pycache__/model_runner.cpython-311.pyc +0 -0
- clarifai/runners/models/__pycache__/openai_class.cpython-311.pyc +0 -0
- clarifai/runners/models/base_typed_model.py +0 -238
- clarifai/runners/models/model_upload.py +0 -607
- clarifai/runners/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/runners/utils/__pycache__/code_script.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/code_script.cpython-39.pyc +0 -0
- clarifai/runners/utils/__pycache__/const.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/data_utils.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/data_utils.cpython-39.pyc +0 -0
- clarifai/runners/utils/__pycache__/loader.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/method_signatures.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/model_utils.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/openai_convertor.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/serializers.cpython-311.pyc +0 -0
- clarifai/runners/utils/__pycache__/url_fetcher.cpython-311.pyc +0 -0
- clarifai/runners/utils/data_handler.py +0 -231
- clarifai/runners/utils/data_types/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/runners/utils/data_types/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/runners/utils/data_types/__pycache__/data_types.cpython-311.pyc +0 -0
- clarifai/runners/utils/data_types/__pycache__/data_types.cpython-39.pyc +0 -0
- clarifai/runners/utils/data_types.py +0 -471
- clarifai/runners/utils/temp.py +0 -59
- clarifai/schema/__pycache__/search.cpython-311.pyc +0 -0
- clarifai/urls/__pycache__/helper.cpython-311.pyc +0 -0
- clarifai/urls/__pycache__/helper.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/__init__.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/cli.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/config.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/config.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/constants.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/constants.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/logging.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/logging.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/misc.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/misc.cpython-39.pyc +0 -0
- clarifai/utils/__pycache__/model_train.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/protobuf.cpython-311.pyc +0 -0
- clarifai/utils/__pycache__/protobuf.cpython-39.pyc +0 -0
- clarifai/workflows/__pycache__/__init__.cpython-311.pyc +0 -0
- clarifai/workflows/__pycache__/export.cpython-311.pyc +0 -0
- clarifai/workflows/__pycache__/utils.cpython-311.pyc +0 -0
- clarifai/workflows/__pycache__/validate.cpython-311.pyc +0 -0
- clarifai-11.6.4rc2.dist-info/RECORD +0 -301
- {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/entry_points.txt +0 -0
- {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/licenses/LICENSE +0 -0
- {clarifai-11.6.4rc2.dist-info → clarifai-11.6.6.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import time
|
1
2
|
from typing import Iterator
|
2
3
|
|
3
4
|
from clarifai_grpc.grpc.api import service_pb2
|
@@ -6,6 +7,8 @@ from clarifai_protocol import BaseRunner
|
|
6
7
|
from clarifai_protocol.utils.health import HealthProbeRequestHandler
|
7
8
|
|
8
9
|
from clarifai.client.auth.helper import ClarifaiAuthHelper
|
10
|
+
from clarifai.utils.constants import STATUS_FAIL, STATUS_MIXED, STATUS_OK, STATUS_UNKNOWN
|
11
|
+
from clarifai.utils.logging import get_req_id_from_context, logger
|
9
12
|
|
10
13
|
from ..utils.url_fetcher import ensure_urls_downloaded
|
11
14
|
from .model_class import ModelClass
|
@@ -106,6 +109,20 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
106
109
|
raise Exception("Unexpected work item type: {}".format(runner_item))
|
107
110
|
request = runner_item.post_model_outputs_request
|
108
111
|
ensure_urls_downloaded(request, auth_helper=self._auth_helper)
|
112
|
+
start_time = time.time()
|
113
|
+
req_id = get_req_id_from_context()
|
114
|
+
status_str = STATUS_UNKNOWN
|
115
|
+
# Endpoint is always POST /v2/.../outputs for this runner
|
116
|
+
endpoint = "POST /v2/.../outputs "
|
117
|
+
|
118
|
+
# if method_name == '_GET_SIGNATURES' then the request is for getting signatures and we don't want to log it.
|
119
|
+
# This is a workaround to avoid logging the _GET_SIGNATURES method call.
|
120
|
+
method_name = None
|
121
|
+
logging = True
|
122
|
+
if len(request.inputs) > 0 and '_method_name' in request.inputs[0].data.metadata:
|
123
|
+
method_name = request.inputs[0].data.metadata['_method_name']
|
124
|
+
if method_name == '_GET_SIGNATURES':
|
125
|
+
logging = False
|
109
126
|
|
110
127
|
resp = self.model.predict_wrapper(request)
|
111
128
|
# if we have any non-successful code already it's an error we can return.
|
@@ -113,6 +130,9 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
113
130
|
resp.status.code != status_code_pb2.SUCCESS
|
114
131
|
and resp.status.code != status_code_pb2.ZERO
|
115
132
|
):
|
133
|
+
status_str = f"{resp.status.code} ERROR"
|
134
|
+
duration_ms = (time.time() - start_time) * 1000
|
135
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
116
136
|
return service_pb2.RunnerItemOutput(multi_output_response=resp)
|
117
137
|
successes = []
|
118
138
|
for output in resp.outputs:
|
@@ -126,18 +146,24 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
126
146
|
code=status_code_pb2.SUCCESS,
|
127
147
|
description="Success",
|
128
148
|
)
|
149
|
+
status_str = STATUS_OK
|
129
150
|
elif any(successes):
|
130
151
|
status = status_pb2.Status(
|
131
152
|
code=status_code_pb2.MIXED_STATUS,
|
132
153
|
description="Mixed Status",
|
133
154
|
)
|
155
|
+
status_str = STATUS_MIXED
|
134
156
|
else:
|
135
157
|
status = status_pb2.Status(
|
136
158
|
code=status_code_pb2.FAILURE,
|
137
159
|
description="Failed",
|
138
160
|
)
|
161
|
+
status_str = STATUS_FAIL
|
139
162
|
|
140
163
|
resp.status.CopyFrom(status)
|
164
|
+
if logging:
|
165
|
+
duration_ms = (time.time() - start_time) * 1000
|
166
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
141
167
|
return service_pb2.RunnerItemOutput(multi_output_response=resp)
|
142
168
|
|
143
169
|
def runner_item_generate(
|
@@ -150,12 +176,21 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
150
176
|
request = runner_item.post_model_outputs_request
|
151
177
|
ensure_urls_downloaded(request, auth_helper=self._auth_helper)
|
152
178
|
|
179
|
+
# --- Live logging additions ---
|
180
|
+
start_time = time.time()
|
181
|
+
req_id = get_req_id_from_context()
|
182
|
+
status_str = STATUS_UNKNOWN
|
183
|
+
endpoint = "POST /v2/.../outputs/generate"
|
184
|
+
|
153
185
|
for resp in self.model.generate_wrapper(request):
|
154
186
|
# if we have any non-successful code already it's an error we can return.
|
155
187
|
if (
|
156
188
|
resp.status.code != status_code_pb2.SUCCESS
|
157
189
|
and resp.status.code != status_code_pb2.ZERO
|
158
190
|
):
|
191
|
+
status_str = f"{resp.status.code} ERROR"
|
192
|
+
duration_ms = (time.time() - start_time) * 1000
|
193
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
159
194
|
yield service_pb2.RunnerItemOutput(multi_output_response=resp)
|
160
195
|
continue
|
161
196
|
successes = []
|
@@ -170,30 +205,44 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
170
205
|
code=status_code_pb2.SUCCESS,
|
171
206
|
description="Success",
|
172
207
|
)
|
208
|
+
status_str = STATUS_OK
|
173
209
|
elif any(successes):
|
174
210
|
status = status_pb2.Status(
|
175
211
|
code=status_code_pb2.MIXED_STATUS,
|
176
212
|
description="Mixed Status",
|
177
213
|
)
|
214
|
+
status_str = STATUS_MIXED
|
178
215
|
else:
|
179
216
|
status = status_pb2.Status(
|
180
217
|
code=status_code_pb2.FAILURE,
|
181
218
|
description="Failed",
|
182
219
|
)
|
220
|
+
status_str = STATUS_FAIL
|
183
221
|
resp.status.CopyFrom(status)
|
184
222
|
|
185
223
|
yield service_pb2.RunnerItemOutput(multi_output_response=resp)
|
186
224
|
|
225
|
+
duration_ms = (time.time() - start_time) * 1000
|
226
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
227
|
+
|
187
228
|
def runner_item_stream(
|
188
229
|
self, runner_item_iterator: Iterator[service_pb2.RunnerItem]
|
189
230
|
) -> Iterator[service_pb2.RunnerItemOutput]:
|
190
231
|
# Call the generate() method the underlying model implements.
|
232
|
+
start_time = time.time()
|
233
|
+
req_id = get_req_id_from_context()
|
234
|
+
status_str = STATUS_UNKNOWN
|
235
|
+
endpoint = "POST /v2/.../outputs/stream "
|
236
|
+
|
191
237
|
for resp in self.model.stream_wrapper(pmo_iterator(runner_item_iterator)):
|
192
238
|
# if we have any non-successful code already it's an error we can return.
|
193
239
|
if (
|
194
240
|
resp.status.code != status_code_pb2.SUCCESS
|
195
241
|
and resp.status.code != status_code_pb2.ZERO
|
196
242
|
):
|
243
|
+
status_str = f"{resp.status.code} ERROR"
|
244
|
+
duration_ms = (time.time() - start_time) * 1000
|
245
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
197
246
|
yield service_pb2.RunnerItemOutput(multi_output_response=resp)
|
198
247
|
continue
|
199
248
|
successes = []
|
@@ -208,20 +257,26 @@ class ModelRunner(BaseRunner, HealthProbeRequestHandler):
|
|
208
257
|
code=status_code_pb2.SUCCESS,
|
209
258
|
description="Success",
|
210
259
|
)
|
260
|
+
status_str = STATUS_OK
|
211
261
|
elif any(successes):
|
212
262
|
status = status_pb2.Status(
|
213
263
|
code=status_code_pb2.MIXED_STATUS,
|
214
264
|
description="Mixed Status",
|
215
265
|
)
|
266
|
+
status_str = STATUS_MIXED
|
216
267
|
else:
|
217
268
|
status = status_pb2.Status(
|
218
269
|
code=status_code_pb2.FAILURE,
|
219
270
|
description="Failed",
|
220
271
|
)
|
272
|
+
status_str = STATUS_FAIL
|
221
273
|
resp.status.CopyFrom(status)
|
222
274
|
|
223
275
|
yield service_pb2.RunnerItemOutput(multi_output_response=resp)
|
224
276
|
|
277
|
+
duration_ms = (time.time() - start_time) * 1000
|
278
|
+
logger.info(f"{endpoint} | {status_str} | {duration_ms:.2f}ms | req_id={req_id}")
|
279
|
+
|
225
280
|
|
226
281
|
def pmo_iterator(runner_item_iterator, auth_helper=None):
|
227
282
|
for runner_item in runner_item_iterator:
|
clarifai/runners/server.py
CHANGED
@@ -92,6 +92,7 @@ def serve(
|
|
92
92
|
runner_id: str = os.environ.get("CLARIFAI_RUNNER_ID", None),
|
93
93
|
base_url: str = os.environ.get("CLARIFAI_API_BASE", "https://api.clarifai.com"),
|
94
94
|
pat: str = os.environ.get("CLARIFAI_PAT", None),
|
95
|
+
context=None, # This is the current context object that contains user_id, app_id, model_id, etc.
|
95
96
|
):
|
96
97
|
builder = ModelBuilder(model_path, download_validation_only=True)
|
97
98
|
|
@@ -133,7 +134,28 @@ def serve(
|
|
133
134
|
pat=pat,
|
134
135
|
num_parallel_polls=num_threads,
|
135
136
|
)
|
136
|
-
|
137
|
+
method_signatures = builder.get_method_signatures(mocking=False)
|
138
|
+
from clarifai.runners.utils import code_script
|
139
|
+
|
140
|
+
snippet = code_script.generate_client_script(
|
141
|
+
method_signatures,
|
142
|
+
user_id=context.user_id,
|
143
|
+
app_id=context.app_id,
|
144
|
+
model_id=context.model_id,
|
145
|
+
deployment_id=context.deployment_id,
|
146
|
+
base_url=context.api_base,
|
147
|
+
)
|
148
|
+
logger.info("✅ Your model is running locally and is ready for requests from the API...\n")
|
149
|
+
logger.info(
|
150
|
+
f"> Code Snippet: To call your model via the API, use this code snippet:\n{snippet}"
|
151
|
+
)
|
152
|
+
logger.info(
|
153
|
+
f"> Playground: To chat with your model, visit: {context.ui}/playground?model={context.model_id}__{context.model_version_id}&user_id={context.user_id}&app_id={context.app_id}\n"
|
154
|
+
)
|
155
|
+
logger.info(
|
156
|
+
f"> API URL: To call your model via the API, use this model URL: {context.ui}/users/{context.user_id}/apps/{context.app_id}/models/{context.model_id}\n"
|
157
|
+
)
|
158
|
+
logger.info("Press CTRL+C to stop the runner.\n")
|
137
159
|
runner.start() # start the runner to fetch work from the API.
|
138
160
|
|
139
161
|
|
@@ -68,13 +68,13 @@ def kill_process_tree(parent_pid, include_parent: bool = True, skip_pid: int = N
|
|
68
68
|
logger.warning(f"Failed to kill parent process {parent_pid}: {e}")
|
69
69
|
|
70
70
|
|
71
|
-
def execute_shell_command(
|
72
|
-
command: str,
|
73
|
-
) -> subprocess.Popen:
|
71
|
+
def execute_shell_command(command: str, stdout=None, stderr=subprocess.STDOUT) -> subprocess.Popen:
|
74
72
|
"""Execute a shell command and return its process handle.
|
75
73
|
|
76
74
|
Args:
|
77
75
|
command (str): The shell command to execute.
|
76
|
+
stdout : Verbose logging control,
|
77
|
+
stderr : Verbose error logging control
|
78
78
|
|
79
79
|
Returns:
|
80
80
|
subprocess.Popen: Process handle for the executed command.
|
@@ -90,7 +90,7 @@ def execute_shell_command(
|
|
90
90
|
parts = shlex.split(command)
|
91
91
|
|
92
92
|
try:
|
93
|
-
process = subprocess.Popen(parts, text=True, stderr=
|
93
|
+
process = subprocess.Popen(parts, text=True, stdout=stdout, stderr=stderr)
|
94
94
|
|
95
95
|
return process
|
96
96
|
except subprocess.SubprocessError as e:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import concurrent.futures
|
2
2
|
|
3
3
|
import fsspec
|
4
|
+
import requests
|
4
5
|
|
5
6
|
from clarifai.utils.logging import logger
|
6
7
|
|
@@ -12,6 +13,28 @@ def download_input(input, auth_helper=None):
|
|
12
13
|
_download_input_data(input.data.parts[i].data, auth_helper=auth_helper)
|
13
14
|
|
14
15
|
|
16
|
+
def _download_with_handling(url, mode, auth_kwargs, setter, media_type):
|
17
|
+
fsspec_exceptions = (
|
18
|
+
getattr(fsspec.exceptions, 'FSTimeoutError', Exception),
|
19
|
+
getattr(fsspec.exceptions, 'BlocksizeMismatchError', Exception),
|
20
|
+
)
|
21
|
+
try:
|
22
|
+
with fsspec.open(url, mode, **auth_kwargs) as f:
|
23
|
+
setter(f.read())
|
24
|
+
except fsspec_exceptions as e:
|
25
|
+
logger.error(f"FSSpec error downloading {media_type} from {url}: {e}")
|
26
|
+
raise RuntimeError(f"FSSpec error downloading {media_type} from {url}: {e}") from e
|
27
|
+
except requests.RequestException as e:
|
28
|
+
logger.error(f"Requests error downloading {media_type} from {url}: {e}")
|
29
|
+
raise RuntimeError(f"Requests error downloading {media_type} from {url}: {e}") from e
|
30
|
+
except (IOError, OSError) as e:
|
31
|
+
logger.error(f"IO error downloading {media_type} from {url}: {e}")
|
32
|
+
raise RuntimeError(f"IO error downloading {media_type} from {url}: {e}") from e
|
33
|
+
except Exception as e:
|
34
|
+
logger.error(f"Unexpected error downloading {media_type} from {url}: {e}")
|
35
|
+
raise RuntimeError(f"Unexpected error downloading {media_type} from {url}: {e}") from e
|
36
|
+
|
37
|
+
|
15
38
|
def _download_input_data(input_data, auth_helper=None):
|
16
39
|
"""
|
17
40
|
This function will download any urls that are not already bytes.
|
@@ -26,21 +49,37 @@ def _download_input_data(input_data, auth_helper=None):
|
|
26
49
|
auth_kwargs = _get_auth_kwargs(auth_helper)
|
27
50
|
|
28
51
|
if input_data.image.url and not input_data.image.base64:
|
29
|
-
|
30
|
-
|
31
|
-
|
52
|
+
_download_with_handling(
|
53
|
+
input_data.image.url,
|
54
|
+
'rb',
|
55
|
+
auth_kwargs,
|
56
|
+
lambda val: setattr(input_data.image, 'base64', val),
|
57
|
+
'image',
|
58
|
+
)
|
32
59
|
if input_data.video.url and not input_data.video.base64:
|
33
|
-
|
34
|
-
|
35
|
-
|
60
|
+
_download_with_handling(
|
61
|
+
input_data.video.url,
|
62
|
+
'rb',
|
63
|
+
auth_kwargs,
|
64
|
+
lambda val: setattr(input_data.video, 'base64', val),
|
65
|
+
'video',
|
66
|
+
)
|
36
67
|
if input_data.audio.url and not input_data.audio.base64:
|
37
|
-
|
38
|
-
|
39
|
-
|
68
|
+
_download_with_handling(
|
69
|
+
input_data.audio.url,
|
70
|
+
'rb',
|
71
|
+
auth_kwargs,
|
72
|
+
lambda val: setattr(input_data.audio, 'base64', val),
|
73
|
+
'audio',
|
74
|
+
)
|
40
75
|
if input_data.text.url and not input_data.text.raw:
|
41
|
-
|
42
|
-
|
43
|
-
|
76
|
+
_download_with_handling(
|
77
|
+
input_data.text.url,
|
78
|
+
'r',
|
79
|
+
auth_kwargs,
|
80
|
+
lambda val: setattr(input_data.text, 'raw', val),
|
81
|
+
'text',
|
82
|
+
)
|
44
83
|
|
45
84
|
|
46
85
|
def _get_auth_kwargs(auth_helper):
|
clarifai/utils/cli.py
CHANGED
@@ -4,6 +4,7 @@ import pkgutil
|
|
4
4
|
import sys
|
5
5
|
import typing as t
|
6
6
|
from collections import defaultdict
|
7
|
+
from pathlib import Path
|
7
8
|
from typing import OrderedDict
|
8
9
|
|
9
10
|
import click
|
@@ -205,18 +206,152 @@ def validate_context_auth(pat: str, user_id: str, api_base: str = None):
|
|
205
206
|
logger.info("✅ Context is valid")
|
206
207
|
|
207
208
|
except Exception as e:
|
208
|
-
error_msg = str(e)
|
209
|
-
|
210
209
|
# Check for common authentication errors and provide user-friendly messages
|
211
|
-
|
212
|
-
logger.error(f"Invalid PAT token or incorrect user ID '{user_id}': {error_msg}")
|
213
|
-
elif "UNAUTHENTICATED" in error_msg:
|
214
|
-
logger.error(f"Invalid PAT token or user ID: {error_msg}")
|
215
|
-
elif "SSL" in error_msg or "certificate" in error_msg:
|
216
|
-
logger.error(f"SSL/Certificate error: {error_msg}")
|
217
|
-
elif "Connection" in error_msg or "timeout" in error_msg:
|
218
|
-
logger.error(f"Network connection error: {error_msg}")
|
219
|
-
else:
|
220
|
-
logger.error(f"❌ Validation failed: \n{error_msg}")
|
221
|
-
logger.error("Please check your credentials and try again.")
|
210
|
+
logger.error("❌ Authentication failed. Please check your token and user ID.")
|
222
211
|
raise click.Abort() # Exit without saving the configuration
|
212
|
+
|
213
|
+
|
214
|
+
def customize_ollama_model(
|
215
|
+
model_path, model_name=None, port=None, context_length=None, verbose=False
|
216
|
+
):
|
217
|
+
"""Customize the Ollama model name in the cloned template files.
|
218
|
+
Args:
|
219
|
+
model_path: Path to the cloned model directory
|
220
|
+
model_name: The model name to set (e.g., 'llama3.1', 'mistral') - optional
|
221
|
+
port: Port for Ollama server - optional
|
222
|
+
context_length: Context length for the model - optional
|
223
|
+
verbose: Whether to enable verbose logging - optional (defaults to False)
|
224
|
+
|
225
|
+
"""
|
226
|
+
model_py_path = os.path.join(model_path, "1", "model.py")
|
227
|
+
|
228
|
+
if not os.path.exists(model_py_path):
|
229
|
+
logger.warning(f"Model file {model_py_path} not found, skipping model name customization")
|
230
|
+
return
|
231
|
+
|
232
|
+
try:
|
233
|
+
# Read the model.py file
|
234
|
+
with open(model_py_path, 'r') as file:
|
235
|
+
content = file.read()
|
236
|
+
if model_name:
|
237
|
+
# Replace the default model name in the load_model method
|
238
|
+
content = content.replace(
|
239
|
+
'self.model = os.environ.get("OLLAMA_MODEL_NAME", \'llama3.2\')',
|
240
|
+
f'self.model = os.environ.get("OLLAMA_MODEL_NAME", \'{model_name}\')',
|
241
|
+
)
|
242
|
+
|
243
|
+
if port:
|
244
|
+
# Replace the default port variable in the model.py file
|
245
|
+
content = content.replace("PORT = '23333'", f"PORT = '{port}'")
|
246
|
+
|
247
|
+
if context_length:
|
248
|
+
# Replace the default context length variable in the model.py file
|
249
|
+
content = content.replace(
|
250
|
+
"context_length = '8192'", f"context_length = '{context_length}'"
|
251
|
+
)
|
252
|
+
|
253
|
+
verbose_str = str(verbose)
|
254
|
+
if "VERBOSE_OLLAMA = True" in content:
|
255
|
+
content = content.replace("VERBOSE_OLLAMA = True", f"VERBOSE_OLLAMA = {verbose_str}")
|
256
|
+
elif "VERBOSE_OLLAMA = False" in content:
|
257
|
+
content = content.replace("VERBOSE_OLLAMA = False", f"VERBOSE_OLLAMA = {verbose_str}")
|
258
|
+
|
259
|
+
# Write the modified content back to model.py
|
260
|
+
with open(model_py_path, 'w') as file:
|
261
|
+
file.write(content)
|
262
|
+
|
263
|
+
except Exception as e:
|
264
|
+
logger.error(f"Failed to customize Ollama model name in {model_py_path}: {e}")
|
265
|
+
raise
|
266
|
+
|
267
|
+
|
268
|
+
def check_ollama_installed():
|
269
|
+
"""Check if the Ollama CLI is installed."""
|
270
|
+
try:
|
271
|
+
import subprocess
|
272
|
+
|
273
|
+
result = subprocess.run(
|
274
|
+
['ollama', '--version'], capture_output=True, text=True, check=False
|
275
|
+
)
|
276
|
+
if result.returncode == 0:
|
277
|
+
return True
|
278
|
+
else:
|
279
|
+
return False
|
280
|
+
except FileNotFoundError:
|
281
|
+
return False
|
282
|
+
|
283
|
+
|
284
|
+
def _is_package_installed(package_name):
|
285
|
+
"""Helper function to check if a single package in requirements.txt is installed."""
|
286
|
+
import importlib.metadata
|
287
|
+
|
288
|
+
try:
|
289
|
+
importlib.metadata.distribution(package_name)
|
290
|
+
logger.debug(f"✅ {package_name} - installed")
|
291
|
+
return True
|
292
|
+
except importlib.metadata.PackageNotFoundError:
|
293
|
+
logger.debug(f"❌ {package_name} - not installed")
|
294
|
+
return False
|
295
|
+
except Exception as e:
|
296
|
+
logger.warning(f"Error checking {package_name}: {e}")
|
297
|
+
return False
|
298
|
+
|
299
|
+
|
300
|
+
def parse_requirements(model_path: str):
|
301
|
+
"""Parse requirements.txt in the model directory and return a dictionary of dependencies."""
|
302
|
+
from packaging.requirements import Requirement
|
303
|
+
|
304
|
+
requirements_path = Path(model_path) / "requirements.txt"
|
305
|
+
|
306
|
+
if not requirements_path.exists():
|
307
|
+
logger.warning(f"requirements.txt not found at {requirements_path}")
|
308
|
+
return []
|
309
|
+
|
310
|
+
deps = {}
|
311
|
+
for line in requirements_path.read_text().splitlines():
|
312
|
+
line = line.strip()
|
313
|
+
if not line or line.startswith("#"):
|
314
|
+
continue
|
315
|
+
try:
|
316
|
+
req = Requirement(line)
|
317
|
+
deps[req.name] = str(req.specifier) if req.specifier else None
|
318
|
+
except Exception as e:
|
319
|
+
logger.warning(f"⚠️ Could not parse line: {line!r} — {e}")
|
320
|
+
return deps
|
321
|
+
|
322
|
+
|
323
|
+
def check_requirements_installed(model_path):
|
324
|
+
"""Check if all dependencies in requirements.txt are installed."""
|
325
|
+
|
326
|
+
try:
|
327
|
+
# Getting package name and version (for logging)
|
328
|
+
requirements = parse_requirements(model_path)
|
329
|
+
|
330
|
+
if not requirements:
|
331
|
+
logger.info("No dependencies found in requirements.txt")
|
332
|
+
return True
|
333
|
+
|
334
|
+
logger.info(f"Checking {len(requirements)} dependencies...")
|
335
|
+
|
336
|
+
missing = [
|
337
|
+
full_req
|
338
|
+
for package_name, full_req in requirements.items()
|
339
|
+
if not _is_package_installed(package_name)
|
340
|
+
]
|
341
|
+
|
342
|
+
if not missing:
|
343
|
+
logger.info(f"✅ All {len(requirements)} dependencies are installed!")
|
344
|
+
return True
|
345
|
+
|
346
|
+
# Report missing packages
|
347
|
+
logger.error(
|
348
|
+
f"❌ {len(missing)} of {len(requirements)} required packages are missing in the current environment"
|
349
|
+
)
|
350
|
+
logger.error("\n".join(f" - {pkg}" for pkg in missing))
|
351
|
+
requirements_path = Path(model_path) / "requirements.txt"
|
352
|
+
logger.warning(f"To install: pip install -r {requirements_path}")
|
353
|
+
return False
|
354
|
+
|
355
|
+
except Exception as e:
|
356
|
+
logger.error(f"Failed to check requirements: {e}")
|
357
|
+
return False
|
clarifai/utils/constants.py
CHANGED
@@ -64,3 +64,8 @@ DEFAULT_LOCAL_RUNNER_NODEPOOL_CONFIG = {
|
|
64
64
|
}
|
65
65
|
DEFAULT_OLLAMA_MODEL_REPO = "https://github.com/Clarifai/runners-examples"
|
66
66
|
DEFAULT_OLLAMA_MODEL_REPO_BRANCH = "ollama"
|
67
|
+
|
68
|
+
STATUS_OK = "200 OK"
|
69
|
+
STATUS_MIXED = "207 MIXED"
|
70
|
+
STATUS_FAIL = "500 FAIL"
|
71
|
+
STATUS_UNKNOWN = "UNKNOWN"
|
clarifai/utils/misc.py
CHANGED
@@ -78,6 +78,53 @@ def get_from_env(key: str, env_key: str) -> str:
|
|
78
78
|
)
|
79
79
|
|
80
80
|
|
81
|
+
def get_from_dict_env_or_config(key: str, env_key: str, **data) -> str:
|
82
|
+
"""Get a value from a dictionary, environment variable, or CLI config context."""
|
83
|
+
# First try the provided data/kwargs
|
84
|
+
if key in data and data[key]:
|
85
|
+
return data[key]
|
86
|
+
|
87
|
+
# Then try environment variables
|
88
|
+
if env_key in os.environ and os.environ[env_key]:
|
89
|
+
return os.environ[env_key]
|
90
|
+
|
91
|
+
# Finally try CLI config context as fallback
|
92
|
+
try:
|
93
|
+
from clarifai.utils.config import Config
|
94
|
+
from clarifai.utils.constants import DEFAULT_CONFIG
|
95
|
+
|
96
|
+
config = Config.from_yaml(filename=DEFAULT_CONFIG)
|
97
|
+
current_context = config.current
|
98
|
+
|
99
|
+
# Convert env_key to the attribute name expected by Context
|
100
|
+
# e.g., CLARIFAI_PAT -> pat, CLARIFAI_USER_ID -> user_id, CLARIFAI_API_BASE -> api_base
|
101
|
+
if env_key == "CLARIFAI_PAT":
|
102
|
+
attr_name = "pat"
|
103
|
+
elif env_key == "CLARIFAI_USER_ID":
|
104
|
+
attr_name = "user_id"
|
105
|
+
elif env_key == "CLARIFAI_API_BASE":
|
106
|
+
attr_name = "api_base"
|
107
|
+
else:
|
108
|
+
# For other cases, convert CLARIFAI_SOMETHING to something
|
109
|
+
attr_name = env_key.replace("CLARIFAI_", "").lower()
|
110
|
+
|
111
|
+
if hasattr(current_context, attr_name):
|
112
|
+
value = getattr(current_context, attr_name)
|
113
|
+
if value:
|
114
|
+
return value
|
115
|
+
except Exception:
|
116
|
+
# If CLI config loading fails, fall through to raise error
|
117
|
+
pass
|
118
|
+
|
119
|
+
# If all methods fail, raise an error suggesting clarifai login
|
120
|
+
raise UserError(
|
121
|
+
f"Configuration Required. Could not find '{key}'. Please provide it in one of the following ways:\n\n"
|
122
|
+
f"- Pass '{key}' as a named parameter to your function.\n"
|
123
|
+
f"- Set the {env_key} environment variable in your environment.\n"
|
124
|
+
f"- Run `clarifai login` in your terminal to configure CLI authentication."
|
125
|
+
)
|
126
|
+
|
127
|
+
|
81
128
|
def concept_relations_accumulation(
|
82
129
|
relations_dict: Dict[str, Any], subject_concept: str, object_concept: str, predicate: str
|
83
130
|
) -> Dict[str, Any]:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: clarifai
|
3
|
-
Version: 11.6.
|
3
|
+
Version: 11.6.6
|
4
4
|
Home-page: https://github.com/Clarifai/clarifai-python
|
5
5
|
Author: Clarifai
|
6
6
|
Author-email: support@clarifai.com
|
@@ -19,7 +19,7 @@ Classifier: Operating System :: OS Independent
|
|
19
19
|
Requires-Python: >=3.8
|
20
20
|
Description-Content-Type: text/markdown
|
21
21
|
License-File: LICENSE
|
22
|
-
Requires-Dist: clarifai-grpc>=11.6.
|
22
|
+
Requires-Dist: clarifai-grpc>=11.6.6
|
23
23
|
Requires-Dist: clarifai-protocol>=0.0.25
|
24
24
|
Requires-Dist: numpy>=1.22.0
|
25
25
|
Requires-Dist: tqdm>=4.65.0
|