[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
This commit is contained in:
parent
1fd58891cd
commit
2c2f587346
4 changed files with 1234 additions and 1234 deletions
|
|
@ -128,7 +128,7 @@ def _probe_dns_dead(host: str = "huggingface.co", timeout: float = 2.0) -> bool:
|
|||
except Exception:
|
||||
result[0] = True
|
||||
|
||||
t = threading.Thread(target=_probe, daemon=True)
|
||||
t = threading.Thread(target = _probe, daemon = True)
|
||||
t.start()
|
||||
t.join(timeout)
|
||||
# Thread still running -> resolver wedged -> treat as dead.
|
||||
|
|
@ -185,10 +185,10 @@ def _load_swa_cache() -> dict:
|
|||
def _save_swa_cache(cache: dict) -> None:
|
||||
try:
|
||||
path = _swa_cache_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.parent.mkdir(parents = True, exist_ok = True)
|
||||
tmp = path.with_suffix(".json.tmp")
|
||||
with open(tmp, "w") as f:
|
||||
json.dump(cache, f, indent=2, sort_keys=True)
|
||||
json.dump(cache, f, indent = 2, sort_keys = True)
|
||||
tmp.replace(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
|
@ -211,7 +211,7 @@ def _fetch_swa_entry_from_hf(repo_id: str) -> Optional[object]:
|
|||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
|
||||
cfg_path = hf_hub_download(repo_id, "config.json", repo_type="model")
|
||||
cfg_path = hf_hub_download(repo_id, "config.json", repo_type = "model")
|
||||
with open(cfg_path) as f:
|
||||
cfg = json.load(f)
|
||||
except Exception:
|
||||
|
|
@ -835,7 +835,7 @@ class LlamaCppBackend:
|
|||
# Read VmRSS from /proc/<pid>/status. Kilobytes on Linux.
|
||||
bytes_loaded = 0
|
||||
try:
|
||||
with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f:
|
||||
with open(f"/proc/{pid}/status", "r", encoding = "utf-8") as f:
|
||||
for line in f:
|
||||
if line.startswith("VmRSS:"):
|
||||
kb = int(line.split()[1])
|
||||
|
|
@ -1105,10 +1105,10 @@ class LlamaCppBackend:
|
|||
try:
|
||||
result = subprocess.run(
|
||||
[bin_path, "--help"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
check=False,
|
||||
capture_output = True,
|
||||
text = True,
|
||||
timeout = 10,
|
||||
check = False,
|
||||
)
|
||||
help_text = (result.stdout or "") + "\n" + (result.stderr or "")
|
||||
# Split into per-flag blocks: each --flag line plus its
|
||||
|
|
@ -1257,10 +1257,10 @@ class LlamaCppBackend:
|
|||
"--query-gpu=index,memory.free",
|
||||
"--format=csv,noheader,nounits",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
env=child_env_without_native_path_secret(),
|
||||
capture_output = True,
|
||||
text = True,
|
||||
timeout = 10,
|
||||
env = child_env_without_native_path_secret(),
|
||||
**_windows_hidden_subprocess_kwargs(),
|
||||
)
|
||||
if result.returncode == 0:
|
||||
|
|
@ -1290,7 +1290,7 @@ class LlamaCppBackend:
|
|||
# Match the docstring's sort-by-id guarantee. nvidia-smi
|
||||
# almost always returns sorted output, but driver order
|
||||
# is not formally guaranteed.
|
||||
gpus.sort(key=lambda g: g[0])
|
||||
gpus.sort(key = lambda g: g[0])
|
||||
if gpus:
|
||||
return gpus
|
||||
except Exception as e:
|
||||
|
|
@ -1349,7 +1349,7 @@ class LlamaCppBackend:
|
|||
)
|
||||
gpus.append((idx, free_bytes // (1024 * 1024)))
|
||||
# Match the nvidia-smi path's docstring guarantee of sorted-by-id.
|
||||
return sorted(gpus, key=lambda g: g[0])
|
||||
return sorted(gpus, key = lambda g: g[0])
|
||||
except Exception as e:
|
||||
logger.debug(f"torch GPU probe failed: {e}")
|
||||
return []
|
||||
|
|
@ -1534,7 +1534,7 @@ class LlamaCppBackend:
|
|||
usable_fraction = LlamaCppBackend._GPU_PIN_VRAM_FRACTION
|
||||
|
||||
# Sort GPUs by free memory descending
|
||||
ranked = sorted(gpus, key=lambda g: g[1], reverse=True)
|
||||
ranked = sorted(gpus, key = lambda g: g[1], reverse = True)
|
||||
|
||||
# Try fitting on 1 GPU at the usable-VRAM threshold.
|
||||
if ranked[0][1] * usable_fraction >= model_size_mib:
|
||||
|
|
@ -1552,8 +1552,8 @@ class LlamaCppBackend:
|
|||
# Model is too large even for all GPUs, let --fit handle it
|
||||
logger.debug(
|
||||
"Model does not fit in available GPU memory, falling back to --fit",
|
||||
model_size_mib=round(model_size_mib, 2),
|
||||
ranked_gpus=ranked,
|
||||
model_size_mib = round(model_size_mib, 2),
|
||||
ranked_gpus = ranked,
|
||||
)
|
||||
return None, True
|
||||
|
||||
|
|
@ -1799,8 +1799,8 @@ class LlamaCppBackend:
|
|||
if not self._can_estimate_kv():
|
||||
logger.debug(
|
||||
"Skipping context fit because KV cache metadata is unavailable",
|
||||
requested_ctx=requested_ctx,
|
||||
available_mib=available_mib,
|
||||
requested_ctx = requested_ctx,
|
||||
available_mib = available_mib,
|
||||
)
|
||||
return requested_ctx
|
||||
|
||||
|
|
@ -1809,10 +1809,10 @@ class LlamaCppBackend:
|
|||
return requested_ctx
|
||||
|
||||
kv_kwargs = dict(
|
||||
swa_full=swa_full,
|
||||
n_parallel=n_parallel,
|
||||
kv_unified=kv_unified,
|
||||
ctx_checkpoints=ctx_checkpoints,
|
||||
swa_full = swa_full,
|
||||
n_parallel = n_parallel,
|
||||
kv_unified = kv_unified,
|
||||
ctx_checkpoints = ctx_checkpoints,
|
||||
)
|
||||
|
||||
# MTP needs a tighter budget; drop from 0.90 to 0.85.
|
||||
|
|
@ -1830,9 +1830,9 @@ class LlamaCppBackend:
|
|||
if model_footprint >= budget_bytes:
|
||||
logger.debug(
|
||||
"Model footprint exceeds GPU budget before KV cache",
|
||||
requested_ctx=requested_ctx,
|
||||
available_mib=available_mib,
|
||||
model_size_gb=round(model_footprint / (1024**3), 2),
|
||||
requested_ctx = requested_ctx,
|
||||
available_mib = available_mib,
|
||||
model_size_gb = round(model_footprint / (1024**3), 2),
|
||||
)
|
||||
return requested_ctx
|
||||
|
||||
|
|
@ -1874,7 +1874,7 @@ class LlamaCppBackend:
|
|||
try:
|
||||
from huggingface_hub import get_paths_info, list_repo_files
|
||||
|
||||
files = list_repo_files(hf_repo, token=hf_token)
|
||||
files = list_repo_files(hf_repo, token = hf_token)
|
||||
gguf_files = [
|
||||
f for f in files if f.endswith(".gguf") and "mmproj" not in f.lower()
|
||||
]
|
||||
|
|
@ -1882,7 +1882,7 @@ class LlamaCppBackend:
|
|||
return None
|
||||
|
||||
# Get sizes for all GGUF files
|
||||
path_infos = list(get_paths_info(hf_repo, gguf_files, token=hf_token))
|
||||
path_infos = list(get_paths_info(hf_repo, gguf_files, token = hf_token))
|
||||
size_map = {p.path: (p.size or 0) for p in path_infos}
|
||||
|
||||
# Group files by variant: shards share a prefix before -NNNNN-of-NNNNN
|
||||
|
|
@ -1900,7 +1900,7 @@ class LlamaCppBackend:
|
|||
variant_sizes.append((first, total, shard_files))
|
||||
|
||||
# Sort by total size ascending and pick the smallest that fits
|
||||
variant_sizes.sort(key=lambda x: x[1])
|
||||
variant_sizes.sort(key = lambda x: x[1])
|
||||
for first_file, total_size, _ in variant_sizes:
|
||||
if total_size > 0 and total_size <= free_bytes:
|
||||
return first_file, total_size
|
||||
|
|
@ -2215,7 +2215,7 @@ class LlamaCppBackend:
|
|||
flags = detect_reasoning_flags(
|
||||
self._chat_template,
|
||||
self._model_identifier,
|
||||
log_source="GGUF metadata",
|
||||
log_source = "GGUF metadata",
|
||||
)
|
||||
self._supports_reasoning = flags["supports_reasoning"]
|
||||
self._reasoning_style = flags["reasoning_style"]
|
||||
|
|
@ -2255,7 +2255,7 @@ class LlamaCppBackend:
|
|||
try:
|
||||
from huggingface_hub import list_repo_files
|
||||
|
||||
files = list_repo_files(hf_repo, token=hf_token)
|
||||
files = list_repo_files(hf_repo, token = hf_token)
|
||||
variant_lower = hf_variant.lower()
|
||||
boundary = re.compile(
|
||||
r"(?<![a-zA-Z0-9])" + re.escape(variant_lower) + r"(?![a-zA-Z0-9])"
|
||||
|
|
@ -2342,7 +2342,7 @@ class LlamaCppBackend:
|
|||
try:
|
||||
from huggingface_hub import get_paths_info, try_to_load_from_cache
|
||||
|
||||
path_infos = list(get_paths_info(hf_repo, all_gguf_files, token=hf_token))
|
||||
path_infos = list(get_paths_info(hf_repo, all_gguf_files, token = hf_token))
|
||||
total_bytes = sum((p.size or 0) for p in path_infos)
|
||||
|
||||
# Subtract bytes already present in the HF cache so we only
|
||||
|
|
@ -2374,7 +2374,7 @@ class LlamaCppBackend:
|
|||
"HF_HUB_CACHE",
|
||||
str(Path.home() / ".cache" / "huggingface" / "hub"),
|
||||
)
|
||||
Path(cache_dir).mkdir(parents=True, exist_ok=True)
|
||||
Path(cache_dir).mkdir(parents = True, exist_ok = True)
|
||||
free_bytes = shutil.disk_usage(cache_dir).free
|
||||
|
||||
total_gb = total_download_bytes / (1024**3)
|
||||
|
|
@ -2431,18 +2431,18 @@ class LlamaCppBackend:
|
|||
raise RuntimeError("Cancelled")
|
||||
dl_start = time.monotonic()
|
||||
local_path = hf_hub_download(
|
||||
repo_id=hf_repo,
|
||||
filename=gguf_filename,
|
||||
token=hf_token,
|
||||
repo_id = hf_repo,
|
||||
filename = gguf_filename,
|
||||
token = hf_token,
|
||||
)
|
||||
for shard in gguf_extra_shards:
|
||||
if self._cancel_event.is_set():
|
||||
raise RuntimeError("Cancelled")
|
||||
logger.info(f"Resolving GGUF shard: {shard}")
|
||||
hf_hub_download(
|
||||
repo_id=hf_repo,
|
||||
filename=shard,
|
||||
token=hf_token,
|
||||
repo_id = hf_repo,
|
||||
filename = shard,
|
||||
token = hf_token,
|
||||
)
|
||||
except RuntimeError as e:
|
||||
if "Cancelled" in str(e):
|
||||
|
|
@ -2491,7 +2491,7 @@ class LlamaCppBackend:
|
|||
try:
|
||||
from huggingface_hub import list_repo_files
|
||||
|
||||
target = _pick_mmproj(list_repo_files(hf_repo, token=hf_token))
|
||||
target = _pick_mmproj(list_repo_files(hf_repo, token = hf_token))
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not list repo files for mmproj: {e}")
|
||||
|
||||
|
|
@ -2520,9 +2520,9 @@ class LlamaCppBackend:
|
|||
|
||||
logger.info(f"Downloading mmproj: {hf_repo}/{target}")
|
||||
local_path = hf_hub_download(
|
||||
repo_id=hf_repo,
|
||||
filename=target,
|
||||
token=hf_token,
|
||||
repo_id = hf_repo,
|
||||
filename = target,
|
||||
token = hf_token,
|
||||
)
|
||||
return local_path
|
||||
except Exception as e:
|
||||
|
|
@ -2604,16 +2604,16 @@ class LlamaCppBackend:
|
|||
# (the first one hadn't published _healthy=True yet). If the
|
||||
# live server already satisfies this request, do nothing.
|
||||
if self._already_in_target_state(
|
||||
gguf_path=gguf_path,
|
||||
model_identifier=model_identifier,
|
||||
hf_variant=hf_variant,
|
||||
n_ctx=n_ctx,
|
||||
cache_type_kv=cache_type_kv,
|
||||
speculative_type=speculative_type,
|
||||
spec_draft_n_max=spec_draft_n_max,
|
||||
chat_template_override=chat_template_override,
|
||||
extra_args=extra_args,
|
||||
is_vision=is_vision,
|
||||
gguf_path = gguf_path,
|
||||
model_identifier = model_identifier,
|
||||
hf_variant = hf_variant,
|
||||
n_ctx = n_ctx,
|
||||
cache_type_kv = cache_type_kv,
|
||||
speculative_type = speculative_type,
|
||||
spec_draft_n_max = spec_draft_n_max,
|
||||
chat_template_override = chat_template_override,
|
||||
extra_args = extra_args,
|
||||
is_vision = is_vision,
|
||||
):
|
||||
logger.info(
|
||||
f"load_model: backend already in target state for "
|
||||
|
|
@ -2676,15 +2676,15 @@ class LlamaCppBackend:
|
|||
if hf_repo:
|
||||
with _hf_offline_if_dns_dead():
|
||||
model_path = self._download_gguf(
|
||||
hf_repo=hf_repo,
|
||||
hf_variant=hf_variant,
|
||||
hf_token=hf_token,
|
||||
hf_repo = hf_repo,
|
||||
hf_variant = hf_variant,
|
||||
hf_token = hf_token,
|
||||
)
|
||||
# Auto-download mmproj for vision models
|
||||
if is_vision and not mmproj_path:
|
||||
mmproj_path = self._download_mmproj(
|
||||
hf_repo=hf_repo,
|
||||
hf_token=hf_token,
|
||||
hf_repo = hf_repo,
|
||||
hf_token = hf_token,
|
||||
)
|
||||
elif gguf_path:
|
||||
if not Path(gguf_path).is_file():
|
||||
|
|
@ -2708,7 +2708,7 @@ class LlamaCppBackend:
|
|||
# not blocked. ``unload_model`` also records the kill, so
|
||||
# the frontend /unload+/load Apply path engages the wait
|
||||
# here even though no in-process kill happened.
|
||||
self._wait_for_vram_settle(since_kill=self._last_kill_monotonic)
|
||||
self._wait_for_vram_settle(since_kill = self._last_kill_monotonic)
|
||||
|
||||
# ── Phase 3: start llama-server (under lock) ──────────────
|
||||
with self._lock:
|
||||
|
|
@ -2795,7 +2795,7 @@ class LlamaCppBackend:
|
|||
native_ctx_for_cap = self._context_length or effective_ctx
|
||||
if native_ctx_for_cap > 0:
|
||||
ranked_for_cap = sorted(
|
||||
gpus, key=lambda g: g[1], reverse=True
|
||||
gpus, key = lambda g: g[1], reverse = True
|
||||
)
|
||||
best_cap = 0
|
||||
for n_gpus in range(1, len(ranked_for_cap) + 1):
|
||||
|
|
@ -2806,11 +2806,11 @@ class LlamaCppBackend:
|
|||
pool_mib,
|
||||
model_size,
|
||||
cache_type_kv,
|
||||
n_parallel=n_parallel,
|
||||
mtp_engaged=_mtp_will_engage,
|
||||
n_parallel = n_parallel,
|
||||
mtp_engaged = _mtp_will_engage,
|
||||
)
|
||||
kv = self._estimate_kv_cache_bytes(
|
||||
capped, cache_type_kv, n_parallel=n_parallel
|
||||
capped, cache_type_kv, n_parallel = n_parallel
|
||||
)
|
||||
total_mib = (model_size + kv) / (1024 * 1024)
|
||||
if total_mib <= pool_mib * 0.90:
|
||||
|
|
@ -2837,7 +2837,7 @@ class LlamaCppBackend:
|
|||
requested_total = (
|
||||
model_size
|
||||
+ self._estimate_kv_cache_bytes(
|
||||
effective_ctx, cache_type_kv, n_parallel=n_parallel
|
||||
effective_ctx, cache_type_kv, n_parallel = n_parallel
|
||||
)
|
||||
)
|
||||
gpu_indices, use_fit = self._select_gpus(
|
||||
|
|
@ -2848,7 +2848,7 @@ class LlamaCppBackend:
|
|||
# Auto context: prefer fewer GPUs, cap context
|
||||
# to fit. Same headroom threshold as
|
||||
# _select_gpus (#5106).
|
||||
ranked = sorted(gpus, key=lambda g: g[1], reverse=True)
|
||||
ranked = sorted(gpus, key = lambda g: g[1], reverse = True)
|
||||
pin_fraction = self._GPU_PIN_VRAM_FRACTION
|
||||
for n_gpus in range(1, len(ranked) + 1):
|
||||
subset = ranked[:n_gpus]
|
||||
|
|
@ -2858,11 +2858,11 @@ class LlamaCppBackend:
|
|||
pool_mib,
|
||||
model_size,
|
||||
cache_type_kv,
|
||||
n_parallel=n_parallel,
|
||||
mtp_engaged=_mtp_will_engage,
|
||||
n_parallel = n_parallel,
|
||||
mtp_engaged = _mtp_will_engage,
|
||||
)
|
||||
kv = self._estimate_kv_cache_bytes(
|
||||
capped, cache_type_kv, n_parallel=n_parallel
|
||||
capped, cache_type_kv, n_parallel = n_parallel
|
||||
)
|
||||
total_mib = (model_size + kv) / (1024 * 1024)
|
||||
if total_mib <= pool_mib * pin_fraction:
|
||||
|
|
@ -2883,7 +2883,7 @@ class LlamaCppBackend:
|
|||
kv = self._estimate_kv_cache_bytes(
|
||||
effective_ctx,
|
||||
cache_type_kv,
|
||||
n_parallel=n_parallel,
|
||||
n_parallel = n_parallel,
|
||||
)
|
||||
total_mib = (model_size + kv) / (1024 * 1024)
|
||||
if total_mib <= pool_mib * pin_fraction:
|
||||
|
|
@ -2899,7 +2899,7 @@ class LlamaCppBackend:
|
|||
# keep the ceiling at the native context (already the default).
|
||||
logger.debug(
|
||||
"Falling back to file-size-only GPU selection",
|
||||
model_size_gb=round(model_size / (1024**3), 2),
|
||||
model_size_gb = round(model_size / (1024**3), 2),
|
||||
)
|
||||
gpu_indices, use_fit = self._select_gpus(model_size, gpus)
|
||||
if use_fit and not explicit_ctx:
|
||||
|
|
@ -2912,7 +2912,7 @@ class LlamaCppBackend:
|
|||
|
||||
if effective_ctx < original_ctx:
|
||||
kv_est = self._estimate_kv_cache_bytes(
|
||||
effective_ctx, cache_type_kv, n_parallel=n_parallel
|
||||
effective_ctx, cache_type_kv, n_parallel = n_parallel
|
||||
)
|
||||
logger.info(
|
||||
f"Context auto-reduced: {original_ctx} -> {effective_ctx} "
|
||||
|
|
@ -2921,7 +2921,7 @@ class LlamaCppBackend:
|
|||
)
|
||||
|
||||
kv_cache_bytes = self._estimate_kv_cache_bytes(
|
||||
effective_ctx, cache_type_kv, n_parallel=n_parallel
|
||||
effective_ctx, cache_type_kv, n_parallel = n_parallel
|
||||
)
|
||||
logger.info(
|
||||
f"GGUF size: {model_size / (1024**3):.1f} GB, "
|
||||
|
|
@ -2935,8 +2935,8 @@ class LlamaCppBackend:
|
|||
effective_ctx = n_ctx # fall back to original
|
||||
|
||||
launch_mmproj_path = self._resolve_launch_mmproj_path(
|
||||
model_path=model_path,
|
||||
mmproj_path=mmproj_path,
|
||||
model_path = model_path,
|
||||
mmproj_path = mmproj_path,
|
||||
)
|
||||
# Need both a resolved mmproj AND the config vision flag; a stray
|
||||
# mmproj passing the family-name heuristic must not flip a non-VLM
|
||||
|
|
@ -3030,13 +3030,13 @@ class LlamaCppBackend:
|
|||
# fallback to -MTP in name. GPU: MTP-only. CPU/Mac: chain
|
||||
# with ngram-mod. See unsloth.ai/docs/models/qwen3.6#mtp-guide.
|
||||
spec_flags = self._build_speculative_flags(
|
||||
speculative_type=speculative_type,
|
||||
spec_draft_n_max=spec_draft_n_max,
|
||||
extra_args=extra_args,
|
||||
model_identifier=model_identifier,
|
||||
model_path=model_path,
|
||||
gpus=bool(gpus),
|
||||
binary=binary,
|
||||
speculative_type = speculative_type,
|
||||
spec_draft_n_max = spec_draft_n_max,
|
||||
extra_args = extra_args,
|
||||
model_identifier = model_identifier,
|
||||
model_path = model_path,
|
||||
gpus = bool(gpus),
|
||||
binary = binary,
|
||||
)
|
||||
cmd.extend(spec_flags)
|
||||
|
||||
|
|
@ -3048,7 +3048,7 @@ class LlamaCppBackend:
|
|||
flags = detect_reasoning_flags(
|
||||
chat_template_override,
|
||||
self._model_identifier,
|
||||
log_source="GGUF chat template override",
|
||||
log_source = "GGUF chat template override",
|
||||
)
|
||||
self._supports_reasoning = flags["supports_reasoning"]
|
||||
self._reasoning_style = flags["reasoning_style"]
|
||||
|
|
@ -3059,10 +3059,10 @@ class LlamaCppBackend:
|
|||
self._supports_tools = flags["supports_tools"]
|
||||
|
||||
self._chat_template_file = tempfile.NamedTemporaryFile(
|
||||
mode="w",
|
||||
suffix=".jinja",
|
||||
delete=False,
|
||||
prefix="unsloth_chat_template_",
|
||||
mode = "w",
|
||||
suffix = ".jinja",
|
||||
delete = False,
|
||||
prefix = "unsloth_chat_template_",
|
||||
)
|
||||
self._chat_template_file.write(chat_template_override)
|
||||
self._chat_template_file.close()
|
||||
|
|
@ -3243,15 +3243,15 @@ class LlamaCppBackend:
|
|||
self._llama_log_fh = None
|
||||
try:
|
||||
log_dir = _swa_cache_path().parent / "logs" / "llama-server"
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
log_dir.mkdir(parents = True, exist_ok = True)
|
||||
self._llama_log_path = (
|
||||
log_dir / f"llama-{int(time.time())}-port-{self._port}.log"
|
||||
)
|
||||
self._llama_log_fh = open(
|
||||
self._llama_log_path,
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
buffering=1,
|
||||
encoding = "utf-8",
|
||||
buffering = 1,
|
||||
)
|
||||
logger.info(f"llama-server stdout/stderr -> {self._llama_log_path}")
|
||||
except OSError as e:
|
||||
|
|
@ -3260,16 +3260,16 @@ class LlamaCppBackend:
|
|||
self._llama_log_path = None
|
||||
self._process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
env=env,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.STDOUT,
|
||||
text = True,
|
||||
env = env,
|
||||
**_windows_hidden_subprocess_kwargs(),
|
||||
)
|
||||
|
||||
# Start background thread to drain stdout and prevent pipe deadlock
|
||||
self._stdout_thread = threading.Thread(
|
||||
target=self._drain_stdout, daemon=True, name="llama-stdout"
|
||||
target = self._drain_stdout, daemon = True, name = "llama-stdout"
|
||||
)
|
||||
self._stdout_thread.start()
|
||||
|
||||
|
|
@ -3308,7 +3308,7 @@ class LlamaCppBackend:
|
|||
)
|
||||
|
||||
# Wait for llama-server to become healthy
|
||||
if not self._wait_for_health(timeout=600.0):
|
||||
if not self._wait_for_health(timeout = 600.0):
|
||||
self._kill_process()
|
||||
_gguf = gguf_path or ""
|
||||
_is_ollama = (
|
||||
|
|
@ -3573,7 +3573,7 @@ class LlamaCppBackend:
|
|||
"fall back to spec-off if no nextn head is present. "
|
||||
"Engaging anyway (user override)."
|
||||
)
|
||||
_emit_mtp(chain_ngram=False)
|
||||
_emit_mtp(chain_ngram = False)
|
||||
return flags
|
||||
if effective_mode == "mtp+ngram":
|
||||
if _mtp_too_small:
|
||||
|
|
@ -3588,14 +3588,14 @@ class LlamaCppBackend:
|
|||
"may fall back to ngram-only if no nextn head is "
|
||||
"present. Engaging anyway (user override)."
|
||||
)
|
||||
_emit_mtp(chain_ngram=True)
|
||||
_emit_mtp(chain_ngram = True)
|
||||
return flags
|
||||
|
||||
# effective_mode == "auto": today's promotion path. llama.cpp
|
||||
# #22673: MTP is compatible with mmproj, so there's no vision gate.
|
||||
if is_mtp_model and not _mtp_too_small:
|
||||
# GPU: MTP-only. CPU/Mac: chain ngram-mod + MTP.
|
||||
_emit_mtp(chain_ngram=not gpus)
|
||||
_emit_mtp(chain_ngram = not gpus)
|
||||
elif is_mtp_model and _mtp_too_small:
|
||||
# Sub-3B fallback: drop the MTP draft head, keep ngram-mod
|
||||
# when the binary supports it.
|
||||
|
|
@ -3807,11 +3807,11 @@ class LlamaCppBackend:
|
|||
return
|
||||
try:
|
||||
self._process.terminate()
|
||||
self._process.wait(timeout=5)
|
||||
self._process.wait(timeout = 5)
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.warning("llama-server did not exit on SIGTERM, sending SIGKILL")
|
||||
self._process.kill()
|
||||
self._process.wait(timeout=5)
|
||||
self._process.wait(timeout = 5)
|
||||
except Exception as e:
|
||||
logger.warning(f"Error killing llama-server process: {e}")
|
||||
finally:
|
||||
|
|
@ -3825,7 +3825,7 @@ class LlamaCppBackend:
|
|||
# /unload+/load Apply paths record the kill.
|
||||
self._last_kill_monotonic = time.monotonic()
|
||||
if self._stdout_thread is not None:
|
||||
self._stdout_thread.join(timeout=2)
|
||||
self._stdout_thread.join(timeout = 2)
|
||||
self._stdout_thread = None
|
||||
fh = getattr(self, "_llama_log_fh", None)
|
||||
if fh is not None:
|
||||
|
|
@ -3968,10 +3968,10 @@ class LlamaCppBackend:
|
|||
return
|
||||
result = subprocess.run(
|
||||
["pgrep", "-a", "-f", "llama-server"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
env=child_env_without_native_path_secret(),
|
||||
capture_output = True,
|
||||
text = True,
|
||||
timeout = 5,
|
||||
env = child_env_without_native_path_secret(),
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return
|
||||
|
|
@ -3991,13 +3991,13 @@ class LlamaCppBackend:
|
|||
# unavailable.
|
||||
proc_exe = Path(f"/proc/{pid}/exe")
|
||||
try:
|
||||
binary = proc_exe.resolve(strict=True)
|
||||
binary = proc_exe.resolve(strict = True)
|
||||
except (OSError, ValueError):
|
||||
cmdline = parts[1]
|
||||
token = cmdline.split()[0] if cmdline.strip() else ""
|
||||
if not token:
|
||||
continue
|
||||
binary = Path(token).resolve(strict=False)
|
||||
binary = Path(token).resolve(strict = False)
|
||||
|
||||
owned = binary in exact_binaries or any(
|
||||
binary.is_relative_to(root) for root in resolved_roots
|
||||
|
|
@ -4013,7 +4013,7 @@ class LlamaCppBackend:
|
|||
except PermissionError:
|
||||
pass
|
||||
except Exception:
|
||||
logger.warning("Error during orphan server cleanup", exc_info=True)
|
||||
logger.warning("Error during orphan server cleanup", exc_info = True)
|
||||
|
||||
def _cleanup(self):
|
||||
"""atexit handler to ensure llama-server is terminated."""
|
||||
|
|
@ -4033,7 +4033,7 @@ class LlamaCppBackend:
|
|||
if self._process.poll() is not None:
|
||||
# Give the drain thread a moment to collect final output
|
||||
if self._stdout_thread is not None:
|
||||
self._stdout_thread.join(timeout=2)
|
||||
self._stdout_thread.join(timeout = 2)
|
||||
output = "\n".join(self._stdout_lines[-50:])
|
||||
logger.error(
|
||||
f"llama-server exited with code {self._process.returncode}. "
|
||||
|
|
@ -4042,7 +4042,7 @@ class LlamaCppBackend:
|
|||
return False
|
||||
|
||||
try:
|
||||
resp = httpx.get(url, timeout=2.0)
|
||||
resp = httpx.get(url, timeout = 2.0)
|
||||
if resp.status_code == 200:
|
||||
return True
|
||||
except (
|
||||
|
|
@ -4173,7 +4173,7 @@ class LlamaCppBackend:
|
|||
|
||||
def _cancel_watcher():
|
||||
while not _cancel_closed.is_set():
|
||||
if cancel_event.wait(timeout=0.3):
|
||||
if cancel_event.wait(timeout = 0.3):
|
||||
# Cancel requested. Keep polling until the response object
|
||||
# exists so we can close it, or until the main thread
|
||||
# finishes on its own (_cancel_closed is set in finally).
|
||||
|
|
@ -4188,13 +4188,13 @@ class LlamaCppBackend:
|
|||
f"Error closing response in cancel watcher: {e}"
|
||||
)
|
||||
# Response not created yet -- wait briefly and retry
|
||||
_cancel_closed.wait(timeout=0.1)
|
||||
_cancel_closed.wait(timeout = 0.1)
|
||||
return
|
||||
|
||||
watcher = None
|
||||
if cancel_event is not None:
|
||||
watcher = threading.Thread(
|
||||
target=_cancel_watcher, daemon=True, name="prefill-cancel"
|
||||
target = _cancel_watcher, daemon = True, name = "prefill-cancel"
|
||||
)
|
||||
watcher.start()
|
||||
|
||||
|
|
@ -4204,17 +4204,17 @@ class LlamaCppBackend:
|
|||
# prefill and streaming is handled by the watcher thread
|
||||
# which closes the response, unblocking any httpx read.
|
||||
prefill_timeout = httpx.Timeout(
|
||||
connect=30,
|
||||
read=120.0,
|
||||
write=10,
|
||||
pool=10,
|
||||
connect = 30,
|
||||
read = 120.0,
|
||||
write = 10,
|
||||
pool = 10,
|
||||
)
|
||||
with client.stream(
|
||||
"POST",
|
||||
url,
|
||||
json=payload,
|
||||
timeout=prefill_timeout,
|
||||
headers=headers,
|
||||
json = payload,
|
||||
timeout = prefill_timeout,
|
||||
headers = headers,
|
||||
) as response:
|
||||
_response_ref[0] = response
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
|
|
@ -4299,19 +4299,19 @@ class LlamaCppBackend:
|
|||
# _stream_with_retry uses a 120 s read timeout so prefill
|
||||
# can finish. Cancel during streaming is handled by the
|
||||
# watcher thread (closes the response on cancel_event).
|
||||
stream_timeout = httpx.Timeout(connect=10, read=0.5, write=10, pool=10)
|
||||
stream_timeout = httpx.Timeout(connect = 10, read = 0.5, write = 10, pool = 10)
|
||||
_auth_headers = (
|
||||
{"Authorization": f"Bearer {self._api_key}"} if self._api_key else None
|
||||
)
|
||||
with httpx.Client(
|
||||
timeout=stream_timeout, limits=httpx.Limits(max_keepalive_connections=0)
|
||||
timeout = stream_timeout, limits = httpx.Limits(max_keepalive_connections = 0)
|
||||
) as client:
|
||||
with self._stream_with_retry(
|
||||
client,
|
||||
url,
|
||||
payload,
|
||||
cancel_event,
|
||||
headers=_auth_headers,
|
||||
headers = _auth_headers,
|
||||
) as response:
|
||||
if response.status_code != 200:
|
||||
error_body = response.read().decode()
|
||||
|
|
@ -4451,7 +4451,7 @@ class LlamaCppBackend:
|
|||
def _strip_tool_markup(text: str, *, final: bool = False) -> str:
|
||||
if not auto_heal_tool_calls:
|
||||
return text
|
||||
return strip_tool_call_markup(text, final=final)
|
||||
return strip_tool_call_markup(text, final = final)
|
||||
|
||||
# XML prefixes that signal a tool call in content.
|
||||
# Empty when auto_heal is disabled so the buffer never
|
||||
|
|
@ -4543,21 +4543,21 @@ class LlamaCppBackend:
|
|||
_last_emitted = ""
|
||||
|
||||
stream_timeout = httpx.Timeout(
|
||||
connect=10,
|
||||
read=0.5,
|
||||
write=10,
|
||||
pool=10,
|
||||
connect = 10,
|
||||
read = 0.5,
|
||||
write = 10,
|
||||
pool = 10,
|
||||
)
|
||||
with httpx.Client(
|
||||
timeout=stream_timeout,
|
||||
limits=httpx.Limits(max_keepalive_connections=0),
|
||||
timeout = stream_timeout,
|
||||
limits = httpx.Limits(max_keepalive_connections = 0),
|
||||
) as client:
|
||||
with self._stream_with_retry(
|
||||
client,
|
||||
url,
|
||||
payload,
|
||||
cancel_event,
|
||||
headers=_auth_headers,
|
||||
headers = _auth_headers,
|
||||
) as response:
|
||||
if response.status_code != 200:
|
||||
error_body = response.read().decode()
|
||||
|
|
@ -4587,7 +4587,7 @@ class LlamaCppBackend:
|
|||
"type": "content",
|
||||
"text": _strip_tool_markup(
|
||||
cumulative_display,
|
||||
final=True,
|
||||
final = True,
|
||||
),
|
||||
}
|
||||
else:
|
||||
|
|
@ -4771,7 +4771,7 @@ class LlamaCppBackend:
|
|||
"type": "content",
|
||||
"text": _strip_tool_markup(
|
||||
cumulative_display,
|
||||
final=True,
|
||||
final = True,
|
||||
),
|
||||
}
|
||||
elif reasoning_accum and not has_content_tokens:
|
||||
|
|
@ -4909,7 +4909,7 @@ class LlamaCppBackend:
|
|||
tool_calls = _safety_tc
|
||||
content_text = _strip_tool_markup(
|
||||
content_accum,
|
||||
final=True,
|
||||
final = True,
|
||||
)
|
||||
logger.info(
|
||||
f"Safety net: parsed {len(tool_calls)} tool call(s) "
|
||||
|
|
@ -4943,7 +4943,7 @@ class LlamaCppBackend:
|
|||
if tool_calls and not has_structured_tc:
|
||||
content_text = _strip_tool_markup(
|
||||
content_text,
|
||||
final=True,
|
||||
final = True,
|
||||
)
|
||||
if tool_calls:
|
||||
logger.info(
|
||||
|
|
@ -4958,7 +4958,7 @@ class LlamaCppBackend:
|
|||
if content_accum:
|
||||
# Strip leaked tool-call XML before yielding
|
||||
content_accum = _strip_tool_markup(
|
||||
content_accum, final=True
|
||||
content_accum, final = True
|
||||
)
|
||||
if content_accum:
|
||||
yield {"type": "content", "text": content_accum}
|
||||
|
|
@ -5094,9 +5094,9 @@ class LlamaCppBackend:
|
|||
result = execute_tool(
|
||||
tool_name,
|
||||
arguments,
|
||||
cancel_event=cancel_event,
|
||||
timeout=_effective_timeout,
|
||||
session_id=session_id,
|
||||
cancel_event = cancel_event,
|
||||
timeout = _effective_timeout,
|
||||
session_id = session_id,
|
||||
)
|
||||
|
||||
yield {
|
||||
|
|
@ -5210,19 +5210,19 @@ class LlamaCppBackend:
|
|||
_stream_done = False
|
||||
|
||||
try:
|
||||
stream_timeout = httpx.Timeout(connect=10, read=0.5, write=10, pool=10)
|
||||
stream_timeout = httpx.Timeout(connect = 10, read = 0.5, write = 10, pool = 10)
|
||||
_auth_headers = (
|
||||
{"Authorization": f"Bearer {self._api_key}"} if self._api_key else None
|
||||
)
|
||||
with httpx.Client(
|
||||
timeout=stream_timeout, limits=httpx.Limits(max_keepalive_connections=0)
|
||||
timeout = stream_timeout, limits = httpx.Limits(max_keepalive_connections = 0)
|
||||
) as client:
|
||||
with self._stream_with_retry(
|
||||
client,
|
||||
url,
|
||||
stream_payload,
|
||||
cancel_event,
|
||||
headers=_auth_headers,
|
||||
headers = _auth_headers,
|
||||
) as response:
|
||||
if response.status_code != 200:
|
||||
error_body = response.read().decode()
|
||||
|
|
@ -5248,7 +5248,7 @@ class LlamaCppBackend:
|
|||
yield {
|
||||
"type": "content",
|
||||
"text": _strip_tool_markup(
|
||||
cumulative, final=True
|
||||
cumulative, final = True
|
||||
),
|
||||
}
|
||||
else:
|
||||
|
|
@ -5358,12 +5358,12 @@ class LlamaCppBackend:
|
|||
_auth_headers = (
|
||||
{"Authorization": f"Bearer {self._api_key}"} if self._api_key else None
|
||||
)
|
||||
with httpx.Client(timeout=10, headers=_auth_headers) as client:
|
||||
with httpx.Client(timeout = 10, headers = _auth_headers) as client:
|
||||
|
||||
def _detok(tid: int) -> str:
|
||||
# Non-200 means "marker not in vocab" -- keep probing.
|
||||
# Transport / JSON errors still raise.
|
||||
r = client.post(f"{self.base_url}/detokenize", json={"tokens": [tid]})
|
||||
r = client.post(f"{self.base_url}/detokenize", json = {"tokens": [tid]})
|
||||
if r.status_code != 200:
|
||||
return ""
|
||||
return r.json().get("content", "")
|
||||
|
|
@ -5371,7 +5371,7 @@ class LlamaCppBackend:
|
|||
def _tok(text: str) -> list[int]:
|
||||
r = client.post(
|
||||
f"{self.base_url}/tokenize",
|
||||
json={"content": text, "add_special": False},
|
||||
json = {"content": text, "add_special": False},
|
||||
)
|
||||
if r.status_code != 200:
|
||||
return []
|
||||
|
|
@ -5436,12 +5436,12 @@ class LlamaCppBackend:
|
|||
import os
|
||||
|
||||
repo_path = snapshot_download(
|
||||
"unsloth/Spark-TTS-0.5B", local_dir="Spark-TTS-0.5B"
|
||||
"unsloth/Spark-TTS-0.5B", local_dir = "Spark-TTS-0.5B"
|
||||
)
|
||||
model_repo_path = os.path.abspath(repo_path)
|
||||
|
||||
LlamaCppBackend._codec_mgr.load_codec(
|
||||
audio_type, device, model_repo_path=model_repo_path
|
||||
audio_type, device, model_repo_path = model_repo_path
|
||||
)
|
||||
logger.info(f"Loaded audio codec for GGUF TTS: {audio_type}")
|
||||
|
||||
|
|
@ -5466,7 +5466,7 @@ class LlamaCppBackend:
|
|||
tpl, stop, need_ids = self._TTS_PROMPTS[audio_type]
|
||||
|
||||
payload: dict = {
|
||||
"prompt": tpl.format(text=text),
|
||||
"prompt": tpl.format(text = text),
|
||||
"stream": False,
|
||||
"n_predict": max_new_tokens,
|
||||
"temperature": temperature,
|
||||
|
|
@ -5484,9 +5484,9 @@ class LlamaCppBackend:
|
|||
{"Authorization": f"Bearer {self._api_key}"} if self._api_key else None
|
||||
)
|
||||
with httpx.Client(
|
||||
timeout=httpx.Timeout(300, connect=10), headers=_auth_headers
|
||||
timeout = httpx.Timeout(300, connect = 10), headers = _auth_headers
|
||||
) as client:
|
||||
resp = client.post(f"{self.base_url}/completion", json=payload)
|
||||
resp = client.post(f"{self.base_url}/completion", json = payload)
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError(
|
||||
f"llama-server returned {resp.status_code}: {resp.text}"
|
||||
|
|
@ -5503,5 +5503,5 @@ class LlamaCppBackend:
|
|||
|
||||
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||
return LlamaCppBackend._codec_mgr.decode(
|
||||
audio_type, device, token_ids=token_ids, text=data.get("content", "")
|
||||
audio_type, device, token_ids = token_ids, text = data.get("content", "")
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ if sys.platform == "linux":
|
|||
|
||||
_libc_name = ctypes.util.find_library("c")
|
||||
if _libc_name:
|
||||
_libc = ctypes.CDLL(_libc_name, use_errno=True)
|
||||
_libc = ctypes.CDLL(_libc_name, use_errno = True)
|
||||
except (OSError, AttributeError):
|
||||
pass
|
||||
|
||||
|
|
@ -159,9 +159,9 @@ def _find_blocked_commands(command: str) -> set[str]:
|
|||
# position after the `;` separator).
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
tokens = shlex.split(command, posix=False)
|
||||
tokens = shlex.split(command, posix = False)
|
||||
else:
|
||||
lexer = shlex.shlex(command, posix=True, punctuation_chars=";&|()`")
|
||||
lexer = shlex.shlex(command, posix = True, punctuation_chars = ";&|()`")
|
||||
lexer.whitespace_split = True
|
||||
tokens = list(lexer)
|
||||
except ValueError:
|
||||
|
|
@ -428,7 +428,7 @@ def _get_workdir(session_id: str | None = None) -> str:
|
|||
workdir = os.path.join(sandbox_root, "_invalid")
|
||||
else:
|
||||
workdir = os.path.join(sandbox_root, "_default")
|
||||
os.makedirs(workdir, exist_ok=True)
|
||||
os.makedirs(workdir, exist_ok = True)
|
||||
try:
|
||||
os.chmod(sandbox_root, 0o700)
|
||||
except OSError:
|
||||
|
|
@ -548,7 +548,7 @@ def _render_html_result(arguments: dict) -> str:
|
|||
def execute_tool(
|
||||
name: str,
|
||||
arguments: dict,
|
||||
cancel_event=None,
|
||||
cancel_event = None,
|
||||
timeout: int | None = _TIMEOUT_UNSET,
|
||||
session_id: str | None = None,
|
||||
) -> str:
|
||||
|
|
@ -567,8 +567,8 @@ def execute_tool(
|
|||
if name == "web_search":
|
||||
return _web_search(
|
||||
arguments.get("query", ""),
|
||||
url=arguments.get("url"),
|
||||
timeout=effective_timeout,
|
||||
url = arguments.get("url"),
|
||||
timeout = effective_timeout,
|
||||
)
|
||||
if name == "python":
|
||||
return _python_exec(
|
||||
|
|
@ -627,7 +627,7 @@ class _PinnedHTTPSConnection(http.client.HTTPSConnection):
|
|||
# TLS handshake with the real hostname for SNI + cert verification.
|
||||
self.sock = self._context.wrap_socket(
|
||||
self.sock,
|
||||
server_hostname=self._sni_hostname,
|
||||
server_hostname = self._sni_hostname,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -640,7 +640,7 @@ class _SNIHTTPSHandler(urllib.request.HTTPSHandler):
|
|||
"""
|
||||
|
||||
def __init__(self, hostname: str):
|
||||
super().__init__(context=_tls_ctx)
|
||||
super().__init__(context = _tls_ctx)
|
||||
self._sni_hostname = hostname
|
||||
|
||||
def https_open(self, req):
|
||||
|
|
@ -648,7 +648,7 @@ class _SNIHTTPSHandler(urllib.request.HTTPSHandler):
|
|||
|
||||
def _sni_connection(self, host, **kwargs):
|
||||
kwargs["context"] = _tls_ctx
|
||||
return _PinnedHTTPSConnection(host, sni_hostname=self._sni_hostname, **kwargs)
|
||||
return _PinnedHTTPSConnection(host, sni_hostname = self._sni_hostname, **kwargs)
|
||||
|
||||
|
||||
def _validate_and_resolve_host(hostname: str, port: int) -> tuple[bool, str, str]:
|
||||
|
|
@ -662,7 +662,7 @@ def _validate_and_resolve_host(hostname: str, port: int) -> tuple[bool, str, str
|
|||
import socket
|
||||
|
||||
try:
|
||||
infos = socket.getaddrinfo(hostname, port, type=socket.SOCK_STREAM)
|
||||
infos = socket.getaddrinfo(hostname, port, type = socket.SOCK_STREAM)
|
||||
except OSError as e:
|
||||
return False, f"Failed to resolve host: {e}", ""
|
||||
|
||||
|
|
@ -723,7 +723,7 @@ def _fetch_page_text(
|
|||
# Bracket IPv6 addresses so the netloc is valid in a URL.
|
||||
ip_str = f"[{pinned_ip}]" if ":" in pinned_ip else pinned_ip
|
||||
ip_netloc = f"{ip_str}:{cp.port}" if cp.port else ip_str
|
||||
pinned_url = urlunparse(cp._replace(netloc=ip_netloc))
|
||||
pinned_url = urlunparse(cp._replace(netloc = ip_netloc))
|
||||
|
||||
opener = urllib.request.build_opener(
|
||||
_NoRedirect,
|
||||
|
|
@ -732,13 +732,13 @@ def _fetch_page_text(
|
|||
|
||||
req = urllib.request.Request(
|
||||
pinned_url,
|
||||
headers={
|
||||
headers = {
|
||||
"User-Agent": ua,
|
||||
"Host": current_host,
|
||||
},
|
||||
)
|
||||
try:
|
||||
resp = opener.open(req, timeout=timeout)
|
||||
resp = opener.open(req, timeout = timeout)
|
||||
except _HTTPError as e:
|
||||
if e.code not in (301, 302, 303, 307, 308):
|
||||
return (
|
||||
|
|
@ -767,7 +767,7 @@ def _fetch_page_text(
|
|||
return "Failed to fetch URL: too many redirects."
|
||||
|
||||
charset = resp.headers.get_content_charset() or "utf-8"
|
||||
raw_html = raw_bytes.decode(charset, errors="replace")
|
||||
raw_html = raw_bytes.decode(charset, errors = "replace")
|
||||
except _HTTPError as e:
|
||||
return f"Failed to fetch URL: HTTP {e.code} {getattr(e, 'reason', '')}"
|
||||
except Exception as e:
|
||||
|
|
@ -798,14 +798,14 @@ def _web_search(
|
|||
# Direct URL fetch mode
|
||||
if url and url.strip():
|
||||
fetch_timeout = 60 if timeout is None else min(timeout, 60)
|
||||
return _fetch_page_text(url.strip(), timeout=fetch_timeout)
|
||||
return _fetch_page_text(url.strip(), timeout = fetch_timeout)
|
||||
|
||||
if not query or not query.strip():
|
||||
return "No query provided."
|
||||
try:
|
||||
from ddgs import DDGS
|
||||
|
||||
results = DDGS(timeout=timeout).text(query, max_results=max_results)
|
||||
results = DDGS(timeout = timeout).text(query, max_results = max_results)
|
||||
if not results:
|
||||
return "No results found."
|
||||
parts = []
|
||||
|
|
@ -1883,7 +1883,7 @@ def _kill_process_tree(proc) -> None:
|
|||
pass
|
||||
|
||||
|
||||
def _cancel_watcher(proc, cancel_event, poll_interval=0.2):
|
||||
def _cancel_watcher(proc, cancel_event, poll_interval = 0.2):
|
||||
"""Daemon thread that kills a process when cancel_event is set."""
|
||||
while proc.poll() is None:
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
|
|
@ -1900,7 +1900,7 @@ def _truncate(text: str, limit: int = _MAX_OUTPUT_CHARS) -> str:
|
|||
|
||||
def _python_exec(
|
||||
code: str,
|
||||
cancel_event=None,
|
||||
cancel_event = None,
|
||||
timeout: int = _EXEC_TIMEOUT,
|
||||
session_id: str | None = None,
|
||||
) -> str:
|
||||
|
|
@ -1928,18 +1928,18 @@ def _python_exec(
|
|||
pass
|
||||
try:
|
||||
fd, tmp_path = tempfile.mkstemp(
|
||||
suffix=".py", prefix="studio_exec_", dir=workdir
|
||||
suffix = ".py", prefix = "studio_exec_", dir = workdir
|
||||
)
|
||||
with os.fdopen(fd, "w") as f:
|
||||
f.write(code)
|
||||
|
||||
safe_env = _build_safe_env(workdir)
|
||||
popen_kwargs = dict(
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
cwd=workdir,
|
||||
env=safe_env,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.STDOUT,
|
||||
text = True,
|
||||
cwd = workdir,
|
||||
env = safe_env,
|
||||
)
|
||||
if sys.platform != "win32":
|
||||
popen_kwargs["preexec_fn"] = _sandbox_preexec
|
||||
|
|
@ -1951,16 +1951,16 @@ def _python_exec(
|
|||
# Spawn cancel watcher if we have a cancel event
|
||||
if cancel_event is not None:
|
||||
watcher = threading.Thread(
|
||||
target=_cancel_watcher, args=(proc, cancel_event), daemon=True
|
||||
target = _cancel_watcher, args = (proc, cancel_event), daemon = True
|
||||
)
|
||||
watcher.start()
|
||||
|
||||
try:
|
||||
output, _ = proc.communicate(timeout=timeout)
|
||||
output, _ = proc.communicate(timeout = timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
_kill_process_tree(proc)
|
||||
try:
|
||||
proc.communicate(timeout=5)
|
||||
proc.communicate(timeout = 5)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
return _truncate(f"Execution timed out after {timeout} seconds.")
|
||||
|
|
@ -2007,7 +2007,7 @@ def _python_exec(
|
|||
|
||||
def _bash_exec(
|
||||
command: str,
|
||||
cancel_event=None,
|
||||
cancel_event = None,
|
||||
timeout: int = _EXEC_TIMEOUT,
|
||||
session_id: str | None = None,
|
||||
) -> str:
|
||||
|
|
@ -2024,11 +2024,11 @@ def _bash_exec(
|
|||
workdir = _get_workdir(session_id)
|
||||
safe_env = _build_safe_env(workdir)
|
||||
popen_kwargs = dict(
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
cwd=workdir,
|
||||
env=safe_env,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.STDOUT,
|
||||
text = True,
|
||||
cwd = workdir,
|
||||
env = safe_env,
|
||||
)
|
||||
if sys.platform != "win32":
|
||||
popen_kwargs["preexec_fn"] = _sandbox_preexec
|
||||
|
|
@ -2039,16 +2039,16 @@ def _bash_exec(
|
|||
|
||||
if cancel_event is not None:
|
||||
watcher = threading.Thread(
|
||||
target=_cancel_watcher, args=(proc, cancel_event), daemon=True
|
||||
target = _cancel_watcher, args = (proc, cancel_event), daemon = True
|
||||
)
|
||||
watcher.start()
|
||||
|
||||
try:
|
||||
output, _ = proc.communicate(timeout=timeout)
|
||||
output, _ = proc.communicate(timeout = timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
_kill_process_tree(proc)
|
||||
try:
|
||||
proc.communicate(timeout=5)
|
||||
proc.communicate(timeout = 5)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
return _truncate(f"Execution timed out after {timeout} seconds.")
|
||||
|
|
|
|||
|
|
@ -24,31 +24,31 @@ from pydantic import (
|
|||
class LoadRequest(BaseModel):
|
||||
"""Request to load a model for inference"""
|
||||
|
||||
model_path: str = Field(..., description="Model identifier or local path")
|
||||
model_path: str = Field(..., description = "Model identifier or local path")
|
||||
native_path_lease: Optional[str] = Field(
|
||||
None, description="Frontend-visible signed native path grant"
|
||||
None, description = "Frontend-visible signed native path grant"
|
||||
)
|
||||
hf_token: Optional[str] = Field(
|
||||
None, description="HuggingFace token for gated models"
|
||||
None, description = "HuggingFace token for gated models"
|
||||
)
|
||||
max_seq_length: int = Field(
|
||||
0,
|
||||
ge=0,
|
||||
le=1048576,
|
||||
description="Maximum sequence length (0 = model default for GGUF)",
|
||||
ge = 0,
|
||||
le = 1048576,
|
||||
description = "Maximum sequence length (0 = model default for GGUF)",
|
||||
)
|
||||
load_in_4bit: bool = Field(True, description="Load model in 4-bit quantization")
|
||||
is_lora: bool = Field(False, description="Whether this is a LoRA adapter")
|
||||
load_in_4bit: bool = Field(True, description = "Load model in 4-bit quantization")
|
||||
is_lora: bool = Field(False, description = "Whether this is a LoRA adapter")
|
||||
gguf_variant: Optional[str] = Field(
|
||||
None, description="GGUF quantization variant (e.g. 'Q4_K_M')"
|
||||
None, description = "GGUF quantization variant (e.g. 'Q4_K_M')"
|
||||
)
|
||||
trust_remote_code: bool = Field(
|
||||
False,
|
||||
description="Allow loading models with custom code (e.g. NVIDIA Nemotron). Only enable for repos you trust.",
|
||||
description = "Allow loading models with custom code (e.g. NVIDIA Nemotron). Only enable for repos you trust.",
|
||||
)
|
||||
chat_template_override: Optional[str] = Field(
|
||||
None,
|
||||
description="Custom Jinja2 chat template to use instead of the model's default",
|
||||
description = "Custom Jinja2 chat template to use instead of the model's default",
|
||||
)
|
||||
|
||||
@field_validator("chat_template_override")
|
||||
|
|
@ -62,15 +62,15 @@ class LoadRequest(BaseModel):
|
|||
|
||||
cache_type_kv: Optional[str] = Field(
|
||||
None,
|
||||
description="KV cache data type for both K and V (e.g. 'f16', 'bf16', 'q8_0', 'q4_1', 'q5_1')",
|
||||
description = "KV cache data type for both K and V (e.g. 'f16', 'bf16', 'q8_0', 'q4_1', 'q5_1')",
|
||||
)
|
||||
gpu_ids: Optional[List[int]] = Field(
|
||||
None,
|
||||
description="Physical GPU indices to use, for example [0, 1]. Omit or pass [] to use automatic selection. Explicit gpu_ids are unsupported when the parent CUDA_VISIBLE_DEVICES uses UUID/MIG entries. Not supported for GGUF models.",
|
||||
description = "Physical GPU indices to use, for example [0, 1]. Omit or pass [] to use automatic selection. Explicit gpu_ids are unsupported when the parent CUDA_VISIBLE_DEVICES uses UUID/MIG entries. Not supported for GGUF models.",
|
||||
)
|
||||
speculative_type: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Speculative decoding mode for GGUF models. Canonical values: "
|
||||
"'auto' (platform-aware: MTP on MTP GGUFs, ngram-mod fallback "
|
||||
"for sub-3B), 'mtp' (force draft-mtp only on both GPU and CPU), "
|
||||
|
|
@ -83,9 +83,9 @@ class LoadRequest(BaseModel):
|
|||
)
|
||||
spec_draft_n_max: Optional[int] = Field(
|
||||
None,
|
||||
ge=1,
|
||||
le=16,
|
||||
description=(
|
||||
ge = 1,
|
||||
le = 16,
|
||||
description = (
|
||||
"Max draft tokens per step for MTP speculative decoding "
|
||||
"(--spec-draft-n-max). Defaults to 2 on GPU and 3 on CPU/Mac "
|
||||
"when unset (upstream-bench sweet spot for dense Qwen3.6 MTP "
|
||||
|
|
@ -95,7 +95,7 @@ class LoadRequest(BaseModel):
|
|||
)
|
||||
llama_extra_args: Optional[List[str]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Extra arguments forwarded verbatim to llama-server for GGUF models. "
|
||||
"One token per list entry, e.g. ['--top-k', '20', '--seed', '42']. "
|
||||
"Studio-managed flags (model identity, port, context length, GPU placement, "
|
||||
|
|
@ -108,7 +108,7 @@ class LoadRequest(BaseModel):
|
|||
class UnloadRequest(BaseModel):
|
||||
"""Request to unload a model"""
|
||||
|
||||
model_path: str = Field(..., description="Model identifier to unload")
|
||||
model_path: str = Field(..., description = "Model identifier to unload")
|
||||
|
||||
|
||||
class ValidateModelRequest(BaseModel):
|
||||
|
|
@ -119,15 +119,15 @@ class ValidateModelRequest(BaseModel):
|
|||
This does NOT actually load weights into GPU memory.
|
||||
"""
|
||||
|
||||
model_path: str = Field(..., description="Model identifier or local path")
|
||||
model_path: str = Field(..., description = "Model identifier or local path")
|
||||
native_path_lease: Optional[str] = Field(
|
||||
None, description="Frontend-visible signed native path grant"
|
||||
None, description = "Frontend-visible signed native path grant"
|
||||
)
|
||||
hf_token: Optional[str] = Field(
|
||||
None, description="HuggingFace token for gated models"
|
||||
None, description = "HuggingFace token for gated models"
|
||||
)
|
||||
gguf_variant: Optional[str] = Field(
|
||||
None, description="GGUF quantization variant (e.g. 'Q4_K_M')"
|
||||
None, description = "GGUF quantization variant (e.g. 'Q4_K_M')"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -139,107 +139,107 @@ class ValidateModelResponse(BaseModel):
|
|||
introspection (GGUF / LoRA / vision flags) is available.
|
||||
"""
|
||||
|
||||
valid: bool = Field(..., description="Whether the model identifier looks valid")
|
||||
message: str = Field(..., description="Human-readable validation message")
|
||||
identifier: Optional[str] = Field(None, description="Resolved model identifier")
|
||||
valid: bool = Field(..., description = "Whether the model identifier looks valid")
|
||||
message: str = Field(..., description = "Human-readable validation message")
|
||||
identifier: Optional[str] = Field(None, description = "Resolved model identifier")
|
||||
display_name: Optional[str] = Field(
|
||||
None, description="Display name derived from identifier"
|
||||
None, description = "Display name derived from identifier"
|
||||
)
|
||||
is_gguf: bool = Field(False, description="Whether this is a GGUF model (llama.cpp)")
|
||||
is_lora: bool = Field(False, description="Whether this is a LoRA adapter")
|
||||
is_vision: bool = Field(False, description="Whether this is a vision-capable model")
|
||||
is_gguf: bool = Field(False, description = "Whether this is a GGUF model (llama.cpp)")
|
||||
is_lora: bool = Field(False, description = "Whether this is a LoRA adapter")
|
||||
is_vision: bool = Field(False, description = "Whether this is a vision-capable model")
|
||||
requires_trust_remote_code: bool = Field(
|
||||
False,
|
||||
description="Whether the model defaults require trust_remote_code to be enabled for loading.",
|
||||
description = "Whether the model defaults require trust_remote_code to be enabled for loading.",
|
||||
)
|
||||
|
||||
|
||||
class GenerateRequest(BaseModel):
|
||||
"""Request for text generation (legacy /generate/stream endpoint)"""
|
||||
|
||||
messages: List[dict] = Field(..., description="Chat messages in OpenAI format")
|
||||
system_prompt: str = Field("", description="System prompt")
|
||||
temperature: float = Field(0.6, ge=0.0, le=2.0, description="Sampling temperature")
|
||||
top_p: float = Field(0.95, ge=0.0, le=1.0, description="Top-p sampling")
|
||||
top_k: int = Field(20, ge=-1, le=100, description="Top-k sampling")
|
||||
messages: List[dict] = Field(..., description = "Chat messages in OpenAI format")
|
||||
system_prompt: str = Field("", description = "System prompt")
|
||||
temperature: float = Field(0.6, ge = 0.0, le = 2.0, description = "Sampling temperature")
|
||||
top_p: float = Field(0.95, ge = 0.0, le = 1.0, description = "Top-p sampling")
|
||||
top_k: int = Field(20, ge = -1, le = 100, description = "Top-k sampling")
|
||||
max_new_tokens: int = Field(
|
||||
2048, ge=1, le=4096, description="Maximum tokens to generate"
|
||||
2048, ge = 1, le = 4096, description = "Maximum tokens to generate"
|
||||
)
|
||||
repetition_penalty: float = Field(
|
||||
1.0, ge=1.0, le=2.0, description="Repetition penalty"
|
||||
1.0, ge = 1.0, le = 2.0, description = "Repetition penalty"
|
||||
)
|
||||
presence_penalty: float = Field(0.0, ge=0.0, le=2.0, description="Presence penalty")
|
||||
presence_penalty: float = Field(0.0, ge = 0.0, le = 2.0, description = "Presence penalty")
|
||||
image_base64: Optional[str] = Field(
|
||||
None, description="Base64 encoded image for vision models"
|
||||
None, description = "Base64 encoded image for vision models"
|
||||
)
|
||||
|
||||
|
||||
class LoadResponse(BaseModel):
|
||||
"""Response after loading a model"""
|
||||
|
||||
status: str = Field(..., description="Load status")
|
||||
model: str = Field(..., description="Model identifier")
|
||||
display_name: str = Field(..., description="Display name of the model")
|
||||
is_vision: bool = Field(False, description="Whether model is a vision model")
|
||||
is_lora: bool = Field(False, description="Whether model is a LoRA adapter")
|
||||
status: str = Field(..., description = "Load status")
|
||||
model: str = Field(..., description = "Model identifier")
|
||||
display_name: str = Field(..., description = "Display name of the model")
|
||||
is_vision: bool = Field(False, description = "Whether model is a vision model")
|
||||
is_lora: bool = Field(False, description = "Whether model is a LoRA adapter")
|
||||
is_gguf: bool = Field(
|
||||
False, description="Whether model is a GGUF model (llama.cpp)"
|
||||
False, description = "Whether model is a GGUF model (llama.cpp)"
|
||||
)
|
||||
is_audio: bool = Field(False, description="Whether model is a TTS audio model")
|
||||
is_audio: bool = Field(False, description = "Whether model is a TTS audio model")
|
||||
audio_type: Optional[str] = Field(
|
||||
None, description="Audio codec type: snac, csm, bicodec, dac"
|
||||
None, description = "Audio codec type: snac, csm, bicodec, dac"
|
||||
)
|
||||
has_audio_input: bool = Field(
|
||||
False, description="Whether model accepts audio input (ASR)"
|
||||
False, description = "Whether model accepts audio input (ASR)"
|
||||
)
|
||||
inference: dict = Field(
|
||||
..., description="Inference parameters (temperature, top_p, top_k, min_p)"
|
||||
..., description = "Inference parameters (temperature, top_p, top_k, min_p)"
|
||||
)
|
||||
requires_trust_remote_code: bool = Field(
|
||||
False,
|
||||
description="Whether the model defaults require trust_remote_code to be enabled for loading.",
|
||||
description = "Whether the model defaults require trust_remote_code to be enabled for loading.",
|
||||
)
|
||||
context_length: Optional[int] = Field(
|
||||
None, description="Model's native context length (from GGUF metadata)"
|
||||
None, description = "Model's native context length (from GGUF metadata)"
|
||||
)
|
||||
max_context_length: Optional[int] = Field(
|
||||
None, description="Maximum context length currently available on this hardware"
|
||||
None, description = "Maximum context length currently available on this hardware"
|
||||
)
|
||||
native_context_length: Optional[int] = Field(
|
||||
None,
|
||||
description="Model's native context length from GGUF metadata (not capped by VRAM)",
|
||||
description = "Model's native context length from GGUF metadata (not capped by VRAM)",
|
||||
)
|
||||
supports_reasoning: bool = Field(
|
||||
False,
|
||||
description="Whether model supports thinking/reasoning mode (enable_thinking or reasoning_effort)",
|
||||
description = "Whether model supports thinking/reasoning mode (enable_thinking or reasoning_effort)",
|
||||
)
|
||||
reasoning_style: Literal["enable_thinking", "reasoning_effort"] = Field(
|
||||
"enable_thinking",
|
||||
description="Reasoning control style: 'enable_thinking' (boolean) or 'reasoning_effort' (low|medium|high)",
|
||||
description = "Reasoning control style: 'enable_thinking' (boolean) or 'reasoning_effort' (low|medium|high)",
|
||||
)
|
||||
reasoning_always_on: bool = Field(
|
||||
False,
|
||||
description="Whether reasoning is always on (hardcoded <think> tags, not toggleable)",
|
||||
description = "Whether reasoning is always on (hardcoded <think> tags, not toggleable)",
|
||||
)
|
||||
supports_preserve_thinking: bool = Field(
|
||||
False,
|
||||
description="Whether the template understands the optional preserve_thinking kwarg (Qwen3.6-style)",
|
||||
description = "Whether the template understands the optional preserve_thinking kwarg (Qwen3.6-style)",
|
||||
)
|
||||
supports_tools: bool = Field(
|
||||
False,
|
||||
description="Whether model supports tool calling (web search, etc.)",
|
||||
description = "Whether model supports tool calling (web search, etc.)",
|
||||
)
|
||||
cache_type_kv: Optional[str] = Field(
|
||||
None,
|
||||
description="KV cache data type for K and V (e.g. 'f16', 'bf16', 'q8_0')",
|
||||
description = "KV cache data type for K and V (e.g. 'f16', 'bf16', 'q8_0')",
|
||||
)
|
||||
chat_template: Optional[str] = Field(
|
||||
None,
|
||||
description="Jinja2 chat template string (from GGUF metadata or tokenizer)",
|
||||
description = "Jinja2 chat template string (from GGUF metadata or tokenizer)",
|
||||
)
|
||||
speculative_type: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Canonical UI-facing requested speculative decoding mode "
|
||||
"('auto' / 'mtp' / 'ngram' / 'mtp+ngram' / 'off' / "
|
||||
"'ngram-simple'), round-tripped from the original LoadRequest "
|
||||
|
|
@ -248,7 +248,7 @@ class LoadResponse(BaseModel):
|
|||
)
|
||||
spec_draft_n_max: Optional[int] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Active --spec-draft-n-max for MTP speculative decoding, or "
|
||||
"None when the platform default is in effect."
|
||||
),
|
||||
|
|
@ -258,8 +258,8 @@ class LoadResponse(BaseModel):
|
|||
class UnloadResponse(BaseModel):
|
||||
"""Response after unloading a model"""
|
||||
|
||||
status: str = Field(..., description="Unload status")
|
||||
model: str = Field(..., description="Model identifier that was unloaded")
|
||||
status: str = Field(..., description = "Unload status")
|
||||
model: str = Field(..., description = "Model identifier that was unloaded")
|
||||
|
||||
|
||||
class LoadProgressResponse(BaseModel):
|
||||
|
|
@ -273,7 +273,7 @@ class LoadProgressResponse(BaseModel):
|
|||
|
||||
phase: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Load phase: 'mmap' (weights paging into RAM via mmap), "
|
||||
"'ready' (llama-server reported healthy), or null when no "
|
||||
"load is in flight."
|
||||
|
|
@ -281,17 +281,17 @@ class LoadProgressResponse(BaseModel):
|
|||
)
|
||||
bytes_loaded: int = Field(
|
||||
0,
|
||||
description=(
|
||||
description = (
|
||||
"Bytes of the model already resident in the llama-server "
|
||||
"process (VmRSS on Linux)."
|
||||
),
|
||||
)
|
||||
bytes_total: int = Field(
|
||||
0,
|
||||
description="Total bytes across all GGUF shards for the active model.",
|
||||
description = "Total bytes across all GGUF shards for the active model.",
|
||||
)
|
||||
fraction: float = Field(
|
||||
0.0, description="bytes_loaded / bytes_total, clamped to 0..1."
|
||||
0.0, description = "bytes_loaded / bytes_total, clamped to 0..1."
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -299,81 +299,81 @@ class InferenceStatusResponse(BaseModel):
|
|||
"""Current inference backend status"""
|
||||
|
||||
active_model: Optional[str] = Field(
|
||||
None, description="Currently active model identifier"
|
||||
None, description = "Currently active model identifier"
|
||||
)
|
||||
is_vision: bool = Field(
|
||||
False, description="Whether the active model is a vision model"
|
||||
False, description = "Whether the active model is a vision model"
|
||||
)
|
||||
is_gguf: bool = Field(
|
||||
False, description="Whether the active model is a GGUF model (llama.cpp)"
|
||||
False, description = "Whether the active model is a GGUF model (llama.cpp)"
|
||||
)
|
||||
gguf_variant: Optional[str] = Field(
|
||||
None, description="GGUF quantization variant (e.g. Q4_K_M)"
|
||||
None, description = "GGUF quantization variant (e.g. Q4_K_M)"
|
||||
)
|
||||
is_audio: bool = Field(
|
||||
False, description="Whether the active model is a TTS audio model"
|
||||
False, description = "Whether the active model is a TTS audio model"
|
||||
)
|
||||
audio_type: Optional[str] = Field(
|
||||
None, description="Audio codec type: snac, csm, bicodec, dac"
|
||||
None, description = "Audio codec type: snac, csm, bicodec, dac"
|
||||
)
|
||||
has_audio_input: bool = Field(
|
||||
False, description="Whether model accepts audio input (ASR)"
|
||||
False, description = "Whether model accepts audio input (ASR)"
|
||||
)
|
||||
loading: List[str] = Field(
|
||||
default_factory=list, description="Models currently being loaded"
|
||||
default_factory = list, description = "Models currently being loaded"
|
||||
)
|
||||
loaded: List[str] = Field(
|
||||
default_factory=list, description="Models currently loaded"
|
||||
default_factory = list, description = "Models currently loaded"
|
||||
)
|
||||
inference: Optional[Dict[str, Any]] = Field(
|
||||
None, description="Recommended inference parameters for the active model"
|
||||
None, description = "Recommended inference parameters for the active model"
|
||||
)
|
||||
requires_trust_remote_code: bool = Field(
|
||||
False,
|
||||
description="Whether the active model requires trust_remote_code to be enabled for loading.",
|
||||
description = "Whether the active model requires trust_remote_code to be enabled for loading.",
|
||||
)
|
||||
supports_reasoning: bool = Field(
|
||||
False, description="Whether the active model supports reasoning/thinking mode"
|
||||
False, description = "Whether the active model supports reasoning/thinking mode"
|
||||
)
|
||||
reasoning_style: Literal["enable_thinking", "reasoning_effort"] = Field(
|
||||
"enable_thinking",
|
||||
description="Reasoning control style: 'enable_thinking' (boolean) or 'reasoning_effort' (low|medium|high)",
|
||||
description = "Reasoning control style: 'enable_thinking' (boolean) or 'reasoning_effort' (low|medium|high)",
|
||||
)
|
||||
reasoning_always_on: bool = Field(
|
||||
False, description="Whether reasoning is always on (not toggleable)"
|
||||
False, description = "Whether reasoning is always on (not toggleable)"
|
||||
)
|
||||
supports_preserve_thinking: bool = Field(
|
||||
False,
|
||||
description="Whether the active model's template understands the optional preserve_thinking kwarg",
|
||||
description = "Whether the active model's template understands the optional preserve_thinking kwarg",
|
||||
)
|
||||
supports_tools: bool = Field(
|
||||
False, description="Whether the active model supports tool calling"
|
||||
False, description = "Whether the active model supports tool calling"
|
||||
)
|
||||
context_length: Optional[int] = Field(
|
||||
None, description="Context length of the active model"
|
||||
None, description = "Context length of the active model"
|
||||
)
|
||||
max_context_length: Optional[int] = Field(
|
||||
None,
|
||||
description="Maximum context length currently available for the active model",
|
||||
description = "Maximum context length currently available for the active model",
|
||||
)
|
||||
native_context_length: Optional[int] = Field(
|
||||
None,
|
||||
description="Model's native context length from GGUF metadata (not capped by VRAM)",
|
||||
description = "Model's native context length from GGUF metadata (not capped by VRAM)",
|
||||
)
|
||||
cache_type_kv: Optional[str] = Field(
|
||||
None,
|
||||
description="KV cache quantization dtype (e.g. 'q8_0'), or None for default",
|
||||
description = "KV cache quantization dtype (e.g. 'q8_0'), or None for default",
|
||||
)
|
||||
chat_template: Optional[str] = Field(
|
||||
None, description="Model's default chat template (Jinja2 source), if any"
|
||||
None, description = "Model's default chat template (Jinja2 source), if any"
|
||||
)
|
||||
chat_template_override: Optional[str] = Field(
|
||||
None,
|
||||
description="Active chat template override applied at load time, or None if model is using its default",
|
||||
description = "Active chat template override applied at load time, or None if model is using its default",
|
||||
)
|
||||
speculative_type: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Canonical UI-facing requested speculative decoding mode "
|
||||
"('auto' / 'mtp' / 'ngram' / 'mtp+ngram' / 'off' / "
|
||||
"'ngram-simple'), round-tripped from the original LoadRequest. "
|
||||
|
|
@ -382,32 +382,32 @@ class InferenceStatusResponse(BaseModel):
|
|||
)
|
||||
spec_draft_n_max: Optional[int] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Active --spec-draft-n-max for MTP speculative decoding, or "
|
||||
"None when the platform default is in effect."
|
||||
),
|
||||
)
|
||||
llama_cpp_supports_mtp: bool = Field(
|
||||
True,
|
||||
description=(
|
||||
description = (
|
||||
"Whether llama.cpp supports MTP (--spec-type mtp/draft-mtp). "
|
||||
"False -> recommend `unsloth studio update`."
|
||||
),
|
||||
)
|
||||
llama_cpp_prebuilt_stale: bool = Field(
|
||||
False,
|
||||
description=(
|
||||
description = (
|
||||
"Installed llama.cpp prebuilt is >=3 days behind the latest "
|
||||
"release. True -> show `unsloth studio update` banner."
|
||||
),
|
||||
)
|
||||
llama_cpp_installed_tag: Optional[str] = Field(
|
||||
None,
|
||||
description="Installed llama.cpp tag, or None if unknown.",
|
||||
description = "Installed llama.cpp tag, or None if unknown.",
|
||||
)
|
||||
llama_cpp_latest_tag: Optional[str] = Field(
|
||||
None,
|
||||
description="Latest published llama.cpp tag, or None if GitHub unreachable.",
|
||||
description = "Latest published llama.cpp tag, or None if GitHub unreachable.",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -429,7 +429,7 @@ class TextContentPart(BaseModel):
|
|||
class ImageUrl(BaseModel):
|
||||
"""Image URL object — supports data URIs and remote URLs."""
|
||||
|
||||
url: str = Field(..., description="data:image/png;base64,... or https://...")
|
||||
url: str = Field(..., description = "data:image/png;base64,... or https://...")
|
||||
detail: Optional[Literal["auto", "low", "high"]] = "auto"
|
||||
|
||||
|
||||
|
|
@ -455,19 +455,19 @@ class InputDocumentContentPart(BaseModel):
|
|||
type: Literal["input_document"]
|
||||
file_data: Optional[str] = Field(
|
||||
None,
|
||||
description="data:<media_type>;base64,<DATA> URI for inline payloads. Either file_data or file_url must be set; otherwise the part is dropped.",
|
||||
description = "data:<media_type>;base64,<DATA> URI for inline payloads. Either file_data or file_url must be set; otherwise the part is dropped.",
|
||||
)
|
||||
file_url: Optional[str] = Field(
|
||||
None,
|
||||
description="Remote URL pointing to the document (https://...).",
|
||||
description = "Remote URL pointing to the document (https://...).",
|
||||
)
|
||||
filename: Optional[str] = Field(
|
||||
None,
|
||||
description="Display filename, forwarded to providers as `title`/`filename`.",
|
||||
description = "Display filename, forwarded to providers as `title`/`filename`.",
|
||||
)
|
||||
media_type: Optional[str] = Field(
|
||||
None,
|
||||
description='Override the media type sniffed from the data URI (e.g. "application/pdf").',
|
||||
description = 'Override the media type sniffed from the data URI (e.g. "application/pdf").',
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -489,7 +489,7 @@ class CompactionContentPart(BaseModel):
|
|||
type: Literal["compaction"]
|
||||
content: str = Field(
|
||||
...,
|
||||
description="Anthropic-produced summary of the compacted-away conversation prefix.",
|
||||
description = "Anthropic-produced summary of the compacted-away conversation prefix.",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -524,25 +524,25 @@ class ChatMessage(BaseModel):
|
|||
"""
|
||||
|
||||
role: Literal["system", "user", "assistant", "tool"] = Field(
|
||||
..., description="Message role"
|
||||
..., description = "Message role"
|
||||
)
|
||||
content: Optional[Union[str, list[ContentPart]]] = Field(
|
||||
None, description="Message content (string or multimodal parts)"
|
||||
None, description = "Message content (string or multimodal parts)"
|
||||
)
|
||||
tool_call_id: Optional[str] = Field(
|
||||
None,
|
||||
description="OpenAI tool-result messages: id of the tool call this result belongs to.",
|
||||
description = "OpenAI tool-result messages: id of the tool call this result belongs to.",
|
||||
)
|
||||
tool_calls: Optional[list[dict]] = Field(
|
||||
None,
|
||||
description="OpenAI assistant messages: structured tool calls the model decided to make.",
|
||||
description = "OpenAI assistant messages: structured tool calls the model decided to make.",
|
||||
)
|
||||
name: Optional[str] = Field(
|
||||
None,
|
||||
description="OpenAI tool-result messages: name of the tool whose result this is.",
|
||||
description = "OpenAI tool-result messages: name of the tool whose result this is.",
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
@model_validator(mode = "after")
|
||||
def _validate_role_shape(self) -> "ChatMessage":
|
||||
if self.tool_calls is not None and self.role != "assistant":
|
||||
raise ValueError('"tool_calls" is only valid on role="assistant" messages.')
|
||||
|
|
@ -580,29 +580,29 @@ class ChatCompletionRequest(BaseModel):
|
|||
|
||||
model: str = Field(
|
||||
"default",
|
||||
description="Model identifier (informational; the active model is used)",
|
||||
description = "Model identifier (informational; the active model is used)",
|
||||
)
|
||||
messages: list[ChatMessage] = Field(..., description="Conversation messages")
|
||||
messages: list[ChatMessage] = Field(..., description = "Conversation messages")
|
||||
stream: bool = Field(
|
||||
False,
|
||||
description=(
|
||||
description = (
|
||||
"Whether to stream the response via SSE. Default matches OpenAI's "
|
||||
"spec (`false`); opt into streaming by sending `stream: true`."
|
||||
),
|
||||
)
|
||||
temperature: float = Field(0.6, ge=0.0, le=2.0)
|
||||
top_p: float = Field(0.95, ge=0.0, le=1.0)
|
||||
temperature: float = Field(0.6, ge = 0.0, le = 2.0)
|
||||
top_p: float = Field(0.95, ge = 0.0, le = 1.0)
|
||||
max_tokens: Optional[int] = Field(
|
||||
None, ge=1, description="Maximum tokens to generate (None = until EOS)"
|
||||
None, ge = 1, description = "Maximum tokens to generate (None = until EOS)"
|
||||
)
|
||||
presence_penalty: float = Field(0.0, ge=0.0, le=2.0, description="Presence penalty")
|
||||
presence_penalty: float = Field(0.0, ge = 0.0, le = 2.0, description = "Presence penalty")
|
||||
stop: Optional[Union[str, list[str]]] = Field(
|
||||
None,
|
||||
description="OpenAI stop sequences: a single string or list of strings at which generation halts.",
|
||||
description = "OpenAI stop sequences: a single string or list of strings at which generation halts.",
|
||||
)
|
||||
tools: Optional[list[dict]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"OpenAI function-tool definitions. When provided without `enable_tools=true`, "
|
||||
"Studio forwards the tools to the backend so the model returns structured "
|
||||
"tool_calls for the client to execute (standard OpenAI function calling)."
|
||||
|
|
@ -610,29 +610,29 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
tool_choice: Optional[Union[str, dict]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"OpenAI tool choice: 'auto' | 'required' | 'none' | "
|
||||
"{'type': 'function', 'function': {'name': ...}}"
|
||||
),
|
||||
)
|
||||
|
||||
# ── Unsloth extensions (ignored by standard OpenAI clients) ──
|
||||
top_k: int = Field(20, ge=-1, le=100, description="[x-unsloth] Top-k sampling")
|
||||
top_k: int = Field(20, ge = -1, le = 100, description = "[x-unsloth] Top-k sampling")
|
||||
min_p: float = Field(
|
||||
0.01, ge=0.0, le=1.0, description="[x-unsloth] Min-p sampling threshold"
|
||||
0.01, ge = 0.0, le = 1.0, description = "[x-unsloth] Min-p sampling threshold"
|
||||
)
|
||||
repetition_penalty: float = Field(
|
||||
1.0, ge=1.0, le=2.0, description="[x-unsloth] Repetition penalty"
|
||||
1.0, ge = 1.0, le = 2.0, description = "[x-unsloth] Repetition penalty"
|
||||
)
|
||||
image_base64: Optional[str] = Field(
|
||||
None, description="[x-unsloth] Base64-encoded image for vision models"
|
||||
None, description = "[x-unsloth] Base64-encoded image for vision models"
|
||||
)
|
||||
audio_base64: Optional[str] = Field(
|
||||
None, description="[x-unsloth] Base64-encoded WAV for audio-input models (ASR)"
|
||||
None, description = "[x-unsloth] Base64-encoded WAV for audio-input models (ASR)"
|
||||
)
|
||||
use_adapter: Optional[Union[bool, str]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] Adapter control for compare mode. "
|
||||
"null = no change (default), "
|
||||
"false = disable adapters (base model), "
|
||||
|
|
@ -642,25 +642,25 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
enable_thinking: Optional[bool] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Enable/disable thinking/reasoning mode for supported models",
|
||||
description = "[x-unsloth] Enable/disable thinking/reasoning mode for supported models",
|
||||
)
|
||||
reasoning_effort: Optional[
|
||||
Literal["none", "minimal", "low", "medium", "high", "max", "xhigh"]
|
||||
] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Reasoning effort level ('none'|'minimal'|'low'|'medium'|'high'|'max'|'xhigh'). OpenAI `/v1/responses` accepts model-dependent subsets; Anthropic adaptive thinking uses `max` as the top tier on Claude 4.6 Opus/Sonnet (inbound `xhigh` is mapped to `max`) and `xhigh` on Claude 4.7 Opus; local Harmony/gpt-oss templates support low|medium|high.",
|
||||
description = "[x-unsloth] Reasoning effort level ('none'|'minimal'|'low'|'medium'|'high'|'max'|'xhigh'). OpenAI `/v1/responses` accepts model-dependent subsets; Anthropic adaptive thinking uses `max` as the top tier on Claude 4.6 Opus/Sonnet (inbound `xhigh` is mapped to `max`) and `xhigh` on Claude 4.7 Opus; local Harmony/gpt-oss templates support low|medium|high.",
|
||||
)
|
||||
preserve_thinking: Optional[bool] = Field(
|
||||
None,
|
||||
description="[x-unsloth] When true, keep historical <think> blocks from past assistant turns in the prompt (Qwen3.6 templates). Independent of enable_thinking / reasoning_effort.",
|
||||
description = "[x-unsloth] When true, keep historical <think> blocks from past assistant turns in the prompt (Qwen3.6 templates). Independent of enable_thinking / reasoning_effort.",
|
||||
)
|
||||
enable_tools: Optional[bool] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Enable tool calling for supported models",
|
||||
description = "[x-unsloth] Enable tool calling for supported models",
|
||||
)
|
||||
enabled_tools: Optional[list[str]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] List of enabled tool names. Local GGUF/safetensors models "
|
||||
"accept ['web_search', 'python', 'terminal', 'render_html']. External "
|
||||
"providers accept ['web_search', 'web_fetch', 'code_execution'] for "
|
||||
|
|
@ -671,51 +671,51 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
auto_heal_tool_calls: Optional[bool] = Field(
|
||||
True,
|
||||
description="[x-unsloth] Auto-detect and fix malformed tool calls from model output.",
|
||||
description = "[x-unsloth] Auto-detect and fix malformed tool calls from model output.",
|
||||
)
|
||||
max_tool_calls_per_message: Optional[int] = Field(
|
||||
25,
|
||||
ge=0,
|
||||
description="[x-unsloth] Maximum number of tool call iterations per message (0 = disabled, 9999 = unlimited).",
|
||||
ge = 0,
|
||||
description = "[x-unsloth] Maximum number of tool call iterations per message (0 = disabled, 9999 = unlimited).",
|
||||
)
|
||||
tool_call_timeout: Optional[int] = Field(
|
||||
300,
|
||||
ge=1,
|
||||
description="[x-unsloth] Timeout in seconds for each tool call execution (9999 = no limit).",
|
||||
ge = 1,
|
||||
description = "[x-unsloth] Timeout in seconds for each tool call execution (9999 = no limit).",
|
||||
)
|
||||
session_id: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Session/thread ID for scoping tool execution sandbox.",
|
||||
description = "[x-unsloth] Session/thread ID for scoping tool execution sandbox.",
|
||||
)
|
||||
cancel_id: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Per-request cancellation token. Frontend sends a fresh UUID per run so /inference/cancel matches one specific generation.",
|
||||
description = "[x-unsloth] Per-request cancellation token. Frontend sends a fresh UUID per run so /inference/cancel matches one specific generation.",
|
||||
)
|
||||
|
||||
# ── External provider routing (x-unsloth extensions) ──────────
|
||||
provider_id: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Saved provider config ID. If set with encrypted_api_key, routes to external LLM.",
|
||||
description = "[x-unsloth] Saved provider config ID. If set with encrypted_api_key, routes to external LLM.",
|
||||
)
|
||||
provider_type: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Provider type (e.g. 'openai', 'mistral'). Used if provider_id is not set.",
|
||||
description = "[x-unsloth] Provider type (e.g. 'openai', 'mistral'). Used if provider_id is not set.",
|
||||
)
|
||||
external_model: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Model ID at the external provider.",
|
||||
description = "[x-unsloth] Model ID at the external provider.",
|
||||
)
|
||||
encrypted_api_key: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] RSA-encrypted, base64-encoded API key for the external provider.",
|
||||
description = "[x-unsloth] RSA-encrypted, base64-encoded API key for the external provider.",
|
||||
)
|
||||
provider_base_url: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] Override base URL for the external provider.",
|
||||
description = "[x-unsloth] Override base URL for the external provider.",
|
||||
)
|
||||
enable_prompt_caching: Optional[bool] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] Opt in to provider-side prompt caching. On Anthropic, "
|
||||
"attaches cache_control={type:ephemeral} to the system block so the "
|
||||
"static prefix is reused across turns. On OpenAI cloud, caching is "
|
||||
|
|
@ -726,7 +726,7 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
prompt_cache_ttl: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] Anthropic cache_control TTL. Defaults to the 5-minute "
|
||||
"ephemeral pool when omitted. Pass `1h` to write into the 1-hour "
|
||||
"pool instead -- 1h writes are billed at 2x base input vs 1.25x "
|
||||
|
|
@ -739,9 +739,9 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
compaction_threshold: Optional[int] = Field(
|
||||
None,
|
||||
ge=1,
|
||||
le=2_000_000,
|
||||
description=(
|
||||
ge = 1,
|
||||
le = 2_000_000,
|
||||
description = (
|
||||
"[x-unsloth] Server-side context compaction trigger, in tokens. "
|
||||
"Per-provider routing:\n"
|
||||
" - Anthropic (Opus 4.6+, Sonnet 4.6, Mythos preview): attaches "
|
||||
|
|
@ -763,7 +763,7 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
openai_code_exec_container_id: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] OpenAI shell-tool container id from the prior response "
|
||||
"in the same chat thread. When set and `code_execution` is in "
|
||||
"`enabled_tools`, the next /v1/responses call uses "
|
||||
|
|
@ -775,7 +775,7 @@ class ChatCompletionRequest(BaseModel):
|
|||
)
|
||||
anthropic_code_exec_container_id: Optional[str] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"[x-unsloth] Anthropic code_execution container id from the prior "
|
||||
"response in the same chat thread. When set and `code_execution` "
|
||||
"is in `enabled_tools`, the next /v1/messages call carries a "
|
||||
|
|
@ -788,7 +788,7 @@ class ChatCompletionRequest(BaseModel):
|
|||
),
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
@model_validator(mode = "after")
|
||||
def _resolve_missing_tool_call_ids(self) -> "ChatCompletionRequest":
|
||||
"""Fill missing tool_call_id by walking back to the preceding assistant.
|
||||
|
||||
|
|
@ -873,26 +873,26 @@ class OpenAIContainerRequest(BaseModel):
|
|||
|
||||
encrypted_api_key: str = Field(
|
||||
...,
|
||||
description="[x-unsloth] RSA-encrypted, base64-encoded OpenAI API key.",
|
||||
description = "[x-unsloth] RSA-encrypted, base64-encoded OpenAI API key.",
|
||||
)
|
||||
provider_base_url: Optional[str] = Field(
|
||||
None,
|
||||
description="[x-unsloth] OpenAI base URL. Only api.openai.com is supported; non-cloud bases are rejected with 400.",
|
||||
description = "[x-unsloth] OpenAI base URL. Only api.openai.com is supported; non-cloud bases are rejected with 400.",
|
||||
)
|
||||
|
||||
|
||||
class CreateOpenAIContainerBody(OpenAIContainerRequest):
|
||||
name: str = Field(
|
||||
...,
|
||||
min_length=1,
|
||||
max_length=256,
|
||||
description="Human-readable container name. Surfaces in the picker UI.",
|
||||
min_length = 1,
|
||||
max_length = 256,
|
||||
description = "Human-readable container name. Surfaces in the picker UI.",
|
||||
)
|
||||
ttl_minutes: int = Field(
|
||||
20,
|
||||
ge=1,
|
||||
le=20,
|
||||
description=(
|
||||
ge = 1,
|
||||
le = 20,
|
||||
description = (
|
||||
"Idle-timeout TTL the new container will inherit (anchor="
|
||||
"last_active_at). OpenAI hard-caps this at 20 minutes and "
|
||||
"rejects larger values with integer_above_max_value."
|
||||
|
|
@ -903,7 +903,7 @@ class CreateOpenAIContainerBody(OpenAIContainerRequest):
|
|||
class DeleteOpenAIContainerBody(OpenAIContainerRequest):
|
||||
container_id: str = Field(
|
||||
...,
|
||||
description="OpenAI container id (cntr_...) to delete.",
|
||||
description = "OpenAI container id (cntr_...) to delete.",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -943,9 +943,9 @@ class ChunkChoice(BaseModel):
|
|||
class ChatCompletionChunk(BaseModel):
|
||||
"""A single SSE chunk in OpenAI streaming format."""
|
||||
|
||||
id: str = Field(default_factory=lambda: f"chatcmpl-{uuid.uuid4().hex[:12]}")
|
||||
id: str = Field(default_factory = lambda: f"chatcmpl-{uuid.uuid4().hex[:12]}")
|
||||
object: Literal["chat.completion.chunk"] = "chat.completion.chunk"
|
||||
created: int = Field(default_factory=lambda: int(time.time()))
|
||||
created: int = Field(default_factory = lambda: int(time.time()))
|
||||
model: str = "default"
|
||||
choices: list[ChunkChoice]
|
||||
usage: Optional[CompletionUsage] = None
|
||||
|
|
@ -981,12 +981,12 @@ class CompletionUsage(BaseModel):
|
|||
class ChatCompletion(BaseModel):
|
||||
"""Non-streaming chat completion response."""
|
||||
|
||||
id: str = Field(default_factory=lambda: f"chatcmpl-{uuid.uuid4().hex[:12]}")
|
||||
id: str = Field(default_factory = lambda: f"chatcmpl-{uuid.uuid4().hex[:12]}")
|
||||
object: Literal["chat.completion"] = "chat.completion"
|
||||
created: int = Field(default_factory=lambda: int(time.time()))
|
||||
created: int = Field(default_factory = lambda: int(time.time()))
|
||||
model: str = "default"
|
||||
choices: list[CompletionChoice]
|
||||
usage: CompletionUsage = Field(default_factory=CompletionUsage)
|
||||
usage: CompletionUsage = Field(default_factory = CompletionUsage)
|
||||
|
||||
|
||||
# =====================================================================
|
||||
|
|
@ -1008,7 +1008,7 @@ class ResponsesInputImagePart(BaseModel):
|
|||
"""Image content part in a Responses API message (type=input_image)."""
|
||||
|
||||
type: Literal["input_image"]
|
||||
image_url: str = Field(..., description="data:image/png;base64,... or https://...")
|
||||
image_url: str = Field(..., description = "data:image/png;base64,... or https://...")
|
||||
detail: Optional[Literal["auto", "low", "high"]] = "auto"
|
||||
|
||||
|
||||
|
|
@ -1074,15 +1074,15 @@ class ResponsesFunctionCallInputItem(BaseModel):
|
|||
|
||||
type: Literal["function_call"]
|
||||
id: Optional[str] = Field(
|
||||
None, description="Item id assigned by the server (e.g. fc_...)"
|
||||
None, description = "Item id assigned by the server (e.g. fc_...)"
|
||||
)
|
||||
call_id: str = Field(
|
||||
...,
|
||||
description="Correlation id matching a function_call_output on the next turn.",
|
||||
description = "Correlation id matching a function_call_output on the next turn.",
|
||||
)
|
||||
name: str
|
||||
arguments: str = Field(
|
||||
..., description="JSON string of the arguments the model produced."
|
||||
..., description = "JSON string of the arguments the model produced."
|
||||
)
|
||||
status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
|
||||
|
||||
|
|
@ -1098,7 +1098,7 @@ class ResponsesFunctionCallOutputInputItem(BaseModel):
|
|||
id: Optional[str] = None
|
||||
call_id: str
|
||||
output: Union[str, list] = Field(
|
||||
..., description="String or content-array result of the tool call."
|
||||
..., description = "String or content-array result of the tool call."
|
||||
)
|
||||
status: Optional[Literal["in_progress", "completed", "incomplete"]] = None
|
||||
|
||||
|
|
@ -1174,18 +1174,18 @@ class ResponsesFunctionTool(BaseModel):
|
|||
class ResponsesRequest(BaseModel):
|
||||
"""OpenAI Responses API request."""
|
||||
|
||||
model: str = Field("default", description="Model identifier")
|
||||
model: str = Field("default", description = "Model identifier")
|
||||
input: Union[str, list[ResponsesInputItem]] = Field(
|
||||
default=[],
|
||||
description="Input text or list of messages / function_call / function_call_output items",
|
||||
default = [],
|
||||
description = "Input text or list of messages / function_call / function_call_output items",
|
||||
)
|
||||
instructions: Optional[str] = Field(
|
||||
None, description="System / developer instructions"
|
||||
None, description = "System / developer instructions"
|
||||
)
|
||||
temperature: Optional[float] = Field(None, ge=0.0, le=2.0)
|
||||
top_p: Optional[float] = Field(None, ge=0.0, le=1.0)
|
||||
max_output_tokens: Optional[int] = Field(None, ge=1)
|
||||
stream: bool = Field(False, description="Whether to stream the response via SSE")
|
||||
temperature: Optional[float] = Field(None, ge = 0.0, le = 2.0)
|
||||
top_p: Optional[float] = Field(None, ge = 0.0, le = 1.0)
|
||||
max_output_tokens: Optional[int] = Field(None, ge = 1)
|
||||
stream: bool = Field(False, description = "Whether to stream the response via SSE")
|
||||
|
||||
# OpenAI function-calling fields — forwarded to llama-server via the
|
||||
# Chat Completions pass-through (see routes/inference.py). Typed as a
|
||||
|
|
@ -1194,7 +1194,7 @@ class ResponsesRequest(BaseModel):
|
|||
# picks out only ``type=="function"`` entries for forwarding.
|
||||
tools: Optional[list[dict]] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"Responses-shape function tool definitions. Entries with "
|
||||
'`type="function"` are translated to the Chat Completions nested '
|
||||
"shape before being forwarded to llama-server; other tool types "
|
||||
|
|
@ -1204,7 +1204,7 @@ class ResponsesRequest(BaseModel):
|
|||
)
|
||||
tool_choice: Optional[Any] = Field(
|
||||
None,
|
||||
description=(
|
||||
description = (
|
||||
"'auto' | 'required' | 'none' | {'type': 'function', 'name': ...} — "
|
||||
"the Responses-shape forcing object is translated to the Chat "
|
||||
"Completions nested shape internally."
|
||||
|
|
@ -1231,17 +1231,17 @@ class ResponsesOutputTextContent(BaseModel):
|
|||
|
||||
type: Literal["output_text"] = "output_text"
|
||||
text: str
|
||||
annotations: list = Field(default_factory=list)
|
||||
annotations: list = Field(default_factory = list)
|
||||
|
||||
|
||||
class ResponsesOutputMessage(BaseModel):
|
||||
"""An output message in the Responses API response."""
|
||||
|
||||
type: Literal["message"] = "message"
|
||||
id: str = Field(default_factory=lambda: f"msg_{uuid.uuid4().hex[:12]}")
|
||||
id: str = Field(default_factory = lambda: f"msg_{uuid.uuid4().hex[:12]}")
|
||||
status: Literal["completed", "in_progress"] = "completed"
|
||||
role: Literal["assistant"] = "assistant"
|
||||
content: list[ResponsesOutputTextContent] = Field(default_factory=list)
|
||||
content: list[ResponsesOutputTextContent] = Field(default_factory = list)
|
||||
|
||||
|
||||
class ResponsesOutputFunctionCall(BaseModel):
|
||||
|
|
@ -1254,11 +1254,11 @@ class ResponsesOutputFunctionCall(BaseModel):
|
|||
"""
|
||||
|
||||
type: Literal["function_call"] = "function_call"
|
||||
id: str = Field(default_factory=lambda: f"fc_{uuid.uuid4().hex[:12]}")
|
||||
id: str = Field(default_factory = lambda: f"fc_{uuid.uuid4().hex[:12]}")
|
||||
call_id: str
|
||||
name: str
|
||||
arguments: str = Field(
|
||||
..., description="JSON string of the arguments the model produced."
|
||||
..., description = "JSON string of the arguments the model produced."
|
||||
)
|
||||
status: Literal["completed", "in_progress", "incomplete"] = "completed"
|
||||
|
||||
|
|
@ -1277,24 +1277,24 @@ class ResponsesUsage(BaseModel):
|
|||
class ResponsesResponse(BaseModel):
|
||||
"""Top-level Responses API response object."""
|
||||
|
||||
id: str = Field(default_factory=lambda: f"resp_{uuid.uuid4().hex[:12]}")
|
||||
id: str = Field(default_factory = lambda: f"resp_{uuid.uuid4().hex[:12]}")
|
||||
object: Literal["response"] = "response"
|
||||
created_at: int = Field(default_factory=lambda: int(time.time()))
|
||||
created_at: int = Field(default_factory = lambda: int(time.time()))
|
||||
status: Literal["completed", "in_progress", "failed"] = "completed"
|
||||
model: str = "default"
|
||||
output: list[ResponsesOutputItem] = Field(default_factory=list)
|
||||
usage: ResponsesUsage = Field(default_factory=ResponsesUsage)
|
||||
output: list[ResponsesOutputItem] = Field(default_factory = list)
|
||||
usage: ResponsesUsage = Field(default_factory = ResponsesUsage)
|
||||
error: Optional[Any] = None
|
||||
incomplete_details: Optional[Any] = None
|
||||
instructions: Optional[str] = None
|
||||
metadata: dict = Field(default_factory=dict)
|
||||
metadata: dict = Field(default_factory = dict)
|
||||
temperature: Optional[float] = None
|
||||
top_p: Optional[float] = None
|
||||
max_output_tokens: Optional[int] = None
|
||||
previous_response_id: Optional[str] = None
|
||||
text: Optional[Any] = None
|
||||
tool_choice: Optional[Any] = None
|
||||
tools: list = Field(default_factory=list)
|
||||
tools: list = Field(default_factory = list)
|
||||
truncation: Optional[Any] = None
|
||||
|
||||
|
||||
|
|
@ -1373,13 +1373,13 @@ class AnthropicMessagesRequest(BaseModel):
|
|||
metadata: Optional[dict] = None
|
||||
# [x-unsloth] extensions — mirror the OpenAI endpoint convenience fields
|
||||
min_p: Optional[float] = Field(
|
||||
None, ge=0.0, le=1.0, description="[x-unsloth] Min-p sampling threshold"
|
||||
None, ge = 0.0, le = 1.0, description = "[x-unsloth] Min-p sampling threshold"
|
||||
)
|
||||
repetition_penalty: Optional[float] = Field(
|
||||
None, ge=1.0, le=2.0, description="[x-unsloth] Repetition penalty"
|
||||
None, ge = 1.0, le = 2.0, description = "[x-unsloth] Repetition penalty"
|
||||
)
|
||||
presence_penalty: Optional[float] = Field(
|
||||
None, ge=0.0, le=2.0, description="[x-unsloth] Presence penalty"
|
||||
None, ge = 0.0, le = 2.0, description = "[x-unsloth] Presence penalty"
|
||||
)
|
||||
enable_tools: Optional[bool] = None
|
||||
enabled_tools: Optional[list[str]] = None
|
||||
|
|
@ -1414,11 +1414,11 @@ AnthropicResponseBlock = Union[
|
|||
|
||||
|
||||
class AnthropicMessagesResponse(BaseModel):
|
||||
id: str = Field(default_factory=lambda: f"msg_{uuid.uuid4().hex[:24]}")
|
||||
id: str = Field(default_factory = lambda: f"msg_{uuid.uuid4().hex[:24]}")
|
||||
type: Literal["message"] = "message"
|
||||
role: Literal["assistant"] = "assistant"
|
||||
content: list[AnthropicResponseBlock] = Field(default_factory=list)
|
||||
content: list[AnthropicResponseBlock] = Field(default_factory = list)
|
||||
model: str = "default"
|
||||
stop_reason: Optional[str] = None
|
||||
stop_sequence: Optional[str] = None
|
||||
usage: AnthropicUsage = Field(default_factory=AnthropicUsage)
|
||||
usage: AnthropicUsage = Field(default_factory = AnthropicUsage)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue