Models know how much context they hold

A column rather than a key in capabilities_json, which is rebuilt wholesale
from the submitted checkboxes on every save and would destroy a number
living in it.

0 means unknown, and unknown has to stay tellable from small: the context
percentage and automatic compaction both refuse to act on a figure nobody
supplied. Filled in from /v1/models where the runner advertises it --
OpenRouter, vLLM and llama.cpp each spell it differently, so context_from()
reads the four spellings actually in use, accepts a quoted number but not
"8192 tokens", and rejects anything outside 256..10,000,000. Applied on
discovery only when nothing is set: a refresh must never undo a correction,
since an administrator sets this precisely because the endpoint was wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-01 00:33:50 +02:00
parent 50484b2483
commit 90623b461d
6 changed files with 205 additions and 2 deletions
+122
View File
@@ -0,0 +1,122 @@
"""How full the context is, what a reply cost, and how fast it arrived."""
from __future__ import annotations
from fastapi.testclient import TestClient
from sqlalchemy import select
from lembas.db.models import Connection, Model
from lembas.services.crypto import encrypt
from lembas.services.llm.openai_client import context_from
def _model(db, **kwargs) -> Model:
connection = Connection(
name="Test", base_url="http://127.0.0.1:1", api_key_encrypted=encrypt("")
)
db.add(connection)
db.commit()
model = Model(connection_id=connection.id, model_id="test-model", **kwargs)
db.add(model)
db.commit()
return model
# --- Reading a context length off /v1/models ---------------------------------
def test_context_length_is_read_from_any_of_the_spellings():
assert context_from({"id": "m", "context_length": 8192}) == 8192
assert context_from({"id": "m", "max_model_len": 32768}) == 32768
assert context_from({"id": "m", "context_window": 4096}) == 4096
assert context_from({"id": "m", "meta": {"n_ctx": 2048}}) == 2048
def test_a_quoted_number_is_accepted_but_a_label_is_not():
"""Some servers quote it. "8192 tokens" is a label, not a measurement."""
assert context_from({"id": "m", "context_length": "8192"}) == 8192
assert context_from({"id": "m", "context_length": "8192 tokens"}) == 0
def test_an_absent_or_implausible_context_length_is_zero():
assert context_from({"id": "m"}) == 0
assert context_from({"id": "m", "context_length": 0}) == 0
assert context_from({"id": "m", "context_length": 64}) == 0
assert context_from({"id": "m", "context_length": 10**9}) == 0
# True is an int in Python, and it is not a context length.
assert context_from({"id": "m", "context_length": True}) == 0
# --- Discovery ---------------------------------------------------------------
async def test_discovery_fills_in_a_context_length(client: TestClient, db, registered, mock_http):
import httpx
mock_http(
lambda _r: httpx.Response(
200, json={"data": [{"id": "big-model", "context_length": 16384}]}
)
)
client.post(
"/admin/connections",
data={"name": "Local", "base_url": "http://x.test", "api_key": ""},
follow_redirects=False,
)
model = db.scalar(select(Model).where(Model.model_id == "big-model"))
assert model.context_length == 16384
async def test_discovery_never_overwrites_a_number_an_admin_typed(
client: TestClient, db, registered, mock_http
):
"""A refresh must not undo a correction. Administrators set this precisely
because the endpoint was wrong or silent."""
import httpx
mock_http(
lambda _r: httpx.Response(200, json={"data": [{"id": "m", "context_length": 4096}]})
)
client.post(
"/admin/connections",
data={"name": "Local", "base_url": "http://x.test", "api_key": ""},
follow_redirects=False,
)
model = db.scalar(select(Model).where(Model.model_id == "m"))
model.context_length = 131072
db.commit()
connection = db.scalar(select(Connection))
client.post(f"/admin/connections/{connection.id}/refresh", follow_redirects=False)
db.refresh(model)
assert model.context_length == 131072
# --- The admin field ---------------------------------------------------------
def test_an_admin_can_set_and_clear_the_context_length(client: TestClient, db, registered):
model = _model(db)
client.post(
f"/admin/models/{model.id}",
data={"context_length": "8192", "position": ""},
follow_redirects=False,
)
db.refresh(model)
assert model.context_length == 8192
client.post(
f"/admin/models/{model.id}", data={"context_length": "", "position": ""},
follow_redirects=False,
)
db.refresh(model)
assert model.context_length == 0
def test_junk_in_the_context_length_field_is_ignored_not_a_500(
client: TestClient, db, registered
):
model = _model(db, context_length=4096)
response = client.post(
f"/admin/models/{model.id}",
data={"context_length": "eight thousand", "position": ""},
follow_redirects=False,
)
assert response.status_code == 303
db.refresh(model)
assert model.context_length == 4096