Prompt steps take a caller-supplied time limit (X-8a)
Prompt steps called call_provider without a timeout, so every AI call was cut at its 300 s default whatever the caller allowed. Now: - PromptStep has an optional `timeout` (seconds) in its YAML; - a universal --provider-timeout (tool wrappers, `cmdforge run`, `run-once`) sets the caller's limit, carried in ProviderExecutionPolicy so nested tools get it too; - precedence: --provider-timeout, then the step's timeout, then 300 (unchanged default). Requested by Orchestrated Discussions, whose work phases need attempts longer than 300 s. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -1256,3 +1256,35 @@ class TestProviderExecutionPolicies:
|
||||
|
||||
assert success is False
|
||||
resolver.assert_not_called()
|
||||
|
||||
|
||||
class TestPromptStepTimeout:
|
||||
"""X-8a: the provider's time limit comes from the caller or the step, not a fixed 300 s."""
|
||||
|
||||
@patch('cmdforge.runner.call_provider')
|
||||
def test_default_stays_300_seconds(self, mock_call):
|
||||
mock_call.return_value = ProviderResult(text="ok", success=True)
|
||||
execute_prompt_step(PromptStep(prompt="x", provider="claude", output_var="o", plain_text=True), {})
|
||||
assert mock_call.call_args.kwargs["timeout"] == 300
|
||||
|
||||
@patch('cmdforge.runner.call_provider')
|
||||
def test_step_timeout_is_used(self, mock_call):
|
||||
mock_call.return_value = ProviderResult(text="ok", success=True)
|
||||
step = PromptStep(prompt="x", provider="claude", output_var="o", plain_text=True, timeout=1800)
|
||||
execute_prompt_step(step, {})
|
||||
assert mock_call.call_args.kwargs["timeout"] == 1800
|
||||
|
||||
@patch('cmdforge.runner.call_provider')
|
||||
def test_callers_provider_timeout_overrides_the_step(self, mock_call):
|
||||
from cmdforge.providers import ProviderExecutionPolicy
|
||||
mock_call.return_value = ProviderResult(text="ok", success=True)
|
||||
step = PromptStep(prompt="x", provider="claude", output_var="o", plain_text=True, timeout=1800)
|
||||
execute_prompt_step(step, {}, execution_policy=ProviderExecutionPolicy(provider_timeout=120))
|
||||
assert mock_call.call_args.kwargs["timeout"] == 120
|
||||
|
||||
def test_step_timeout_round_trips_and_is_validated(self):
|
||||
step = PromptStep.from_dict({"prompt": "x", "provider": "claude", "output_var": "o", "timeout": "900"})
|
||||
assert step.timeout == 900 and step.to_dict()["timeout"] == 900
|
||||
assert "timeout" not in PromptStep(prompt="x", provider="claude", output_var="o").to_dict()
|
||||
with pytest.raises(ValueError):
|
||||
PromptStep(prompt="x", provider="claude", output_var="o", timeout=0)
|
||||
|
||||
Reference in New Issue
Block a user