urun-cli 0.7.0__tar.gz → 0.7.1__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.
- {urun_cli-0.7.0 → urun_cli-0.7.1}/CHANGELOG.md +11 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/PKG-INFO +1 -1
- {urun_cli-0.7.0 → urun_cli-0.7.1}/pyproject.toml +1 -1
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/cli.py +22 -1
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/errors.py +54 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/retry.py +18 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/rust.py +7 -3
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/serve.py +49 -7
- {urun_cli-0.7.0 → urun_cli-0.7.1}/.gitignore +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/LICENSE +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/README.md +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/SECURITY.md +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/hatch_build.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/__init__.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/_bin/README.md +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/api.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/buildlogs.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/catalog.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/client_tokens.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/config.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/deps.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/dev.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/devconsole.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/devstream.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/discovery.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/fleet.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/frontend_launch.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/launch_auth.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/manifest.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/nodes.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/openai_proxy_bin.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/repo_deps.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/runtime_health.py +0 -0
- {urun_cli-0.7.0 → urun_cli-0.7.1}/src/urun/scratch.py +0 -0
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## 0.7.1
|
|
2
|
+
|
|
3
|
+
- `urun serve --warm N` — an idle replica floor on a serve deploy (the
|
|
4
|
+
catalog's `warm` marker for platform-operated shared endpoints; absent =
|
|
5
|
+
the unchanged scale-to-zero default). Pins `URUN_SERVE_WARM` into the
|
|
6
|
+
generated shim so the bundled app's `@app.function(warm=...)` reads it.
|
|
7
|
+
- `urun serve --exo-class / --exo-image` — pin a deploy's provider on the EXO rail.
|
|
8
|
+
- Deploy Python and Rust functions in one app release.
|
|
9
|
+
- Compat: bundle the proxy with the queued session identity; stop reporting
|
|
10
|
+
an unconverged roll as ready and stop dropping the server's error message.
|
|
11
|
+
|
|
1
12
|
## 0.6.3
|
|
2
13
|
|
|
3
14
|
- Bump the vendored `urun-openai` binary pin to `@urun-sh/openai@0.5.0` — the
|
|
@@ -20,7 +20,7 @@ name = "urun-cli"
|
|
|
20
20
|
# so every merge remains on the already-published version until this value is
|
|
21
21
|
# advanced. Wiring semantic-release into CI remains separate work; this patch
|
|
22
22
|
# bump is the minimal release unblock (post-v0.6.3 fixes + the Rust feature minor).
|
|
23
|
-
version = "0.7.
|
|
23
|
+
version = "0.7.1"
|
|
24
24
|
description = "End-user CLI for deploying apps to urun"
|
|
25
25
|
readme = "README.md"
|
|
26
26
|
requires-python = ">=3.11"
|
|
@@ -2675,7 +2675,7 @@ def deploy_once(args: argparse.Namespace) -> DeployOutcome:
|
|
|
2675
2675
|
# showing" and hides a broken build-log path (owner, 2026-07-17).
|
|
2676
2676
|
if not tailer.emitted_any:
|
|
2677
2677
|
print(f"{BUILD_LOG_PREFIX}{NO_BUILD_LOGS_NOTE}")
|
|
2678
|
-
watch_rollout(
|
|
2678
|
+
rollout = watch_rollout(
|
|
2679
2679
|
client,
|
|
2680
2680
|
app=app_name,
|
|
2681
2681
|
manifest_hash=mh,
|
|
@@ -2683,6 +2683,27 @@ def deploy_once(args: argparse.Namespace) -> DeployOutcome:
|
|
|
2683
2683
|
on_phase=lambda phase: print(f" rollout: {phase}"),
|
|
2684
2684
|
)
|
|
2685
2685
|
mark_phase("roll-wait")
|
|
2686
|
+
# A ROLL THAT DID NOT CONVERGE IS NOT A READY DEPLOYMENT. watch_rollout's
|
|
2687
|
+
# verdict used to be discarded, so a `timeout` printed its own honest
|
|
2688
|
+
# "still not converged" line and was then immediately contradicted by
|
|
2689
|
+
# "Deployment ready" and exit 0 -- the one signal automation reads.
|
|
2690
|
+
# Live receipt, dev-usw2 2026-09-09 `urun serve qwen3.6-27b:bf16`:
|
|
2691
|
+
# `rollout: still not converged after 300s` followed by
|
|
2692
|
+
# `Deployment ready`, exit 0, while the app sat at
|
|
2693
|
+
# `no_ready_gpu_units`. A CI job gating on the exit code called that a
|
|
2694
|
+
# successful deploy. `skipped` (no presence signal for this app at all)
|
|
2695
|
+
# stays exit 0: nothing was observed, so nothing is contradicted.
|
|
2696
|
+
if rollout == "timeout":
|
|
2697
|
+
print(format_phase_timing(phases))
|
|
2698
|
+
print(
|
|
2699
|
+
"Deployment NOT confirmed ready: the build succeeded and the "
|
|
2700
|
+
"roll continues server-side, but this command did not observe "
|
|
2701
|
+
"the new release serving before its deadline. Re-check with "
|
|
2702
|
+
"`urun app status`, or pass --no-wait to finalize without "
|
|
2703
|
+
"waiting.",
|
|
2704
|
+
file=sys.stderr,
|
|
2705
|
+
)
|
|
2706
|
+
return DeployOutcome(exit_code=1, manifest_hash=mh, deps=deps, status=status)
|
|
2686
2707
|
print(format_phase_timing(phases))
|
|
2687
2708
|
print_ready(status)
|
|
2688
2709
|
return DeployOutcome(exit_code=0, manifest_hash=mh, deps=deps, status=status)
|
|
@@ -31,6 +31,55 @@ MAX_ERROR_BODY_CHARS = 500
|
|
|
31
31
|
ENDPOINT_HINT = "the endpoint may not be deployed on this server yet"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
# ── credential redaction ──────────────────────────────────────────────────────
|
|
35
|
+
#
|
|
36
|
+
# Server-supplied error text is echoed to terminals and CI logs (the exception
|
|
37
|
+
# string itself, and urun.retry.describe_error's retry line). A PROXIED upstream
|
|
38
|
+
# error can carry credential material verbatim -- a signed URL's query token, an
|
|
39
|
+
# echoed Authorization header, a JWT. Whitespace normalisation and truncation do
|
|
40
|
+
# not prevent it reaching a log, so redact before any of it is rendered.
|
|
41
|
+
#
|
|
42
|
+
# Patterns are TARGETED, never "anything long and hex": a build id is 64 hex
|
|
43
|
+
# chars and is exactly the value an operator needs to keep reading. Redacting it
|
|
44
|
+
# would trade a credential leak for an unusable diagnostic.
|
|
45
|
+
_REDACTIONS: tuple[tuple[re.Pattern[str], str], ...] = (
|
|
46
|
+
# urun API keys, both shapes (cli.API_KEY_RE).
|
|
47
|
+
(re.compile(r"\burun_(?:[0-9a-f]{32}|sk_[A-Za-z0-9_-]{43})\b"), "urun_[redacted]"),
|
|
48
|
+
# Authorization headers echoed back by a proxy.
|
|
49
|
+
(
|
|
50
|
+
re.compile(r"\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]{8,}", re.IGNORECASE),
|
|
51
|
+
r"\1 [redacted]",
|
|
52
|
+
),
|
|
53
|
+
# JWTs (three base64url segments), wherever they appear.
|
|
54
|
+
(
|
|
55
|
+
re.compile(r"\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}"),
|
|
56
|
+
"[redacted-jwt]",
|
|
57
|
+
),
|
|
58
|
+
# Signed-URL and query-string secrets: ?token=, &sig=, &X-Amz-Signature=, ...
|
|
59
|
+
(
|
|
60
|
+
re.compile(
|
|
61
|
+
r"([?&](?:[A-Za-z0-9_-]*(?:token|signature|sig|secret|password|api[_-]?key|"
|
|
62
|
+
r"access[_-]?key|credential)[A-Za-z0-9_-]*)=)[^&\s\"']+",
|
|
63
|
+
re.IGNORECASE,
|
|
64
|
+
),
|
|
65
|
+
r"\1[redacted]",
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def redact_credentials(text: str) -> str:
|
|
71
|
+
"""Strip credential material from server-supplied text before it is logged.
|
|
72
|
+
|
|
73
|
+
Applied at :class:`ApiError` construction so EVERY render of a server
|
|
74
|
+
message -- the exception string, ``describe_error``'s retry line, anything
|
|
75
|
+
downstream -- is safe by construction rather than by each caller
|
|
76
|
+
remembering.
|
|
77
|
+
"""
|
|
78
|
+
for pattern, replacement in _REDACTIONS:
|
|
79
|
+
text = pattern.sub(replacement, text)
|
|
80
|
+
return text
|
|
81
|
+
|
|
82
|
+
|
|
34
83
|
def sanitize_error_text(text: str, limit: int = MAX_ERROR_BODY_CHARS) -> str:
|
|
35
84
|
"""Make server-supplied text safe to print: no ANSI/control chars, bounded.
|
|
36
85
|
|
|
@@ -74,6 +123,11 @@ class ApiError(UrunError):
|
|
|
74
123
|
method: str | None = None,
|
|
75
124
|
url: str | None = None,
|
|
76
125
|
):
|
|
126
|
+
# Redact FIRST: `message` (and a signed `url`) are server-supplied and
|
|
127
|
+
# end up in this exception's string, in retry lines, and in CI logs.
|
|
128
|
+
message = redact_credentials(message)
|
|
129
|
+
if url:
|
|
130
|
+
url = redact_credentials(url)
|
|
77
131
|
detail = f"{status} {code}: {message}"
|
|
78
132
|
if method and url:
|
|
79
133
|
detail += f" ({method} {url})"
|
|
@@ -157,9 +157,27 @@ def retry_call(
|
|
|
157
157
|
sleep(wait)
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
#: Cap for the server message echoed by :func:`describe_error`. Long enough for
|
|
161
|
+
#: a real upstream diagnosis ("cube load failed: upstream status 503"), short
|
|
162
|
+
#: enough that a retry line stays one line.
|
|
163
|
+
_DESCRIBE_DETAIL_MAX = 160
|
|
164
|
+
|
|
165
|
+
|
|
160
166
|
def describe_error(exc: BaseException) -> str:
|
|
161
167
|
"""Short, honest one-line description of a transient failure for logging."""
|
|
162
168
|
if isinstance(exc, ApiError):
|
|
169
|
+
# The server's MESSAGE is the diagnosis; the code alone is only a label.
|
|
170
|
+
# Dropping it turned a 30-second read into an investigation: a `urun
|
|
171
|
+
# serve` on dev-usw2 2026-09-09 retried the build-log fetch 7 times
|
|
172
|
+
# showing `HTTP 502 logs_upstream_error` and gave up, while the very
|
|
173
|
+
# same response body said `cube load failed: upstream status 503` --
|
|
174
|
+
# i.e. the log backend had no healthy upstream at all. Bounded so a
|
|
175
|
+
# verbose server message cannot turn a retry line into a paragraph.
|
|
176
|
+
detail = " ".join((exc.message or "").split())
|
|
177
|
+
if detail:
|
|
178
|
+
if len(detail) > _DESCRIBE_DETAIL_MAX:
|
|
179
|
+
detail = detail[: _DESCRIBE_DETAIL_MAX - 1].rstrip() + "\u2026"
|
|
180
|
+
return f"HTTP {exc.status} {exc.code}: {detail}"
|
|
163
181
|
return f"HTTP {exc.status} {exc.code}"
|
|
164
182
|
if isinstance(exc, urllib.error.HTTPError):
|
|
165
183
|
return f"HTTP {exc.code} {exc.reason}"
|
|
@@ -909,11 +909,15 @@ def _resolve_symlink(logical: Path, ws_root: Path, rel: str) -> Path:
|
|
|
909
909
|
def _dir_ignored(rel: str, name: str, patterns: list[str]) -> bool:
|
|
910
910
|
if rel in _BUILTIN_EXCLUDED_DIRS or name in _BUILTIN_EXCLUDED_DIRS:
|
|
911
911
|
return True
|
|
912
|
-
# gitignore-style directory patterns ("docs/") match the directory itself
|
|
912
|
+
# gitignore-style directory patterns ("docs/") match the directory itself;
|
|
913
|
+
# a descendant pattern ("cache/**", "cache/*") ignores every entry under
|
|
914
|
+
# the directory, so the directory is skipped as a whole.
|
|
913
915
|
for pat in patterns:
|
|
914
916
|
base = pat.rstrip("/")
|
|
915
917
|
if fnmatch.fnmatch(rel, base) or fnmatch.fnmatch(name, base):
|
|
916
918
|
return True
|
|
919
|
+
if fnmatch.fnmatch(rel + "/", pat):
|
|
920
|
+
return True
|
|
917
921
|
return False
|
|
918
922
|
|
|
919
923
|
|
|
@@ -938,6 +942,8 @@ def _collect_files(ws_root: Path, patterns: list[str]) -> dict[str, Path]:
|
|
|
938
942
|
f"{rel}: '.urun' is generated exclusively by the builder; "
|
|
939
943
|
"remove it from the project before deploying"
|
|
940
944
|
)
|
|
945
|
+
if _dir_ignored(rel, d, patterns):
|
|
946
|
+
continue # an ignored tree never ships, symlinked or not
|
|
941
947
|
if logical.is_symlink():
|
|
942
948
|
target = _resolve_symlink(logical, ws_root, rel)
|
|
943
949
|
raise UrunError(
|
|
@@ -945,8 +951,6 @@ def _collect_files(ws_root: Path, patterns: list[str]) -> dict[str, Path]:
|
|
|
945
951
|
f"(only file symlinks inside the workspace ship; {rel} resolves "
|
|
946
952
|
f"to {target})"
|
|
947
953
|
)
|
|
948
|
-
if _dir_ignored(rel, d, patterns):
|
|
949
|
-
continue
|
|
950
954
|
keep.append(d)
|
|
951
955
|
dirnames[:] = keep
|
|
952
956
|
for f in sorted(filenames):
|
|
@@ -120,6 +120,19 @@ def _add_serve_model_args(parser: argparse.ArgumentParser) -> None:
|
|
|
120
120
|
"own this flag pins nothing and is refused)."
|
|
121
121
|
),
|
|
122
122
|
)
|
|
123
|
+
parser.add_argument(
|
|
124
|
+
"--warm",
|
|
125
|
+
dest="warm",
|
|
126
|
+
type=int,
|
|
127
|
+
default=None,
|
|
128
|
+
metavar="N",
|
|
129
|
+
help=(
|
|
130
|
+
"Idle replica floor (app_functions.warm_replicas) for THIS deploy: "
|
|
131
|
+
"N idle replicas stay resident at zero live sessions. Omit = legacy "
|
|
132
|
+
"always-on (NULL). 0 = scale to zero when idle. Shared-endpoint "
|
|
133
|
+
"deploys pass the catalog row's warm (default 0)."
|
|
134
|
+
),
|
|
135
|
+
)
|
|
123
136
|
parser.add_argument(
|
|
124
137
|
"--entrypoint",
|
|
125
138
|
help=(
|
|
@@ -314,14 +327,30 @@ def _build_deploy_args(args: argparse.Namespace, row: CatalogRow) -> argparse.Na
|
|
|
314
327
|
The shim does NOT bake the catalog row — it sets ``URUN_SERVE_MODEL`` (the
|
|
315
328
|
``<id:variant>`` string), ``URUN_SERVE_GPUS`` (the resolved gpu spec), and
|
|
316
329
|
``URUN_SERVE_ENGINE`` (the row's engine) in its own module body and re-exports
|
|
317
|
-
``urun.serve.app:app``.
|
|
318
|
-
|
|
319
|
-
app
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
``urun.serve.app:app``. When ``--warm`` is given, the idle replica floor rides
|
|
331
|
+
the same channel (``URUN_SERVE_WARM``, JSON int) so the bundled app's
|
|
332
|
+
``@app.function(warm=...)`` declares it at import; an omitted ``--warm``
|
|
333
|
+
renders nothing (byte-identical shim, legacy always-on NULL). Those land
|
|
334
|
+
scoped to THIS deploy artifact (NOT a shared ``URUN_SERVE_CONFIG``
|
|
335
|
+
materializer env, the retired coupling bug). The bundled app resolves engine
|
|
336
|
+
ARGS by-string at boot; the engine NAME is pinned here only to select the
|
|
337
|
+
build-time dependency group (vllm / llama-cpp-python / ...) so the deps venv
|
|
338
|
+
carries the engine the adapter imports at load.
|
|
322
339
|
"""
|
|
323
340
|
name = getattr(args, "name", None) or _default_app_name(row)
|
|
324
341
|
exo_class, exo_image = _validated_exo_pin(args)
|
|
342
|
+
# --warm re-validation (argparse guarantees the int): a negative floor is a
|
|
343
|
+
# config contradiction the deployer must see here, never at app import in
|
|
344
|
+
# a build pod minutes later. Warm rides the shim env, so --entrypoint (the
|
|
345
|
+
# user's own app) + --warm cannot combine — validate against entrypoint too.
|
|
346
|
+
warm = getattr(args, "warm", None)
|
|
347
|
+
if warm is not None and warm < 0:
|
|
348
|
+
raise UrunError(f"--warm must be a non-negative integer, got {warm}")
|
|
349
|
+
if warm is not None and getattr(args, "entrypoint", None):
|
|
350
|
+
raise UrunError(
|
|
351
|
+
"--warm only applies to the bundled serve app (it is pinned in the "
|
|
352
|
+
"generated shim); it cannot be combined with --entrypoint."
|
|
353
|
+
)
|
|
325
354
|
|
|
326
355
|
entrypoint = getattr(args, "entrypoint", None)
|
|
327
356
|
if entrypoint:
|
|
@@ -334,7 +363,7 @@ def _build_deploy_args(args: argparse.Namespace, row: CatalogRow) -> argparse.Na
|
|
|
334
363
|
shim_root = None
|
|
335
364
|
else:
|
|
336
365
|
project_root, rel_entrypoint = _write_serve_shim(
|
|
337
|
-
row, exo_class=exo_class, exo_image=exo_image
|
|
366
|
+
row, exo_class=exo_class, exo_image=exo_image, warm=warm
|
|
338
367
|
)
|
|
339
368
|
# PLATFORM RELEASE SCOPE (catalog prebuilt instant serve). The shim is
|
|
340
369
|
# deterministic given the resolved row + gpu, so its manifest hash is an
|
|
@@ -399,13 +428,14 @@ def _write_serve_shim(
|
|
|
399
428
|
*,
|
|
400
429
|
exo_class: str | None = None,
|
|
401
430
|
exo_image: str | None = None,
|
|
431
|
+
warm: int | None = None,
|
|
402
432
|
) -> tuple[Path, str]:
|
|
403
433
|
"""Write the bundled-app shim ``app.py`` pinned to ``row`` into a temp root."""
|
|
404
434
|
project_root = Path(tempfile.mkdtemp(prefix="urun-serve-"))
|
|
405
435
|
try:
|
|
406
436
|
app_path = project_root / "app.py"
|
|
407
437
|
app_path.write_text(
|
|
408
|
-
render_serve_shim_py(row, exo_class=exo_class, exo_image=exo_image),
|
|
438
|
+
render_serve_shim_py(row, exo_class=exo_class, exo_image=exo_image, warm=warm),
|
|
409
439
|
encoding="utf-8",
|
|
410
440
|
)
|
|
411
441
|
# Function-engine map for the launcher's PRE-IMPORT topology decision
|
|
@@ -523,6 +553,7 @@ def render_serve_shim_py(
|
|
|
523
553
|
*,
|
|
524
554
|
exo_class: str | None = None,
|
|
525
555
|
exo_image: str | None = None,
|
|
556
|
+
warm: int | None = None,
|
|
526
557
|
) -> str:
|
|
527
558
|
"""Render the tiny shim that deploys the BUNDLED urun-python serve app.
|
|
528
559
|
|
|
@@ -540,6 +571,10 @@ def render_serve_shim_py(
|
|
|
540
571
|
``deps=_deploy_deps()`` selects this engine's dependency group — the build
|
|
541
572
|
installs vllm / llama-cpp-python / ... into the per-serve deps venv (the
|
|
542
573
|
engine the adapter lazily imports at load).
|
|
574
|
+
* When ``warm`` is not None, ``URUN_SERVE_WARM`` is set before the import so
|
|
575
|
+
the bundled app's ``@app.function(warm=...)`` declares the idle replica
|
|
576
|
+
floor (app_functions.warm_replicas) at import. Unset renders NOTHING —
|
|
577
|
+
byte-identical shim, legacy always-on.
|
|
543
578
|
|
|
544
579
|
Values are embedded as JSON (NOT interpolated Python) so a weird model string,
|
|
545
580
|
gpu spec, or engine name can never break the module. The catalog row's
|
|
@@ -605,6 +640,7 @@ os.environ["URUN_SERVE_MODEL"] = %(model)s
|
|
|
605
640
|
os.environ["URUN_SERVE_GPUS"] = %(gpus)s
|
|
606
641
|
os.environ["URUN_SERVE_ENGINE"] = %(engine)s
|
|
607
642
|
%(exo_env)s
|
|
643
|
+
%(warm_env)s
|
|
608
644
|
%(serve_deps_env)s
|
|
609
645
|
|
|
610
646
|
from urun.serve.app import app # noqa: E402 (bundled urun-python serve app)
|
|
@@ -626,6 +662,11 @@ if False: # AST-visible deps declaration; never runs.
|
|
|
626
662
|
__all__ = ["app"]
|
|
627
663
|
'''
|
|
628
664
|
exo_env = render_exo_pin_env(exo_class, exo_image)
|
|
665
|
+
warm_env = (
|
|
666
|
+
'os.environ["URUN_SERVE_WARM"] = ' + json.dumps(int(warm)) + "\n"
|
|
667
|
+
if warm is not None
|
|
668
|
+
else ""
|
|
669
|
+
)
|
|
629
670
|
return template % {
|
|
630
671
|
"entrypoint": BUNDLED_SERVE_ENTRYPOINT,
|
|
631
672
|
"model": model,
|
|
@@ -634,6 +675,7 @@ __all__ = ["app"]
|
|
|
634
675
|
"deps_literal": deps_literal,
|
|
635
676
|
"serve_deps_env": serve_deps_env,
|
|
636
677
|
"exo_env": exo_env,
|
|
678
|
+
"warm_env": warm_env,
|
|
637
679
|
}
|
|
638
680
|
|
|
639
681
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|