Skip to content

Commit e952e6d

Browse files
authored
Update test_config.py
1 parent da9a55c commit e952e6d

1 file changed

Lines changed: 138 additions & 90 deletions

File tree

tests/test_config.py

Lines changed: 138 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,12 @@ def test_template_contains_llm(self):
176176
self.assertIn("reasoning_effort", config.CONFIG_TEMPLATE)
177177
self.assertIn('"stream"', config.CONFIG_TEMPLATE)
178178
self.assertIn('"subagent_llm"', config.CONFIG_TEMPLATE)
179-
self.assertIn('"context_windows"', config.CONFIG_TEMPLATE)
179+
self.assertIn('"context_window"', config.CONFIG_TEMPLATE)
180180

181181

182-
class TestContextWindowsConfig(unittest.TestCase):
183-
"""Config-file context-window overrides: loaded from the
184-
``context_windows`` object, matched before the built-in table."""
182+
class TestContextWindowConfig(unittest.TestCase):
183+
"""Context-window resolution: ``llm.context_window`` in the config
184+
file, then built-in CONTEXT_WINDOWS table, then default."""
185185

186186
def setUp(self):
187187
self._saved = {k: os.environ.get(k) for k in ENV_KEYS}
@@ -195,119 +195,91 @@ def tearDown(self):
195195
else:
196196
os.environ[k] = v
197197

198-
def test_missing_file_returns_empty(self):
199-
self.assertEqual(config.load_context_windows_config("/no/such/file.json"), [])
200-
201-
def test_empty_section_returns_empty(self):
202-
with tempfile.TemporaryDirectory() as d:
203-
p = Path(d) / "config.json"
204-
p.write_text('{"llm": {"model": "m"}}', encoding="utf-8")
205-
self.assertEqual(config.load_context_windows_config(p), [])
206-
207-
def test_bad_json_returns_empty(self):
208-
with tempfile.TemporaryDirectory() as d:
209-
p = Path(d) / "config.json"
210-
p.write_text("not {valid json", encoding="utf-8")
211-
self.assertEqual(config.load_context_windows_config(p), [])
212-
213-
def test_overrides_loaded_in_order(self):
198+
def test_llm_context_window_loaded(self):
199+
"""llm.context_window in the config file is read by load_llm_config."""
214200
with tempfile.TemporaryDirectory() as d:
215201
p = Path(d) / "config.json"
216-
p.write_text(
217-
'{"context_windows": {"deepseek-v4": 1000000, "gpt-5": 400000}}',
218-
encoding="utf-8",
219-
)
220-
self.assertEqual(
221-
config.load_context_windows_config(p),
222-
[("deepseek-v4", 1000000), ("gpt-5", 400000)],
223-
)
202+
p.write_text('{"llm": {"context_window": 256000}}', encoding="utf-8")
203+
settings = config.load_llm_config(p)
204+
self.assertEqual(settings["context_window"], 256000)
224205

225-
def test_comment_keys_skipped(self):
226-
with tempfile.TemporaryDirectory() as d:
227-
p = Path(d) / "config.json"
228-
p.write_text(
229-
'{"context_windows": {"_comment": "hi", "kimi": 256000}}',
230-
encoding="utf-8",
231-
)
232-
self.assertEqual(config.load_context_windows_config(p), [("kimi", 256000)])
206+
def test_llm_context_window_none_by_default(self):
207+
"""When unset, context_window defaults to None (resolve at runtime)."""
208+
settings = config.load_llm_config("/no/such/file.json")
209+
self.assertIsNone(settings["context_window"])
233210

234-
def test_section_must_be_object(self):
211+
def test_context_window_bool_rejected(self):
212+
"""True is an int subclass but not a valid context window."""
235213
with tempfile.TemporaryDirectory() as d:
236214
p = Path(d) / "config.json"
237-
p.write_text('{"context_windows": "nope"}', encoding="utf-8")
215+
p.write_text('{"llm": {"context_window": true}}', encoding="utf-8")
238216
with self.assertRaises(ValueError):
239-
config.load_context_windows_config(p)
217+
config.load_llm_config(p)
240218

241-
def test_size_must_be_integer(self):
219+
def test_context_window_zero_rejected(self):
242220
with tempfile.TemporaryDirectory() as d:
243221
p = Path(d) / "config.json"
244-
p.write_text('{"context_windows": {"m": "big"}}', encoding="utf-8")
222+
p.write_text('{"llm": {"context_window": 0}}', encoding="utf-8")
245223
with self.assertRaises(ValueError):
246-
config.load_context_windows_config(p)
224+
config.load_llm_config(p)
247225

248-
def test_bool_size_rejected(self):
249-
"""True is an int subclass but not a valid token count."""
226+
def test_context_window_negative_rejected(self):
250227
with tempfile.TemporaryDirectory() as d:
251228
p = Path(d) / "config.json"
252-
p.write_text('{"context_windows": {"m": true}}', encoding="utf-8")
229+
p.write_text('{"llm": {"context_window": -1}}', encoding="utf-8")
253230
with self.assertRaises(ValueError):
254-
config.load_context_windows_config(p)
231+
config.load_llm_config(p)
255232

256-
def test_context_windows_zero_rejected(self):
257-
"""A zero context window would cause a division-by-zero at runtime."""
233+
def test_context_window_string_rejected(self):
258234
with tempfile.TemporaryDirectory() as d:
259235
p = Path(d) / "config.json"
260-
p.write_text('{"context_windows": {"m": 0}}', encoding="utf-8")
236+
p.write_text('{"llm": {"context_window": "big"}}', encoding="utf-8")
261237
with self.assertRaises(ValueError):
262-
config.load_context_windows_config(p)
238+
config.load_llm_config(p)
263239

264-
def test_context_windows_negative_rejected(self):
240+
def test_get_context_window_llm_setting_wins(self):
241+
"""llm.context_window beats the built-in table."""
265242
with tempfile.TemporaryDirectory() as d:
266243
p = Path(d) / "config.json"
267-
p.write_text('{"context_windows": {"m": -1}}', encoding="utf-8")
268-
with self.assertRaises(ValueError):
269-
config.load_context_windows_config(p)
244+
p.write_text('{"llm": {"context_window": 999999}}', encoding="utf-8")
245+
self.assertEqual(config.get_context_window_for_model("gpt-5-mini", p), 999999)
270246

271-
def test_get_context_window_precedence(self):
272-
"""Config-file override -> CONTEXT_WINDOWS -> default."""
273-
with tempfile.TemporaryDirectory() as d:
274-
p = Path(d) / "config.json"
275-
p.write_text(
276-
'{"context_windows": {"deepseek-v4": 1000000, "gpt-4-turbo": 300000}}',
277-
encoding="utf-8",
278-
)
279-
self.assertEqual(config.get_context_window_for_model("deepseek-v4-flash", p), 1000000)
280-
# config-file match beats the built-in table
281-
self.assertEqual(config.get_context_window_for_model("gpt-4-turbo", p), 300000)
282-
# built-in table still applies when no override matches
283-
self.assertEqual(config.get_context_window_for_model("gpt-5-mini", p), 128000)
284-
self.assertEqual(config.get_context_window_for_model("kimi-k2.7-0613", p), 256000)
285-
# unknown model -> default
286-
self.assertEqual(
287-
config.get_context_window_for_model("unknown-model", p),
288-
config.DEFAULT_CONTEXT_WINDOW,
289-
)
247+
def test_get_context_window_falls_to_table(self):
248+
"""No llm.context_window -> built-in CONTEXT_WINDOWS table."""
249+
self.assertEqual(
250+
config.get_context_window_for_model("deepseek-v4", "/no/such/file.json"),
251+
1_000_000,
252+
)
253+
self.assertEqual(
254+
config.get_context_window_for_model("gpt-5-mini", "/no/such/file.json"),
255+
128_000,
256+
)
257+
258+
def test_get_context_window_falls_to_default(self):
259+
"""Unknown model with no config -> DEFAULT_CONTEXT_WINDOW."""
260+
self.assertEqual(
261+
config.get_context_window_for_model("totally-unknown", "/no/such/file.json"),
262+
config.DEFAULT_CONTEXT_WINDOW,
263+
)
290264

291265
def test_get_context_window_case_insensitive(self):
292-
with tempfile.TemporaryDirectory() as d:
293-
p = Path(d) / "config.json"
294-
p.write_text('{"context_windows": {"DeepSeek-V4": 1000000}}', encoding="utf-8")
295-
self.assertEqual(config.get_context_window_for_model("deepseek-v4-flash", p), 1000000)
266+
"""Built-in table matching is case-insensitive."""
267+
self.assertEqual(
268+
config.get_context_window_for_model("DeepSeek-V4", "/no/such/file.json"),
269+
1_000_000,
270+
)
296271

297272
def test_get_context_window_provider_prefixed_name(self):
298273
"""Provider-prefixed model names (e.g. "ZhipuAI/GLM-5.2") match
299274
their table entry via case-insensitive substring match."""
300-
# provider prefix + uppercase org, model name in the middle
301275
self.assertEqual(
302276
config.get_context_window_for_model("ZhipuAI/GLM-5.2", "/no/such/config.json"),
303277
1_000_000,
304278
)
305-
# lowercased provider prefix matches too
306279
self.assertEqual(
307280
config.get_context_window_for_model("zhipuai/glm-5.2", "/no/such/config.json"),
308281
1_000_000,
309282
)
310-
# plain model name still matches
311283
self.assertEqual(
312284
config.get_context_window_for_model("glm-5.2", "/no/such/config.json"),
313285
1_000_000,
@@ -317,23 +289,99 @@ def test_get_context_window_provider_prefixed_name(self):
317289
config.get_context_window_for_model("ZhipuAI/GLM-5.3", "/no/such/config.json"),
318290
config.DEFAULT_CONTEXT_WINDOW,
319291
)
320-
# provider prefix must not break other families either
321292
self.assertEqual(
322293
config.get_context_window_for_model(
323294
"DeepSeek/deepseek-v4-flash", "/no/such/config.json"
324295
),
325296
1_000_000,
326297
)
327298

328-
def test_get_context_window_no_file(self):
329-
self.assertEqual(
330-
config.get_context_window_for_model("deepseek-v4", "/no/such/file.json"),
331-
1_000_000,
332-
)
333-
self.assertEqual(
334-
config.get_context_window_for_model("totally-unknown", "/no/such/file.json"),
335-
config.DEFAULT_CONTEXT_WINDOW,
336-
)
299+
def test_resolve_context_window_from_settings(self):
300+
"""resolve_context_window uses settings['context_window'] first."""
301+
settings = {"model": "gpt-5-mini", "context_window": 500_000}
302+
self.assertEqual(config.resolve_context_window(settings), 500_000)
303+
304+
def test_resolve_context_window_falls_to_table(self):
305+
"""resolve_context_window falls to built-in table when context_window is None."""
306+
settings = {"model": "deepseek-v4", "context_window": None}
307+
self.assertEqual(config.resolve_context_window(settings), 1_000_000)
308+
309+
def test_resolve_context_window_falls_to_default(self):
310+
"""resolve_context_window uses DEFAULT_CONTEXT_WINDOW for unknown models."""
311+
settings = {"model": "unknown", "context_window": None}
312+
self.assertEqual(config.resolve_context_window(settings), config.DEFAULT_CONTEXT_WINDOW)
313+
314+
def test_context_window_inherits_in_subagent(self):
315+
"""context_window set in llm inherits to subagent_llm when unset."""
316+
with tempfile.TemporaryDirectory() as d:
317+
p = Path(d) / "config.json"
318+
p.write_text(
319+
'{"llm": {"model": "m", "context_window": 500000}}',
320+
encoding="utf-8",
321+
)
322+
main = config.load_llm_config(p)
323+
sub = config.load_subagent_llm_config(p, main=main)
324+
self.assertEqual(sub["context_window"], 500000)
325+
326+
def test_context_window_subagent_override(self):
327+
"""subagent_llm.context_window overrides the inherited value."""
328+
with tempfile.TemporaryDirectory() as d:
329+
p = Path(d) / "config.json"
330+
p.write_text(
331+
'{"llm": {"model": "m", "context_window": 500000},'
332+
' "subagent_llm": {"context_window": 200000}}',
333+
encoding="utf-8",
334+
)
335+
main = config.load_llm_config(p)
336+
sub = config.load_subagent_llm_config(p, main=main)
337+
self.assertEqual(sub["context_window"], 200000)
338+
339+
def test_context_window_profile_override(self):
340+
"""A models profile's context_window overrides the llm value
341+
when referenced via subagent_llm.profile."""
342+
with tempfile.TemporaryDirectory() as d:
343+
p = Path(d) / "config.json"
344+
p.write_text(
345+
'{"llm": {"model": "m", "context_window": 500000},'
346+
' "models": {"cheap": {"model": "cheap-m", "context_window": 300000}},'
347+
' "subagent_llm": {"profile": "cheap"}}',
348+
encoding="utf-8",
349+
)
350+
main = config.load_llm_config(p)
351+
sub = config.load_subagent_llm_config(p, main=main)
352+
self.assertEqual(sub["context_window"], 300000)
353+
354+
def test_old_context_windows_section_emits_deprecation(self):
355+
"""A config file with the old top-level context_windows section
356+
triggers a DeprecationWarning on load_llm_config."""
357+
import warnings
358+
359+
with tempfile.TemporaryDirectory() as d:
360+
p = Path(d) / "config.json"
361+
p.write_text(
362+
'{"llm": {"model": "m"}, "context_windows": {"deepseek-v4": 1000000}}',
363+
encoding="utf-8",
364+
)
365+
with warnings.catch_warnings(record=True) as w:
366+
warnings.simplefilter("always")
367+
config.load_llm_config(p)
368+
dep_warnings = [x for x in w if issubclass(x.category, DeprecationWarning)]
369+
self.assertEqual(len(dep_warnings), 1)
370+
self.assertIn("context_windows", str(dep_warnings[0].message))
371+
self.assertIn("deprecated", str(dep_warnings[0].message))
372+
373+
def test_no_deprecation_without_old_section(self):
374+
"""No warning when the config file has no context_windows section."""
375+
import warnings
376+
377+
with tempfile.TemporaryDirectory() as d:
378+
p = Path(d) / "config.json"
379+
p.write_text('{"llm": {"model": "m", "context_window": 500000}}', encoding="utf-8")
380+
with warnings.catch_warnings(record=True) as w:
381+
warnings.simplefilter("always")
382+
config.load_llm_config(p)
383+
dep_warnings = [x for x in w if issubclass(x.category, DeprecationWarning)]
384+
self.assertEqual(len(dep_warnings), 0)
337385

338386

339387
class TestSubagentLlmConfig(unittest.TestCase):

0 commit comments

Comments
 (0)