aboutsummaryrefslogtreecommitdiffstats
path: root/test_llamachat.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-08-09 15:21:32 +0200
committerDanilo M. <danix@danix.xyz>2026-08-09 15:21:32 +0200
commit86c5cdb4bb94b8d6da93c42ac48ec1abb6fa87ef (patch)
tree47a68f23f5597b732589b8670be86998a50fbc8f /test_llamachat.py
parent009496882033fa9f01e0faaffe9d6d797650b621 (diff)
downloadllamachat-86c5cdb4bb94b8d6da93c42ac48ec1abb6fa87ef.tar.gz
llamachat-86c5cdb4bb94b8d6da93c42ac48ec1abb6fa87ef.zip
fix: warn when [[providers.local]] discards its fields
A malformed local entry was the one skip that said nothing, on the reasoning that local still ends up working. It does, but the merge rebuilds it from the bare base_url, so an api_key, filter or ctx_size set on that entry is dropped without a word. The app then comes up looking healthy, which is the strongest possible signal that nothing is wrong, making this the case that most needs saying, not least. Local surviving at all depends on DEFAULTS supplying base_url, since that is what the rebuild reads. Noted in config.py so the key is not removed as redundant, and pinned by a test. The not-a-table wording now follows what was written: a list gets the single-bracket fix by name, while a scalar entry does not, since telling someone to fix a [[...]] they never typed points at the wrong line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0176tzAW6H1i2Kz8vm2XXVGV
Diffstat (limited to 'test_llamachat.py')
-rwxr-xr-xtest_llamachat.py42
1 files changed, 42 insertions, 0 deletions
diff --git a/test_llamachat.py b/test_llamachat.py
index ed50995..29f305f 100755
--- a/test_llamachat.py
+++ b/test_llamachat.py
@@ -1054,6 +1054,48 @@ def test_provider_malformed_shapes():
messages = err.getvalue()
assert "listy" in messages and "urlless" in messages and "a:b" in messages
assert "base_url" in messages # the missing-URL case names what is missing
+ # A list is the double-bracket slip, so the message names the fix. Any
+ # other scalar was not written that way, and must not be told to change a
+ # bracket it never had.
+ assert "not [[providers.listy]]" in messages
+ scalar = io.StringIO()
+ with redirect_stderr(scalar):
+ providers.parse({"providers": {"n": 5}})
+ assert "[[" not in scalar.getvalue(), scalar.getvalue()
+
+ # A malformed [[providers.local]] is the loudest case that needs saying,
+ # not the quietest: the merge below it rebuilds local from the bare URL,
+ # so the app comes up working and every field the user set is discarded
+ # silently. The skip is reported at the merge site because the loop never
+ # sees this entry.
+ err = io.StringIO()
+ with redirect_stderr(err):
+ clobbered = providers.parse(
+ {
+ "base_url": "http://localhost:8181",
+ "providers": {"local": [{"api_key": "env:SOME_VAR",
+ "filter": ["qwen"],
+ "ctx_size": 32768}]},
+ }
+ )
+ assert "local" in err.getvalue()
+ assert "not [[providers.local]]" in err.getvalue()
+ # The local provider survives, which is the non-negotiable.
+ assert clobbered["local"].base_url == "http://localhost:8181"
+ # Pinning the loss rather than only the survival: these fields are gone,
+ # and the warning above is the only thing that tells the user so.
+ assert clobbered["local"].api_key == ""
+ assert clobbered["local"].filter == []
+ assert clobbered["local"].ctx_size is None
+
+ # Without a bare base_url there is nothing to rebuild local from, so it
+ # vanishes entirely. config.DEFAULTS always supplies one, which is what
+ # keeps the real app safe; this pins that the safety net is that default
+ # and not something parse() does on its own.
+ assert providers.parse(
+ {"providers": {"local": [{"base_url": "http://y.example.org"}]}}
+ ) == {}
+ assert "base_url" in config.DEFAULTS
print("ok malformed provider shapes are skipped, not raised")