7Normative resource caps — hard ceilings model args cannot exceed (M8.0 WU-9).
11Appendix B of the locked V1.0 spec (LD-3) fixes hard ceilings that bound the
12transcript-flood / provider-egress-cost / runaway-call DoS surface (F38/F52/F19;
13every turn re-ships the whole transcript, F72). A *model-supplied* argument can
14never exceed them: ``timeout``, ``max_bytes``/``max_chars``, ``max_results`` are
15clamped at the tool boundary; the per-turn tool-call count and the parallel-worker
16pool are bounded in the react-loop. The values are unified at **5 MB** for the
17read / fetch-body / output ceilings (LD-3). The per-turn tool-call ceiling is an
18**absolute ceiling a settings write may LOWER but never RAISE** (amended from the
19V1.0 Appendix-B 10 on operator approval). Plan 238 (CONFIG-01) moved every value
20below into the shipped, read-only ``oc_chat.defaults.toml``, hard-denied for
21writes by name in ``tools/_sandbox.py``: the names here are import-time aliases
22bound from :mod:`oc_chat.core.defaults`, and no value is written twice. The workspace
23``max_tool_calls_per_turn`` setting is a model-reachable, human-confirm-tier write,
24so F33/§5.4 bar it from raising a security ceiling; raising the ceiling above the
25default is done only via the operator-out-of-band env knob
26``OC_CHAT_MAX_TOOL_CALLS_CEILING`` (``_env_guard``-enrolled, stripped from a
27workspace ``.env``). The F52 "non-overridable by model args" DoS bound thus holds
28against the settings-file path too — see :func:`resolve_max_tool_calls_per_turn`.
32- The cap constants (:data:`MAX_TOOL_CALLS_PER_TURN`, :data:`MAX_PARALLEL_WORKERS`,
33 :data:`MAX_TIMEOUT_S`, :data:`MAX_BYTES`, :data:`MAX_SEARCH_RESULTS`).
34- :func:`clamp_timeout` / :func:`clamp_bytes` / :func:`clamp_results` /
35 :func:`clamp_workers` — clamp a (possibly hostile) model value to its ceiling.
36- :func:`partition_call_budget` — split this iteration's tool calls into
37 (allowed, denied) given the per-turn budget already spent.
38- :func:`resolve_session_cost_hard_cap_usd` / :func:`resolve_cost_thresholds_usd`
39 — the plan-237 (CAPS-01) money bounds, resolved under the same F33/§5.4 rule.
43- A clamp NEVER returns a value above its ceiling; a non-int / unparseable value
44 fails to the ceiling (bounded), never to "unbounded".
45- Clamps only bound the UPPER side; a small/negative value passes through (a tiny
46 read/timeout is not a DoS).
47- :func:`partition_call_budget` is the deterministic per-turn cap helper; the
48 react-loop denies (does not execute) calls beyond the budget but still answers
49 them so the tool-call/result pairing stays well-formed.
53- oc_diagnostics (structured debug logging via _dbg)
54- oc_chat.core.defaults (shipped cap values, ceilings, env-var names and setting keys)
55- oc_chat.core.settings (lazy; runtime_settings for workspace cap and checkpoint requests)
61 L2 — a model arg was clamped below its requested value
62 L3 — per-call clamp detail
65from __future__
import annotations
67from oc_diagnostics
import _dbg
83MAX_TOOL_CALLS_PER_TURN = shipped(
"tool_calls_per_turn")
84MAX_PARALLEL_WORKERS = shipped(
"parallel_workers")
85MAX_TIMEOUT_S = shipped(
"timeout_seconds")
86MAX_BYTES = shipped(
"byte_budget")
87MAX_SEARCH_RESULTS = shipped(
"search_results")
88MAX_DIR_DEPTH = shipped(
"dir_depth")
96_CAP_CEILING_ENV_VAR = env_var(
"tool_calls_per_turn")
103_COST_CEILING_ENV_VAR = env_var(
"session_cost_hard_cap_usd")
107 """Return ``int(value)`` bounded above by *ceiling*; an unparseable value fails
108 to *ceiling* (bounded, never unbounded). Values at or below the ceiling pass."""
109 func =
"_clamp_upper"
112 except (TypeError, ValueError, OverflowError):
113 _dbg(
"OC_CHAT.CORE", func, f
"{label}: non-int {value!r} -> ceiling {ceiling}", 2)
116 _dbg(
"OC_CHAT.CORE", func, f
"{label}: clamped {v} -> {ceiling}", 2)
122 """Clamp a model ``timeout`` (seconds) to :data:`MAX_TIMEOUT_S`."""
123 return _clamp_upper(timeout, MAX_TIMEOUT_S, label=
"timeout")
127 """Clamp a model byte/char budget to :data:`MAX_BYTES` (5 MB)."""
132 """Clamp a model result count to :data:`MAX_SEARCH_RESULTS`."""
133 return _clamp_upper(n, MAX_SEARCH_RESULTS, label=
"results")
137 """Clamp a model directory-walk depth to :data:`MAX_DIR_DEPTH`."""
142 """Bound a parallel-worker pool to :data:`MAX_PARALLEL_WORKERS` (≥ 1)."""
145 except (TypeError, ValueError, OverflowError):
147 return max(1, min(MAX_PARALLEL_WORKERS, v))
151 """The effective per-turn tool-call ceiling — an ABSOLUTE ceiling a settings
152 write may LOWER but never RAISE (F33/§5.4).
154 The workspace ``max_tool_calls_per_turn`` setting (a model-reachable,
155 human-confirm-tier write) can only *lower* the effective cap; it can never
156 raise it above the operator's out-of-band ceiling (the shipped value
157 :data:`MAX_TOOL_CALLS_PER_TURN`, or the OOB env knob
158 :data:`_CAP_CEILING_ENV_VAR` when set, which ``_env_guard`` keeps off the
159 ``.env`` path). So the F52 "non-overridable by model args" DoS bound holds
160 against the settings-file path too. Degrades to the bounded default on a
161 non-int / unavailable settings read.
163 Plan 238 (CONFIG-01) moved the rule itself into
164 :func:`oc_chat.core.defaults.ceiling`, which applies it to every ceiling in
165 the system; this function keeps its name, its callers and its semantics.
167 return ceiling(
"tool_calls_per_turn")
171 """The workspace settings' numeric REQUEST for *setting*, or ``None``.
173 ``None`` means "no request was made" — the settings subsystem is
174 unavailable, the key is absent, the value is unparseable, or it is
175 non-positive (``0`` is the documented "no request" spelling). Every such
176 answer degrades the caller to its bounded default, never to "unbounded".
178 func =
"_settings_request"
183 raw = getattr(runtime_settings(), setting,
None)
184 except Exception
as exc:
185 _dbg(
"OC_CHAT.CORE", func, f
"VALIDATION_ERROR: {type(exc).__name__}: {exc}", 3)
189 except (TypeError, ValueError):
191 return value
if value > 0
else None
195 """The shipped ceiling for *key*, raised ONLY by the env variable it names.
197 This is :func:`oc_chat.core.defaults.ceiling`'s first half, reached through
198 the public accessors. It exists because the two ceilings compose the F33
199 rule from OPPOSITE starting points, and one function cannot be both:
201 - ``tool_calls_per_turn``'s settings key carries a shipped-valued default
202 (``DEFAULT_SETTINGS["max_tool_calls_per_turn"] == shipped(...)``), so
203 "unset" is indistinguishable from "requested exactly the shipped value".
204 Its resolver is therefore ``min(request, env_or_shipped)`` and the env
205 raises the ceiling the settings request is clamped by — unchanged here.
206 - ``session_cost_hard_cap_usd``'s settings key defaults to ``0`` = "no
207 request", so an operator who exports the env variable and writes nothing
208 else must get the raised cap. That is P4's "raisable only out of band".
210 Both are the same rule — settings may LOWER, never RAISE; the operator's
211 out-of-band environment raises — applied to a key whose settings default is
212 a value, and to one whose settings default is a sentinel.
214 func =
"_env_raised_ceiling"
215 absolute = int(shipped(key))
219 raw = os.environ.get(name)
224 except (TypeError, ValueError):
228 _dbg(
"OC_CHAT.CORE", func, f
"{key}: OOB ceiling {name}={candidate}", 2)
233 """The effective session dollar hard cap — settings may LOWER, env raises.
235 CAPS-01 / P4. The shipped ``session_cost_hard_cap_usd`` (50) is the
236 adopters' protection; the workspace settings key the table names may only
237 lower it (``0`` = no request); the ceiling itself moves only when the
238 operator exports :data:`_COST_CEILING_ENV_VAR` out of band, which
239 ``_env_guard`` keeps off the workspace ``.env`` path. Whole dollars: the cap
240 is an int so it reuses the same env parsing every other ceiling uses, and a
241 fractional cap would suggest a precision the honesty gate cannot deliver.
247 return min(absolute, int(request))
251 """The effective session-cost checkpoint ladder, ascending.
253 CAPS-01 / P3, under the same F33 rule as every ceiling: the shipped ladder
254 is a FLOOR of protection. A workspace settings list may add checkpoints
255 BELOW the shipped ceiling — which only makes the session ask sooner — and
256 can neither remove a shipped checkpoint nor add one above the top of the
257 shipped ladder (that would move the last warning later, i.e. raise a bound
258 from a model-reachable file). An unavailable or malformed settings value
259 leaves the shipped ladder exactly as it is.
261 func =
"resolve_cost_thresholds_usd"
262 ladder = [float(v)
for v
in shipped(
"session_cost_thresholds_usd")]
264 setting = setting_key(
"session_cost_thresholds_usd")
265 extra: list[float] = []
269 raw = getattr(runtime_settings(), setting,
None)
270 except Exception
as exc:
271 _dbg(
"OC_CHAT.CORE", func, f
"VALIDATION_ERROR: {type(exc).__name__}: {exc}", 3)
273 if isinstance(raw, (list, tuple)):
277 except (TypeError, ValueError):
282 _dbg(
"OC_CHAT.CORE", func,
283 f
"settings checkpoint {value!r} ignored: not below the "
284 f
"shipped ceiling {top}", 3)
285 return tuple(sorted(set(ladder) | set(extra)))
289 """Return ``(allowed, denied)`` for *n* new tool calls this iteration given
290 *prior* calls already spent this turn, capped at *cap* (default: the resolved
291 per-turn ceiling, :func:`resolve_max_tool_calls_per_turn`)."""
294 allowed = max(0, min(n, cap - max(0, prior)))
295 return allowed, n - allowed
299 "MAX_TOOL_CALLS_PER_TURN",
300 "MAX_PARALLEL_WORKERS",
303 "MAX_SEARCH_RESULTS",
310 "partition_call_budget",
311 "resolve_cost_thresholds_usd",
312 "resolve_max_tool_calls_per_turn",
313 "resolve_session_cost_hard_cap_usd",
int resolve_session_cost_hard_cap_usd()
int clamp_bytes(object n)
int clamp_depth(object n)
int _env_raised_ceiling(str key)
tuple[int, int] partition_call_budget(int prior, int n, "int | None" cap=None)
int clamp_timeout(object timeout)
int resolve_max_tool_calls_per_turn()
int _clamp_upper(object value, int ceiling, *, str label)
int clamp_results(object n)
tuple[float,...] resolve_cost_thresholds_usd()
"float | None" _settings_request(str setting)