openai-agents 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of openai-agents might be problematic. Click here for more details.
- agents/__init__.py +37 -1
- agents/_run_impl.py +252 -26
- agents/exceptions.py +35 -0
- agents/extensions/memory/__init__.py +21 -0
- agents/extensions/memory/advanced_sqlite_session.py +1285 -0
- agents/extensions/memory/redis_session.py +267 -0
- agents/extensions/memory/sqlalchemy_session.py +12 -3
- agents/extensions/models/litellm_model.py +123 -3
- agents/items.py +100 -4
- agents/mcp/server.py +43 -11
- agents/mcp/util.py +17 -1
- agents/memory/openai_conversations_session.py +2 -2
- agents/models/chatcmpl_converter.py +50 -20
- agents/models/openai_chatcompletions.py +27 -26
- agents/models/openai_responses.py +31 -29
- agents/realtime/handoffs.py +1 -1
- agents/result.py +55 -11
- agents/run.py +225 -27
- agents/strict_schema.py +14 -0
- agents/tool.py +80 -3
- agents/tool_guardrails.py +279 -0
- {openai_agents-0.3.2.dist-info → openai_agents-0.4.0.dist-info}/METADATA +14 -3
- {openai_agents-0.3.2.dist-info → openai_agents-0.4.0.dist-info}/RECORD +25 -22
- {openai_agents-0.3.2.dist-info → openai_agents-0.4.0.dist-info}/WHEEL +0 -0
- {openai_agents-0.3.2.dist-info → openai_agents-0.4.0.dist-info}/licenses/LICENSE +0 -0
agents/__init__.py
CHANGED
|
@@ -21,6 +21,8 @@ from .exceptions import (
|
|
|
21
21
|
ModelBehaviorError,
|
|
22
22
|
OutputGuardrailTripwireTriggered,
|
|
23
23
|
RunErrorDetails,
|
|
24
|
+
ToolInputGuardrailTripwireTriggered,
|
|
25
|
+
ToolOutputGuardrailTripwireTriggered,
|
|
24
26
|
UserError,
|
|
25
27
|
)
|
|
26
28
|
from .guardrail import (
|
|
@@ -79,10 +81,27 @@ from .tool import (
|
|
|
79
81
|
MCPToolApprovalFunctionResult,
|
|
80
82
|
MCPToolApprovalRequest,
|
|
81
83
|
Tool,
|
|
84
|
+
ToolOutputFileContent,
|
|
85
|
+
ToolOutputFileContentDict,
|
|
86
|
+
ToolOutputImage,
|
|
87
|
+
ToolOutputImageDict,
|
|
88
|
+
ToolOutputText,
|
|
89
|
+
ToolOutputTextDict,
|
|
82
90
|
WebSearchTool,
|
|
83
91
|
default_tool_error_function,
|
|
84
92
|
function_tool,
|
|
85
93
|
)
|
|
94
|
+
from .tool_guardrails import (
|
|
95
|
+
ToolGuardrailFunctionOutput,
|
|
96
|
+
ToolInputGuardrail,
|
|
97
|
+
ToolInputGuardrailData,
|
|
98
|
+
ToolInputGuardrailResult,
|
|
99
|
+
ToolOutputGuardrail,
|
|
100
|
+
ToolOutputGuardrailData,
|
|
101
|
+
ToolOutputGuardrailResult,
|
|
102
|
+
tool_input_guardrail,
|
|
103
|
+
tool_output_guardrail,
|
|
104
|
+
)
|
|
86
105
|
from .tracing import (
|
|
87
106
|
AgentSpanData,
|
|
88
107
|
CustomSpanData,
|
|
@@ -125,7 +144,7 @@ from .version import __version__
|
|
|
125
144
|
|
|
126
145
|
|
|
127
146
|
def set_default_openai_key(key: str, use_for_tracing: bool = True) -> None:
|
|
128
|
-
"""Set the default OpenAI API key to use for LLM requests (and optionally tracing(). This is
|
|
147
|
+
"""Set the default OpenAI API key to use for LLM requests (and optionally tracing()). This is
|
|
129
148
|
only necessary if the OPENAI_API_KEY environment variable is not already set.
|
|
130
149
|
|
|
131
150
|
If provided, this key will be used instead of the OPENAI_API_KEY environment variable.
|
|
@@ -191,6 +210,8 @@ __all__ = [
|
|
|
191
210
|
"AgentsException",
|
|
192
211
|
"InputGuardrailTripwireTriggered",
|
|
193
212
|
"OutputGuardrailTripwireTriggered",
|
|
213
|
+
"ToolInputGuardrailTripwireTriggered",
|
|
214
|
+
"ToolOutputGuardrailTripwireTriggered",
|
|
194
215
|
"DynamicPromptFunction",
|
|
195
216
|
"GenerateDynamicPromptData",
|
|
196
217
|
"Prompt",
|
|
@@ -204,6 +225,15 @@ __all__ = [
|
|
|
204
225
|
"GuardrailFunctionOutput",
|
|
205
226
|
"input_guardrail",
|
|
206
227
|
"output_guardrail",
|
|
228
|
+
"ToolInputGuardrail",
|
|
229
|
+
"ToolOutputGuardrail",
|
|
230
|
+
"ToolGuardrailFunctionOutput",
|
|
231
|
+
"ToolInputGuardrailData",
|
|
232
|
+
"ToolInputGuardrailResult",
|
|
233
|
+
"ToolOutputGuardrailData",
|
|
234
|
+
"ToolOutputGuardrailResult",
|
|
235
|
+
"tool_input_guardrail",
|
|
236
|
+
"tool_output_guardrail",
|
|
207
237
|
"handoff",
|
|
208
238
|
"Handoff",
|
|
209
239
|
"HandoffInputData",
|
|
@@ -249,6 +279,12 @@ __all__ = [
|
|
|
249
279
|
"MCPToolApprovalFunction",
|
|
250
280
|
"MCPToolApprovalRequest",
|
|
251
281
|
"MCPToolApprovalFunctionResult",
|
|
282
|
+
"ToolOutputText",
|
|
283
|
+
"ToolOutputTextDict",
|
|
284
|
+
"ToolOutputImage",
|
|
285
|
+
"ToolOutputImageDict",
|
|
286
|
+
"ToolOutputFileContent",
|
|
287
|
+
"ToolOutputFileContentDict",
|
|
252
288
|
"function_tool",
|
|
253
289
|
"Usage",
|
|
254
290
|
"add_trace_processor",
|
agents/_run_impl.py
CHANGED
|
@@ -44,7 +44,13 @@ from openai.types.responses.response_reasoning_item import ResponseReasoningItem
|
|
|
44
44
|
from .agent import Agent, ToolsToFinalOutputResult
|
|
45
45
|
from .agent_output import AgentOutputSchemaBase
|
|
46
46
|
from .computer import AsyncComputer, Computer
|
|
47
|
-
from .exceptions import
|
|
47
|
+
from .exceptions import (
|
|
48
|
+
AgentsException,
|
|
49
|
+
ModelBehaviorError,
|
|
50
|
+
ToolInputGuardrailTripwireTriggered,
|
|
51
|
+
ToolOutputGuardrailTripwireTriggered,
|
|
52
|
+
UserError,
|
|
53
|
+
)
|
|
48
54
|
from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult
|
|
49
55
|
from .handoffs import Handoff, HandoffInputData
|
|
50
56
|
from .items import (
|
|
@@ -80,6 +86,12 @@ from .tool import (
|
|
|
80
86
|
Tool,
|
|
81
87
|
)
|
|
82
88
|
from .tool_context import ToolContext
|
|
89
|
+
from .tool_guardrails import (
|
|
90
|
+
ToolInputGuardrailData,
|
|
91
|
+
ToolInputGuardrailResult,
|
|
92
|
+
ToolOutputGuardrailData,
|
|
93
|
+
ToolOutputGuardrailResult,
|
|
94
|
+
)
|
|
83
95
|
from .tracing import (
|
|
84
96
|
SpanError,
|
|
85
97
|
Trace,
|
|
@@ -208,6 +220,12 @@ class SingleStepResult:
|
|
|
208
220
|
next_step: NextStepHandoff | NextStepFinalOutput | NextStepRunAgain
|
|
209
221
|
"""The next step to take."""
|
|
210
222
|
|
|
223
|
+
tool_input_guardrail_results: list[ToolInputGuardrailResult]
|
|
224
|
+
"""Tool input guardrail results from this step."""
|
|
225
|
+
|
|
226
|
+
tool_output_guardrail_results: list[ToolOutputGuardrailResult]
|
|
227
|
+
"""Tool output guardrail results from this step."""
|
|
228
|
+
|
|
211
229
|
@property
|
|
212
230
|
def generated_items(self) -> list[RunItem]:
|
|
213
231
|
"""Items generated during the agent run (i.e. everything generated after
|
|
@@ -249,8 +267,12 @@ class RunImpl:
|
|
|
249
267
|
new_step_items: list[RunItem] = []
|
|
250
268
|
new_step_items.extend(processed_response.new_items)
|
|
251
269
|
|
|
252
|
-
# First, lets run the tool calls - function tools and
|
|
253
|
-
|
|
270
|
+
# First, lets run the tool calls - function tools, computer actions, and local shell calls
|
|
271
|
+
(
|
|
272
|
+
(function_results, tool_input_guardrail_results, tool_output_guardrail_results),
|
|
273
|
+
computer_results,
|
|
274
|
+
local_shell_results,
|
|
275
|
+
) = await asyncio.gather(
|
|
254
276
|
cls.execute_function_tool_calls(
|
|
255
277
|
agent=agent,
|
|
256
278
|
tool_runs=processed_response.functions,
|
|
@@ -265,9 +287,17 @@ class RunImpl:
|
|
|
265
287
|
context_wrapper=context_wrapper,
|
|
266
288
|
config=run_config,
|
|
267
289
|
),
|
|
290
|
+
cls.execute_local_shell_calls(
|
|
291
|
+
agent=agent,
|
|
292
|
+
calls=processed_response.local_shell_calls,
|
|
293
|
+
hooks=hooks,
|
|
294
|
+
context_wrapper=context_wrapper,
|
|
295
|
+
config=run_config,
|
|
296
|
+
),
|
|
268
297
|
)
|
|
269
298
|
new_step_items.extend([result.run_item for result in function_results])
|
|
270
299
|
new_step_items.extend(computer_results)
|
|
300
|
+
new_step_items.extend(local_shell_results)
|
|
271
301
|
|
|
272
302
|
# Next, run the MCP approval requests
|
|
273
303
|
if processed_response.mcp_approval_requests:
|
|
@@ -320,6 +350,8 @@ class RunImpl:
|
|
|
320
350
|
final_output=check_tool_use.final_output,
|
|
321
351
|
hooks=hooks,
|
|
322
352
|
context_wrapper=context_wrapper,
|
|
353
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
354
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
323
355
|
)
|
|
324
356
|
|
|
325
357
|
# Now we can check if the model also produced a final output
|
|
@@ -343,6 +375,8 @@ class RunImpl:
|
|
|
343
375
|
final_output=final_output,
|
|
344
376
|
hooks=hooks,
|
|
345
377
|
context_wrapper=context_wrapper,
|
|
378
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
379
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
346
380
|
)
|
|
347
381
|
elif not output_schema or output_schema.is_plain_text():
|
|
348
382
|
return await cls.execute_final_output(
|
|
@@ -354,6 +388,8 @@ class RunImpl:
|
|
|
354
388
|
final_output=potential_final_output_text or "",
|
|
355
389
|
hooks=hooks,
|
|
356
390
|
context_wrapper=context_wrapper,
|
|
391
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
392
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
357
393
|
)
|
|
358
394
|
|
|
359
395
|
# If there's no final output, we can just run again
|
|
@@ -363,6 +399,8 @@ class RunImpl:
|
|
|
363
399
|
pre_step_items=pre_step_items,
|
|
364
400
|
new_step_items=new_step_items,
|
|
365
401
|
next_step=NextStepRunAgain(),
|
|
402
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
403
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
366
404
|
)
|
|
367
405
|
|
|
368
406
|
@classmethod
|
|
@@ -547,6 +585,155 @@ class RunImpl:
|
|
|
547
585
|
mcp_approval_requests=mcp_approval_requests,
|
|
548
586
|
)
|
|
549
587
|
|
|
588
|
+
@classmethod
|
|
589
|
+
async def _execute_input_guardrails(
|
|
590
|
+
cls,
|
|
591
|
+
*,
|
|
592
|
+
func_tool: FunctionTool,
|
|
593
|
+
tool_context: ToolContext[TContext],
|
|
594
|
+
agent: Agent[TContext],
|
|
595
|
+
tool_input_guardrail_results: list[ToolInputGuardrailResult],
|
|
596
|
+
) -> str | None:
|
|
597
|
+
"""Execute input guardrails for a tool.
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
func_tool: The function tool being executed.
|
|
601
|
+
tool_context: The tool execution context.
|
|
602
|
+
agent: The agent executing the tool.
|
|
603
|
+
tool_input_guardrail_results: List to append guardrail results to.
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
None if tool execution should proceed, or a message string if execution should be
|
|
607
|
+
skipped.
|
|
608
|
+
|
|
609
|
+
Raises:
|
|
610
|
+
ToolInputGuardrailTripwireTriggered: If a guardrail triggers an exception.
|
|
611
|
+
"""
|
|
612
|
+
if not func_tool.tool_input_guardrails:
|
|
613
|
+
return None
|
|
614
|
+
|
|
615
|
+
for guardrail in func_tool.tool_input_guardrails:
|
|
616
|
+
gr_out = await guardrail.run(
|
|
617
|
+
ToolInputGuardrailData(
|
|
618
|
+
context=tool_context,
|
|
619
|
+
agent=agent,
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
# Store the guardrail result
|
|
624
|
+
tool_input_guardrail_results.append(
|
|
625
|
+
ToolInputGuardrailResult(
|
|
626
|
+
guardrail=guardrail,
|
|
627
|
+
output=gr_out,
|
|
628
|
+
)
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
# Handle different behavior types
|
|
632
|
+
if gr_out.behavior["type"] == "raise_exception":
|
|
633
|
+
raise ToolInputGuardrailTripwireTriggered(guardrail=guardrail, output=gr_out)
|
|
634
|
+
elif gr_out.behavior["type"] == "reject_content":
|
|
635
|
+
# Set final_result to the message and skip tool execution
|
|
636
|
+
return gr_out.behavior["message"]
|
|
637
|
+
elif gr_out.behavior["type"] == "allow":
|
|
638
|
+
# Continue to next guardrail or tool execution
|
|
639
|
+
continue
|
|
640
|
+
|
|
641
|
+
return None
|
|
642
|
+
|
|
643
|
+
@classmethod
|
|
644
|
+
async def _execute_output_guardrails(
|
|
645
|
+
cls,
|
|
646
|
+
*,
|
|
647
|
+
func_tool: FunctionTool,
|
|
648
|
+
tool_context: ToolContext[TContext],
|
|
649
|
+
agent: Agent[TContext],
|
|
650
|
+
real_result: Any,
|
|
651
|
+
tool_output_guardrail_results: list[ToolOutputGuardrailResult],
|
|
652
|
+
) -> Any:
|
|
653
|
+
"""Execute output guardrails for a tool.
|
|
654
|
+
|
|
655
|
+
Args:
|
|
656
|
+
func_tool: The function tool being executed.
|
|
657
|
+
tool_context: The tool execution context.
|
|
658
|
+
agent: The agent executing the tool.
|
|
659
|
+
real_result: The actual result from the tool execution.
|
|
660
|
+
tool_output_guardrail_results: List to append guardrail results to.
|
|
661
|
+
|
|
662
|
+
Returns:
|
|
663
|
+
The final result after guardrail processing (may be modified).
|
|
664
|
+
|
|
665
|
+
Raises:
|
|
666
|
+
ToolOutputGuardrailTripwireTriggered: If a guardrail triggers an exception.
|
|
667
|
+
"""
|
|
668
|
+
if not func_tool.tool_output_guardrails:
|
|
669
|
+
return real_result
|
|
670
|
+
|
|
671
|
+
final_result = real_result
|
|
672
|
+
for output_guardrail in func_tool.tool_output_guardrails:
|
|
673
|
+
gr_out = await output_guardrail.run(
|
|
674
|
+
ToolOutputGuardrailData(
|
|
675
|
+
context=tool_context,
|
|
676
|
+
agent=agent,
|
|
677
|
+
output=real_result,
|
|
678
|
+
)
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
# Store the guardrail result
|
|
682
|
+
tool_output_guardrail_results.append(
|
|
683
|
+
ToolOutputGuardrailResult(
|
|
684
|
+
guardrail=output_guardrail,
|
|
685
|
+
output=gr_out,
|
|
686
|
+
)
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
# Handle different behavior types
|
|
690
|
+
if gr_out.behavior["type"] == "raise_exception":
|
|
691
|
+
raise ToolOutputGuardrailTripwireTriggered(
|
|
692
|
+
guardrail=output_guardrail, output=gr_out
|
|
693
|
+
)
|
|
694
|
+
elif gr_out.behavior["type"] == "reject_content":
|
|
695
|
+
# Override the result with the guardrail message
|
|
696
|
+
final_result = gr_out.behavior["message"]
|
|
697
|
+
break
|
|
698
|
+
elif gr_out.behavior["type"] == "allow":
|
|
699
|
+
# Continue to next guardrail
|
|
700
|
+
continue
|
|
701
|
+
|
|
702
|
+
return final_result
|
|
703
|
+
|
|
704
|
+
@classmethod
|
|
705
|
+
async def _execute_tool_with_hooks(
|
|
706
|
+
cls,
|
|
707
|
+
*,
|
|
708
|
+
func_tool: FunctionTool,
|
|
709
|
+
tool_context: ToolContext[TContext],
|
|
710
|
+
agent: Agent[TContext],
|
|
711
|
+
hooks: RunHooks[TContext],
|
|
712
|
+
tool_call: ResponseFunctionToolCall,
|
|
713
|
+
) -> Any:
|
|
714
|
+
"""Execute the core tool function with before/after hooks.
|
|
715
|
+
|
|
716
|
+
Args:
|
|
717
|
+
func_tool: The function tool being executed.
|
|
718
|
+
tool_context: The tool execution context.
|
|
719
|
+
agent: The agent executing the tool.
|
|
720
|
+
hooks: The run hooks to execute.
|
|
721
|
+
tool_call: The tool call details.
|
|
722
|
+
|
|
723
|
+
Returns:
|
|
724
|
+
The result from the tool execution.
|
|
725
|
+
"""
|
|
726
|
+
await asyncio.gather(
|
|
727
|
+
hooks.on_tool_start(tool_context, agent, func_tool),
|
|
728
|
+
(
|
|
729
|
+
agent.hooks.on_tool_start(tool_context, agent, func_tool)
|
|
730
|
+
if agent.hooks
|
|
731
|
+
else _coro.noop_coroutine()
|
|
732
|
+
),
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
return await func_tool.on_invoke_tool(tool_context, tool_call.arguments)
|
|
736
|
+
|
|
550
737
|
@classmethod
|
|
551
738
|
async def execute_function_tool_calls(
|
|
552
739
|
cls,
|
|
@@ -556,7 +743,13 @@ class RunImpl:
|
|
|
556
743
|
hooks: RunHooks[TContext],
|
|
557
744
|
context_wrapper: RunContextWrapper[TContext],
|
|
558
745
|
config: RunConfig,
|
|
559
|
-
) ->
|
|
746
|
+
) -> tuple[
|
|
747
|
+
list[FunctionToolResult], list[ToolInputGuardrailResult], list[ToolOutputGuardrailResult]
|
|
748
|
+
]:
|
|
749
|
+
# Collect guardrail results
|
|
750
|
+
tool_input_guardrail_results: list[ToolInputGuardrailResult] = []
|
|
751
|
+
tool_output_guardrail_results: list[ToolOutputGuardrailResult] = []
|
|
752
|
+
|
|
560
753
|
async def run_single_tool(
|
|
561
754
|
func_tool: FunctionTool, tool_call: ResponseFunctionToolCall
|
|
562
755
|
) -> Any:
|
|
@@ -569,24 +762,48 @@ class RunImpl:
|
|
|
569
762
|
if config.trace_include_sensitive_data:
|
|
570
763
|
span_fn.span_data.input = tool_call.arguments
|
|
571
764
|
try:
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
),
|
|
579
|
-
func_tool.on_invoke_tool(tool_context, tool_call.arguments),
|
|
765
|
+
# 1) Run input tool guardrails, if any
|
|
766
|
+
rejected_message = await cls._execute_input_guardrails(
|
|
767
|
+
func_tool=func_tool,
|
|
768
|
+
tool_context=tool_context,
|
|
769
|
+
agent=agent,
|
|
770
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
580
771
|
)
|
|
581
772
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
773
|
+
if rejected_message is not None:
|
|
774
|
+
# Input guardrail rejected the tool call
|
|
775
|
+
final_result = rejected_message
|
|
776
|
+
else:
|
|
777
|
+
# 2) Actually run the tool
|
|
778
|
+
real_result = await cls._execute_tool_with_hooks(
|
|
779
|
+
func_tool=func_tool,
|
|
780
|
+
tool_context=tool_context,
|
|
781
|
+
agent=agent,
|
|
782
|
+
hooks=hooks,
|
|
783
|
+
tool_call=tool_call,
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
# 3) Run output tool guardrails, if any
|
|
787
|
+
final_result = await cls._execute_output_guardrails(
|
|
788
|
+
func_tool=func_tool,
|
|
789
|
+
tool_context=tool_context,
|
|
790
|
+
agent=agent,
|
|
791
|
+
real_result=real_result,
|
|
792
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
# 4) Tool end hooks (with final result, which may have been overridden)
|
|
796
|
+
await asyncio.gather(
|
|
797
|
+
hooks.on_tool_end(tool_context, agent, func_tool, final_result),
|
|
798
|
+
(
|
|
799
|
+
agent.hooks.on_tool_end(
|
|
800
|
+
tool_context, agent, func_tool, final_result
|
|
801
|
+
)
|
|
802
|
+
if agent.hooks
|
|
803
|
+
else _coro.noop_coroutine()
|
|
804
|
+
),
|
|
805
|
+
)
|
|
806
|
+
result = final_result
|
|
590
807
|
except Exception as e:
|
|
591
808
|
_error_tracing.attach_error_to_current_span(
|
|
592
809
|
SpanError(
|
|
@@ -609,19 +826,21 @@ class RunImpl:
|
|
|
609
826
|
|
|
610
827
|
results = await asyncio.gather(*tasks)
|
|
611
828
|
|
|
612
|
-
|
|
829
|
+
function_tool_results = [
|
|
613
830
|
FunctionToolResult(
|
|
614
831
|
tool=tool_run.function_tool,
|
|
615
832
|
output=result,
|
|
616
833
|
run_item=ToolCallOutputItem(
|
|
617
834
|
output=result,
|
|
618
|
-
raw_item=ItemHelpers.tool_call_output_item(tool_run.tool_call,
|
|
835
|
+
raw_item=ItemHelpers.tool_call_output_item(tool_run.tool_call, result),
|
|
619
836
|
agent=agent,
|
|
620
837
|
),
|
|
621
838
|
)
|
|
622
839
|
for tool_run, result in zip(tool_runs, results)
|
|
623
840
|
]
|
|
624
841
|
|
|
842
|
+
return function_tool_results, tool_input_guardrail_results, tool_output_guardrail_results
|
|
843
|
+
|
|
625
844
|
@classmethod
|
|
626
845
|
async def execute_local_shell_calls(
|
|
627
846
|
cls,
|
|
@@ -825,6 +1044,8 @@ class RunImpl:
|
|
|
825
1044
|
pre_step_items=pre_step_items,
|
|
826
1045
|
new_step_items=new_step_items,
|
|
827
1046
|
next_step=NextStepHandoff(new_agent),
|
|
1047
|
+
tool_input_guardrail_results=[],
|
|
1048
|
+
tool_output_guardrail_results=[],
|
|
828
1049
|
)
|
|
829
1050
|
|
|
830
1051
|
@classmethod
|
|
@@ -873,6 +1094,8 @@ class RunImpl:
|
|
|
873
1094
|
final_output: Any,
|
|
874
1095
|
hooks: RunHooks[TContext],
|
|
875
1096
|
context_wrapper: RunContextWrapper[TContext],
|
|
1097
|
+
tool_input_guardrail_results: list[ToolInputGuardrailResult],
|
|
1098
|
+
tool_output_guardrail_results: list[ToolOutputGuardrailResult],
|
|
876
1099
|
) -> SingleStepResult:
|
|
877
1100
|
# Run the on_end hooks
|
|
878
1101
|
await cls.run_final_output_hooks(agent, hooks, context_wrapper, final_output)
|
|
@@ -883,6 +1106,8 @@ class RunImpl:
|
|
|
883
1106
|
pre_step_items=pre_step_items,
|
|
884
1107
|
new_step_items=new_step_items,
|
|
885
1108
|
next_step=NextStepFinalOutput(final_output),
|
|
1109
|
+
tool_input_guardrail_results=tool_input_guardrail_results,
|
|
1110
|
+
tool_output_guardrail_results=tool_output_guardrail_results,
|
|
886
1111
|
)
|
|
887
1112
|
|
|
888
1113
|
@classmethod
|
|
@@ -1198,12 +1423,13 @@ class LocalShellAction:
|
|
|
1198
1423
|
|
|
1199
1424
|
return ToolCallOutputItem(
|
|
1200
1425
|
agent=agent,
|
|
1201
|
-
output=
|
|
1202
|
-
|
|
1426
|
+
output=result,
|
|
1427
|
+
# LocalShellCallOutput type uses the field name "id", but the server wants "call_id".
|
|
1428
|
+
# raw_item keeps the upstream type, so we ignore the type checker here.
|
|
1429
|
+
raw_item={ # type: ignore[misc, arg-type]
|
|
1203
1430
|
"type": "local_shell_call_output",
|
|
1204
|
-
"
|
|
1431
|
+
"call_id": call.tool_call.call_id,
|
|
1205
1432
|
"output": result,
|
|
1206
|
-
# "id": "out" + call.tool_call.id, # TODO remove this, it should be optional
|
|
1207
1433
|
},
|
|
1208
1434
|
)
|
|
1209
1435
|
|
agents/exceptions.py
CHANGED
|
@@ -8,6 +8,11 @@ if TYPE_CHECKING:
|
|
|
8
8
|
from .guardrail import InputGuardrailResult, OutputGuardrailResult
|
|
9
9
|
from .items import ModelResponse, RunItem, TResponseInputItem
|
|
10
10
|
from .run_context import RunContextWrapper
|
|
11
|
+
from .tool_guardrails import (
|
|
12
|
+
ToolGuardrailFunctionOutput,
|
|
13
|
+
ToolInputGuardrail,
|
|
14
|
+
ToolOutputGuardrail,
|
|
15
|
+
)
|
|
11
16
|
|
|
12
17
|
from .util._pretty_print import pretty_print_run_error_details
|
|
13
18
|
|
|
@@ -94,3 +99,33 @@ class OutputGuardrailTripwireTriggered(AgentsException):
|
|
|
94
99
|
super().__init__(
|
|
95
100
|
f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire"
|
|
96
101
|
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class ToolInputGuardrailTripwireTriggered(AgentsException):
|
|
105
|
+
"""Exception raised when a tool input guardrail tripwire is triggered."""
|
|
106
|
+
|
|
107
|
+
guardrail: ToolInputGuardrail[Any]
|
|
108
|
+
"""The guardrail that was triggered."""
|
|
109
|
+
|
|
110
|
+
output: ToolGuardrailFunctionOutput
|
|
111
|
+
"""The output from the guardrail function."""
|
|
112
|
+
|
|
113
|
+
def __init__(self, guardrail: ToolInputGuardrail[Any], output: ToolGuardrailFunctionOutput):
|
|
114
|
+
self.guardrail = guardrail
|
|
115
|
+
self.output = output
|
|
116
|
+
super().__init__(f"Tool input guardrail {guardrail.__class__.__name__} triggered tripwire")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class ToolOutputGuardrailTripwireTriggered(AgentsException):
|
|
120
|
+
"""Exception raised when a tool output guardrail tripwire is triggered."""
|
|
121
|
+
|
|
122
|
+
guardrail: ToolOutputGuardrail[Any]
|
|
123
|
+
"""The guardrail that was triggered."""
|
|
124
|
+
|
|
125
|
+
output: ToolGuardrailFunctionOutput
|
|
126
|
+
"""The output from the guardrail function."""
|
|
127
|
+
|
|
128
|
+
def __init__(self, guardrail: ToolOutputGuardrail[Any], output: ToolGuardrailFunctionOutput):
|
|
129
|
+
self.guardrail = guardrail
|
|
130
|
+
self.output = output
|
|
131
|
+
super().__init__(f"Tool output guardrail {guardrail.__class__.__name__} triggered tripwire")
|
|
@@ -12,7 +12,9 @@ from typing import Any
|
|
|
12
12
|
|
|
13
13
|
__all__: list[str] = [
|
|
14
14
|
"EncryptedSession",
|
|
15
|
+
"RedisSession",
|
|
15
16
|
"SQLAlchemySession",
|
|
17
|
+
"AdvancedSQLiteSession",
|
|
16
18
|
]
|
|
17
19
|
|
|
18
20
|
|
|
@@ -28,6 +30,17 @@ def __getattr__(name: str) -> Any:
|
|
|
28
30
|
"Install it with: pip install openai-agents[encrypt]"
|
|
29
31
|
) from e
|
|
30
32
|
|
|
33
|
+
if name == "RedisSession":
|
|
34
|
+
try:
|
|
35
|
+
from .redis_session import RedisSession # noqa: F401
|
|
36
|
+
|
|
37
|
+
return RedisSession
|
|
38
|
+
except ModuleNotFoundError as e:
|
|
39
|
+
raise ImportError(
|
|
40
|
+
"RedisSession requires the 'redis' extra. "
|
|
41
|
+
"Install it with: pip install openai-agents[redis]"
|
|
42
|
+
) from e
|
|
43
|
+
|
|
31
44
|
if name == "SQLAlchemySession":
|
|
32
45
|
try:
|
|
33
46
|
from .sqlalchemy_session import SQLAlchemySession # noqa: F401
|
|
@@ -39,4 +52,12 @@ def __getattr__(name: str) -> Any:
|
|
|
39
52
|
"Install it with: pip install openai-agents[sqlalchemy]"
|
|
40
53
|
) from e
|
|
41
54
|
|
|
55
|
+
if name == "AdvancedSQLiteSession":
|
|
56
|
+
try:
|
|
57
|
+
from .advanced_sqlite_session import AdvancedSQLiteSession # noqa: F401
|
|
58
|
+
|
|
59
|
+
return AdvancedSQLiteSession
|
|
60
|
+
except ModuleNotFoundError as e:
|
|
61
|
+
raise ImportError(f"Failed to import AdvancedSQLiteSession: {e}") from e
|
|
62
|
+
|
|
42
63
|
raise AttributeError(f"module {__name__} has no attribute {name}")
|