|
3 | 3 | from openkb.config import ( |
4 | 4 | DEFAULT_CONFIG, |
5 | 5 | get_extra_headers, |
| 6 | + get_parallel_tool_calls, |
6 | 7 | get_timeout, |
7 | 8 | load_config, |
8 | 9 | resolve_extra_headers, |
9 | 10 | resolve_litellm_settings, |
| 11 | + resolve_model_settings, |
| 12 | + resolve_parallel_tool_calls, |
10 | 13 | resolve_timeout, |
11 | 14 | save_config, |
12 | 15 | set_extra_headers, |
| 16 | + set_parallel_tool_calls, |
13 | 17 | set_timeout, |
14 | 18 | ) |
15 | 19 |
|
| 20 | +# --- parallel_tool_calls ------------------------------------------------------ |
| 21 | +# |
| 22 | +# (value, was_explicit) distinguishes "not configured" (each agent uses its own |
| 23 | +# default) from an explicit true/false/null (overrides every agent uniformly). |
| 24 | + |
| 25 | + |
| 26 | +def test_parallel_tool_calls_not_in_default_config(): |
| 27 | + # No single default fits every agent (see module docstring above), so this |
| 28 | + # key is intentionally absent from DEFAULT_CONFIG — load_config's merge |
| 29 | + # must not mask "the user's config.yaml doesn't mention this key". |
| 30 | + assert "parallel_tool_calls" not in DEFAULT_CONFIG |
| 31 | + |
| 32 | + |
| 33 | +def test_resolve_parallel_tool_calls_absent_is_unset(): |
| 34 | + assert resolve_parallel_tool_calls({}) == (None, False) |
| 35 | + |
| 36 | + |
| 37 | +def test_resolve_parallel_tool_calls_explicit_bools(): |
| 38 | + assert resolve_parallel_tool_calls({"parallel_tool_calls": True}) == (True, True) |
| 39 | + assert resolve_parallel_tool_calls({"parallel_tool_calls": False}) == (False, True) |
| 40 | + |
| 41 | + |
| 42 | +def test_resolve_parallel_tool_calls_null_means_omit(caplog): |
| 43 | + # Explicit null = "don't send the param" (provider default). This is the |
| 44 | + # escape hatch for Amazon Bedrock, and is silent (not an invalid value) — |
| 45 | + # explicit and distinct from "absent" even though both currently carry a |
| 46 | + # value of None; was_explicit is what tells them apart. |
| 47 | + with caplog.at_level(logging.WARNING, logger="openkb.config"): |
| 48 | + assert resolve_parallel_tool_calls({"parallel_tool_calls": None}) == (None, True) |
| 49 | + assert caplog.text == "" |
| 50 | + |
| 51 | + |
| 52 | +def test_resolve_parallel_tool_calls_rejects_non_bool(caplog): |
| 53 | + # An invalid value (not true/false/null) degrades to the one value known |
| 54 | + # to never break any provider — omit the setting — rather than to a fixed |
| 55 | + # bool that could reproduce the exact failure (e.g. Amazon Bedrock) the |
| 56 | + # user may have been trying to escape via this exact key. |
| 57 | + with caplog.at_level(logging.WARNING, logger="openkb.config"): |
| 58 | + assert resolve_parallel_tool_calls({"parallel_tool_calls": "true"}) == (None, True) |
| 59 | + assert "parallel_tool_calls" in caplog.text |
| 60 | + |
| 61 | + |
| 62 | +def test_parallel_tool_calls_stash_roundtrip(): |
| 63 | + set_parallel_tool_calls(False, True) |
| 64 | + assert get_parallel_tool_calls() == (False, True) |
| 65 | + set_parallel_tool_calls(True, True) |
| 66 | + assert get_parallel_tool_calls() == (True, True) |
| 67 | + set_parallel_tool_calls(None, True) |
| 68 | + assert get_parallel_tool_calls() == (None, True) |
| 69 | + set_parallel_tool_calls(None, False) |
| 70 | + assert get_parallel_tool_calls() == (None, False) |
| 71 | + |
| 72 | + |
| 73 | +def test_parallel_tool_calls_stash_default_is_unset(): |
| 74 | + # The raw stash default must mean "not configured", matching an absent key, |
| 75 | + # so an agent built before _setup_llm_key runs defers to its own default. |
| 76 | + set_parallel_tool_calls(None, False) |
| 77 | + assert get_parallel_tool_calls() == resolve_parallel_tool_calls({}) |
| 78 | + |
| 79 | + |
| 80 | +# --- resolve_model_settings --------------------------------------------------- |
| 81 | + |
| 82 | + |
| 83 | +def test_resolve_model_settings_uses_own_default_when_unset(): |
| 84 | + set_extra_headers({}) |
| 85 | + set_timeout(None) |
| 86 | + set_parallel_tool_calls(None, False) |
| 87 | + assert resolve_model_settings() == { |
| 88 | + "extra_headers": None, |
| 89 | + "extra_args": None, |
| 90 | + "parallel_tool_calls": False, # the function's own default |
| 91 | + } |
| 92 | + assert resolve_model_settings(default_parallel_tool_calls=None) == { |
| 93 | + "extra_headers": None, |
| 94 | + "extra_args": None, |
| 95 | + "parallel_tool_calls": None, |
| 96 | + } |
| 97 | + assert resolve_model_settings(default_parallel_tool_calls=True) == { |
| 98 | + "extra_headers": None, |
| 99 | + "extra_args": None, |
| 100 | + "parallel_tool_calls": True, |
| 101 | + } |
| 102 | + |
| 103 | + |
| 104 | +def test_resolve_model_settings_explicit_value_overrides_every_default(): |
| 105 | + # An explicit config choice always wins over whatever default a specific |
| 106 | + # caller would otherwise apply — the whole point of the escape hatch is |
| 107 | + # that it works uniformly, regardless of which agent is asking. |
| 108 | + set_extra_headers({"X-A": "1"}) |
| 109 | + set_timeout(1200.0) |
| 110 | + set_parallel_tool_calls(None, True) # explicit null: omit, for everyone |
| 111 | + for default in (False, True, None): |
| 112 | + assert resolve_model_settings(default_parallel_tool_calls=default) == { |
| 113 | + "extra_headers": {"X-A": "1"}, |
| 114 | + "extra_args": {"timeout": 1200.0}, |
| 115 | + "parallel_tool_calls": None, |
| 116 | + } |
| 117 | + |
| 118 | + set_parallel_tool_calls(True, True) # explicit true: allow parallel, for everyone |
| 119 | + for default in (False, True, None): |
| 120 | + assert ( |
| 121 | + resolve_model_settings(default_parallel_tool_calls=default)["parallel_tool_calls"] |
| 122 | + is True |
| 123 | + ) |
| 124 | + |
16 | 125 |
|
17 | 126 | def test_default_config_keys(): |
18 | 127 | assert "model" in DEFAULT_CONFIG |
|
0 commit comments