You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_query_local_context_length() in agent/model_metadata.py queries LM Studio's native /api/v1/models endpoint and reads loaded_instances[0].config.context_length, but only when the model is currently loaded. When the model isn't loaded (e.g. JIT enabled, fresh server start, model just unloaded by TTL), loaded_instances is [] and the probe returns None — even though the same response always carries max_context_length (the model's training-time max), which would be a usable fallback.
The probe then falls through to the hardcoded family default (e.g. 256K for gemma-4, 64K for unknowns). For a user who actually loads the model with a smaller context window than the family default (very common — LM Studio's loader UI defaults to 4K–8K depending on free VRAM), this lets Hermes treat the model as having far more context than its KV cache can hold. The conversation grows past the loaded window before any compression triggers, and on the next turn LM Studio either silently truncates or — under VRAM pressure — the GPU driver fails to allocate KV cache buffers and the system hangs/BSODs.
This is distinct from #47200 (remote LM Studio is is_local_endpoint()-gated), #25989 (manual preload uses MINIMUM_CONTEXT_LENGTH), and #30178 (per-model override regression). Those affect different code paths; this one is in the probe itself.
Steps to Reproduce
Start LM Studio locally on port 1234 with JIT enabled. Do not load any model into memory yet.
Configure Hermes with an LM Studio custom_provider but without any per-model context_length override:
Could not detect context length for model 'google/gemma-4-12b-qat' at http://localhost:1234/v1 — defaulting to 256,000 tokens (probe-down). Set model.context_length in config.yaml to override.
Using hardcoded context length 256,000 for model 'google/gemma-4-12b-qat' (custom endpoint, catalog match on 'gemma-4')
When loaded_instances is empty, the probe should fall back to max_context_length from the same response. That gives the training-time max (262,144 in this example), which is still better than ignoring a known-good number and resolving via a coarser fallback.
Optionally and even better: when loaded_instances is non-empty, prefer loaded_instances[0].config.context_length (current behaviour); when it's empty, return max_context_length. Either way, never return None if the model is listed and max_context_length is present.
Actual Behavior
_query_local_context_length returns None whenever loaded_instances is [], even though max_context_length is in the same JSON response. The resolver falls through to family-default heuristics (256K for gemma-4, etc.), which can be much larger than what the user will actually load the model with at JIT time. Hermes then treats the conversation budget as far larger than the actual loaded KV cache, and inputs sized to the wrong budget can cause LM Studio truncation or — when VRAM is already tight — drive nvlddmkm into STATUS_INSUFFICIENT_RESOURCES (VIDEO_TDR_FAILURE 0x116) on NVIDIA hardware.
Once the matching model entry is found and loaded_instances is empty, the inner for doesn't run, and break exits the outer loop. The function falls through to the OpenAI-compat /v1/models/{model} and /v1/models paths — neither of which carries context info on LM Studio — and ultimately returns None.
Proposed Fix
Inside the if server_type == "lm-studio": block, after the loaded_instances loop, fall back to m["max_context_length"] if it's present and positive, and only break (without returning) when neither is available. Roughly:
ifserver_type=="lm-studio":
resp=client.get(f"{server_url}/api/v1/models")
ifresp.status_code==200:
data=resp.json()
formindata.get("models", []):
if_model_id_matches(m.get("key", ""), model) or_model_id_matches(m.get("id", ""), model):
# 1) Prefer the runtime ctx of a currently-loaded instance.forinstinm.get("loaded_instances", []):
cfg=inst.get("config", {})
ctx=cfg.get("context_length")
ifctxandisinstance(ctx, (int, float)):
returnint(ctx)
# 2) Otherwise fall back to the model's training-time max,# which LM Studio always reports even when not loaded.max_ctx=m.get("max_context_length")
ifmax_ctxandisinstance(max_ctx, (int, float)) andmax_ctx>0:
returnint(max_ctx)
break
The training-time max is an over-estimate of what JIT will actually load, so users who load with a smaller context still need a per-model context_length override under custom_providers[].models.<id> to be safe (see [Bug]: LM Studio custom_providers per-model context_length broken in 0.14.0 — regressed to 64K #30178). But returning the training max is strictly better than returning None — it bounds the budget instead of unbounding it via the family-default heuristic.
The cache-bypass for provider == "lmstudio" at line ~1670 (# LM Studio is excluded — its loaded context length is transient...) is correct and should remain. The fix only affects the fallback ordering inside the probe itself.
Are you willing to submit a PR for this?
I'd like to fix this myself and submit a PR
(I have a workaround in place — explicit per-model context_length: 8192 under custom_providers[].models.<id> — but that's a config-side band-aid, not a fix.)
Bug Description
_query_local_context_length()inagent/model_metadata.pyqueries LM Studio's native/api/v1/modelsendpoint and readsloaded_instances[0].config.context_length, but only when the model is currently loaded. When the model isn't loaded (e.g. JIT enabled, fresh server start, model just unloaded by TTL),loaded_instancesis[]and the probe returnsNone— even though the same response always carriesmax_context_length(the model's training-time max), which would be a usable fallback.The probe then falls through to the hardcoded family default (e.g. 256K for
gemma-4, 64K for unknowns). For a user who actually loads the model with a smaller context window than the family default (very common — LM Studio's loader UI defaults to 4K–8K depending on free VRAM), this lets Hermes treat the model as having far more context than its KV cache can hold. The conversation grows past the loaded window before any compression triggers, and on the next turn LM Studio either silently truncates or — under VRAM pressure — the GPU driver fails to allocate KV cache buffers and the system hangs/BSODs.This is distinct from #47200 (remote LM Studio is
is_local_endpoint()-gated), #25989 (manual preload usesMINIMUM_CONTEXT_LENGTH), and #30178 (per-model override regression). Those affect different code paths; this one is in the probe itself.Steps to Reproduce
context_lengthoverride:{ "key": "google/gemma-4-12b-qat", "loaded_instances": [], "max_context_length": 262144, ... }Expected Behavior
When
loaded_instancesis empty, the probe should fall back tomax_context_lengthfrom the same response. That gives the training-time max (262,144 in this example), which is still better than ignoring a known-good number and resolving via a coarser fallback.Optionally and even better: when
loaded_instancesis non-empty, preferloaded_instances[0].config.context_length(current behaviour); when it's empty, returnmax_context_length. Either way, never returnNoneif the model is listed andmax_context_lengthis present.Actual Behavior
_query_local_context_lengthreturnsNonewheneverloaded_instancesis[], even thoughmax_context_lengthis in the same JSON response. The resolver falls through to family-default heuristics (256K forgemma-4, etc.), which can be much larger than what the user will actually load the model with at JIT time. Hermes then treats the conversation budget as far larger than the actual loaded KV cache, and inputs sized to the wrong budget can cause LM Studio truncation or — when VRAM is already tight — drivenvlddmkmintoSTATUS_INSUFFICIENT_RESOURCES(VIDEO_TDR_FAILURE0x116) on NVIDIA hardware.Affected Component
Configuration (config.yaml, .env, hermes setup); Agent Core (conversation loop, context compression, memory)
Operating System
Windows 11 (RTX 3090, 24 GB VRAM)
Hermes Version
(version stamp will be filled in by the issue submission flow)
Root Cause Analysis
Probe code in
agent/model_metadata.pyat_query_local_context_length()(around lines 1337–1349 on currentmain):Once the matching model entry is found and
loaded_instancesis empty, the innerfordoesn't run, andbreakexits the outer loop. The function falls through to the OpenAI-compat/v1/models/{model}and/v1/modelspaths — neither of which carries context info on LM Studio — and ultimately returnsNone.Proposed Fix
Inside the
if server_type == "lm-studio":block, after theloaded_instancesloop, fall back tom["max_context_length"]if it's present and positive, and onlybreak(without returning) when neither is available. Roughly:Notes for reviewers:
context_lengthoverride undercustom_providers[].models.<id>to be safe (see [Bug]: LM Studio custom_providers per-model context_length broken in 0.14.0 — regressed to 64K #30178). But returning the training max is strictly better than returningNone— it bounds the budget instead of unbounding it via the family-default heuristic.provider == "lmstudio"at line ~1670 (# LM Studio is excluded — its loaded context length is transient...) is correct and should remain. The fix only affects the fallback ordering inside the probe itself.Are you willing to submit a PR for this?
(I have a workaround in place — explicit per-model
context_length: 8192undercustom_providers[].models.<id>— but that's a config-side band-aid, not a fix.)