-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Ensure that input_guardrails can block model/tools from running #1622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Ensure that input_guardrails can block model/tools from running #1622
Conversation
…model events are produced RunResultStreaming.stream_events() previously awaited indefinitely on . If a blocking input guardrail trips before any model output, no events are ever enqueued, causing the consumer to hang. Adding a small timeout lets the loop periodically re-check the guardrail queue and background task state, surfacing tripwires and exceptions promptly without waiting for model events.
Happy to get your feedback @seratch and @rm-openai here as I started from your original PR :). Hope that helps! |
@seratch @rm-openai would you have some time to review the PR this week :)? |
Sorry for our slow response here. Overall, this approach looks good to me. This has been a customization option we've been requested several times from users, so I think having this change would be good. @rm-openai This PR is based on your past PR, so you should be comfortable to review this. Can you take a look soon? |
Summary
main
branch it has been redone from scratch. It should close FileSearchTool runs despite InputGuardrailTripwireTriggered #889 and Input guardrail tripwire allows tool execution to continue after exception is raised #991or run in parallel.
task errors surface even if no model events are produced.
Motivation
concurrently (e.g., telemetry or advisory checks). Previously, all input guardrails ran in parallel with the first
model call, which meant tripwires could trigger after downstream actions had already started.
awaiting events that never arrive.
Key Changes
block_downstream_calls: bool = True
toInputGuardrail
.@input_guardrail
decorator withblock_downstream_calls
(defaultTrue
for safety/back-compat)._separate_blocking_guardrails(...)
: returns(blocking, non_blocking)
._get_model_response_only(...)
: obtains the model response without executing tools._execute_tools_from_model_response(...)
: runs tool execution/side effects from a model response.tripwire triggers, the run task exits and the stream consumer detects the error.
RunResultStreaming.input_guardrail_results
to avoid overwriting.RunResultStreaming.stream_events()
, wrapself._event_queue.get()
in a short timeout:item = await asyncio.wait_for(self._event_queue.get(), timeout=0.1)
TimeoutError
, loop continues and re-checks for stored exceptions and guardrail tripwires.forever. The timeout wakes the loop to:
_input_guardrail_queue
and raiseInputGuardrailTripwireTriggered
.consumer still yields events as they arrive.
Behavior
safest behavior.
call/tools.
responsiveness.
Tests
test_agent_runner_streamed.py::test_input_guardrail_tripwire_triggered_causes_exception_streamed previously hung; with
the timeout it surfaces the tripwire and exits as expected.
Files Touched