"""executor — runs ONE /v1/run inside a fresh child process (isolation).
Module-level so it is picklable for multiprocessing. execute_request returns an
envelope dict and never raises for 400/424/500; only a hard crash (segfault/OOM)
kills the child, which the parent sees as a nonzero exitcode -> 500.
Deployment constraint: MUST NOT import outside ttk.remote.server (no
ttk.core_modules, no ttk.remote) — the server deploys standalone on the XPU box.
Only stdlib + numpy + sibling server modules + lazy torch.
"""
import importlib
import inspect
import logging
import os
import sys
import time
from collections.abc import Callable as _Callable
from functools import partial
import numpy as np
try:
from . import execution_container as _ec
except ImportError:
import execution_container as _ec
(UnknownParamError, bind_params, format_device,
has_data, has_perf, match_params_v1, resolve_callable) = (
_ec.UnknownParamError, _ec.bind_params, _ec.format_device,
_ec.has_data, _ec.has_perf, _ec.match_params_v1, _ec.resolve_callable)
class _MissingSpecDependency(ImportError):
"""Spec-module import failed -> 424 (syncable), not 500."""
_TORCH_DEV_MODULE = None
_TP_ALIASES = {"tensorflow": "tf", "np": "numpy"}
def _api_from_kwargs(kwargs):
"""从请求 kwargs 取 client 携带的 api 标识(X-API 或 spec_class)。
用于 server-side 错误路径回传 X-API:成功路径用
executor 推导的 resolved api(api_label),错误路径只能回传 client 发来的原始值。
"""
return kwargs.get("api") or kwargs.get("spec_class")
def _ok(output_path, count, shapes, dtypes, perf, api=None, schema=None):
return {"ok": True, "http_status": 200, "output_path": output_path,
"output_count": count, "shapes": shapes, "dtypes": dtypes,
"perf": perf, "missing": None, "error": None, "api": api, "schema": schema}
def _err(status, error, missing=None, api=None):
return {"ok": False, "http_status": status, "output_path": None,
"output_count": 0, "shapes": [], "dtypes": [], "perf": None,
"missing": missing, "error": error, "api": api}
def _client_error(e) -> str:
"""Client-facing error string for a 500: exception TYPE + MESSAGE only.
Security (OWASP Improper Error Handling / OTG-ERR-002): the full traceback
carries server FS paths, line numbers and source lines, so it is logged
server-side only (logging.exception) and NEVER put in the wire envelope.
The exception message can still embed a path (residual risk, accepted). To
tighten the contract later (generic message, add error_id, ...), change ONLY
this function — both 500 exit points (execute_request, child_main) route
through it, so it is the single control point.
"""
return f"{type(e).__name__}: {e}"
def _resolve_attr_path(module, dotted):
obj = module
for part in dotted.split("."):
obj = getattr(obj, part)
return obj
def _torch_resolve(name):
"""Port of backup get_torch_func. Single snake-form arg.
Only invoked when provider == 'torch' (gated by _resolve_3party_api's
dispatch); a TF request never reaches this import.
"""
import torch
torch_funcs = {
"kl_div": torch.nn.functional.kl_div,
"ctc_loss_v2": torch.nn.functional.ctc_loss,
"equal": torch.eq,
"gelu": torch.nn.GELU(),
"floor_mod": torch.fmod,
"floor_div": partial(torch.div, rounding_mode='floor'),
"pows": torch.pow,
"real_div": torch.div,
"top_k_v2": torch.topk,
"select": torch.where,
"select_v2": torch.where,
"is_inf": torch.isinf,
"is_nan": torch.isnan,
"is_close": torch.isclose,
"is_finite": torch.isfinite,
}
if name in torch_funcs:
return torch_funcs[name]
sources = [torch, torch.ops.aten]
result = None
for source in sources:
try:
result = getattr(source, name, None)
except RuntimeError:
pass
if result is not None:
return result
if name.endswith("_d"):
result = _torch_resolve(name[:-2])
if name.endswith("_grad"):
result = _torch_resolve(name[:-5] + "_backward")
if name.endswith("_v2"):
result = _torch_resolve(name[:-3])
return result
def _tf_resolve(name):
"""Port of backup get_tf_func. Single Camel-form arg."""
import tensorflow as tf
from tensorflow.python.ops import gen_nn_ops, gen_state_ops, gen_array_ops
from tensorflow.python.ops import gen_math_ops, nn_impl, gen_bitwise_ops, gen_image_ops
from tensorflow import raw_ops
tf_func_map = {
"batch_norm_v3": nn_impl.fused_batch_norm,
"spence": tf.math.special.spence,
"space_to_batch": gen_array_ops.SpaceToBatch
}
if name in tf_func_map:
return tf_func_map[name]
sources = [tf, tf.math, gen_image_ops, gen_nn_ops, tf.nn, gen_state_ops,
gen_array_ops, gen_math_ops, nn_impl, gen_bitwise_ops, raw_ops]
try:
from tensorflow.python.training import training_ops
sources.append(training_ops)
except (ModuleNotFoundError, ImportError):
pass
try:
from tensorflow.python.ops import gen_stateless_random_ops_v2
sources.append(gen_stateless_random_ops_v2)
except (ModuleNotFoundError, ImportError):
pass
for source in sources:
result = getattr(source, name, None)
if result is not None:
return result
if "_d" in name:
r = _tf_resolve(name.replace("_d", ""))
if r is not None:
return r
if "_v2" in name:
r = _tf_resolve(name.replace("_v2", ""))
if r is not None:
return r
if "_v1" in name:
r = _tf_resolve(name.replace("_v1", ""))
if r is not None:
return r
if "_v3" in name:
r = _tf_resolve(name.replace("_v3", ""))
if r is not None:
return r
if "_1d" in name:
r = _tf_resolve(name.replace("_1d", "1d"))
if r is not None:
return r
if "_2d" in name:
r = _tf_resolve(name.replace("_2d", "2d"))
if r is not None:
return r
if "_3d" in name:
r = _tf_resolve(name.replace("_3d", "3d"))
if r is not None:
return r
if "_" in name:
r = _tf_resolve(name.replace("_", ""))
if r is not None:
return r
return None
def camel_to_snake(camel_name: str) -> str:
"""
Operator Registered Camel name convert to snake name.
Verbatim copy from ttk/utilities/string_utils.py:70 (do NOT import ttk.*).
"""
snake_name = ""
sub_head = False
name_list = list(camel_name)
for _idx, _char in enumerate(name_list):
if _char.islower():
sub_head = False
if _char.isdigit():
sub_head = True
if _char.isupper() and _idx != 0:
if not sub_head:
snake_name += "_"
sub_head = True
else:
_idx_next = _idx + 1
if _idx_next < len(name_list):
if name_list[_idx_next].islower():
snake_name += "_"
snake_name += _char
return snake_name.lower()
def _find_torch_api(torch_module, snake_name: str):
try:
torch_api = getattr(torch_module, snake_name, None)
except RuntimeError:
torch_api = None
if torch_api and isinstance(torch_api, _Callable):
return torch_api
else:
if snake_name.startswith("inplace_"):
return _find_torch_api(torch_module, snake_name[len("inplace_"):])
elif snake_name.endswith("_scalar"):
return _find_torch_api(torch_module, snake_name[:-len("_scalar")])
elif snake_name.endswith("_tensor"):
return _find_torch_api(torch_module, snake_name[:-len("_tensor")])
elif snake_name.endswith("_v2"):
return _find_torch_api(torch_module, snake_name[:-len("_v2")])
elif snake_name.endswith("s"):
return _find_torch_api(torch_module, snake_name[:-len("s")])
elif not snake_name.startswith("_"):
return _find_torch_api(torch_module, "_" + snake_name)
else:
return None
def _auto_import_from_torch(snake_name: str):
"""Search torch / torch.nn.functional / torch.ops.aten.
ACLNN-only path (provider=='torch'): sole caller is _aclnn_resolve; a TF
request never reaches here. Verbatim port of golden_generation.
"""
import torch
torch_api = _find_torch_api(torch, snake_name)
if torch_api is not None:
return torch_api
torch_nn_api = _find_torch_api(torch.nn.functional, snake_name)
if torch_nn_api is not None:
return torch_nn_api
return _find_torch_api(torch.ops.aten, snake_name)
def _aclnn_resolve(api_name):
"""ACLNN: strip 'aclnn' prefix + camel_to_snake + torch search.
Returns (callable, name) | (None, None). name = strip+snake 后的实际搜索 key
(如 aclnnAdd → add),供 _resolve_callable 拼 provider.name(如 torch.add)——
X-API 回传的是 server 实际执行的 API,不是请求的原始 op_name。
Port of golden_generation._import_golden_funcs's torch-search segment (NOT
the plugin lookup — that's not portable). The original returns (func, src);
here we return func plus the resolved snake key.
"""
if not api_name or not api_name.startswith("aclnn"):
return None, None
snake = camel_to_snake(api_name[5:])
f = _auto_import_from_torch(snake)
return (f, snake) if f is not None else (None, None)
def _resolve_3party_api(op_name, op_type, provider):
"""KERNEL/ACLNN: resolve op_name(snake)/op_type(Camel) -> (callable, name).
aclnn-prefixed op_name routes through _aclnn_resolve (strip + torch search),
returning (callable, op_name). Otherwise dual-form KERNEL port: try op_name
then op_type, first hit wins (tf benefits from Camel; torch resolves via
snake). name = 命中的搜索入参(op_name 或 op_type),非 callable 的
__name__(suffix stripping 脱钩)。Returns (None, None) if unresolvable.
"""
if op_name and op_name.startswith("aclnn"):
return _aclnn_resolve(op_name)
resolve = _torch_resolve if provider == "torch" else _tf_resolve
for name in (op_name, op_type):
if not name:
continue
f = resolve(name)
if f is not None:
return f, name
return None, None
def _resolve_callable(exec_type, provider, api, op_name, op_type, spec_module,
spec_class):
"""Resolve the callable to execute. Returns (callable, api_label).
api_label = 实际执行 api 的标识,供 X-API response header 回传:
- spec 模式: spec_class(dotted 路径,e.g. "MySpec.AddSpec")
- api 模式 (explicit api): api 字符串本身(e.g. "torch.nn.functional.softmax")
- api=None 推导模式: provider.命中name(e.g. "torch.add"、"tf.Relu"、"torch.aclnnAdd")
"""
if exec_type == "spec":
if not spec_class:
raise ValueError("spec mode requires spec_class")
try:
mod = importlib.import_module(spec_module)
except ImportError as e:
name = getattr(e, "name", None) or spec_module
err = _MissingSpecDependency(name)
err.name = name
raise err from e
cls = _resolve_attr_path(mod, spec_class) if spec_class else mod
tp = getattr(cls, "third_party", None)
if tp is None:
return cls, spec_class
if isinstance(tp, str):
return resolve_callable(tp), spec_class
if isinstance(tp, dict):
tp = {_TP_ALIASES.get(k, k): v for k, v in tp.items()}
if provider not in tp:
raise ValueError(f"provider '{provider}' not in third_party")
v = tp[provider]
return (resolve_callable(v) if isinstance(v, str) else v), spec_class
raise ValueError("unsupported third_party format")
if api:
return resolve_callable(api), api
f, name = _resolve_3party_api(op_name, op_type, provider)
if f is None:
raise ValueError(
f"cannot resolve api for op_name={op_name!r} op_type={op_type!r} "
f"provider={provider!r}")
return f, f"{provider}.{name}"
def _bind(func, named, attrs, device, warn_leftover=True):
merged = dict(attrs or {})
merged.update(named)
return bind_params(func, merged, device=device, warn_leftover=warn_leftover)
def _invoke(callable_fn, named, attrs, provider, device_id, use_device,
profile=None):
"""Run the callable once, return RAW outputs (no numpy cast)."""
if inspect.isclass(callable_fn):
cls = callable_fn
dev = format_device(provider, profile,
"cpu" if not use_device else device_id)
if cls.__init__ is object.__init__:
inst = cls()
else:
ia, ik = _bind(cls.__init__, named, attrs, dev, warn_leftover=False)
inst = cls(*ia, **ik)
ca, ck = _bind(inst.__call__, named, attrs, dev, warn_leftover=False)
return inst(*ca, **ck)
try:
return callable_fn(**dict(named, **(attrs or {})))
except TypeError:
try:
return callable_fn(*list(named.values()), **(attrs or {}))
except TypeError:
sig = inspect.signature(callable_fn)
param_names = [
p for p, v in sig.parameters.items()
if v.kind not in (inspect.Parameter.VAR_KEYWORD,
inspect.Parameter.VAR_POSITIONAL)
and p != 'self'
]
vals = list(named.values())
if len(vals) > len(param_names):
raise TypeError(
f"{getattr(callable_fn, '__name__', callable_fn)} expects "
f"{len(param_names)} params {param_names}, "
f"got {len(vals)} inputs {list(named.keys())}")
return callable_fn(**dict(zip(param_names, vals), **(attrs or {})))
def _to_numpy_pair(v, provider):
"""Convert one output to (numpy_array, semantic_dtype_name), provider-aware.
bfloat16 can't round-trip through numpy savez, so it's stored as raw int16
bits with 'bfloat16' declared (client reinterprets). float8 likewise has no
numpy storage class — stored as raw uint8 bits with the float8 dtype name
declared. torch is imported ONLY for the torch path; tf/other paths never
touch it (tf bfloat16 ships a numpy bf16 dtype via
tensorflow.bfloat16.as_numpy_dtype).
"""
if provider == "torch":
try:
import torch
if isinstance(v, torch.Tensor):
if v.dtype == torch.bfloat16:
return v.contiguous().view(torch.int16).cpu().numpy(), "bfloat16"
if "float8" in str(v.dtype):
return v.contiguous().view(torch.uint8).cpu().numpy(), str(v.dtype).replace("torch.", "")
return v.detach().cpu().numpy(), str(v.dtype).replace("torch.", "")
except ImportError:
pass
a = np.asarray(v)
dt = a.dtype.name
if "bfloat16" in dt or "bf16" in dt:
return np.ascontiguousarray(a).view(np.int16), "bfloat16"
return a, dt
def _outputs_to_numpy(outputs, provider):
"""保留嵌套(不 flatten)。遍历顶层 slots,构造 schema。
返回 (schema, arrays_叶子)。schema: [{index|indices|null, dtype}, ...]"""
if not isinstance(outputs, (list, tuple)):
outputs = [outputs]
schema = []
arrays = []
npz_idx = 0
for slot in outputs:
if slot is None:
schema.append({"index": None, "dtype": None})
continue
if isinstance(slot, (list, tuple)):
leaves = []
dt = None
for leaf in slot:
arr, dt = _to_numpy_pair(leaf, provider)
arrays.append(arr)
leaves.append(npz_idx)
npz_idx += 1
schema.append({"indices": leaves, "dtype": dt})
else:
arr, dt = _to_numpy_pair(slot, provider)
arrays.append(arr)
schema.append({"index": npz_idx, "dtype": dt})
npz_idx += 1
return schema, arrays
def _to_vendor_tensor(value, provider, device_str, dtype_name=None):
"""Framework H2D: numpy input -> provider tensor on device.
Inputs arrive as numpy (restored from the tmp_in savez). torch/tf callables
need tensors, so convert before binding. ``dtype_name`` is the dtype declared
in X-Input-Schema by the client (whose numpy knows the real dtype) — used to
convert dtypes the server's numpy can't represent (bfloat16) without guessing.
None / lists recurse; non-numpy / unknown provider pass through.
"""
if value is None:
return None
if isinstance(value, (list, tuple)):
return type(value)(
_to_vendor_tensor(v, provider, device_str, dtype_name) for v in value)
if provider == "torch":
try:
import torch
except ImportError:
return value
if isinstance(value, torch.Tensor):
return value.to(device_str)
if isinstance(value, np.ndarray):
if dtype_name == "bfloat16":
return torch.from_numpy(
value.view(np.int16)).view(torch.bfloat16).to(device_str)
return torch.from_numpy(value).to(device_str)
return value
if provider == "tf":
try:
import tensorflow as tf
if dtype_name == "bfloat16" and isinstance(value, np.ndarray):
value = value.view(tf.bfloat16.as_numpy_dtype)
if device_str and device_str != "cpu":
with tf.device(device_str):
return tf.convert_to_tensor(value)
return tf.convert_to_tensor(value)
except ImportError:
return value
return value
def _device_available(provider, profile) -> bool:
"""Provider-aware device availability — never imports torch for a tf request.
profile-driven: torch -> getattr(torch, torch_lib).is_available();
tf -> tf_type = profile.get("tf_device_type"); False when tf_type is missing
(graceful degrade, NOT a config error — distinguishes from format_device's
ValueError on the same missing field). The torch_{lib} extension is imported
upstream in execute_request before this is reached.
"""
if provider == "torch":
try:
import torch
except ImportError:
return False
lib_name = profile.get("torch_lib")
if lib_name is None:
return False
dev = _TORCH_DEV_MODULE if _TORCH_DEV_MODULE is not None else getattr(torch, lib_name, None)
if dev is None:
return False
return dev.is_available()
if provider == "tf":
tf_type = profile.get("tf_device_type")
if tf_type is None:
return False
try:
import tensorflow as tf
return bool(tf.config.list_physical_devices(tf_type))
except ImportError:
return False
return False
def _device_time(evt, device) -> float:
"""server inline 2-candidate self_ time. is not None guard
(not truthiness) so a genuine 0.0 on candidate1 is NOT skipped."""
v = getattr(evt, "self_device_time_total", None)
if v is not None:
return v
return getattr(evt, f"self_{device}_time_total", 0.0)
def _run_perf(callable_fn, named, attrs, provider, device_id, use_device,
profile=None, runtime=3):
"""PERF timing via profiler (torch: Self device time; TF: xplane.pb device plane).
Two passes: (1) profiler pass for device_us (no empty_cache); (2) peak pass
for peak_memory_mb (separate, reset_peak then re-invoke). device_us is the
per-iteration average over ``runtime`` active iterations (μs, 3 sig figs).
CPU / no-device -> device_us=NA. Device EXCLUSIVITY is enforced by the PARENT
(it holds a lock around this child); this function does NOT lock. reset_peak
is per-device global, so the parent's lock keeps concurrent PERF from
corrupting it.
"""
profile = profile or {}
perf = {"device_us": "NA", "peak_memory_mb": "NA"}
torch_dev = use_device and provider == "torch" and _device_available(provider, profile)
tf_dev = use_device and provider == "tf" and _device_available(provider, profile)
outputs = None
device_us = 0.0
if torch_dev:
outputs, device_us = _torch_profiler_pass(
callable_fn, named, attrs, provider, device_id, use_device,
profile, runtime)
elif tf_dev:
outputs, device_us = _tf_profiler_pass(
callable_fn, named, attrs, provider, device_id, use_device,
profile, runtime)
else:
outputs = _invoke(callable_fn, named, attrs, provider, device_id,
use_device, profile=profile)
if device_us > 0:
perf["device_us"] = float(f"{device_us:.3g}")
if torch_dev:
try:
lib = _TORCH_DEV_MODULE
lib.reset_peak_memory_stats(device_id)
_invoke(callable_fn, named, attrs, provider, device_id, use_device,
profile=profile)
lib.synchronize()
perf["peak_memory_mb"] = (
lib.max_memory_allocated(device_id) / 1e6)
except Exception:
logging.exception("torch peak_memory measure failed; peak_memory_mb=NA")
elif tf_dev:
try:
import tensorflow as tf
tf_device = f"{profile['tf_device_type']}:{device_id}"
tf.config.experimental.reset_memory_stats(tf_device)
_invoke(callable_fn, named, attrs, provider, device_id, use_device,
profile=profile)
info = tf.config.experimental.get_memory_info(tf_device)
perf["peak_memory_mb"] = info.get("peak", 0) / 1e6
except Exception:
logging.exception("tf peak_memory measure failed; peak_memory_mb=NA")
return outputs, perf
def _torch_profiler_pass(callable_fn, named, attrs, provider, device_id,
use_device, profile, runtime):
"""torch.profiler Self device time. Returns (outputs, device_us).
device_us = per-iteration average (sum of self_device_time_total over the
active window / runtime). torch 2.7+ renamed self_cuda_time_total ->
self_device_time_total; the 2-candidate _device_time helper covers both.
Error policy: an OP execution failure PROPAGATES (-> execute_request FAIL) —
it must never be masked as device_us=NA + PASS. Only PROFILER machinery
(start / stop / key_averages readout) degrades to NA, and each such failure
is logged so it is debuggable server-side. A misconfigured profile (missing
torch_profiler/activities or an unknown activity enum) raises RuntimeError
OUTSIDE the try block (server config error -> 500, not an NA degrade).
"""
import torch
device = profile["torch_lib"]
try:
activities_cfg = profile["torch_profiler"]["activities"]
except KeyError:
raise RuntimeError("profile missing torch_profiler/activities")
activities = []
for name in activities_cfg:
try:
activities.append(getattr(torch.profiler.ProfilerActivity, name))
except AttributeError:
raise RuntimeError(f"unknown ProfilerActivity: {name}")
device_us = 0.0
outputs = None
try:
_ctx = torch.profiler.profile(
activities=activities,
schedule=torch.profiler.schedule(
wait=1, warmup=1, active=runtime, repeat=1))
prof = _ctx.__enter__()
except Exception:
logging.exception("torch.profiler start failed; device_us=NA")
return outputs, device_us
try:
for _ in range(runtime + 2):
outputs = _invoke(callable_fn, named, attrs, provider,
device_id, use_device, profile=profile)
prof.step()
finally:
try:
_ctx.__exit__(None, None, None)
except Exception:
logging.exception("torch.profiler stop failed")
try:
total = sum(_device_time(e, device) for e in prof.key_averages())
if runtime > 0:
total /= runtime
device_us = total
except Exception:
logging.exception("torch.profiler key_averages failed; device_us=NA")
return outputs, device_us
def _tf_profiler_pass(callable_fn, named, attrs, provider, device_id,
use_device, profile, runtime):
"""tf.profiler.experimental + xplane.pb. Returns (outputs, device_us).
device_us = per-iteration average (sum of ev.duration_ps/1e6 over the device
plane events / runtime). num_occurrences is NOT multiplied (proto3 unset = 0).
Two warmup invokes precede the profiled runtime invokes. logdir is removed in
a finally guard so a stop() crash still cleans up.
Error policy: an OP execution failure PROPAGATES (-> execute_request FAIL).
Only TF profiler machinery (start / stop / xplane parse) degrades to NA, and
each such failure is logged so it is debuggable server-side.
"""
import tempfile, shutil
from pathlib import Path
try:
from tensorflow.core.profiler.protobuf import xplane_pb2
except ImportError:
xplane_pb2 = None
device_us = 0.0
outputs = None
logdir = tempfile.mkdtemp(prefix="tfprof_")
try:
import tensorflow as tf
for _ in range(2):
_invoke(callable_fn, named, attrs, provider, device_id, use_device,
profile=profile)
try:
tf.profiler.experimental.start(logdir)
except Exception:
logging.exception("tf.profiler start failed; device_us=NA")
return outputs, device_us
try:
for _ in range(runtime):
outputs = _invoke(callable_fn, named, attrs, provider, device_id,
use_device, profile=profile)
finally:
try:
tf.profiler.experimental.stop()
except Exception:
logging.exception("tf.profiler stop failed")
pb_path = next(Path(logdir).rglob("*.xplane.pb"), None)
if pb_path and xplane_pb2:
try:
xs = xplane_pb2.XSpace()
xs.ParseFromString(pb_path.read_bytes())
total = 0.0
_TF_XPLANE_EVENT_CAP = 100000
_seen = 0
_truncated = False
for plane in xs.planes:
if plane.name.startswith("/device"):
for line in plane.lines:
for ev in line.events:
if _seen >= _TF_XPLANE_EVENT_CAP:
_truncated = True
break
total += ev.duration_ps / 1e6
_seen += 1
if _truncated:
break
if _truncated:
break
if _truncated:
logging.warning(
"TF xplane event count exceeded %d, truncating",
_TF_XPLANE_EVENT_CAP)
if runtime > 0:
total /= runtime
device_us = total
except Exception:
logging.exception("tf xplane parse failed; device_us=NA")
finally:
shutil.rmtree(logdir, ignore_errors=True)
return outputs, device_us
def execute_request(*, tenant_sync_dir, exec_type, provider, api, spec_module,
spec_class, mode, input_schema, attrs, tmp_in_path,
input_count, device_id, use_device, output_dir,
profile=None, op_name=None, op_type=None, runtime=3,
**_extra):
"""Run one request. Returns an envelope dict (never raises for 4xx/5xx)."""
try:
if tenant_sync_dir and tenant_sync_dir not in sys.path:
sys.path.insert(0, tenant_sync_dir)
importlib.invalidate_caches()
if use_device and provider == "torch" and profile:
lib = profile.get("torch_lib")
if lib is None:
raise RuntimeError("profile missing torch_lib")
import torch
if not hasattr(torch, lib):
try:
importlib.import_module(f"torch_{lib}")
except ModuleNotFoundError as e:
raise RuntimeError(f"torch backend '{lib}' unavailable: {e}")
global _TORCH_DEV_MODULE
_TORCH_DEV_MODULE = getattr(torch, lib)
named = {}
if input_count and tmp_in_path:
npz = np.load(tmp_in_path)
flat = [npz[f"a{i}"] for i in range(input_count)]
named = match_params_v1(input_schema, flat)
device_str = format_device(provider, profile,
"cpu" if not use_device else device_id)
_dtypes = {e.get("name"): e.get("dtype") for e in (input_schema or [])}
named = {k: _to_vendor_tensor(v, provider, device_str, _dtypes.get(k))
for k, v in named.items()}
callable_fn, api_label = _resolve_callable(
exec_type, provider, api, op_name, op_type, spec_module, spec_class)
if has_perf(mode):
raw_outputs, perf = _run_perf(callable_fn, named, attrs or {},
provider, device_id, use_device,
profile=profile, runtime=runtime)
else:
raw_outputs = _invoke(callable_fn, named, attrs or {},
provider, device_id, use_device,
profile=profile)
perf = None
schema, outs = _outputs_to_numpy(raw_outputs, provider)
if has_data(mode):
path = os.path.join(output_dir, "out.npz")
np.savez_compressed(path, **{f"a{i}": o for i, o in enumerate(outs)})
return _ok(path, len(schema), [list(o.shape) for o in outs],
dtypes=None, perf=perf, api=api_label, schema=schema)
return _ok(None, 0, [], [], perf=perf, api=api_label, schema=[])
except _MissingSpecDependency as e:
logging.info("spec dependency missing: %s (awaiting client sync)", e.name)
return _err(424, f"missing spec dependency: {e}", missing=e.name,
api=api or spec_class)
except ImportError as e:
logging.exception("request failed: import error (api=%s)", api or spec_class)
return _err(500, f"import failed: {e}", api=api or spec_class)
except (UnknownParamError, ValueError) as e:
logging.warning("request failed: bad params (api=%s): %s",
api or spec_class, e)
return _err(400, str(e), api=api or spec_class)
except Exception as e:
logging.exception("request failed (api=%s)", api or spec_class)
return _err(500, _client_error(e), api=api or spec_class)
def child_main(conn, kwargs):
"""Child-process entry point: run execute_request, send the envelope back.
A hard crash (segfault/OOM) kills the child before conn.send -> the parent
sees a nonzero exitcode and no message -> 500.
"""
os.environ.update(kwargs.pop("env", {}))
try:
conn.send(execute_request(**kwargs))
except Exception as e:
logging.exception("child_main: execute_request raised (api=%s)",
_api_from_kwargs(kwargs))
conn.send(_err(500, _client_error(e), api=_api_from_kwargs(kwargs)))
finally:
conn.close()