From aaec57a426b35079480d96cef473a7494c95b0ad Mon Sep 17 00:00:00 2001 From: Rohan Mehta Date: Tue, 11 Mar 2025 09:42:28 -0700 Subject: [PATCH] Initial commit --- .github/workflows/docs.yml | 26 + .github/workflows/publish.yml | 35 + .github/workflows/tests.yml | 86 ++ .prettierrc | 11 + LICENSE | 21 + Makefile | 37 + README.md | 174 +++ docs/agents.md | 131 ++ docs/assets/images/favicon-platform.svg | 16 + docs/assets/images/orchestration.png | Bin 0 -> 432783 bytes docs/assets/logo.svg | 15 + docs/config.md | 94 ++ docs/context.md | 76 + docs/guardrails.md | 154 ++ docs/handoffs.md | 113 ++ docs/index.md | 52 + docs/models.md | 73 + docs/multi_agent.md | 37 + docs/quickstart.md | 186 +++ docs/ref/agent.md | 3 + docs/ref/agent_output.md | 3 + docs/ref/exceptions.md | 3 + docs/ref/extensions/handoff_filters.md | 3 + docs/ref/extensions/handoff_prompt.md | 8 + docs/ref/function_schema.md | 3 + docs/ref/guardrail.md | 3 + docs/ref/handoffs.md | 3 + docs/ref/index.md | 13 + docs/ref/items.md | 3 + docs/ref/lifecycle.md | 6 + docs/ref/model_settings.md | 3 + docs/ref/models/interface.md | 3 + docs/ref/models/openai_chatcompletions.md | 3 + docs/ref/models/openai_responses.md | 3 + docs/ref/result.md | 3 + docs/ref/run.md | 8 + docs/ref/run_context.md | 3 + docs/ref/stream_events.md | 3 + docs/ref/tool.md | 3 + docs/ref/tracing/create.md | 3 + docs/ref/tracing/index.md | 3 + docs/ref/tracing/processor_interface.md | 3 + docs/ref/tracing/processors.md | 3 + docs/ref/tracing/scope.md | 3 + docs/ref/tracing/setup.md | 3 + docs/ref/tracing/span_data.md | 3 + docs/ref/tracing/spans.md | 9 + docs/ref/tracing/traces.md | 3 + docs/ref/tracing/util.md | 3 + docs/ref/usage.md | 3 + docs/results.md | 52 + docs/running_agents.md | 95 ++ docs/streaming.md | 87 ++ docs/stylesheets/extra.css | 194 +++ docs/tools.md | 270 ++++ docs/tracing.md | 95 ++ examples/__init__.py | 3 + examples/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 151 bytes examples/agent_patterns/README.md | 54 + examples/agent_patterns/agents_as_tools.py | 79 + examples/agent_patterns/deterministic.py | 80 + examples/agent_patterns/input_guardrails.py | 105 ++ examples/agent_patterns/llm_as_a_judge.py | 76 + examples/agent_patterns/output_guardrails.py | 80 + examples/agent_patterns/parallelization.py | 61 + examples/agent_patterns/routing.py | 70 + examples/basic/agent_lifecycle_example.py | 112 ++ examples/basic/dynamic_system_prompt.py | 69 + examples/basic/hello_world.py | 20 + examples/basic/lifecycle_example.py | 118 ++ examples/basic/stream_items.py | 65 + examples/basic/stream_text.py | 21 + examples/customer_service/main.py | 169 ++ examples/handoffs/message_filter.py | 176 +++ examples/handoffs/message_filter_streaming.py | 176 +++ examples/research_bot/README.md | 25 + examples/research_bot/__init__.py | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 165 bytes .../__pycache__/main.cpython-313.pyc | Bin 0 -> 664 bytes .../__pycache__/manager.cpython-313.pyc | Bin 0 -> 7031 bytes .../__pycache__/printer.cpython-313.pyc | Bin 0 -> 2823 bytes examples/research_bot/agents/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 172 bytes .../__pycache__/base_agent.cpython-313.pyc | Bin 0 -> 929 bytes .../__pycache__/planner_agent.cpython-313.pyc | Bin 0 -> 1133 bytes .../research_manager_agent.cpython-313.pyc | Bin 0 -> 3906 bytes .../__pycache__/search_agent.cpython-313.pyc | Bin 0 -> 964 bytes .../summarization_agent.cpython-313.pyc | Bin 0 -> 625 bytes .../__pycache__/writer_agent.cpython-313.pyc | Bin 0 -> 1293 bytes examples/research_bot/agents/planner_agent.py | 29 + examples/research_bot/agents/search_agent.py | 18 + examples/research_bot/agents/writer_agent.py | 33 + examples/research_bot/main.py | 12 + examples/research_bot/manager.py | 119 ++ examples/research_bot/printer.py | 41 + .../sample_outputs/product_recs.md | 180 +++ .../sample_outputs/product_recs.txt | 212 +++ .../research_bot/sample_outputs/vacation.md | 177 +++ .../research_bot/sample_outputs/vacation.txt | 206 +++ examples/tools/computer_use.py | 165 ++ examples/tools/file_search.py | 36 + examples/tools/web_search.py | 23 + mkdocs.yml | 121 ++ pyproject.toml | 119 ++ src/agents/__init__.py | 223 +++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 6280 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 5761 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5232 bytes .../__pycache__/_config.cpython-311.pyc | Bin 0 -> 1508 bytes .../__pycache__/_config.cpython-313.pyc | Bin 0 -> 1335 bytes src/agents/__pycache__/_config.cpython-39.pyc | Bin 0 -> 999 bytes src/agents/__pycache__/_debug.cpython-313.pyc | Bin 0 -> 739 bytes src/agents/__pycache__/_debug.cpython-39.pyc | Bin 0 -> 518 bytes .../__pycache__/_run_impl.cpython-313.pyc | Bin 0 -> 32583 bytes .../__pycache__/_run_impl.cpython-39.pyc | Bin 0 -> 17935 bytes .../_strict_schema.cpython-313.pyc | Bin 0 -> 5560 bytes src/agents/__pycache__/_utils.cpython-313.pyc | Bin 0 -> 2495 bytes src/agents/__pycache__/_utils.cpython-39.pyc | Bin 0 -> 1981 bytes src/agents/__pycache__/agent.cpython-311.pyc | Bin 0 -> 4695 bytes src/agents/__pycache__/agent.cpython-313.pyc | Bin 0 -> 6193 bytes src/agents/__pycache__/agent.cpython-39.pyc | Bin 0 -> 4877 bytes .../__pycache__/agent_output.cpython-313.pyc | Bin 0 -> 6002 bytes .../__pycache__/agent_output.cpython-39.pyc | Bin 0 -> 4019 bytes .../call_agent_tool.cpython-313.pyc | Bin 0 -> 3070 bytes .../call_agent_tool.cpython-39.pyc | Bin 0 -> 2546 bytes .../__pycache__/computer.cpython-313.pyc | Bin 0 -> 5841 bytes .../__pycache__/computer.cpython-39.pyc | Bin 0 -> 4169 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 3660 bytes .../__pycache__/exceptions.cpython-313.pyc | Bin 0 -> 3235 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 2625 bytes .../function_schema.cpython-313.pyc | Bin 0 -> 12148 bytes .../function_schema.cpython-39.pyc | Bin 0 -> 7825 bytes .../__pycache__/guardrail.cpython-311.pyc | Bin 0 -> 10844 bytes .../__pycache__/guardrail.cpython-313.pyc | Bin 0 -> 9979 bytes .../__pycache__/guardrail.cpython-39.pyc | Bin 0 -> 7825 bytes .../guardrail_base.cpython-313.pyc | Bin 0 -> 3401 bytes .../__pycache__/guardrail_base.cpython-39.pyc | Bin 0 -> 3458 bytes .../__pycache__/guardrails.cpython-313.pyc | Bin 0 -> 4141 bytes .../__pycache__/handoff.cpython-313.pyc | Bin 0 -> 8542 bytes src/agents/__pycache__/handoff.cpython-39.pyc | Bin 0 -> 6070 bytes .../__pycache__/handoffs.cpython-313.pyc | Bin 0 -> 8572 bytes .../__pycache__/handoffs.cpython-39.pyc | Bin 0 -> 6154 bytes src/agents/__pycache__/items.cpython-311.pyc | Bin 0 -> 9200 bytes src/agents/__pycache__/items.cpython-313.pyc | Bin 0 -> 9267 bytes src/agents/__pycache__/items.cpython-39.pyc | Bin 0 -> 6896 bytes .../__pycache__/lifecycle.cpython-313.pyc | Bin 0 -> 3990 bytes .../__pycache__/lifecycle.cpython-39.pyc | Bin 0 -> 3310 bytes src/agents/__pycache__/logger.cpython-313.pyc | Bin 0 -> 261 bytes src/agents/__pycache__/logger.cpython-39.pyc | Bin 0 -> 212 bytes .../model_settings.cpython-313.pyc | Bin 0 -> 2131 bytes .../__pycache__/model_settings.cpython-39.pyc | Bin 0 -> 1469 bytes .../__pycache__/output_tool.cpython-313.pyc | Bin 0 -> 933 bytes .../__pycache__/output_tool.cpython-39.pyc | Bin 0 -> 790 bytes src/agents/__pycache__/result.cpython-313.pyc | Bin 0 -> 10436 bytes src/agents/__pycache__/result.cpython-39.pyc | Bin 0 -> 6887 bytes src/agents/__pycache__/run.cpython-313.pyc | Bin 0 -> 31819 bytes src/agents/__pycache__/run.cpython-39.pyc | Bin 0 -> 20203 bytes .../__pycache__/run_context.cpython-313.pyc | Bin 0 -> 1144 bytes .../__pycache__/run_context.cpython-39.pyc | Bin 0 -> 997 bytes .../__pycache__/stream_events.cpython-313.pyc | Bin 0 -> 2174 bytes .../__pycache__/stream_events.cpython-39.pyc | Bin 0 -> 1843 bytes .../__pycache__/strict_schema.cpython-313.pyc | Bin 0 -> 6118 bytes .../__pycache__/strict_schema.cpython-39.pyc | Bin 0 -> 4099 bytes src/agents/__pycache__/tool.cpython-313.pyc | Bin 0 -> 11731 bytes src/agents/__pycache__/tool.cpython-39.pyc | Bin 0 -> 8572 bytes .../tool_converter.cpython-313.pyc | Bin 0 -> 2144 bytes .../__pycache__/tool_converter.cpython-39.pyc | Bin 0 -> 1534 bytes src/agents/__pycache__/usage.cpython-313.pyc | Bin 0 -> 1357 bytes src/agents/__pycache__/usage.cpython-39.pyc | Bin 0 -> 774 bytes .../__pycache__/version.cpython-313.pyc | Bin 0 -> 465 bytes src/agents/__pycache__/version.cpython-39.pyc | Bin 0 -> 310 bytes src/agents/_config.py | 23 + src/agents/_debug.py | 17 + src/agents/_run_impl.py | 792 ++++++++++ src/agents/_utils.py | 61 + src/agents/agent.py | 159 ++ src/agents/agent_output.py | 144 ++ src/agents/computer.py | 107 ++ src/agents/exceptions.py | 63 + src/agents/extensions/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 166 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 160 bytes .../handoff_filters.cpython-313.pyc | Bin 0 -> 2320 bytes .../handoff_filters.cpython-39.pyc | Bin 0 -> 1636 bytes .../handoff_prompt.cpython-313.pyc | Bin 0 -> 1071 bytes src/agents/extensions/handoff_filters.py | 67 + src/agents/extensions/handoff_prompt.py | 19 + src/agents/function_schema.py | 340 +++++ src/agents/guardrail.py | 320 ++++ src/agents/handoffs.py | 236 +++ src/agents/items.py | 246 +++ src/agents/lifecycle.py | 105 ++ src/agents/logger.py | 3 + src/agents/model_settings.py | 35 + src/agents/models/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1098 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 162 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 156 bytes .../_openai_shared.cpython-311.pyc | Bin 0 -> 1651 bytes .../_openai_shared.cpython-313.pyc | Bin 0 -> 1429 bytes .../__pycache__/_openai_shared.cpython-39.pyc | Bin 0 -> 1308 bytes .../__pycache__/fake_id.cpython-313.pyc | Bin 0 -> 205 bytes .../models/__pycache__/fake_id.cpython-39.pyc | Bin 0 -> 190 bytes .../__pycache__/interface.cpython-313.pyc | Bin 0 -> 4222 bytes .../__pycache__/interface.cpython-39.pyc | Bin 0 -> 3639 bytes .../models/__pycache__/map.cpython-313.pyc | Bin 0 -> 760 bytes .../models/__pycache__/map.cpython-39.pyc | Bin 0 -> 630 bytes .../openai_chatcompletions.cpython-313.pyc | Bin 0 -> 34089 bytes .../openai_chatcompletions.cpython-39.pyc | Bin 0 -> 20220 bytes .../openai_provider.cpython-313.pyc | Bin 0 -> 2868 bytes .../openai_provider.cpython-39.pyc | Bin 0 -> 2044 bytes .../openai_responses.cpython-313.pyc | Bin 0 -> 15415 bytes .../openai_responses.cpython-39.pyc | Bin 0 -> 9521 bytes src/agents/models/_openai_shared.py | 34 + src/agents/models/fake_id.py | 5 + src/agents/models/interface.py | 107 ++ src/agents/models/openai_chatcompletions.py | 952 ++++++++++++ src/agents/models/openai_provider.py | 65 + src/agents/models/openai_responses.py | 384 +++++ src/agents/result.py | 220 +++ src/agents/run.py | 904 +++++++++++ src/agents/run_context.py | 26 + src/agents/stream_events.py | 58 + src/agents/strict_schema.py | 167 ++ src/agents/tool.py | 286 ++++ src/agents/tracing/__init__.py | 97 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2896 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2576 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2212 bytes .../_strict_schema.cpython-313.pyc | Bin 0 -> 5568 bytes .../__pycache__/create.cpython-311.pyc | Bin 0 -> 10389 bytes .../__pycache__/create.cpython-313.pyc | Bin 0 -> 12635 bytes .../tracing/__pycache__/create.cpython-39.pyc | Bin 0 -> 11403 bytes .../__pycache__/logger.cpython-311.pyc | Bin 0 -> 302 bytes .../__pycache__/logger.cpython-313.pyc | Bin 0 -> 278 bytes .../tracing/__pycache__/logger.cpython-39.pyc | Bin 0 -> 228 bytes .../processor_interface.cpython-311.pyc | Bin 0 -> 2644 bytes .../processor_interface.cpython-313.pyc | Bin 0 -> 2943 bytes .../processor_interface.cpython-39.pyc | Bin 0 -> 2574 bytes .../__pycache__/processors.cpython-311.pyc | Bin 0 -> 13530 bytes .../__pycache__/processors.cpython-313.pyc | Bin 0 -> 12774 bytes .../__pycache__/processors.cpython-39.pyc | Bin 0 -> 8801 bytes .../tracing/__pycache__/scope.cpython-311.pyc | Bin 0 -> 3108 bytes .../tracing/__pycache__/scope.cpython-313.pyc | Bin 0 -> 2764 bytes .../tracing/__pycache__/scope.cpython-39.pyc | Bin 0 -> 1960 bytes .../tracing/__pycache__/setup.cpython-311.pyc | Bin 0 -> 10043 bytes .../tracing/__pycache__/setup.cpython-313.pyc | Bin 0 -> 9665 bytes .../tracing/__pycache__/setup.cpython-39.pyc | Bin 0 -> 6798 bytes .../__pycache__/span_data.cpython-311.pyc | Bin 0 -> 6627 bytes .../__pycache__/span_data.cpython-313.pyc | Bin 0 -> 7611 bytes .../__pycache__/span_data.cpython-39.pyc | Bin 0 -> 5704 bytes .../tracing/__pycache__/spans.cpython-311.pyc | Bin 0 -> 11920 bytes .../tracing/__pycache__/spans.cpython-313.pyc | Bin 0 -> 11058 bytes .../tracing/__pycache__/spans.cpython-39.pyc | Bin 0 -> 8284 bytes .../__pycache__/traces.cpython-311.pyc | Bin 0 -> 8065 bytes .../__pycache__/traces.cpython-313.pyc | Bin 0 -> 7498 bytes .../tracing/__pycache__/traces.cpython-39.pyc | Bin 0 -> 5578 bytes .../tracing/__pycache__/util.cpython-311.pyc | Bin 0 -> 1047 bytes .../tracing/__pycache__/util.cpython-313.pyc | Bin 0 -> 1076 bytes .../tracing/__pycache__/util.cpython-39.pyc | Bin 0 -> 771 bytes src/agents/tracing/create.py | 306 ++++ src/agents/tracing/logger.py | 3 + src/agents/tracing/processor_interface.py | 69 + src/agents/tracing/processors.py | 261 ++++ src/agents/tracing/scope.py | 45 + src/agents/tracing/setup.py | 211 +++ src/agents/tracing/span_data.py | 188 +++ src/agents/tracing/spans.py | 264 ++++ src/agents/tracing/traces.py | 195 +++ src/agents/tracing/util.py | 17 + src/agents/usage.py | 22 + src/agents/version.py | 7 + src/openai_agents.egg-info/PKG-INFO | 217 +++ src/openai_agents.egg-info/SOURCES.txt | 81 + .../dependency_links.txt | 1 + src/openai_agents.egg-info/requires.txt | 6 + src/openai_agents.egg-info/top_level.txt | 1 + tests/LICENSE | 21 + tests/Makefile | 37 + tests/README.md | 174 +++ tests/__init__.py | 0 tests/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 167 bytes tests/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 150 bytes tests/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 144 bytes .../conftest.cpython-311-pytest-8.2.0.pyc | Bin 0 -> 3338 bytes .../conftest.cpython-311-pytest-8.3.4.pyc | Bin 0 -> 1205 bytes .../conftest.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 2007 bytes .../conftest.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 2946 bytes .../conftest.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 787 bytes .../conftest.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 2070 bytes tests/__pycache__/fake_model.cpython-313.pyc | Bin 0 -> 4966 bytes tests/__pycache__/fake_model.cpython-39.pyc | Bin 0 -> 3492 bytes ..._agent_config.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 37139 bytes ..._agent_config.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 27941 bytes ...t_agent_config.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 13954 bytes ...t_agent_config.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 10716 bytes ...t_agent_hooks.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 21342 bytes ...t_agent_hooks.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 23133 bytes ...st_agent_hooks.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 8748 bytes ...st_agent_hooks.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 9404 bytes ..._agent_runner.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 53952 bytes ..._agent_runner.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 52554 bytes ...t_agent_runner.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 21847 bytes ...t_agent_runner.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 21721 bytes ...nner_streamed.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 52824 bytes ...nner_streamed.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 64814 bytes ...unner_streamed.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 20914 bytes ...unner_streamed.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 25974 bytes ...amed_warnings.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 3385 bytes ...agent_tracing.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 29840 bytes ...agent_tracing.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 31326 bytes ..._agent_tracing.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 11423 bytes ..._agent_tracing.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 12281 bytes ...mputer_action.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 32309 bytes ...omputer_action.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 16515 bytes .../test_config.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 11706 bytes .../test_config.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 5037 bytes ...t_doc_parsing.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 12159 bytes ...t_doc_parsing.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 12154 bytes ...st_doc_parsing.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 5670 bytes ...st_doc_parsing.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 5665 bytes ...nsion_filters.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 18744 bytes ...ension_filters.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 8023 bytes ...nction_schema.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 58903 bytes ...nction_schema.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 65519 bytes ...unction_schema.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 22779 bytes ...unction_schema.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 25703 bytes ...function_tool.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 24588 bytes ...function_tool.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 36417 bytes ..._function_tool.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 10520 bytes ..._function_tool.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 15033 bytes ...ool_decorator.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 15415 bytes ...tool_decorator.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 7829 bytes ..._global_hooks.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 21501 bytes ..._global_hooks.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 20979 bytes ...t_global_hooks.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 8785 bytes ...t_global_hooks.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 8421 bytes ...st_guardrails.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 10945 bytes ...st_guardrails.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 29558 bytes ...est_guardrails.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 4643 bytes ...est_guardrails.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 11466 bytes ..._handoff_tool.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 43922 bytes ..._handoff_tool.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 44557 bytes ...t_handoff_tool.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 16832 bytes ...t_handoff_tool.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 17350 bytes ...items_helpers.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 37624 bytes ..._items_helpers.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 17961 bytes ...est_lifecycle.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 21404 bytes ...est_max_turns.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 5535 bytes ...est_max_turns.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 5368 bytes ...test_max_turns.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 2965 bytes ...test_max_turns.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 2800 bytes ..._model_mapper.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 1244 bytes ..._model_mapper.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 1237 bytes ...t_model_mapper.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 868 bytes ...t_model_mapper.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 861 bytes ...atcompletions.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 38744 bytes ...hatcompletions.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 17478 bytes ...ons_converter.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 70801 bytes ...ions_converter.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 30211 bytes ..._get_response.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 24226 bytes ...etions_stream.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 32838 bytes ...letions_stream.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 13750 bytes ...ses_converter.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 44041 bytes ...nses_converter.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 19318 bytes ...t_output_tool.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 26450 bytes ...t_output_tool.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 19614 bytes ...st_output_tool.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 11010 bytes ...st_output_tool.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 8661 bytes ...est_responses.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 3336 bytes ...est_responses.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 2754 bytes .../test_responses.cpython-313.pyc | Bin 0 -> 2421 bytes ...test_responses.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 2601 bytes ...test_responses.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 2245 bytes ...onses_tracing.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 22477 bytes ...ponses_tracing.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 11136 bytes ...t_result_cast.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 6161 bytes ...st_result_cast.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 3140 bytes ...st_run_config.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 162 bytes ...st_run_config.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 9070 bytes ...est_run_config.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 156 bytes ...est_run_config.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 5068 bytes ...tep_execution.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 42426 bytes ...tep_execution.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 47487 bytes ...step_execution.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 16468 bytes ...step_execution.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 18291 bytes ...ep_processing.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 38332 bytes ...ep_processing.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 43343 bytes .../test_run_step_processing.cpython-313.pyc | Bin 0 -> 18135 bytes ...tep_processing.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 15585 bytes ...tep_processing.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 19533 bytes ...strict_schema.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 13353 bytes ..._strict_schema.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 6603 bytes ...ool_converter.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 12692 bytes ...ool_converter.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 10174 bytes ...tool_converter.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 5974 bytes ...tool_converter.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 4951 bytes ...ace_processor.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 18173 bytes ...ace_processor.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 23022 bytes ...race_processor.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 9436 bytes ...race_processor.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 11012 bytes .../test_tracing.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 41461 bytes .../test_tracing.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 51291 bytes .../test_tracing.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 17494 bytes .../test_tracing.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 21302 bytes ...racing_errors.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 29045 bytes ...racing_errors.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 33694 bytes ...tracing_errors.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 12537 bytes ...tracing_errors.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 14330 bytes ...rors_streamed.cpython-313-pytest-8.3.4.pyc | Bin 0 -> 29781 bytes ...rors_streamed.cpython-313-pytest-8.3.5.pyc | Bin 0 -> 38822 bytes ...rrors_streamed.cpython-39-pytest-8.3.4.pyc | Bin 0 -> 12834 bytes ...rrors_streamed.cpython-39-pytest-8.3.5.pyc | Bin 0 -> 16366 bytes .../testing_processor.cpython-313.pyc | Bin 0 -> 5362 bytes .../testing_processor.cpython-39.pyc | Bin 0 -> 3974 bytes tests/conftest.py | 55 + tests/docs/agents.md | 131 ++ tests/docs/assets/images/favicon-platform.svg | 16 + tests/docs/assets/images/orchestration.png | Bin 0 -> 432783 bytes tests/docs/assets/logo.svg | 15 + tests/docs/config.md | 94 ++ tests/docs/context.md | 76 + tests/docs/guardrails.md | 154 ++ tests/docs/handoffs.md | 113 ++ tests/docs/index.md | 52 + tests/docs/models.md | 73 + tests/docs/multi_agent.md | 37 + tests/docs/quickstart.md | 186 +++ tests/docs/ref/agent.md | 3 + tests/docs/ref/agent_output.md | 3 + tests/docs/ref/exceptions.md | 3 + tests/docs/ref/extensions/handoff_filters.md | 3 + tests/docs/ref/extensions/handoff_prompt.md | 8 + tests/docs/ref/function_schema.md | 3 + tests/docs/ref/guardrail.md | 3 + tests/docs/ref/handoffs.md | 3 + tests/docs/ref/index.md | 13 + tests/docs/ref/items.md | 3 + tests/docs/ref/lifecycle.md | 6 + tests/docs/ref/model_settings.md | 3 + tests/docs/ref/models/interface.md | 3 + .../docs/ref/models/openai_chatcompletions.md | 3 + tests/docs/ref/models/openai_responses.md | 3 + tests/docs/ref/result.md | 3 + tests/docs/ref/run.md | 8 + tests/docs/ref/run_context.md | 3 + tests/docs/ref/stream_events.md | 3 + tests/docs/ref/tool.md | 3 + tests/docs/ref/tracing/create.md | 3 + tests/docs/ref/tracing/index.md | 3 + tests/docs/ref/tracing/processor_interface.md | 3 + tests/docs/ref/tracing/processors.md | 3 + tests/docs/ref/tracing/scope.md | 3 + tests/docs/ref/tracing/setup.md | 3 + tests/docs/ref/tracing/span_data.md | 3 + tests/docs/ref/tracing/spans.md | 9 + tests/docs/ref/tracing/traces.md | 3 + tests/docs/ref/tracing/util.md | 3 + tests/docs/ref/usage.md | 3 + tests/docs/results.md | 52 + tests/docs/running_agents.md | 95 ++ tests/docs/streaming.md | 87 ++ tests/docs/stylesheets/extra.css | 194 +++ tests/docs/tools.md | 270 ++++ tests/docs/tracing.md | 95 ++ tests/examples/__init__.py | 3 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 151 bytes tests/examples/agent_patterns/README.md | 54 + .../agent_patterns/agents_as_tools.py | 79 + .../examples/agent_patterns/deterministic.py | 80 + .../agent_patterns/input_guardrails.py | 105 ++ .../examples/agent_patterns/llm_as_a_judge.py | 76 + .../agent_patterns/output_guardrails.py | 80 + .../agent_patterns/parallelization.py | 61 + tests/examples/agent_patterns/routing.py | 70 + .../examples/basic/agent_lifecycle_example.py | 112 ++ tests/examples/basic/dynamic_system_prompt.py | 69 + tests/examples/basic/hello_world.py | 20 + tests/examples/basic/lifecycle_example.py | 118 ++ tests/examples/basic/stream_items.py | 65 + tests/examples/basic/stream_text.py | 21 + tests/examples/customer_service/main.py | 169 ++ tests/examples/handoffs/message_filter.py | 176 +++ .../handoffs/message_filter_streaming.py | 176 +++ tests/examples/research_bot/README.md | 25 + tests/examples/research_bot/__init__.py | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 165 bytes .../__pycache__/main.cpython-313.pyc | Bin 0 -> 664 bytes .../__pycache__/manager.cpython-313.pyc | Bin 0 -> 7031 bytes .../__pycache__/printer.cpython-313.pyc | Bin 0 -> 2823 bytes .../examples/research_bot/agents/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 172 bytes .../__pycache__/base_agent.cpython-313.pyc | Bin 0 -> 929 bytes .../__pycache__/planner_agent.cpython-313.pyc | Bin 0 -> 1133 bytes .../research_manager_agent.cpython-313.pyc | Bin 0 -> 3906 bytes .../__pycache__/search_agent.cpython-313.pyc | Bin 0 -> 964 bytes .../summarization_agent.cpython-313.pyc | Bin 0 -> 625 bytes .../__pycache__/writer_agent.cpython-313.pyc | Bin 0 -> 1293 bytes .../research_bot/agents/planner_agent.py | 29 + .../research_bot/agents/search_agent.py | 18 + .../research_bot/agents/writer_agent.py | 33 + tests/examples/research_bot/main.py | 12 + tests/examples/research_bot/manager.py | 119 ++ tests/examples/research_bot/printer.py | 41 + .../sample_outputs/product_recs.md | 180 +++ .../sample_outputs/product_recs.txt | 212 +++ .../research_bot/sample_outputs/vacation.md | 177 +++ .../research_bot/sample_outputs/vacation.txt | 206 +++ tests/examples/tools/computer_use.py | 165 ++ tests/examples/tools/file_search.py | 36 + tests/examples/tools/web_search.py | 23 + tests/fake_model.py | 118 ++ tests/mkdocs.yml | 121 ++ tests/pyproject.toml | 119 ++ tests/src/agents/__init__.py | 223 +++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 6280 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 5759 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 5230 bytes .../__pycache__/_config.cpython-311.pyc | Bin 0 -> 1508 bytes .../__pycache__/_config.cpython-313.pyc | Bin 0 -> 1340 bytes .../agents/__pycache__/_config.cpython-39.pyc | Bin 0 -> 1004 bytes .../agents/__pycache__/_debug.cpython-313.pyc | Bin 0 -> 744 bytes .../agents/__pycache__/_debug.cpython-39.pyc | Bin 0 -> 523 bytes .../__pycache__/_run_impl.cpython-313.pyc | Bin 0 -> 32581 bytes .../__pycache__/_run_impl.cpython-39.pyc | Bin 0 -> 17933 bytes .../_strict_schema.cpython-313.pyc | Bin 0 -> 5560 bytes .../agents/__pycache__/_utils.cpython-313.pyc | Bin 0 -> 2493 bytes .../agents/__pycache__/_utils.cpython-39.pyc | Bin 0 -> 1979 bytes .../agents/__pycache__/agent.cpython-311.pyc | Bin 0 -> 4695 bytes .../agents/__pycache__/agent.cpython-313.pyc | Bin 0 -> 6191 bytes .../agents/__pycache__/agent.cpython-39.pyc | Bin 0 -> 4875 bytes .../__pycache__/agent_output.cpython-313.pyc | Bin 0 -> 6000 bytes .../__pycache__/agent_output.cpython-39.pyc | Bin 0 -> 4017 bytes .../call_agent_tool.cpython-313.pyc | Bin 0 -> 3070 bytes .../call_agent_tool.cpython-39.pyc | Bin 0 -> 2546 bytes .../__pycache__/computer.cpython-313.pyc | Bin 0 -> 5846 bytes .../__pycache__/computer.cpython-39.pyc | Bin 0 -> 4174 bytes .../__pycache__/exceptions.cpython-311.pyc | Bin 0 -> 3660 bytes .../__pycache__/exceptions.cpython-313.pyc | Bin 0 -> 3240 bytes .../__pycache__/exceptions.cpython-39.pyc | Bin 0 -> 2630 bytes .../function_schema.cpython-313.pyc | Bin 0 -> 12146 bytes .../function_schema.cpython-39.pyc | Bin 0 -> 7823 bytes .../__pycache__/guardrail.cpython-311.pyc | Bin 0 -> 10844 bytes .../__pycache__/guardrail.cpython-313.pyc | Bin 0 -> 9977 bytes .../__pycache__/guardrail.cpython-39.pyc | Bin 0 -> 7823 bytes .../guardrail_base.cpython-313.pyc | Bin 0 -> 3401 bytes .../__pycache__/guardrail_base.cpython-39.pyc | Bin 0 -> 3458 bytes .../__pycache__/guardrails.cpython-313.pyc | Bin 0 -> 4141 bytes .../__pycache__/handoff.cpython-313.pyc | Bin 0 -> 8542 bytes .../agents/__pycache__/handoff.cpython-39.pyc | Bin 0 -> 6070 bytes .../__pycache__/handoffs.cpython-313.pyc | Bin 0 -> 8570 bytes .../__pycache__/handoffs.cpython-39.pyc | Bin 0 -> 6152 bytes .../agents/__pycache__/items.cpython-311.pyc | Bin 0 -> 9200 bytes .../agents/__pycache__/items.cpython-313.pyc | Bin 0 -> 9265 bytes .../agents/__pycache__/items.cpython-39.pyc | Bin 0 -> 6894 bytes .../__pycache__/lifecycle.cpython-313.pyc | Bin 0 -> 3988 bytes .../__pycache__/lifecycle.cpython-39.pyc | Bin 0 -> 3308 bytes .../agents/__pycache__/logger.cpython-313.pyc | Bin 0 -> 266 bytes .../agents/__pycache__/logger.cpython-39.pyc | Bin 0 -> 217 bytes .../model_settings.cpython-313.pyc | Bin 0 -> 2129 bytes .../__pycache__/model_settings.cpython-39.pyc | Bin 0 -> 1467 bytes .../__pycache__/output_tool.cpython-313.pyc | Bin 0 -> 933 bytes .../__pycache__/output_tool.cpython-39.pyc | Bin 0 -> 790 bytes .../agents/__pycache__/result.cpython-313.pyc | Bin 0 -> 10434 bytes .../agents/__pycache__/result.cpython-39.pyc | Bin 0 -> 6885 bytes .../agents/__pycache__/run.cpython-313.pyc | Bin 0 -> 31817 bytes .../src/agents/__pycache__/run.cpython-39.pyc | Bin 0 -> 20201 bytes .../__pycache__/run_context.cpython-313.pyc | Bin 0 -> 1142 bytes .../__pycache__/run_context.cpython-39.pyc | Bin 0 -> 995 bytes .../__pycache__/stream_events.cpython-313.pyc | Bin 0 -> 2172 bytes .../__pycache__/stream_events.cpython-39.pyc | Bin 0 -> 1841 bytes .../__pycache__/strict_schema.cpython-313.pyc | Bin 0 -> 6123 bytes .../__pycache__/strict_schema.cpython-39.pyc | Bin 0 -> 4104 bytes .../agents/__pycache__/tool.cpython-313.pyc | Bin 0 -> 11729 bytes .../agents/__pycache__/tool.cpython-39.pyc | Bin 0 -> 8570 bytes .../tool_converter.cpython-313.pyc | Bin 0 -> 2144 bytes .../__pycache__/tool_converter.cpython-39.pyc | Bin 0 -> 1534 bytes .../agents/__pycache__/usage.cpython-313.pyc | Bin 0 -> 1362 bytes .../agents/__pycache__/usage.cpython-39.pyc | Bin 0 -> 779 bytes .../__pycache__/version.cpython-313.pyc | Bin 0 -> 470 bytes .../agents/__pycache__/version.cpython-39.pyc | Bin 0 -> 315 bytes tests/src/agents/_config.py | 23 + tests/src/agents/_debug.py | 17 + tests/src/agents/_run_impl.py | 792 ++++++++++ tests/src/agents/_utils.py | 61 + tests/src/agents/agent.py | 159 ++ tests/src/agents/agent_output.py | 144 ++ tests/src/agents/computer.py | 107 ++ tests/src/agents/exceptions.py | 63 + tests/src/agents/extensions/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 171 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 158 bytes .../handoff_filters.cpython-313.pyc | Bin 0 -> 2318 bytes .../handoff_filters.cpython-39.pyc | Bin 0 -> 1634 bytes .../handoff_prompt.cpython-313.pyc | Bin 0 -> 1071 bytes .../src/agents/extensions/handoff_filters.py | 67 + tests/src/agents/extensions/handoff_prompt.py | 19 + tests/src/agents/function_schema.py | 340 +++++ tests/src/agents/guardrail.py | 320 ++++ tests/src/agents/handoffs.py | 236 +++ tests/src/agents/items.py | 246 +++ tests/src/agents/lifecycle.py | 105 ++ tests/src/agents/logger.py | 3 + tests/src/agents/model_settings.py | 35 + tests/src/agents/models/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1098 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 160 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 154 bytes .../_openai_shared.cpython-311.pyc | Bin 0 -> 1651 bytes .../_openai_shared.cpython-313.pyc | Bin 0 -> 1434 bytes .../__pycache__/_openai_shared.cpython-39.pyc | Bin 0 -> 1313 bytes .../__pycache__/fake_id.cpython-313.pyc | Bin 0 -> 210 bytes .../models/__pycache__/fake_id.cpython-39.pyc | Bin 0 -> 195 bytes .../__pycache__/interface.cpython-313.pyc | Bin 0 -> 4220 bytes .../__pycache__/interface.cpython-39.pyc | Bin 0 -> 3637 bytes .../models/__pycache__/map.cpython-313.pyc | Bin 0 -> 760 bytes .../models/__pycache__/map.cpython-39.pyc | Bin 0 -> 630 bytes .../openai_chatcompletions.cpython-313.pyc | Bin 0 -> 34087 bytes .../openai_chatcompletions.cpython-39.pyc | Bin 0 -> 20218 bytes .../openai_provider.cpython-313.pyc | Bin 0 -> 2866 bytes .../openai_provider.cpython-39.pyc | Bin 0 -> 2042 bytes .../openai_responses.cpython-313.pyc | Bin 0 -> 15413 bytes .../openai_responses.cpython-39.pyc | Bin 0 -> 9519 bytes tests/src/agents/models/_openai_shared.py | 34 + tests/src/agents/models/fake_id.py | 5 + tests/src/agents/models/interface.py | 107 ++ .../agents/models/openai_chatcompletions.py | 952 ++++++++++++ tests/src/agents/models/openai_provider.py | 65 + tests/src/agents/models/openai_responses.py | 384 +++++ tests/src/agents/result.py | 220 +++ tests/src/agents/run.py | 904 +++++++++++ tests/src/agents/run_context.py | 26 + tests/src/agents/stream_events.py | 58 + tests/src/agents/strict_schema.py | 167 ++ tests/src/agents/tool.py | 286 ++++ tests/src/agents/tracing/__init__.py | 97 ++ .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 2896 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 2574 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 2210 bytes .../_strict_schema.cpython-313.pyc | Bin 0 -> 5568 bytes .../__pycache__/create.cpython-311.pyc | Bin 0 -> 10389 bytes .../__pycache__/create.cpython-313.pyc | Bin 0 -> 12633 bytes .../tracing/__pycache__/create.cpython-39.pyc | Bin 0 -> 11401 bytes .../__pycache__/logger.cpython-311.pyc | Bin 0 -> 302 bytes .../__pycache__/logger.cpython-313.pyc | Bin 0 -> 283 bytes .../tracing/__pycache__/logger.cpython-39.pyc | Bin 0 -> 233 bytes .../processor_interface.cpython-311.pyc | Bin 0 -> 2644 bytes .../processor_interface.cpython-313.pyc | Bin 0 -> 2941 bytes .../processor_interface.cpython-39.pyc | Bin 0 -> 2572 bytes .../__pycache__/processors.cpython-311.pyc | Bin 0 -> 13530 bytes .../__pycache__/processors.cpython-313.pyc | Bin 0 -> 12772 bytes .../__pycache__/processors.cpython-39.pyc | Bin 0 -> 8799 bytes .../tracing/__pycache__/scope.cpython-311.pyc | Bin 0 -> 3108 bytes .../tracing/__pycache__/scope.cpython-313.pyc | Bin 0 -> 2769 bytes .../tracing/__pycache__/scope.cpython-39.pyc | Bin 0 -> 1965 bytes .../tracing/__pycache__/setup.cpython-311.pyc | Bin 0 -> 10043 bytes .../tracing/__pycache__/setup.cpython-313.pyc | Bin 0 -> 9663 bytes .../tracing/__pycache__/setup.cpython-39.pyc | Bin 0 -> 6796 bytes .../__pycache__/span_data.cpython-311.pyc | Bin 0 -> 6627 bytes .../__pycache__/span_data.cpython-313.pyc | Bin 0 -> 7609 bytes .../__pycache__/span_data.cpython-39.pyc | Bin 0 -> 5702 bytes .../tracing/__pycache__/spans.cpython-311.pyc | Bin 0 -> 11920 bytes .../tracing/__pycache__/spans.cpython-313.pyc | Bin 0 -> 11056 bytes .../tracing/__pycache__/spans.cpython-39.pyc | Bin 0 -> 8282 bytes .../__pycache__/traces.cpython-311.pyc | Bin 0 -> 8065 bytes .../__pycache__/traces.cpython-313.pyc | Bin 0 -> 7496 bytes .../tracing/__pycache__/traces.cpython-39.pyc | Bin 0 -> 5576 bytes .../tracing/__pycache__/util.cpython-311.pyc | Bin 0 -> 1047 bytes .../tracing/__pycache__/util.cpython-313.pyc | Bin 0 -> 1074 bytes .../tracing/__pycache__/util.cpython-39.pyc | Bin 0 -> 769 bytes tests/src/agents/tracing/create.py | 306 ++++ tests/src/agents/tracing/logger.py | 3 + .../src/agents/tracing/processor_interface.py | 69 + tests/src/agents/tracing/processors.py | 261 ++++ tests/src/agents/tracing/scope.py | 45 + tests/src/agents/tracing/setup.py | 211 +++ tests/src/agents/tracing/span_data.py | 188 +++ tests/src/agents/tracing/spans.py | 264 ++++ tests/src/agents/tracing/traces.py | 195 +++ tests/src/agents/tracing/util.py | 17 + tests/src/agents/usage.py | 22 + tests/src/agents/version.py | 7 + tests/src/openai_agents.egg-info/PKG-INFO | 217 +++ tests/src/openai_agents.egg-info/SOURCES.txt | 81 + .../dependency_links.txt | 1 + tests/src/openai_agents.egg-info/requires.txt | 6 + .../src/openai_agents.egg-info/top_level.txt | 1 + tests/test_agent_config.py | 167 ++ tests/test_agent_hooks.py | 426 ++++++ tests/test_agent_runner.py | 554 +++++++ tests/test_agent_runner_streamed.py | 686 +++++++++ tests/test_agent_tracing.py | 322 ++++ tests/test_computer_action.py | 311 ++++ tests/test_config.py | 61 + tests/test_doc_parsing.py | 115 ++ tests/test_extension_filters.py | 188 +++ tests/test_function_schema.py | 430 ++++++ tests/test_function_tool.py | 257 ++++ tests/test_function_tool_decorator.py | 144 ++ tests/test_global_hooks.py | 373 +++++ tests/test_guardrails.py | 262 ++++ tests/test_handoff_tool.py | 278 ++++ tests/test_items_helpers.py | 281 ++++ tests/test_max_turns.py | 127 ++ tests/test_openai_chatcompletions.py | 290 ++++ .../test_openai_chatcompletions_converter.py | 395 +++++ tests/test_openai_chatcompletions_stream.py | 278 ++++ tests/test_openai_responses_converter.py | 205 +++ tests/test_output_tool.py | 113 ++ tests/test_responses.py | 76 + tests/test_responses_tracing.py | 212 +++ tests/test_result_cast.py | 58 + tests/test_run_config.py | 88 ++ tests/test_run_step_execution.py | 307 ++++ tests/test_run_step_processing.py | 422 +++++ tests/test_strict_schema.py | 126 ++ tests/test_tool_converter.py | 54 + tests/test_trace_processor.py | 276 ++++ tests/test_tracing.py | 402 +++++ tests/test_tracing_errors.py | 328 ++++ tests/test_tracing_errors_streamed.py | 397 +++++ tests/testing_processor.py | 79 + uv.lock | 1360 +++++++++++++++++ 723 files changed, 37387 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/publish.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .prettierrc create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 docs/agents.md create mode 100644 docs/assets/images/favicon-platform.svg create mode 100644 docs/assets/images/orchestration.png create mode 100644 docs/assets/logo.svg create mode 100644 docs/config.md create mode 100644 docs/context.md create mode 100644 docs/guardrails.md create mode 100644 docs/handoffs.md create mode 100644 docs/index.md create mode 100644 docs/models.md create mode 100644 docs/multi_agent.md create mode 100644 docs/quickstart.md create mode 100644 docs/ref/agent.md create mode 100644 docs/ref/agent_output.md create mode 100644 docs/ref/exceptions.md create mode 100644 docs/ref/extensions/handoff_filters.md create mode 100644 docs/ref/extensions/handoff_prompt.md create mode 100644 docs/ref/function_schema.md create mode 100644 docs/ref/guardrail.md create mode 100644 docs/ref/handoffs.md create mode 100644 docs/ref/index.md create mode 100644 docs/ref/items.md create mode 100644 docs/ref/lifecycle.md create mode 100644 docs/ref/model_settings.md create mode 100644 docs/ref/models/interface.md create mode 100644 docs/ref/models/openai_chatcompletions.md create mode 100644 docs/ref/models/openai_responses.md create mode 100644 docs/ref/result.md create mode 100644 docs/ref/run.md create mode 100644 docs/ref/run_context.md create mode 100644 docs/ref/stream_events.md create mode 100644 docs/ref/tool.md create mode 100644 docs/ref/tracing/create.md create mode 100644 docs/ref/tracing/index.md create mode 100644 docs/ref/tracing/processor_interface.md create mode 100644 docs/ref/tracing/processors.md create mode 100644 docs/ref/tracing/scope.md create mode 100644 docs/ref/tracing/setup.md create mode 100644 docs/ref/tracing/span_data.md create mode 100644 docs/ref/tracing/spans.md create mode 100644 docs/ref/tracing/traces.md create mode 100644 docs/ref/tracing/util.md create mode 100644 docs/ref/usage.md create mode 100644 docs/results.md create mode 100644 docs/running_agents.md create mode 100644 docs/streaming.md create mode 100644 docs/stylesheets/extra.css create mode 100644 docs/tools.md create mode 100644 docs/tracing.md create mode 100644 examples/__init__.py create mode 100644 examples/__pycache__/__init__.cpython-313.pyc create mode 100644 examples/agent_patterns/README.md create mode 100644 examples/agent_patterns/agents_as_tools.py create mode 100644 examples/agent_patterns/deterministic.py create mode 100644 examples/agent_patterns/input_guardrails.py create mode 100644 examples/agent_patterns/llm_as_a_judge.py create mode 100644 examples/agent_patterns/output_guardrails.py create mode 100644 examples/agent_patterns/parallelization.py create mode 100644 examples/agent_patterns/routing.py create mode 100644 examples/basic/agent_lifecycle_example.py create mode 100644 examples/basic/dynamic_system_prompt.py create mode 100644 examples/basic/hello_world.py create mode 100644 examples/basic/lifecycle_example.py create mode 100644 examples/basic/stream_items.py create mode 100644 examples/basic/stream_text.py create mode 100644 examples/customer_service/main.py create mode 100644 examples/handoffs/message_filter.py create mode 100644 examples/handoffs/message_filter_streaming.py create mode 100644 examples/research_bot/README.md create mode 100644 examples/research_bot/__init__.py create mode 100644 examples/research_bot/__pycache__/__init__.cpython-313.pyc create mode 100644 examples/research_bot/__pycache__/main.cpython-313.pyc create mode 100644 examples/research_bot/__pycache__/manager.cpython-313.pyc create mode 100644 examples/research_bot/__pycache__/printer.cpython-313.pyc create mode 100644 examples/research_bot/agents/__init__.py create mode 100644 examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/research_manager_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc create mode 100644 examples/research_bot/agents/planner_agent.py create mode 100644 examples/research_bot/agents/search_agent.py create mode 100644 examples/research_bot/agents/writer_agent.py create mode 100644 examples/research_bot/main.py create mode 100644 examples/research_bot/manager.py create mode 100644 examples/research_bot/printer.py create mode 100644 examples/research_bot/sample_outputs/product_recs.md create mode 100644 examples/research_bot/sample_outputs/product_recs.txt create mode 100644 examples/research_bot/sample_outputs/vacation.md create mode 100644 examples/research_bot/sample_outputs/vacation.txt create mode 100644 examples/tools/computer_use.py create mode 100644 examples/tools/file_search.py create mode 100644 examples/tools/web_search.py create mode 100644 mkdocs.yml create mode 100644 pyproject.toml create mode 100644 src/agents/__init__.py create mode 100644 src/agents/__pycache__/__init__.cpython-311.pyc create mode 100644 src/agents/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agents/__pycache__/__init__.cpython-39.pyc create mode 100644 src/agents/__pycache__/_config.cpython-311.pyc create mode 100644 src/agents/__pycache__/_config.cpython-313.pyc create mode 100644 src/agents/__pycache__/_config.cpython-39.pyc create mode 100644 src/agents/__pycache__/_debug.cpython-313.pyc create mode 100644 src/agents/__pycache__/_debug.cpython-39.pyc create mode 100644 src/agents/__pycache__/_run_impl.cpython-313.pyc create mode 100644 src/agents/__pycache__/_run_impl.cpython-39.pyc create mode 100644 src/agents/__pycache__/_strict_schema.cpython-313.pyc create mode 100644 src/agents/__pycache__/_utils.cpython-313.pyc create mode 100644 src/agents/__pycache__/_utils.cpython-39.pyc create mode 100644 src/agents/__pycache__/agent.cpython-311.pyc create mode 100644 src/agents/__pycache__/agent.cpython-313.pyc create mode 100644 src/agents/__pycache__/agent.cpython-39.pyc create mode 100644 src/agents/__pycache__/agent_output.cpython-313.pyc create mode 100644 src/agents/__pycache__/agent_output.cpython-39.pyc create mode 100644 src/agents/__pycache__/call_agent_tool.cpython-313.pyc create mode 100644 src/agents/__pycache__/call_agent_tool.cpython-39.pyc create mode 100644 src/agents/__pycache__/computer.cpython-313.pyc create mode 100644 src/agents/__pycache__/computer.cpython-39.pyc create mode 100644 src/agents/__pycache__/exceptions.cpython-311.pyc create mode 100644 src/agents/__pycache__/exceptions.cpython-313.pyc create mode 100644 src/agents/__pycache__/exceptions.cpython-39.pyc create mode 100644 src/agents/__pycache__/function_schema.cpython-313.pyc create mode 100644 src/agents/__pycache__/function_schema.cpython-39.pyc create mode 100644 src/agents/__pycache__/guardrail.cpython-311.pyc create mode 100644 src/agents/__pycache__/guardrail.cpython-313.pyc create mode 100644 src/agents/__pycache__/guardrail.cpython-39.pyc create mode 100644 src/agents/__pycache__/guardrail_base.cpython-313.pyc create mode 100644 src/agents/__pycache__/guardrail_base.cpython-39.pyc create mode 100644 src/agents/__pycache__/guardrails.cpython-313.pyc create mode 100644 src/agents/__pycache__/handoff.cpython-313.pyc create mode 100644 src/agents/__pycache__/handoff.cpython-39.pyc create mode 100644 src/agents/__pycache__/handoffs.cpython-313.pyc create mode 100644 src/agents/__pycache__/handoffs.cpython-39.pyc create mode 100644 src/agents/__pycache__/items.cpython-311.pyc create mode 100644 src/agents/__pycache__/items.cpython-313.pyc create mode 100644 src/agents/__pycache__/items.cpython-39.pyc create mode 100644 src/agents/__pycache__/lifecycle.cpython-313.pyc create mode 100644 src/agents/__pycache__/lifecycle.cpython-39.pyc create mode 100644 src/agents/__pycache__/logger.cpython-313.pyc create mode 100644 src/agents/__pycache__/logger.cpython-39.pyc create mode 100644 src/agents/__pycache__/model_settings.cpython-313.pyc create mode 100644 src/agents/__pycache__/model_settings.cpython-39.pyc create mode 100644 src/agents/__pycache__/output_tool.cpython-313.pyc create mode 100644 src/agents/__pycache__/output_tool.cpython-39.pyc create mode 100644 src/agents/__pycache__/result.cpython-313.pyc create mode 100644 src/agents/__pycache__/result.cpython-39.pyc create mode 100644 src/agents/__pycache__/run.cpython-313.pyc create mode 100644 src/agents/__pycache__/run.cpython-39.pyc create mode 100644 src/agents/__pycache__/run_context.cpython-313.pyc create mode 100644 src/agents/__pycache__/run_context.cpython-39.pyc create mode 100644 src/agents/__pycache__/stream_events.cpython-313.pyc create mode 100644 src/agents/__pycache__/stream_events.cpython-39.pyc create mode 100644 src/agents/__pycache__/strict_schema.cpython-313.pyc create mode 100644 src/agents/__pycache__/strict_schema.cpython-39.pyc create mode 100644 src/agents/__pycache__/tool.cpython-313.pyc create mode 100644 src/agents/__pycache__/tool.cpython-39.pyc create mode 100644 src/agents/__pycache__/tool_converter.cpython-313.pyc create mode 100644 src/agents/__pycache__/tool_converter.cpython-39.pyc create mode 100644 src/agents/__pycache__/usage.cpython-313.pyc create mode 100644 src/agents/__pycache__/usage.cpython-39.pyc create mode 100644 src/agents/__pycache__/version.cpython-313.pyc create mode 100644 src/agents/__pycache__/version.cpython-39.pyc create mode 100644 src/agents/_config.py create mode 100644 src/agents/_debug.py create mode 100644 src/agents/_run_impl.py create mode 100644 src/agents/_utils.py create mode 100644 src/agents/agent.py create mode 100644 src/agents/agent_output.py create mode 100644 src/agents/computer.py create mode 100644 src/agents/exceptions.py create mode 100644 src/agents/extensions/__init__.py create mode 100644 src/agents/extensions/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agents/extensions/__pycache__/__init__.cpython-39.pyc create mode 100644 src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc create mode 100644 src/agents/extensions/__pycache__/handoff_filters.cpython-39.pyc create mode 100644 src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc create mode 100644 src/agents/extensions/handoff_filters.py create mode 100644 src/agents/extensions/handoff_prompt.py create mode 100644 src/agents/function_schema.py create mode 100644 src/agents/guardrail.py create mode 100644 src/agents/handoffs.py create mode 100644 src/agents/items.py create mode 100644 src/agents/lifecycle.py create mode 100644 src/agents/logger.py create mode 100644 src/agents/model_settings.py create mode 100644 src/agents/models/__init__.py create mode 100644 src/agents/models/__pycache__/__init__.cpython-311.pyc create mode 100644 src/agents/models/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/__init__.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/_openai_shared.cpython-311.pyc create mode 100644 src/agents/models/__pycache__/_openai_shared.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/_openai_shared.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/fake_id.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/fake_id.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/interface.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/interface.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/map.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/map.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/openai_chatcompletions.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/openai_provider.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/openai_provider.cpython-39.pyc create mode 100644 src/agents/models/__pycache__/openai_responses.cpython-313.pyc create mode 100644 src/agents/models/__pycache__/openai_responses.cpython-39.pyc create mode 100644 src/agents/models/_openai_shared.py create mode 100644 src/agents/models/fake_id.py create mode 100644 src/agents/models/interface.py create mode 100644 src/agents/models/openai_chatcompletions.py create mode 100644 src/agents/models/openai_provider.py create mode 100644 src/agents/models/openai_responses.py create mode 100644 src/agents/result.py create mode 100644 src/agents/run.py create mode 100644 src/agents/run_context.py create mode 100644 src/agents/stream_events.py create mode 100644 src/agents/strict_schema.py create mode 100644 src/agents/tool.py create mode 100644 src/agents/tracing/__init__.py create mode 100644 src/agents/tracing/__pycache__/__init__.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/__init__.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/__init__.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/_strict_schema.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/create.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/create.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/create.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/logger.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/logger.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/logger.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/processor_interface.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/processors.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/processors.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/processors.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/scope.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/scope.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/scope.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/setup.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/setup.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/setup.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/span_data.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/span_data.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/span_data.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/spans.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/spans.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/spans.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/traces.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/traces.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/traces.cpython-39.pyc create mode 100644 src/agents/tracing/__pycache__/util.cpython-311.pyc create mode 100644 src/agents/tracing/__pycache__/util.cpython-313.pyc create mode 100644 src/agents/tracing/__pycache__/util.cpython-39.pyc create mode 100644 src/agents/tracing/create.py create mode 100644 src/agents/tracing/logger.py create mode 100644 src/agents/tracing/processor_interface.py create mode 100644 src/agents/tracing/processors.py create mode 100644 src/agents/tracing/scope.py create mode 100644 src/agents/tracing/setup.py create mode 100644 src/agents/tracing/span_data.py create mode 100644 src/agents/tracing/spans.py create mode 100644 src/agents/tracing/traces.py create mode 100644 src/agents/tracing/util.py create mode 100644 src/agents/usage.py create mode 100644 src/agents/version.py create mode 100644 src/openai_agents.egg-info/PKG-INFO create mode 100644 src/openai_agents.egg-info/SOURCES.txt create mode 100644 src/openai_agents.egg-info/dependency_links.txt create mode 100644 src/openai_agents.egg-info/requires.txt create mode 100644 src/openai_agents.egg-info/top_level.txt create mode 100644 tests/LICENSE create mode 100644 tests/Makefile create mode 100644 tests/README.md create mode 100644 tests/__init__.py create mode 100644 tests/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/__pycache__/__init__.cpython-39.pyc create mode 100644 tests/__pycache__/conftest.cpython-311-pytest-8.2.0.pyc create mode 100644 tests/__pycache__/conftest.cpython-311-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/conftest.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/conftest.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/fake_model.cpython-313.pyc create mode 100644 tests/__pycache__/fake_model.cpython-39.pyc create mode 100644 tests/__pycache__/test_agent_config.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_config.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_hooks.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_hooks.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_runner.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_runner.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_runner.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_runner.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_runner_streamed.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_runner_streamed.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_runner_streamed_warnings.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_tracing.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_tracing.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_agent_tracing.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_agent_tracing.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_computer_action.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_computer_action.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_config.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_config.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_doc_parsing.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_doc_parsing.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_extension_filters.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_extension_filters.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_schema.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_function_schema.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_schema.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_function_schema.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_tool_decorator.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_function_tool_decorator.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_global_hooks.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_global_hooks.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_guardrails.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_guardrails.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_guardrails.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_guardrails.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_items_helpers.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_items_helpers.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_lifecycle.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_max_turns.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_max_turns.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions_converter.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions_converter.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions_get_response.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions_stream.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_chatcompletions_stream.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_responses_converter.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_openai_responses_converter.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_output_tool.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_output_tool.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_output_tool.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_output_tool.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_responses.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_responses.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_responses.cpython-313.pyc create mode 100644 tests/__pycache__/test_responses.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_responses.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_responses_tracing.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_responses_tracing.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_result_cast.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_result_cast.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_config.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_config.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_config.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_config.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_step_execution.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_step_execution.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_step_execution.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_step_execution.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_step_processing.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_step_processing.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_run_step_processing.cpython-313.pyc create mode 100644 tests/__pycache__/test_run_step_processing.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_run_step_processing.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_strict_schema.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_strict_schema.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tool_converter.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tool_converter.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_trace_processor.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_trace_processor.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing_errors_streamed.cpython-313-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing_errors_streamed.cpython-313-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/test_tracing_errors_streamed.cpython-39-pytest-8.3.4.pyc create mode 100644 tests/__pycache__/test_tracing_errors_streamed.cpython-39-pytest-8.3.5.pyc create mode 100644 tests/__pycache__/testing_processor.cpython-313.pyc create mode 100644 tests/__pycache__/testing_processor.cpython-39.pyc create mode 100644 tests/conftest.py create mode 100644 tests/docs/agents.md create mode 100644 tests/docs/assets/images/favicon-platform.svg create mode 100644 tests/docs/assets/images/orchestration.png create mode 100644 tests/docs/assets/logo.svg create mode 100644 tests/docs/config.md create mode 100644 tests/docs/context.md create mode 100644 tests/docs/guardrails.md create mode 100644 tests/docs/handoffs.md create mode 100644 tests/docs/index.md create mode 100644 tests/docs/models.md create mode 100644 tests/docs/multi_agent.md create mode 100644 tests/docs/quickstart.md create mode 100644 tests/docs/ref/agent.md create mode 100644 tests/docs/ref/agent_output.md create mode 100644 tests/docs/ref/exceptions.md create mode 100644 tests/docs/ref/extensions/handoff_filters.md create mode 100644 tests/docs/ref/extensions/handoff_prompt.md create mode 100644 tests/docs/ref/function_schema.md create mode 100644 tests/docs/ref/guardrail.md create mode 100644 tests/docs/ref/handoffs.md create mode 100644 tests/docs/ref/index.md create mode 100644 tests/docs/ref/items.md create mode 100644 tests/docs/ref/lifecycle.md create mode 100644 tests/docs/ref/model_settings.md create mode 100644 tests/docs/ref/models/interface.md create mode 100644 tests/docs/ref/models/openai_chatcompletions.md create mode 100644 tests/docs/ref/models/openai_responses.md create mode 100644 tests/docs/ref/result.md create mode 100644 tests/docs/ref/run.md create mode 100644 tests/docs/ref/run_context.md create mode 100644 tests/docs/ref/stream_events.md create mode 100644 tests/docs/ref/tool.md create mode 100644 tests/docs/ref/tracing/create.md create mode 100644 tests/docs/ref/tracing/index.md create mode 100644 tests/docs/ref/tracing/processor_interface.md create mode 100644 tests/docs/ref/tracing/processors.md create mode 100644 tests/docs/ref/tracing/scope.md create mode 100644 tests/docs/ref/tracing/setup.md create mode 100644 tests/docs/ref/tracing/span_data.md create mode 100644 tests/docs/ref/tracing/spans.md create mode 100644 tests/docs/ref/tracing/traces.md create mode 100644 tests/docs/ref/tracing/util.md create mode 100644 tests/docs/ref/usage.md create mode 100644 tests/docs/results.md create mode 100644 tests/docs/running_agents.md create mode 100644 tests/docs/streaming.md create mode 100644 tests/docs/stylesheets/extra.css create mode 100644 tests/docs/tools.md create mode 100644 tests/docs/tracing.md create mode 100644 tests/examples/__init__.py create mode 100644 tests/examples/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/examples/agent_patterns/README.md create mode 100644 tests/examples/agent_patterns/agents_as_tools.py create mode 100644 tests/examples/agent_patterns/deterministic.py create mode 100644 tests/examples/agent_patterns/input_guardrails.py create mode 100644 tests/examples/agent_patterns/llm_as_a_judge.py create mode 100644 tests/examples/agent_patterns/output_guardrails.py create mode 100644 tests/examples/agent_patterns/parallelization.py create mode 100644 tests/examples/agent_patterns/routing.py create mode 100644 tests/examples/basic/agent_lifecycle_example.py create mode 100644 tests/examples/basic/dynamic_system_prompt.py create mode 100644 tests/examples/basic/hello_world.py create mode 100644 tests/examples/basic/lifecycle_example.py create mode 100644 tests/examples/basic/stream_items.py create mode 100644 tests/examples/basic/stream_text.py create mode 100644 tests/examples/customer_service/main.py create mode 100644 tests/examples/handoffs/message_filter.py create mode 100644 tests/examples/handoffs/message_filter_streaming.py create mode 100644 tests/examples/research_bot/README.md create mode 100644 tests/examples/research_bot/__init__.py create mode 100644 tests/examples/research_bot/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/examples/research_bot/__pycache__/main.cpython-313.pyc create mode 100644 tests/examples/research_bot/__pycache__/manager.cpython-313.pyc create mode 100644 tests/examples/research_bot/__pycache__/printer.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__init__.py create mode 100644 tests/examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/research_manager_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc create mode 100644 tests/examples/research_bot/agents/planner_agent.py create mode 100644 tests/examples/research_bot/agents/search_agent.py create mode 100644 tests/examples/research_bot/agents/writer_agent.py create mode 100644 tests/examples/research_bot/main.py create mode 100644 tests/examples/research_bot/manager.py create mode 100644 tests/examples/research_bot/printer.py create mode 100644 tests/examples/research_bot/sample_outputs/product_recs.md create mode 100644 tests/examples/research_bot/sample_outputs/product_recs.txt create mode 100644 tests/examples/research_bot/sample_outputs/vacation.md create mode 100644 tests/examples/research_bot/sample_outputs/vacation.txt create mode 100644 tests/examples/tools/computer_use.py create mode 100644 tests/examples/tools/file_search.py create mode 100644 tests/examples/tools/web_search.py create mode 100644 tests/fake_model.py create mode 100644 tests/mkdocs.yml create mode 100644 tests/pyproject.toml create mode 100644 tests/src/agents/__init__.py create mode 100644 tests/src/agents/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/__init__.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/_config.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/_config.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/_config.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/_debug.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/_debug.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/_run_impl.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/_run_impl.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/_strict_schema.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/_utils.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/_utils.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/agent.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/agent.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/agent.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/agent_output.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/agent_output.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/call_agent_tool.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/call_agent_tool.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/computer.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/computer.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/exceptions.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/exceptions.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/exceptions.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/function_schema.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/function_schema.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/guardrail.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/guardrail.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/guardrail.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/guardrail_base.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/guardrail_base.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/guardrails.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/handoff.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/handoff.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/handoffs.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/handoffs.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/items.cpython-311.pyc create mode 100644 tests/src/agents/__pycache__/items.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/items.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/lifecycle.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/lifecycle.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/logger.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/logger.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/model_settings.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/model_settings.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/output_tool.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/output_tool.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/result.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/result.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/run.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/run.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/run_context.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/run_context.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/stream_events.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/stream_events.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/strict_schema.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/strict_schema.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/tool.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/tool.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/tool_converter.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/tool_converter.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/usage.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/usage.cpython-39.pyc create mode 100644 tests/src/agents/__pycache__/version.cpython-313.pyc create mode 100644 tests/src/agents/__pycache__/version.cpython-39.pyc create mode 100644 tests/src/agents/_config.py create mode 100644 tests/src/agents/_debug.py create mode 100644 tests/src/agents/_run_impl.py create mode 100644 tests/src/agents/_utils.py create mode 100644 tests/src/agents/agent.py create mode 100644 tests/src/agents/agent_output.py create mode 100644 tests/src/agents/computer.py create mode 100644 tests/src/agents/exceptions.py create mode 100644 tests/src/agents/extensions/__init__.py create mode 100644 tests/src/agents/extensions/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/src/agents/extensions/__pycache__/__init__.cpython-39.pyc create mode 100644 tests/src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc create mode 100644 tests/src/agents/extensions/__pycache__/handoff_filters.cpython-39.pyc create mode 100644 tests/src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc create mode 100644 tests/src/agents/extensions/handoff_filters.py create mode 100644 tests/src/agents/extensions/handoff_prompt.py create mode 100644 tests/src/agents/function_schema.py create mode 100644 tests/src/agents/guardrail.py create mode 100644 tests/src/agents/handoffs.py create mode 100644 tests/src/agents/items.py create mode 100644 tests/src/agents/lifecycle.py create mode 100644 tests/src/agents/logger.py create mode 100644 tests/src/agents/model_settings.py create mode 100644 tests/src/agents/models/__init__.py create mode 100644 tests/src/agents/models/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/src/agents/models/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/__init__.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/_openai_shared.cpython-311.pyc create mode 100644 tests/src/agents/models/__pycache__/_openai_shared.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/_openai_shared.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/fake_id.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/fake_id.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/interface.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/interface.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/map.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/map.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_chatcompletions.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_provider.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_provider.cpython-39.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_responses.cpython-313.pyc create mode 100644 tests/src/agents/models/__pycache__/openai_responses.cpython-39.pyc create mode 100644 tests/src/agents/models/_openai_shared.py create mode 100644 tests/src/agents/models/fake_id.py create mode 100644 tests/src/agents/models/interface.py create mode 100644 tests/src/agents/models/openai_chatcompletions.py create mode 100644 tests/src/agents/models/openai_provider.py create mode 100644 tests/src/agents/models/openai_responses.py create mode 100644 tests/src/agents/result.py create mode 100644 tests/src/agents/run.py create mode 100644 tests/src/agents/run_context.py create mode 100644 tests/src/agents/stream_events.py create mode 100644 tests/src/agents/strict_schema.py create mode 100644 tests/src/agents/tool.py create mode 100644 tests/src/agents/tracing/__init__.py create mode 100644 tests/src/agents/tracing/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/__init__.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/__init__.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/_strict_schema.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/create.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/create.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/create.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/logger.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/logger.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/logger.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processor_interface.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processors.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processors.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/processors.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/scope.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/scope.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/scope.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/setup.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/setup.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/setup.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/span_data.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/span_data.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/span_data.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/spans.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/spans.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/spans.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/traces.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/traces.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/traces.cpython-39.pyc create mode 100644 tests/src/agents/tracing/__pycache__/util.cpython-311.pyc create mode 100644 tests/src/agents/tracing/__pycache__/util.cpython-313.pyc create mode 100644 tests/src/agents/tracing/__pycache__/util.cpython-39.pyc create mode 100644 tests/src/agents/tracing/create.py create mode 100644 tests/src/agents/tracing/logger.py create mode 100644 tests/src/agents/tracing/processor_interface.py create mode 100644 tests/src/agents/tracing/processors.py create mode 100644 tests/src/agents/tracing/scope.py create mode 100644 tests/src/agents/tracing/setup.py create mode 100644 tests/src/agents/tracing/span_data.py create mode 100644 tests/src/agents/tracing/spans.py create mode 100644 tests/src/agents/tracing/traces.py create mode 100644 tests/src/agents/tracing/util.py create mode 100644 tests/src/agents/usage.py create mode 100644 tests/src/agents/version.py create mode 100644 tests/src/openai_agents.egg-info/PKG-INFO create mode 100644 tests/src/openai_agents.egg-info/SOURCES.txt create mode 100644 tests/src/openai_agents.egg-info/dependency_links.txt create mode 100644 tests/src/openai_agents.egg-info/requires.txt create mode 100644 tests/src/openai_agents.egg-info/top_level.txt create mode 100644 tests/test_agent_config.py create mode 100644 tests/test_agent_hooks.py create mode 100644 tests/test_agent_runner.py create mode 100644 tests/test_agent_runner_streamed.py create mode 100644 tests/test_agent_tracing.py create mode 100644 tests/test_computer_action.py create mode 100644 tests/test_config.py create mode 100644 tests/test_doc_parsing.py create mode 100644 tests/test_extension_filters.py create mode 100644 tests/test_function_schema.py create mode 100644 tests/test_function_tool.py create mode 100644 tests/test_function_tool_decorator.py create mode 100644 tests/test_global_hooks.py create mode 100644 tests/test_guardrails.py create mode 100644 tests/test_handoff_tool.py create mode 100644 tests/test_items_helpers.py create mode 100644 tests/test_max_turns.py create mode 100644 tests/test_openai_chatcompletions.py create mode 100644 tests/test_openai_chatcompletions_converter.py create mode 100644 tests/test_openai_chatcompletions_stream.py create mode 100644 tests/test_openai_responses_converter.py create mode 100644 tests/test_output_tool.py create mode 100644 tests/test_responses.py create mode 100644 tests/test_responses_tracing.py create mode 100644 tests/test_result_cast.py create mode 100644 tests/test_run_config.py create mode 100644 tests/test_run_step_execution.py create mode 100644 tests/test_run_step_processing.py create mode 100644 tests/test_strict_schema.py create mode 100644 tests/test_tool_converter.py create mode 100644 tests/test_trace_processor.py create mode 100644 tests/test_tracing.py create mode 100644 tests/test_tracing_errors.py create mode 100644 tests/test_tracing_errors_streamed.py create mode 100644 tests/testing_processor.py create mode 100644 uv.lock diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..bf01524 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,26 @@ +name: Deploy docs + +on: + workflow_run: + workflows: ["Tests"] + types: + - completed + +permissions: + contents: write # This allows pushing to gh-pages + +jobs: + deploy_docs: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Deploy docs + run: make deploy-docs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..5d8f8a6 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,35 @@ +name: Publish to PyPI + +on: + release: + types: + - created + +jobs: + publish: + environment: + name: pypi + url: https://pypi.org/p/openai-agents + permissions: + id-token: write # Important for trusted publishing to PyPI + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Run tests + run: make tests + - name: Run mypy + run: make mypy + - name: Run Python 3.9 tests + run: make old_version_tests + - name: Build package + run: uv build + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..6dce5c8 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,86 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Run lint + run: make lint + + typecheck: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Run typecheck + run: make mypy + + tests: + runs-on: ubuntu-latest + env: + OPENAI_API_KEY: fake-for-tests + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Run tests + run: make tests + + build-docs: + runs-on: ubuntu-latest + env: + OPENAI_API_KEY: fake-for-tests + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Build docs + run: make build-docs + + old_versions: + runs-on: ubuntu-latest + env: + OPENAI_API_KEY: fake-for-tests + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install dependencies + run: make sync + - name: Run tests + run: make old_version_tests diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..32ab3e7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 4, + "overrides": [ + { + "files": "*.yml", + "options": { + "tabWidth": 2 + } + } + ] +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e5ad2c5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 OpenAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7dd9bbd --- /dev/null +++ b/Makefile @@ -0,0 +1,37 @@ +.PHONY: sync +sync: + uv sync --all-extras --all-packages --group dev + +.PHONY: format +format: + uv run ruff format + +.PHONY: lint +lint: + uv run ruff check + +.PHONY: mypy +mypy: + uv run mypy . + +.PHONY: tests +tests: + uv run pytest + +.PHONY: old_version_tests +old_version_tests: + UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m pytest + UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m mypy . + +.PHONY: build-docs +build-docs: + uv run mkdocs build + +.PHONY: serve-docs +serve-docs: + uv run mkdocs serve + +.PHONY: deploy-docs +deploy-docs: + uv run mkdocs gh-deploy --force --verbose + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8acd13c --- /dev/null +++ b/README.md @@ -0,0 +1,174 @@ +# OpenAI Agents SDK + +The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. + +Image of the Agents Tracing UI + +### Core concepts: + +1. [**Agents**](docs/agents.md): LLMs configured with instructions, tools, guardrails, and handoffs +2. [**Handoffs**](docs/handoffs.md): Allow agents to transfer control to other agents for specific tasks +3. [**Guardrails**](docs/guardrails.md): Configurable safety checks for input and output validation +4. [**Tracing**](docs/tracing.md): Built-in tracking of agent runs, allowing you to view, debug and optimize your workflows + +Explore the [examples](examples) directory to see the SDK in action. + +## Get started + +1. Set up your Python environment + +``` +python -m venv env +source env/bin/activate +``` + +2. Install Agents SDK + +``` +pip install openai-agents +``` + +## Hello world example + +```python +from agents import Agent, Runner + +agent = Agent(name="Assistant", instructions="You are a helpful assistant") + +result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") +print(result.final_output) + +# Code within the code, +# Functions calling themselves, +# Infinite loop's dance. +``` + +(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_) + +## Handoffs example + +```py +from agents import Agent, Runner +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], +) + + +async def main(): + result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(result.final_output) + # ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás? + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Functions example + +```python +import asyncio + +from agents import Agent, Runner, function_tool + + +@function_tool +def get_weather(city: str) -> str: + return f"The weather in {city} is sunny." + + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent.", + tools=[get_weather], +) + + +async def main(): + result = await Runner.run(agent, input="What's the weather in Tokyo?") + print(result.final_output) + # The weather in Tokyo is sunny. + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## The agent loop + +When you call `Runner.run()`, we run a loop until we get a final output. + +1. We call the LLM, using the model and settings on the agent, and the message history. +2. The LLM returns a response, which may include tool calls. +3. If the response has a final output (see below for the more on this), we return it and end the loop. +4. If the response has a handoff, we set the agent to the new agent and go back to step 1. +5. We process the tool calls (if any) and append the tool responses messsages. Then we go to step 1. + +There is a `max_turns` parameter that you can use to limit the number of times the loop executes. + +### Final output + +Final output is the last thing the agent produces in the loop. + +1. If you set an `output_type` on the agent, the final output is when the LLM returns something of that type. We use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) for this. +2. If there's no `output_type` (i.e. plain text responses), then the first LLM response without any tool calls or handoffs is considered as the final output. + +As a result, the mental model for the agent loop is: + +1. If the current agent has an `output_type`, the loop runs until the agent produces structured output matching that type. +2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls/handoffs. + +## Common agent patterns + +The Agents SDK is designed to be highly flexible, allowing you to model a wide range of LLM workflows including deterministic flows, iterative loops, and more. See examples in [`examples/agent_patterns`](examples/agent_patterns). + +## Tracing + +The Agents SDK includes built-in tracing, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), and [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk). See [Tracing](http://openai.github.io/openai-agents-python/tracing.md) for more details. + +## Development (only needed if you need to edit the SDK/examples) + +0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed. + +```bash +uv --version +``` + +1. Install dependencies + +```bash +make sync +``` + +2. (After making changes) lint/test + +``` +make tests # run tests +make mypy # run typechecker +make lint # run linter +``` + +## Acknowledgements + +We'd like to acknowledge the excellent work of the open-source community, especially: + +- [Pydantic](https://docs.pydantic.dev/latest/) (data validation) and [PydanticAI](https://ai.pydantic.dev/) (advanced agent framework) +- [MkDocs](https://github.com/squidfunk/mkdocs-material) +- [Griffe](https://github.com/mkdocstrings/griffe) +- [uv](https://github.com/astral-sh/uv) and [ruff](https://github.com/astral-sh/ruff) + +We're committed to continuing to build the Agents SDK as an open source framework so others in the community can expand on our approach. diff --git a/docs/agents.md b/docs/agents.md new file mode 100644 index 0000000..9b6264b --- /dev/null +++ b/docs/agents.md @@ -0,0 +1,131 @@ +# Agents + +Agents are the core building block in your apps. An agent is a large language model (LLM), configured with instructions and tools. + +## Basic configuration + +The most common properties of an agent you'll configure are: + +- `instructions`: also known as a developer message or system prompt. +- `model`: which LLM to use, and optional `model_settings` to configure model tuning parameters like temperature, top_p, etc. +- `tools`: Tools that the agent can use to achieve its tasks. + +```python +from agents import Agent, ModelSettings, function_tool + +def get_weather(city: str) -> str: + return f"The weather in {city} is sunny" + +agent = Agent( + name="Haiku agent", + instructions="Always respond in haiku form", + model="o3-mini", + tools=[function_tool(get_weather)], +) +``` + +## Context + +Agents are generic on their `context` type. Context is a dependency-injection tool: it's an object you create and pass to `Runner.run()`, that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context. + +```python +@dataclass +class UserContext: + uid: str + is_pro_user: bool + + async def fetch_purchases() -> list[Purchase]: + return ... + +agent = Agent[UserContext]( + ..., +) +``` + +## Output types + +By default, agents produce plain text (i.e. `str`) outputs. If you want the agent to produce a particular type of output, you can use the `output_type` parameter. A common choice is to use [Pydantic](https://docs.pydantic.dev/) objects, but we support any type that can be wrapped in a Pydantic [TypeAdapter](https://docs.pydantic.dev/latest/api/type_adapter/) - dataclasses, lists, TypedDict, etc. + +```python +from pydantic import BaseModel +from agents import Agent + + +class CalendarEvent(BaseModel): + name: str + date: str + participants: list[str] + +agent = Agent( + name="Calendar extractor", + instructions="Extract calendar events from text", + output_type=CalendarEvent, +) +``` + +!!! note + + When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses. + +## Handoffs + +Handoffs are sub-agents that the agent can delegate to. You provide a list of handoffs, and the agent can choose to delegate to them if relevant. This is a powerful pattern that allows orchestrating modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation. + +```python +from agents import Agent + +booking_agent = Agent(...) +refund_agent = Agent(...) + +triage_agent = Agent( + name="Triage agent", + instructions=( + "Help the user with their questions." + "If they ask about booking, handoff to the booking agent." + "If they ask about refunds, handoff to the refund agent." + ), + handoffs=[booking_agent, refund_agent], +) +``` + +## Dynamic instructions + +In most cases, you can provide instructions when you create the agent. However, you can also provide dynamic instructions via a function. The function will receive the agent and context, and must return the prompt. Both regular and `async` functions are accepted. + +```python +def dynamic_instructions( + context: RunContextWrapper[UserContext], agent: Agent[UserContext] +) -> str: + return f"The user's name is {context.context.name}. Help them with their questions." + + +agent = Agent[UserContext]( + name="Triage agent", + instructions=dynamic_instructions, +) +``` + +## Lifecycle events (hooks) + +Sometimes, you want to observe the lifecycle of an agent. For example, you may want to log events, or pre-fetch data when certain events occur. You can hook into the agent lifecycle with the `hooks` property. Subclass the [`AgentHooks`][agents.lifecycle.AgentHooks] class, and override the methods you're interested in. + +## Guardrails + +Guardrails allow you to run checks/validations on user input, in parallel to the agent running. For example, you could screen the user's input for relevance. Read more in the [guardrails](guardrails.md) documentation. + +## Cloning/copying agents + +By using the `clone()` method on an agent, you can duplicate an Agent, and optionally change any properties you like. + +```python +pirate_agent = Agent( + name="Pirate", + instructions="Write like a pirate", + model="o3-mini", +) + +robot_agent = pirate_agent.clone( + name="Robot", + instructions="Write like a robot", +) +``` diff --git a/docs/assets/images/favicon-platform.svg b/docs/assets/images/favicon-platform.svg new file mode 100644 index 0000000..91ef0ae --- /dev/null +++ b/docs/assets/images/favicon-platform.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/docs/assets/images/orchestration.png b/docs/assets/images/orchestration.png new file mode 100644 index 0000000000000000000000000000000000000000..621a833b52bfa9c5e863bca18f5866ee9fbe2586 GIT binary patch literal 432783 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 z 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%0AVW6d*4@Y` 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 z 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 Ui-AeWwrxL~AN{G^>}2wP0j+0BPXGV_ literal 0 HcmV?d00001 diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg new file mode 100644 index 0000000..ba36fc2 --- /dev/null +++ b/docs/assets/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..198d7b7 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,94 @@ +# Configuring the SDK + +## API keys and clients + +By default, the SDK looks for the `OPENAI_API_KEY` environment variable for LLM requests and tracing, as soon as it is imported. If you are unable to set that environment variable before your app starts, you can use the [set_default_openai_key()][agents.set_default_openai_key] function to set the key. + +```python +from agents import set_default_openai_key + +set_default_openai_key("sk-...") +``` + +Alternatively, you can also configure an OpenAI client to be used. By default, the SDK creates an `AsyncOpenAI` instance, using the API key from the environment variable or the default key set above. You can chnage this by using the [set_default_openai_client()][agents.set_default_openai_client] function. + +```python +from openai import AsyncOpenAI +from agents import set_default_openai_client + +custom_client = AsyncOpenAI(base_url="...", api_key="...") +set_default_openai_client(client) +``` + +Finally, you can also customize the OpenAI API that is used. By default, we use the OpenAI Responses API. You can override this to use the Chat Completions API by using the [set_default_openai_api()][agents.set_default_openai_api] function. + +```python +from agents import set_default_openai_api + +set_default_openai_api("chat_completions") +``` + +## Tracing + +Tracing is enabled by default. It uses the OpenAI API keys from the section above by default (i.e. the environment variable or the default key you set). You can specifically set the API key used for tracing by using the [`set_tracing_export_api_key`][agents.set_tracing_export_api_key] function. + +```python +from agents import set_tracing_export_api_key + +set_tracing_export_api_key("sk-...") +``` + +You can also disable tracing entirely by using the [`set_tracing_disabled()`][agents.set_tracing_disabled] function. + +```python +from agents import set_tracing_disabled + +set_tracing_disabled(True) +``` + +## Debug logging + +The SDK has two Python loggers without any handlers set. By default, this means that warnings and errors are sent to `stdout`, but other logs are suppressed. + +To enable verbose logging, use the [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] function. + +```python +from agents import enable_verbose_stdout_logging + +enable_verbose_stdout_logging() +``` + +Alternatively, you can customize the logs by adding handlers, filters, formatters, etc. You can read more in the [Python logging guide](https://docs.python.org/3/howto/logging.html). + +```python +import logging + +logger = logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger + +# To make all logs show up +logger.setLevel(logging.DEBUG) +# To make info and above show up +logger.setLevel(logging.INFO) +# To make warning and above show up +logger.setLevel(logging.WARNING) +# etc + +# You can customize this as needed, but this will output to `stderr` by default +logger.addHandler(logging.StreamHandler()) +``` + +### Sensitive data in logs + +Certain logs may contain sensitive data (for example, user data). If you want to disable this data from being logged, set the following environment variables. + +To disable logging LLM inputs and outputs: + +```bash +export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1 +``` + +To disable logging tool inputs and outputs: + +```bash +export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1 +``` diff --git a/docs/context.md b/docs/context.md new file mode 100644 index 0000000..5dcaceb --- /dev/null +++ b/docs/context.md @@ -0,0 +1,76 @@ +# Context management + +Context is an overloaded term. There are two main classes of context you might care about: + +1. Context available locally to your code: this is data and dependencies you might need when tool functions run, during callbacks like `on_handoff`, in lifecycle hooks, etc. +2. Context available to LLMs: this is data the LLM sees when generating a response. + +## Local context + +This is represented via the [`RunContextWrapper`][agents.run_context.RunContextWrapper] class and the [`context`][agents.run_context.RunContextWrapper.context] property within it. The way this works is: + +1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object. +2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`. +3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`. + +The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context. + +You can use the context for things like: + +- Contextual data for your run (e.g. things like a username/uid or other information about the user) +- Dependencies (e.g. logger objects, data fetchers, etc) +- Helper functions + +!!! danger "Note" + + The context object is **not** sent to the LLM. It is purely a local object that you can read from, write to and call methods on it. + +```python +import asyncio +from dataclasses import dataclass + +from agents import Agent, RunContextWrapper, Runner, function_tool + +@dataclass +class UserInfo: # (1)! + name: str + uid: int + +async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)! + return f"User {wrapper.context.name} is 47 years old" + +async def main(): + user_info = UserInfo(name="John", uid=123) # (3)! + + agent = Agent[UserInfo]( # (4)! + name="Assistant", + tools=[function_tool(fetch_user_age)], + ) + + result = await Runner.run( + starting_agent=agent, + input="What is the age of the user?", + context=user_info, + ) + + print(result.final_output) # (5)! + # The user John is 47 years old. + +if __name__ == "__main__": + asyncio.run(main()) +``` + +1. This is the context object. We've used a dataclass here, but you can use any type. +2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context. +3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type). +4. The context is passed to the `run` function. +5. The agent correctly calls the tool and gets the age. + +## Agent/LLM context + +When an LLM is called, the **only** data it can see is from the conversation history. This means that if you want to make some new data available to the LLM, you must do it in a way that makes it available in that history. There are a few ways to do this: + +1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date). +2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command). +3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data. +4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data. diff --git a/docs/guardrails.md b/docs/guardrails.md new file mode 100644 index 0000000..2b7369c --- /dev/null +++ b/docs/guardrails.md @@ -0,0 +1,154 @@ +# Guardrails + +Guardrails run _in parallel_ to your agents, enabling you to do checks and validations of user input. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, which stops the expensive model from running and saves you time/money. + +There are two kinds of guardrails: + +1. Input guardrails run on the initial user input +2. Output guardrails run on the final agent output + +## Input guardrails + +Input guardrails run in 3 steps: + +1. First, the guardrail receives the same input passed to the agent. +2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult] +3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. + +!!! Note + + Input guardrails are intended to run on user input, so an agent's guardrails only run if the agent is the *first* agent. You might wonder, why is the `guardrails` property on the agent instead of passed to `Runner.run`? It's because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. + +## Output guardrails + +Output guardrailas run in 3 steps: + +1. First, the guardrail receives the same input passed to the agent. +2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] +3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. + +!!! Note + + Output guardrails are intended to run on the final agent input, so an agent's guardrails only run if the agent is the *last* agent. Similar to the input guardrails, we do this because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. + +## Tripwires + +If the input or output fails the guardrail, the Guardrail can signal this with a tripwire. As soon as we see a guardail that has triggered the tripwires, we immediately raise a `{Input,Output}GuardrailTripwireTriggered` exception and halt the Agent execution. + +## Implementing a guardrail + +You need to provide a function that receives input, and returns a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput]. In this example, we'll do this by running an Agent under the hood. + +```python +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + TResponseInputItem, + input_guardrail, +) + +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + reasoning: str + +guardrail_agent = Agent( # (1)! + name="Guardrail check", + instructions="Check if the user is asking you to do their math homework.", + output_type=MathHomeworkOutput, +) + + +@input_guardrail +async def math_guardrail( # (2)! + ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, input, context=ctx.context) + + return GuardrailFunctionOutput( + output_info=result.final_output, # (3)! + tripwire_triggered=result.final_output.is_math_homework, + ) + + +agent = Agent( # (4)! + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + input_guardrails=[math_guardrail], +) + +async def main(): + # This should trip the guardrail + try: + await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") + print("Guardrail didn't trip - this is unexpected") + + except InputGuardrailTripwireTriggered: + print("Math homework guardrail tripped") +``` + +1. We'll use this agent in our guardrail function. +2. This is the guardrail function that receives the agent's input/context, and returns the result. +3. We can include extra information in the guardrail result. +4. This is the actual agent that defines the workflow. + +Output guardrails are similar. + +```python +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + output_guardrail, +) +class MessageOutput(BaseModel): # (1)! + response: str + +class MathOutput(BaseModel): # (2)! + is_math: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the output includes any math.", + output_type=MathOutput, +) + +@output_guardrail +async def math_guardrail( # (3)! + ctx: RunContextWrapper, agent: Agent, output: MessageOutput +) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, output.response, context=ctx.context) + + return GuardrailFunctionOutput( + output_info=result.final_output, + tripwire_triggered=result.final_output.is_math, + ) + +agent = Agent( # (4)! + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + output_guardrails=[math_guardrail], + output_type=MessageOutput, +) + +async def main(): + # This should trip the guardrail + try: + await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") + print("Guardrail didn't trip - this is unexpected") + + except OutputGuardrailTripwireTriggered: + print("Math output guardrail tripped") +``` + +1. This is the actual agent's output type. +2. This is the guardrail's output type. +3. This is the guardrail function that receives the agent's output, and returns the result. +4. This is the actual agent that defines the workflow. diff --git a/docs/handoffs.md b/docs/handoffs.md new file mode 100644 index 0000000..0b868c4 --- /dev/null +++ b/docs/handoffs.md @@ -0,0 +1,113 @@ +# Handoffs + +Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc. + +Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`. + +## Creating a handoff + +All agents have a [`handoffs`][agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff. + +You can create a handoff using the [`handoff()`][agents.handoffs.handoff] function provided by the Agents SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters. + +### Basic Usage + +Here's how you can create a simple handoff: + +```python +from agents import Agent, handoff + +billing_agent = Agent(name="Billing agent") +refund_agent = Agent(name="Refund agent") + +# (1)! +triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)]) +``` + +1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function. + +### Customizing handoffs via the `handoff()` function + +The [`handoff()`][agents.handoffs.handoff] function lets you customize things. + +- `agent`: This is the agent to which things will be handed off. +- `tool_name_override`: By default, the `Handoff.default_tool_name()` function is used, which resolves to `transfer_to_`. You can override this. +- `tool_description_override`: Override the default tool description from `Handoff.default_tool_description()` +- `on_handoff`: A callback function executed when the handoff is invoked. This is useful for things like kicking off some data fetching as soon as you know a handoff is being invoked. This function receives the agent context, and can optionally also receive LLM generated input. The input data is controlled by the `input_type` param. +- `input_type`: The type of input expected by the handoff (optional). +- `input_filter`: This lets you filter the input received by the next agent. See below for more. + +```python +from agents import Agent, handoff, RunContextWrapper + +def on_handoff(ctx: RunContextWrapper[None]): + print("Handoff called") + +agent = Agent(name="My agent") + +handoff_obj = handoff( + agent=agent, + on_handoff=on_handoff, + tool_name_override="custom_handoff_tool", + tool_description_override="Custom description", +) +``` + +## Handoff inputs + +In certain situations, you want the LLM to provide some data when it calls a handoff. For example, imagine a handoff to an "Escalation agent". You might want a reason to be provided, so you can log it. + +```python +from pydantic import BaseModel + +from agents import Agent, handoff, RunContextWrapper + +class EscalationData(BaseModel): + reason: str + +async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData): + print(f"Escalation agent called with reason: {input_data.reason}") + +agent = Agent(name="Escalation agent") + +handoff_obj = handoff( + agent=agent, + on_handoff=on_handoff, + input_type=EscalationData, +) +``` + +## Input filters + +When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`. + +There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][] + +```python +from agents import Agent, handoff +from agents.extensions import handoff_filters + +agent = Agent(name="FAQ agent") + +handoff_obj = handoff( + agent=agent, + input_filter=handoff_filters.remove_all_tools, # (1)! +) +``` + +1. This will automatically remove all tools from the history when `FAQ agent` is called. + +## Recommended prompts + +To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts. + +```python +from agents import Agent +from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX + +billing_agent = Agent( + name="Billing agent", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + .""", +) +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..28c6870 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,52 @@ +# OpenAI Agents SDK + +The OpenAI Agents SDK enables you to build agentic AI apps in a lightweight, easy to use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, [Swarm](https://github.com/openai/swarm/tree/main). The Agents SDK has a very small set of primitives: + +- **Agents**, which are LLMs equipped with instructions and tools +- **Handoffs**, which allow agents to delegate to other agents for specific tasks +- **Guardrails**, which enable the inputs to agents to be validated + +In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application. + +## Why use the Agents SDK + +The SDK has two driving design principles: + +1. Enough features to be worth using, but few enough primitives to make it quick to learn. +2. Works great out of the box, but you can customize exactly what happens. + +Here are the main features of the SDK: + +- Agent loop: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done. +- Python-first: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions. +- Handoffs: A powerful feature to coordinate and delegate between multiple agents. +- Guardrails: Run input validations and checks in parallel to your agents, breaking early if the checks fail. +- Function tools: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation. +- Tracing: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the OpenAI suite of evaluation, fine-tuning and distillation tools. + +## Installation + +```bash +pip install openai-agents +``` + +## Hello world example + +```python +from agents import Agent, Runner + +agent = Agent(name="Assistant", instructions="You are a helpful assistant") + +result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") +print(result.final_output) + +# Code within the code, +# Functions calling themselves, +# Infinite loop's dance. +``` + +(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_) + +```bash +export OPENAI_API_KEY=sk-... +``` diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 0000000..7d2ff1f --- /dev/null +++ b/docs/models.md @@ -0,0 +1,73 @@ +# Models + +The Agents SDK comes with out of the box support for OpenAI models in two flavors: + +- **Recommended**: the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel], which calls OpenAI APIs using the new [Responses API](https://platform.openai.com/docs/api-reference/responses). +- The [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel], which calls OpenAI APIs using the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). + +## Mixing and matching models + +Within a single workflow, you may want to use different models for each agent. For example, you could use a smaller, faster model for triage, while using a larger, more capable model for complex tasks. When configuring an [`Agent`][agents.Agent], you can select a specific model by either: + +1. Passing the name of an OpenAI model. +2. Passing any model name + a [`ModelProvider`][agents.models.interface.ModelProvider] that can map that name to a Model instance. +3. Directly providing a [`Model`][agents.models.interface.Model] implementation. + +!!!note + + While our SDK supports both the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] and the[`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] shapes, we recommend using a single model shape for each workflow because the two shapes support a different set of features and tools. If your workflow requires mixing and matching model shapes, make sure that all the features you're using are available on both. + +```python +from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", + model="o3-mini", # (1)! +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", + model=OpenAIChatCompletionsModel( # (2)! + model="gpt-4o", + openai_client=AsyncOpenAI() + ), +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], + model="gpt-3.5-turbo", +) + +async def main(): + result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(result.final_output) +``` + +1. Sets the the name of an OpenAI model directly. +2. Provides a [`Model`][agents.models.interface.Model] implementation. + +## Using other LLM providers + +Many providers also support the OpenAI API format, which means you can pass a `base_url` to the existing OpenAI model implementations and use them easily. `ModelSettings` is used to configure tuning parameters (e.g., temperature, top_p) for the model you select. + +```python +external_client = AsyncOpenAI( + api_key="EXTERNAL_API_KEY", + base_url="https://api.external.com/v1/", +) + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", + model=OpenAIChatCompletionsModel( + model="EXTERNAL_MODEL_NAME", + openai_client=external_client, + ), + model_settings=ModelSettings(temperature=0.5), +) +``` diff --git a/docs/multi_agent.md b/docs/multi_agent.md new file mode 100644 index 0000000..c118249 --- /dev/null +++ b/docs/multi_agent.md @@ -0,0 +1,37 @@ +# Orchestrating multiple agents + +Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents: + +1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that. +2. Orchestrating via code: determining the flow of agents via your code. + +You can mix and match these patterns. Each has their own tradeoffs, described below. + +## Orchestrating via LLM + +An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like: + +- Web search to find information online +- File search and retrieval to search through proprietary data and connections +- Computer use to take actions on a computer +- Code execution to do data analysis +- Handoffs to specialized agents that are great at planning, report writing and more. + +This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are: + +1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within. +2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts. +3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve. +4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything. +5. Invest in [evals](https://platform.openai.com/docs/guides/evals). This lets you train your agents to improve and get better at tasks. + +## Orchestrating via code + +While orchestrating via LLM is powerful, orchestrating via LLM makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are: + +- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category. +- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it. +- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria. +- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other. + +We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/openai-agents-python/examples/agent_patterns). diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..19051f4 --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,186 @@ +# Quickstart + +## Create a project and virtual environment + +You'll only need to do this once. + +```bash +mkdir my_project +cd my_project +python -m venv .venv +``` + +### Activate the virtual environment + +Do this every time you start a new terminal session. + +```bash +source .venv/bin/activate +``` + +### Install the Agents SDK + +```bash +pip install openai-agents # or `uv add openai-agents`, etc +``` + +### Set an OpenAI API key + +If you don't have one, follow [these instructions](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to create an OpenAI API key. + +```bash +export OPENAI_API_KEY=sk-... +``` + +## Create your first agent + +Agents are defined with instructions, a name, and optional config (such as `model_config`) + +```python +from agents import Agent + +agent = Agent( + name="Math Tutor", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) +``` + +## Add a few more agents + +Additional agents can be defined in the same way. `handoff_descriptions` provide additional context for determining handoff routing + +```python +from agents import Agent + +history_tutor_agent = Agent( + name="History Tutor", + handoff_description="Specialist agent for historical questions", + instructions="You provide assistance with historical queries. Explain important events and context clearly.", +) + +math_tutor_agent = Agent( + name="Math Tutor", + handoff_description="Specialist agent for math questions", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) +``` + +## Define your handoffs + +On each agent, you can define an inventory of outgoing handoff options that the agent can choose from to decide how to make progress on their task. + +```python +triage_agent = Agent( + name="Triage Agent", + instructions="You determine which agent to use based on the user's homework question", + handoffs=[history_tutor_agent, math_tutor_agent] +) +``` + +## Run the agent orchestration + +Let's check that the workflow runs and the triage agent correctly routes between the two specialist agents. + +```python +from agents import Runner + +async def main(): + result = await Runner.run(triage_agent, "What is the capital of France?") + print(result.final_output) +``` + +## Add a guardrail + +You can define custom guardrails to run on the input or output. + +```python +from agents import GuardrailFunctionOutput, Agent, Runner +from pydantic import BaseModel + +class HomeworkOutput(BaseModel): + is_homework: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking about homework.", + output_type=HomeworkOutput, +) + +async def homework_guardrail(ctx, agent, input_data): + result = await Runner.run(guardrail_agent, input_data, context=ctx.context) + final_output = result.final_output_as(HomeworkOutput) + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_homework, + ) +``` + +## Put it all together + +Let's put it all together and run the entire workflow, using handoffs and the input guardrail. + +```python +from agents import Agent, InputGuardrail,GuardrailFunctionOutput, Runner +from pydantic import BaseModel +import asyncio + +class HomeworkOutput(BaseModel): + is_homework: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking about homework.", + output_type=HomeworkOutput, +) + +math_tutor_agent = Agent( + name="Math Tutor", + handoff_description="Specialist agent for math questions", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) + +history_tutor_agent = Agent( + name="History Tutor", + handoff_description="Specialist agent for historical questions", + instructions="You provide assistance with historical queries. Explain important events and context clearly.", +) + + +async def homework_guardrail(ctx, agent, input_data): + result = await Runner.run(guardrail_agent, input_data, context=ctx.context) + final_output = result.final_output_as(HomeworkOutput) + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_homework, + ) + +triage_agent = Agent( + name="Triage Agent", + instructions="You determine which agent to use based on the user's homework question", + handoffs=[history_tutor_agent, math_tutor_agent], + input_guardrails=[ + InputGuardrail(guardrail_function=homework_guardrail), + ], +) + +async def main(): + result = await Runner.run(triage_agent, "what is life") + print(result.final_output) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## View your traces + +To review what happened during your agent run, navigate to the [Trace viewer in the OpenAI Dashboard](https://platform.openai.com/traces) to view traces of your agent runs. + +## Next steps + +Learn how to build more complex agentic flows: + +- Learn about how to configure [Agents](agents.md). +- Learn about [running agents](running_agents.md). +- Learn about [tools](tools.md), [guardrails](guardrails.md) and [models](models.md). diff --git a/docs/ref/agent.md b/docs/ref/agent.md new file mode 100644 index 0000000..9f8b10d --- /dev/null +++ b/docs/ref/agent.md @@ -0,0 +1,3 @@ +# `Agents` + +::: agents.agent diff --git a/docs/ref/agent_output.md b/docs/ref/agent_output.md new file mode 100644 index 0000000..e453de0 --- /dev/null +++ b/docs/ref/agent_output.md @@ -0,0 +1,3 @@ +# `Agent output` + +::: agents.agent_output diff --git a/docs/ref/exceptions.md b/docs/ref/exceptions.md new file mode 100644 index 0000000..7c1a254 --- /dev/null +++ b/docs/ref/exceptions.md @@ -0,0 +1,3 @@ +# `Exceptions` + +::: agents.exceptions diff --git a/docs/ref/extensions/handoff_filters.md b/docs/ref/extensions/handoff_filters.md new file mode 100644 index 0000000..0ffcb13 --- /dev/null +++ b/docs/ref/extensions/handoff_filters.md @@ -0,0 +1,3 @@ +# `Handoff filters` + +::: agents.extensions.handoff_filters diff --git a/docs/ref/extensions/handoff_prompt.md b/docs/ref/extensions/handoff_prompt.md new file mode 100644 index 0000000..ca80076 --- /dev/null +++ b/docs/ref/extensions/handoff_prompt.md @@ -0,0 +1,8 @@ +# `Handoff prompt` + +::: agents.extensions.handoff_prompt + + options: + members: + - RECOMMENDED_PROMPT_PREFIX + - prompt_with_handoff_instructions diff --git a/docs/ref/function_schema.md b/docs/ref/function_schema.md new file mode 100644 index 0000000..06aac2a --- /dev/null +++ b/docs/ref/function_schema.md @@ -0,0 +1,3 @@ +# `Function schema` + +::: agents.function_schema diff --git a/docs/ref/guardrail.md b/docs/ref/guardrail.md new file mode 100644 index 0000000..17ec929 --- /dev/null +++ b/docs/ref/guardrail.md @@ -0,0 +1,3 @@ +# `Guardrails` + +::: agents.guardrail diff --git a/docs/ref/handoffs.md b/docs/ref/handoffs.md new file mode 100644 index 0000000..717a918 --- /dev/null +++ b/docs/ref/handoffs.md @@ -0,0 +1,3 @@ +# `Handoffs` + +::: agents.handoffs diff --git a/docs/ref/index.md b/docs/ref/index.md new file mode 100644 index 0000000..1b8439f --- /dev/null +++ b/docs/ref/index.md @@ -0,0 +1,13 @@ +# Agents module + +::: agents + + options: + members: + - set_default_openai_key + - set_default_openai_client + - set_default_openai_api + - set_tracing_export_api_key + - set_tracing_disabled + - set_trace_processors + - enable_verbose_stdout_logging diff --git a/docs/ref/items.md b/docs/ref/items.md new file mode 100644 index 0000000..29279e1 --- /dev/null +++ b/docs/ref/items.md @@ -0,0 +1,3 @@ +# `Items` + +::: agents.items diff --git a/docs/ref/lifecycle.md b/docs/ref/lifecycle.md new file mode 100644 index 0000000..432af14 --- /dev/null +++ b/docs/ref/lifecycle.md @@ -0,0 +1,6 @@ +# `Lifecycle` + +::: agents.lifecycle + + options: + show_source: false diff --git a/docs/ref/model_settings.md b/docs/ref/model_settings.md new file mode 100644 index 0000000..f7f411f --- /dev/null +++ b/docs/ref/model_settings.md @@ -0,0 +1,3 @@ +# `Model settings` + +::: agents.model_settings diff --git a/docs/ref/models/interface.md b/docs/ref/models/interface.md new file mode 100644 index 0000000..e7bd89a --- /dev/null +++ b/docs/ref/models/interface.md @@ -0,0 +1,3 @@ +# `Model interface` + +::: agents.models.interface diff --git a/docs/ref/models/openai_chatcompletions.md b/docs/ref/models/openai_chatcompletions.md new file mode 100644 index 0000000..76cf563 --- /dev/null +++ b/docs/ref/models/openai_chatcompletions.md @@ -0,0 +1,3 @@ +# `OpenAI Chat Completions model` + +::: agents.models.openai_chatcompletions diff --git a/docs/ref/models/openai_responses.md b/docs/ref/models/openai_responses.md new file mode 100644 index 0000000..e1794ba --- /dev/null +++ b/docs/ref/models/openai_responses.md @@ -0,0 +1,3 @@ +# `OpenAI Responses model` + +::: agents.models.openai_responses diff --git a/docs/ref/result.md b/docs/ref/result.md new file mode 100644 index 0000000..3a9e4a9 --- /dev/null +++ b/docs/ref/result.md @@ -0,0 +1,3 @@ +# `Results` + +::: agents.result diff --git a/docs/ref/run.md b/docs/ref/run.md new file mode 100644 index 0000000..ddf4475 --- /dev/null +++ b/docs/ref/run.md @@ -0,0 +1,8 @@ +# `Runner` + +::: agents.run + + options: + members: + - Runner + - RunConfig diff --git a/docs/ref/run_context.md b/docs/ref/run_context.md new file mode 100644 index 0000000..49e8730 --- /dev/null +++ b/docs/ref/run_context.md @@ -0,0 +1,3 @@ +# `Run context` + +::: agents.run_context diff --git a/docs/ref/stream_events.md b/docs/ref/stream_events.md new file mode 100644 index 0000000..ea48431 --- /dev/null +++ b/docs/ref/stream_events.md @@ -0,0 +1,3 @@ +# `Streaming events` + +::: agents.stream_events diff --git a/docs/ref/tool.md b/docs/ref/tool.md new file mode 100644 index 0000000..887bef7 --- /dev/null +++ b/docs/ref/tool.md @@ -0,0 +1,3 @@ +# `Tools` + +::: agents.tool diff --git a/docs/ref/tracing/create.md b/docs/ref/tracing/create.md new file mode 100644 index 0000000..c983e33 --- /dev/null +++ b/docs/ref/tracing/create.md @@ -0,0 +1,3 @@ +# `Creating traces/spans` + +::: agents.tracing.create diff --git a/docs/ref/tracing/index.md b/docs/ref/tracing/index.md new file mode 100644 index 0000000..88a0fe6 --- /dev/null +++ b/docs/ref/tracing/index.md @@ -0,0 +1,3 @@ +# Tracing module + +::: agents.tracing diff --git a/docs/ref/tracing/processor_interface.md b/docs/ref/tracing/processor_interface.md new file mode 100644 index 0000000..9fb04e8 --- /dev/null +++ b/docs/ref/tracing/processor_interface.md @@ -0,0 +1,3 @@ +# `Processor interface` + +::: agents.tracing.processor_interface diff --git a/docs/ref/tracing/processors.md b/docs/ref/tracing/processors.md new file mode 100644 index 0000000..d7ac4af --- /dev/null +++ b/docs/ref/tracing/processors.md @@ -0,0 +1,3 @@ +# `Processors` + +::: agents.tracing.processors diff --git a/docs/ref/tracing/scope.md b/docs/ref/tracing/scope.md new file mode 100644 index 0000000..7b5b9fd --- /dev/null +++ b/docs/ref/tracing/scope.md @@ -0,0 +1,3 @@ +# `Scope` + +::: agents.tracing.scope diff --git a/docs/ref/tracing/setup.md b/docs/ref/tracing/setup.md new file mode 100644 index 0000000..1dc6a0f --- /dev/null +++ b/docs/ref/tracing/setup.md @@ -0,0 +1,3 @@ +# `Setup` + +::: agents.tracing.setup diff --git a/docs/ref/tracing/span_data.md b/docs/ref/tracing/span_data.md new file mode 100644 index 0000000..6ace7a8 --- /dev/null +++ b/docs/ref/tracing/span_data.md @@ -0,0 +1,3 @@ +# `Span data` + +::: agents.tracing.span_data diff --git a/docs/ref/tracing/spans.md b/docs/ref/tracing/spans.md new file mode 100644 index 0000000..9071707 --- /dev/null +++ b/docs/ref/tracing/spans.md @@ -0,0 +1,9 @@ +# `Spans` + +::: agents.tracing.spans + + options: + members: + - Span + - NoOpSpan + - SpanImpl diff --git a/docs/ref/tracing/traces.md b/docs/ref/tracing/traces.md new file mode 100644 index 0000000..0b7377f --- /dev/null +++ b/docs/ref/tracing/traces.md @@ -0,0 +1,3 @@ +# `Traces` + +::: agents.tracing.traces diff --git a/docs/ref/tracing/util.md b/docs/ref/tracing/util.md new file mode 100644 index 0000000..2be3d58 --- /dev/null +++ b/docs/ref/tracing/util.md @@ -0,0 +1,3 @@ +# `Util` + +::: agents.tracing.util diff --git a/docs/ref/usage.md b/docs/ref/usage.md new file mode 100644 index 0000000..b8b29db --- /dev/null +++ b/docs/ref/usage.md @@ -0,0 +1,3 @@ +# `Usage` + +::: agents.usage diff --git a/docs/results.md b/docs/results.md new file mode 100644 index 0000000..d1864fa --- /dev/null +++ b/docs/results.md @@ -0,0 +1,52 @@ +# Results + +When you call the `Runner.run` methods, you either get a: + +- [`RunResult`][agents.result.RunResult] if you call `run` or `run_sync` +- [`RunResultStreaming`][agents.result.RunResultStreaming] if you call `run_streamed` + +Both of these inherit from [`RunResultBase`][agents.result.RunResultBase], which is where most useful information is present. + +## Final output + +The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either: + +- a `str`, if the last agent didn't have an `output_type` defined +- an object of type `last_agent.output_type`, if the agent had an output type defined. + +!!! note + + `final_output` is of type `Any`. We can't statically type this, because of handoffs. If handoffs occur, that means any Agent might be the last agent, so we don't statically know the set of possible output types. + +## Inputs for the next turn + +You can use [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] to turn the result into an input list that concatenates the original input you provided, to the items generated during the agent run. This makes it convenient to take the outputs of one agent run and pass them into another run, or to run it in a loop and append new user inputs each time. + +## Last agent + +The [`last_agent`][agents.result.RunResultBase.last_agent] property contains the last agent that ran. Depending on your application, this is often useful for the next time the user inputs something. For example, if you have a frontline triage agent that hands off to a language-specific agent, you can store the last agent, and re-use it the next time the user messages the agent. + +## New items + +The [`new_items`][agents.result.RunResultBase.new_items] property contains the new items generated during the run. The items are [`RunItem`][agents.items.RunItem]s. A run item wraps the raw item generated by the LLM. + +- [`MessageOutputItem`][agents.items.MessageOutputItem] indicates a message from the LLM. The raw item is the message generated. +- [`HandoffCallItem`][agents.items.HandoffCallItem] indicates that the LLM called the handoff tool. The raw item is the tool call item from the LLM. +- [`HandoffOutputItem`][agents.items.HandoffOutputItem] indicates that a handoff occured. The raw item is the tool response to the handoff tool call. You can also access the source/target agents from the item. +- [`ToolCallItem`][agents.items.ToolCallItem] indicates that the LLM invoked a tool. +- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] indicates that a tool was called. The raw item is the tool response. You can also access the tool output from the item. +- [`ReasoningItem`][agents.items.ReasoningItem] indicates a reasoning item from the LLM. The raw item is the reasoning generated. + +## Other information + +### Guardrail results + +The [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] properties contain the results of the guardrails, if any. Guardrail results can sometimes contain useful information you want to log or store, so we make these available to you. + +### Raw responses + +The [`raw_responses`][agents.result.RunResultBase.raw_responses] property contains the [`ModelResponse`][agents.items.ModelResponse]s generated by the LLM. + +### Original input + +The [`input`][agents.result.RunResultBase.input] property contains the original input you provided to the `run` method. In most cases you won't need this, but it's available in case you do. diff --git a/docs/running_agents.md b/docs/running_agents.md new file mode 100644 index 0000000..a2f137c --- /dev/null +++ b/docs/running_agents.md @@ -0,0 +1,95 @@ +# Running agents + +You can run agents via the [`Runner`][agents.run.Runner] class. You have 3 options: + +1. [`Runner.run()`][agents.run.Runner.run], which runs async and returns a [`RunResult`][agents.result.RunResult]. +2. [`Runner.run_sync()`][agents.run.Runner.run_sync], which is a sync method and just runs `.run()` under the hood. +3. [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which runs async and returns a [`RunResultStreaming`][agents.result.RunResultStreaming]. It calls the LLM in streaming mode, and streams those events to you as they are received. + +```python +from agents import Agent, Runner + +async def main(): + agent = Agent(name="Assistant", instructions="You are a helpful assistant") + + result = await Runner.run(agent, "Write a haiku about recursion in programming.") + print(result.final_output) + # Code within the code, + # Functions calling themselves, + # Infinite loop's dance. +``` + +Read more in the [results guide](results.md). + +## The agent loop + +When you use the run method in `Runner`, you pass in a starting agent and input. The input can either be a string (which is considered a user message), or a list of input items, which are the items in the OpenAI Responses API. + +The runner then runs a loop: + +1. We call the LLM for the current agent, with the current input. +2. The LLM produces its output. + 1. If the LLM returns a `final_output`, the loop ends and we return the result. + 2. If the LLM does a handoff, we update the current agent and input, and re-run the loop. + 3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop. +3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception. + +!!! note + + The rule for whether the LLM output is considered as a "final output" is that it produces text output with the desired type, and there are no tool calls. + +## Streaming + +Streaming allows you to additionally receive streaming events as the LLM runs. Once the stream is done, the [`RunResultStreaming`][agents.result.RunResultStreaming] will contain the complete information about the run, including all the new outputs produces. You can call `.stream_events()` for the streaming events. Read more in the [streaming guide](streaming.md). + +## Run config + +The `run_config` parameter lets you configure some global settings for the agent run: + +- [`model`][agents.run.RunConfig.model]: Allows setting a global LLM model to use, irrespective of what `model` each Agent has. +- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to OpenAI. +- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`. +- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs. +- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details. +- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run. +- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs. +- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The session ID is an optional field that lets you link traces across multiple runs. +- [`trace_metadata`][agents.run.RunConfig.trace_metadata]: Metadata to include on all traces. + +## Conversations/chat threads + +Calling any of the run methods can result in one or more agents running (and hence one or more LLM calls), but it represents a single logical turn in a chat conversation. For example: + +1. User turn: user enter text +2. Runner run: first agent calls LLM, runs tools, does a handoff to a second agent, second agent runs more tools, and then produces an output. + +At the end of the agent run, you can choose what to show to the user. For example, you might show the user every new item generated by the agents, or just the final output. Either way, the user might then ask a followup question, in which case you can call the run method again. + +You can use the base [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] method to get the inputs for the next turn. + +```python +async def main(): + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + with trace(workflow_name="Conversation", group_id=thread_id): + # First turn + result = await Runner.run(agent, "What city is the Golden Gate Bridge in?") + print(result.final_output) + # San Francisco + + # Second turn + new_input = output.to_input_list() + [{"role": "user", "content": "What state is it in?"}] + result = await Runner.run(agent, new_input) + print(result.final_output) + # California +``` + +## Exceptions + +The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview: + +- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK. +- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods. +- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools. +- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK. +- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped. diff --git a/docs/streaming.md b/docs/streaming.md new file mode 100644 index 0000000..b2c7c09 --- /dev/null +++ b/docs/streaming.md @@ -0,0 +1,87 @@ +# Streaming + +Streaming lets you subscribe to updates of the agent run as it proceeds. This can be useful for showing the end-user progress updates and partial responses. + +To stream, you can call [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which will give you a [`RunResultStreaming`][agents.result.RunResultStreaming]. Calling `result.stream_events()` gives you an async stream of [`StreamEvent`][agents.stream_events.StreamEvent] objects, which are described below. + +## Raw response events + +[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in OpenAI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated. + +For example, this will output the text generated by the LLM token-by-token. + +```python +import asyncio +from openai.types.responses import ResponseTextDeltaEvent +from agents import Agent, Runner + +async def main(): + agent = Agent( + name="Joker", + instructions="You are a helpful assistant.", + ) + + result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") + async for event in result.stream_events(): + if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): + print(event.data.delta, end="", flush=True) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Run item events and agent events + +[`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent]s are higher level events. They inform you when an item has been fully generated. This allows you to push progress updates at the level of "message generated", "tool ran", etc, instead of each token. Similarly, [`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] gives you updates when the current agent changes (e.g. as the result of a handoff). + +For example, this will ignore raw events and stream updates to the user. + +```python +import asyncio +import random +from agents import Agent, ItemHelpers, Runner, function_tool + +@function_tool +def how_many_jokes() -> int: + return random.randint(1, 10) + + +async def main(): + agent = Agent( + name="Joker", + instructions="First call the `how_many_jokes` tool, then tell that many jokes.", + tools=[how_many_jokes], + ) + + result = Runner.run_streamed( + agent, + input="Hello", + ) + print("=== Run starting ===") + + async for event in result.stream_events(): + # We'll ignore the raw responses event deltas + if event.type == "raw_response_event": + continue + # When the agent updates, print that + elif event.type == "agent_updated_stream_event": + print(f"Agent updated: {event.new_agent.name}") + continue + # When items are generated, print them + elif event.type == "run_item_stream_event": + if event.item.type == "tool_call_item": + print("-- Tool was called") + elif event.item.type == "tool_call_output_item": + print(f"-- Tool output: {event.item.output}") + elif event.item.type == "message_output_item": + print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") + else: + pass # Ignore other event types + + print("=== Run complete ===") + + +if __name__ == "__main__": + asyncio.run(main()) +``` diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..89cf164 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,194 @@ +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 400; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Regular.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 400; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-RegularItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 500; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Medium.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 500; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-MediumItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 600; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Semibold.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 600; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-SemiboldItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 700; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Bold.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 700; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-BoldItalic.woff2") + format("woff2"); +} + +/* + Root variables that apply to all color schemes. + Material for MkDocs automatically switches data-md-color-scheme + between "default" (light) and "slate" (dark) when you use the toggles. +*/ +:root { + /* Font families */ + --md-text-font: "OpenAI Sans", -apple-system, system-ui, Helvetica, Arial, + sans-serif; + --md-typeface-heading: "OpenAI Sans", -apple-system, system-ui, Helvetica, + Arial, sans-serif; + + /* Global color variables */ + --md-default-fg-color: #212121; + --md-default-bg-color: #ffffff; + --md-primary-fg-color: #000; + --md-accent-fg-color: #000; + + /* Code block theming */ + --md-code-fg-color: red; + --md-code-bg-color: #f5f5f5; + + /* Tables, blockquotes, etc. */ + --md-table-row-border-color: #e0e0e0; + --md-admonition-bg-color: #f8f8f8; + --md-admonition-title-fg-color: #373737; + --md-default-fg-color--light: #000; + + --md-typeset-a-color: #000; + --md-accent-fg-color: #000; + + --md-code-fg-color: #000; +} + +/* Header styling */ +.md-header { + background-color: #000; +} + +.md-header--shadow { + box-shadow: none; +} + +.md-content .md-typeset h1 { + color: #000; +} + +.md-typeset p, +.md-typeset li { + font-size: 16px; +} + +.md-typeset__table p { + line-height: 1em; +} + +.md-nav { + font-size: 14px; +} +.md-nav__title { + color: #000; + font-weight: 600; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4 { + font-weight: 600; +} + +.md-typeset h1 code { + color: #000; + padding: 0; + background-color: transparent; +} +.md-footer { + display: none; +} + +.md-header__title { + margin-left: 0 !important; +} + +.md-typeset .admonition, +.md-typeset details { + border: none; + outline: none; + border-radius: 8px; + overflow: hidden; +} + +.md-typeset pre > code { + font-size: 14px; +} + +.md-typeset__table code { + font-size: 14px; +} + +/* Custom link styling */ +.md-content a { + text-decoration: none; +} + +.md-content a:hover { + text-decoration: underline; +} + +/* Code block styling */ +.md-content .md-code__content { + border-radius: 8px; +} + +.md-clipboard.md-icon { + color: #9e9e9e; +} + +/* Reset scrollbar styling to browser default with high priority */ +.md-sidebar__scrollwrap { + scrollbar-color: auto !important; +} diff --git a/docs/tools.md b/docs/tools.md new file mode 100644 index 0000000..f7a8869 --- /dev/null +++ b/docs/tools.md @@ -0,0 +1,270 @@ +# Tools + +Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK: + +- Hosted tools: these run on LLM servers alongside the AI models. OpenAI offers retrieval, web search and computer use as hosted tools. +- Function calling: these allow you to use any Python function as a tool. +- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them. + +## Hosted tools + +OpenAI offers a few built-in tools when using the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel]: + +- The [`WebSearchTool`][agents.tool.WebSearchTool] lets an agent search the web. +- The [`FileSearchTool`][agents.tool.FileSearchTool] allows retrieving information from your OpenAI Vector Stores. +- The [`ComputerTool`][agents.tool.ComputerTool] allows automating computer use tasks. + +```python +from agents import Agent, FileSearchTool, Runner, WebSearchTool + +agent = Agent( + name="Assistant", + tools=[ + WebSearchTool(), + FileSearchTool( + max_num_results=3, + vector_store_ids=["VECTOR_STORE_ID"], + ), + ], +) + +async def main(): + result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?") + print(result.final_output) +``` + +## Function tools + +You can use any Python function as a tool. The Agents SDK will setup the tool automatically: + +- The name of the tool will be the name of the Python function (or you can provide a name) +- Tool description will be taken from the docstring of the function (or you can provide a description) +- The schema for the function inputs is automatically created from the function's arguments +- Descriptions for each input are taken from the docstring of the function, unless disabled + +We use Python's `inspect` module to extract the function signature, along with [`griffe`](https://mkdocstrings.github.io/griffe/) to parse docstrings and `pydantic` for schema creation. + +```python +import json + +from typing_extensions import TypedDict, Any + +from agents import Agent, FunctionTool, RunContextWrapper, function_tool + + +class Location(TypedDict): + lat: float + long: float + +@function_tool # (1)! +async def fetch_weather(location: Location) -> str: + # (2)! + """Fetch the weather for a given location. + + Args: + location: The location to fetch the weather for. + """ + # In real life, we'd fetch the weather from a weather API + return "sunny" + + +@function_tool(name_override="fetch_data") # (3)! +def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str: + """Read the contents of a file. + + Args: + path: The path to the file to read. + directory: The directory to read the file from. + """ + # In real life, we'd read the file from the file system + return "" + + +agent = Agent( + name="Assistant", + tools=[fetch_weather, read_file], # (4)! +) + +for tool in agent.tools: + if isinstance(tool, FunctionTool): + print(tool.name) + print(tool.description) + print(json.dumps(tool.params_json_schema, indent=2)) + print() + +``` + +1. You can use any Python types as arguments to your functions, and the function can be sync or async. +2. Docstrings, if present, are used to capture descriptions and argument descriptions +3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc. +4. You can pass the decorated functions to the list of tools. + +??? note "Expand to see output" + + ``` + fetch_weather + Fetch the weather for a given location. + { + "$defs": { + "Location": { + "properties": { + "lat": { + "title": "Lat", + "type": "number" + }, + "long": { + "title": "Long", + "type": "number" + } + }, + "required": [ + "lat", + "long" + ], + "title": "Location", + "type": "object" + } + }, + "properties": { + "location": { + "$ref": "#/$defs/Location", + "description": "The location to fetch the weather for." + } + }, + "required": [ + "location" + ], + "title": "fetch_weather_args", + "type": "object" + } + + fetch_data + Read the contents of a file. + { + "properties": { + "path": { + "description": "The path to the file to read.", + "title": "Path", + "type": "string" + }, + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The directory to read the file from.", + "title": "Directory" + } + }, + "required": [ + "path" + ], + "title": "fetch_data_args", + "type": "object" + } + ``` + +### Custom function tools + +Sometimes, you don't want to use a Python function as a tool. You can directly create a [`FunctionTool`][agents.tool.FunctionTool] if you prefer. You'll need to provide: + +- `name` +- `description` +- `params_json_schema`, which is the JSON schema for the arguments +- `on_invoke_tool`, which is an async function that receives the context and the arguments as a JSON string, and must return the tool output as a string. + +```python +from typing import Any + +from pydantic import BaseModel + +from agents import RunContextWrapper, FunctionTool + + + +def do_some_work(data: str) -> str: + return "done" + + +class FunctionArgs(BaseModel): + username: str + age: int + + +async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: + parsed = FunctionArgs.model_validate_json(args) + return do_some_work(data=f"{parsed.username} is {parsed.age} years old") + + +tool = FunctionTool( + name="process_user", + description="Processes extracted user data", + params_json_schema=FunctionArgs.model_json_schema(), + on_invoke_tool=run_function, +) +``` + +### Automatic argument and docstring parsing + +As mentioned before, we automatically parse the function signature to extract the schema for the tool, and we parse the docstring to extract descriptions for the tool and for individual arguments. Some notes on that: + +1. The signature parsing is done via the `inspect` module. We use type annotations to understand the types for the arguments, and dynamically build a Pydantic model to represent the overall schema. It supports most types, including Python primitives, Pydantic models, TypedDicts, and more. +2. We use `griffe` to parse docstrings. Supported docstring formats are `google`, `sphinx` and `numpy`. We attempt to automatically detect the docstring format, but this is best-effort and you can explicitly set it when calling `function_tool`. You can also disable docstring parsing by setting `use_docstring_info` to `False`. + +The code for the schema extraction lives in [`agents.function_schema`][]. + +## Agents as tools + +In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools. + +```python +from agents import Agent, Runner +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You translate the user's message to Spanish", +) + +french_agent = Agent( + name="French agent", + instructions="You translate the user's message to French", +) + +orchestrator_agent = Agent( + name="orchestrator_agent", + instructions=( + "You are a translation agent. You use the tools given to you to translate." + "If asked for multiple translations, you call the relevant tools." + ), + tools=[ + spanish_agent.as_tool( + tool_name="translate_to_spanish", + tool_description="Translate the user's message to Spanish", + ), + french_agent.as_tool( + tool_name="translate_to_french", + tool_description="Translate the user's message to French", + ), + ], +) + +async def main(): + result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.") + print(result.final_output) +``` + +## Handling errors in function tools + +When you create a function tool via `@function_tool`, you can pass a `failure_error_function`. This is a function that provides an error response to the LLM in case the tool call crashes. + +- By default (i.e. if you don't pass anything), it runs a `default_tool_error_function` which tells the LLM an error occurred. +- If you pass your own error function, it runs that instead, and sends the response to the LLM. +- If you explicitly pass `None`, then any tool call errors will be re-raised for you to handle. This could be a `ModelBehaviorError` if the model produced invalid JSON, or a `UserError` if your code crashed, etc. + +If you are manually creating a `FunctionTool` object, then you must handle errors inside the `on_invoke_tool` function. diff --git a/docs/tracing.md b/docs/tracing.md new file mode 100644 index 0000000..fbf2ae4 --- /dev/null +++ b/docs/tracing.md @@ -0,0 +1,95 @@ +# Tracing + +The Agents SDK includes built-in tracing, collecting a comprehensive record of events during an agent run: LLM generations, tool calls, handoffs, guardrails, and even custom events that occur. Using the [Traces dashboard](https://platform.openai.com/traces), you can debug, visualize, and monitor your workflows during development and in production. + +!!!note + + Tracing is enabled by default. There are two ways to disable tracing: + + 1. You can globally disable tracing by setting the env var `OPENAI_AGENTS_DISABLE_TRACING=1` + 2. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True` + +## Traces and spans + +- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties: + - `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service". + - `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`. + - `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID. + - `disabled`: If True, the trace will not be recorded. + - `metadata`: Optiona metadata for the trace. +- **Spans** represent operations that have a start and end time. Spans have: + - `started_at` and `ended_at` timestamps. + - `trace_id`, to represent the trace they belong to + - `parent_id`, which points to the parent Span of this Span (if any) + - `span_data`, which is information about the Span. For example, `AgentSpanData` contains information about the Agent, `GenerationSpanData` contains information about the LLM generation, etc. + +## Default tracing + +By default, the SDK traces the following: + +- The entire `Runner.{run, run_sync, run_streamed}()` is wrapped in a `trace()`. +- Each time an agent runs, it is wrapped in `agent_span()` +- LLM generations are wrapped in `generation_span()` +- Function tool calls are each wrapped in `function_span()` +- Guardrails are wrapped in `guardrail_span()` +- Handoffs are wrapped in `handoff_span()` + +By default, the trace is named "Agent trace". You can set this name if you use `trace`, or you can can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig]. + +In addition, you can set up [custom trace processors](#custom-tracing-processors) to push traces to other destinations (as a replacement, or secondary destination). + +## Higher level traces + +Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`. + +```python +from agents import Agent, Runner, trace + +async def main(): + agent = Agent(name="Joke generator", instructions="Tell funny jokes.") + + with trace("Joke workflow"): # (1)! + first_result = await Runner.run(agent, "Tell me a joke") + second_result = await Runner.run(agent, f"Rate this joke: {first_output.final_output}") + print(f"Joke: {first_result.final_output}") + print(f"Rating: {second_result.final_output}") +``` + +1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces. + +## Creating traces + +You can use the [`trace()`][agents.tracing.trace] function to create a trace. Traces need to be started and finished. You have two options to do so: + +1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time. +2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish]. + +The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace. + +## Creating spans + +You can use the various [`*_span()`][agents.tracing.create] methods to create a span. In general, you don't need to manually create spans. A [`custom_span()`][agents.tracing.custom_span] function is available for tracking custom span information. + +Spans are automatically part of the current trace, and are nested under the nearest current span, which is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). + +## Sensitive data + +Some spans track potentially sensitive data. For example, the `generation_span()` stores the inputs/outputs of the LLM generation, and `function_span()` stores the inputs/outputs of function calls. These may contain sensitive data, so you can disable capturing that data via [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]. + +## Custom tracing processors + +The high level architecture for tracing is: + +- At initialization, we create a global [`TraceProvider`][agents.tracing.setup.TraceProvider], which is responsible for creating traces. +- We configure the `TraceProvider` with a [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor] that sends traces/spans in batches to a [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter], which exports the spans and traces to the OpenAI backend in batches. + +To customize this default setup, to send traces to alternative or additional backends or modifying exporter behavior, you have two options: + +1. [`add_trace_processor()`][agents.tracing.add_trace_processor] lets you add an **additional** trace processor that will receive traces and spans as they are ready. This lets you do your own processing in addition to sending traces to OpenAI's backend. +2. [`set_trace_processors()`][agents.tracing.set_trace_processors] lets you **replace** the default processors with your own trace processors. This means traces will not be sent to the OpenAI backend unless you include a `TracingProcessor` that does so. + +External trace processors include: + +- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk) +- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents) +- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk) diff --git a/examples/__init__.py b/examples/__init__.py new file mode 100644 index 0000000..e333a2e --- /dev/null +++ b/examples/__init__.py @@ -0,0 +1,3 @@ +# Make the examples directory into a package to avoid top-level module name collisions. +# This is needed so that mypy treats files like examples/customer_service/main.py and +# examples/researcher_app/main.py as distinct modules rather than both named "main". diff --git a/examples/__pycache__/__init__.cpython-313.pyc b/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b690047891112aa3055c72f6c7c91365406e7d6f GIT binary patch literal 151 zcmey&%ge<81ip^v(wP|<7#@Q-FaYF(!DkjAHI*TqL6gyMB|{MtkoOrRam!3Uv^ce> zSidM&KRG`oRX;I3HLs*NB|E;LD8D4Xq_QAYKeZw;w;(6ASU)~KGcU6wK3=b&@)n0p dZhlH>PO4oIE6^B_HN_ytM`lJw#v*1Q3jp1mBz^z@ literal 0 HcmV?d00001 diff --git a/examples/agent_patterns/README.md b/examples/agent_patterns/README.md new file mode 100644 index 0000000..4599b00 --- /dev/null +++ b/examples/agent_patterns/README.md @@ -0,0 +1,54 @@ +# Common agentic patterns + +This folder contains examples of different common patterns for agents. + +## Deterministic flows + +A common tactic is to break down a task into a series of smaller steps. Each task can be performed by an agent, and the output of one agent is used as input to the next. For example, if your task was to generate a story, you could break it down into the following steps: + +1. Generate an outline +2. Generate the story +3. Generate the ending + +Each of these steps can be performed by an agent. The output of one agent is used as input to the next. + +See the [`deterministic.py`](./deterministic.py) file for an example of this. + +## Handoffs and routing + +In many situations, you have specialized sub-agents that handle specific tasks. You can use handoffs to route the task to the right agent. + +For example, you might have a frontline agent that receives a request, and then hands off to a specialized agent based on the language of the request. +See the [`routing.py`](./routing.py) file for an example of this. + +## Agents as tools + +The mental model for handoffs is that the new agent "takes over". It sees the previous conversation history, and owns the conversation from that point onwards. However, this is not the only way to use agents. You can also use agents as a tool - the tool agent goes off and runs on its own, and then returns the result to the original agent. + +For example, you could model the translation task above as tool calls instead: rather than handing over to the language-specific agent, you could call the agent as a tool, and then use the result in the next step. This enables things like translating multiple languages at once. + +See the [`agents_as_tools.py`](./agents_as_tools.py) file for an example of this. + +## LLM-as-a-judge + +LLMs can often improve the quality of their output if given feedback. A common pattern is to generate a response using a model, and then use a second model to provide feedback. You can even use a small model for the initial generation and a larger model for the feedback, to optimize cost. + +For example, you could use an LLM to generate an outline for a story, and then use a second LLM to evaluate the outline and provide feedback. You can then use the feedback to improve the outline, and repeat until the LLM is satisfied with the outline. + +See the [`llm_as_a_judge.py`](./llm_as_a_judge.py) file for an example of this. + +## Parallelization + +Running multiple agents in parallel is a common pattern. This can be useful for both latency (e.g. if you have multiple steps that don't depend on each other) and also for other reasons e.g. generating multiple responses and picking the best one. + +See the [`parallelization.py`](./parallelization.py) file for an example of this. It runs a translation agent multiple times in parallel, and then picks the best translation. + +## Guardrails + +Related to parallelization, you often want to run input guardrails to make sure the inputs to your agents are valid. For example, if you have a customer support agent, you might want to make sure that the user isn't trying to ask for help with a math problem. + +You can definitely do this without any special Agents SDK features by using parallelization, but we support a special guardrail primitive. Guardrails can have a "tripwire" - if the tripwire is triggered, the agent execution will immediately stop and a `GuardrailTripwireTriggered` exception will be raised. + +This is really useful for latency: for example, you might have a very fast model that runs the guardrail and a slow model that runs the actual agent. You wouldn't want to wait for the slow model to finish, so guardrails let you quickly reject invalid inputs. + +See the [`guardrails.py`](./guardrails.py) file for an example of this. diff --git a/examples/agent_patterns/agents_as_tools.py b/examples/agent_patterns/agents_as_tools.py new file mode 100644 index 0000000..9fd118e --- /dev/null +++ b/examples/agent_patterns/agents_as_tools.py @@ -0,0 +1,79 @@ +import asyncio + +from agents import Agent, ItemHelpers, MessageOutputItem, Runner, trace + +""" +This example shows the agents-as-tools pattern. The frontline agent receives a user message and +then picks which agents to call, as tools. In this case, it picks from a set of translation +agents. +""" + +spanish_agent = Agent( + name="spanish_agent", + instructions="You translate the user's message to Spanish", + handoff_description="An english to spanish translator", +) + +french_agent = Agent( + name="french_agent", + instructions="You translate the user's message to French", + handoff_description="An english to french translator", +) + +italian_agent = Agent( + name="italian_agent", + instructions="You translate the user's message to Italian", + handoff_description="An english to italian translator", +) + +orchestrator_agent = Agent( + name="orchestrator_agent", + instructions=( + "You are a translation agent. You use the tools given to you to translate." + "If asked for multiple translations, you call the relevant tools in order." + "You never translate on your own, you always use the provided tools." + ), + tools=[ + spanish_agent.as_tool( + tool_name="translate_to_spanish", + tool_description="Translate the user's message to Spanish", + ), + french_agent.as_tool( + tool_name="translate_to_french", + tool_description="Translate the user's message to French", + ), + italian_agent.as_tool( + tool_name="translate_to_italian", + tool_description="Translate the user's message to Italian", + ), + ], +) + +synthesizer_agent = Agent( + name="synthesizer_agent", + instructions="You inspect translations, correct them if needed, and produce a final concatenated response.", +) + + +async def main(): + msg = input("Hi! What would you like translated, and to which languages? ") + + # Run the entire orchestration in a single trace + with trace("Orchestrator evaluator"): + orchestrator_result = await Runner.run(orchestrator_agent, msg) + + for item in orchestrator_result.new_items: + if isinstance(item, MessageOutputItem): + text = ItemHelpers.text_message_output(item) + if text: + print(f" - Translation step: {text}") + + synthesizer_result = await Runner.run( + synthesizer_agent, orchestrator_result.to_input_list() + ) + + print(f"\n\nFinal response:\n{synthesizer_result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/deterministic.py b/examples/agent_patterns/deterministic.py new file mode 100644 index 0000000..0c163af --- /dev/null +++ b/examples/agent_patterns/deterministic.py @@ -0,0 +1,80 @@ +import asyncio + +from pydantic import BaseModel + +from agents import Agent, Runner, trace + +""" +This example demonstrates a deterministic flow, where each step is performed by an agent. +1. The first agent generates a story outline +2. We feed the outline into the second agent +3. The second agent checks if the outline is good quality and if it is a scifi story +4. If the outline is not good quality or not a scifi story, we stop here +5. If the outline is good quality and a scifi story, we feed the outline into the third agent +6. The third agent writes the story +""" + +story_outline_agent = Agent( + name="story_outline_agent", + instructions="Generate a very short story outline based on the user's input.", +) + + +class OutlineCheckerOutput(BaseModel): + good_quality: bool + is_scifi: bool + + +outline_checker_agent = Agent( + name="outline_checker_agent", + instructions="Read the given story outline, and judge the quality. Also, determine if it is a scifi story.", + output_type=OutlineCheckerOutput, +) + +story_agent = Agent( + name="story_agent", + instructions="Write a short story based on the given outline.", + output_type=str, +) + + +async def main(): + input_prompt = input("What kind of story do you want? ") + + # Ensure the entire workflow is a single trace + with trace("Deterministic story flow"): + # 1. Generate an outline + outline_result = await Runner.run( + story_outline_agent, + input_prompt, + ) + print("Outline generated") + + # 2. Check the outline + outline_checker_result = await Runner.run( + outline_checker_agent, + outline_result.final_output, + ) + + # 3. Add a gate to stop if the outline is not good quality or not a scifi story + assert isinstance(outline_checker_result.final_output, OutlineCheckerOutput) + if not outline_checker_result.final_output.good_quality: + print("Outline is not good quality, so we stop here.") + exit(0) + + if not outline_checker_result.final_output.is_scifi: + print("Outline is not a scifi story, so we stop here.") + exit(0) + + print("Outline is good quality and a scifi story, so we continue to write the story.") + + # 4. Write the story + story_result = await Runner.run( + story_agent, + outline_result.final_output, + ) + print(f"Story: {story_result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/input_guardrails.py b/examples/agent_patterns/input_guardrails.py new file mode 100644 index 0000000..6259188 --- /dev/null +++ b/examples/agent_patterns/input_guardrails.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import asyncio + +from pydantic import BaseModel + +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + TResponseInputItem, + input_guardrail, +) + +""" +This example shows how to use guardrails. + +Guardrails are checks that run in parallel to the agent's execution. +They can be used to do things like: +- Check if input messages are off-topic +- Check that output messages don't violate any policies +- Take over control of the agent's execution if an unexpected input is detected + +In this example, we'll setup an input guardrail that trips if the user is asking to do math homework. +If the guardrail trips, we'll respond with a refusal message. +""" + + +### 1. An agent-based guardrail that is triggered if the user is asking to do math homework +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + reasoning: str + + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking you to do their math homework.", + output_type=MathHomeworkOutput, +) + + +@input_guardrail +async def math_guardrail( + context: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + """This is an input guardrail function, which happens to call an agent to check if the input + is a math homework question. + """ + result = await Runner.run(guardrail_agent, input, context=context.context) + final_output = result.final_output_as(MathHomeworkOutput) + + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_math_homework, + ) + + +### 2. The run loop + + +async def main(): + agent = Agent( + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + input_guardrails=[math_guardrail], + ) + + input_data: list[TResponseInputItem] = [] + + while True: + user_input = input("Enter a message: ") + input_data.append( + { + "role": "user", + "content": user_input, + } + ) + + try: + result = await Runner.run(agent, input_data) + print(result.final_output) + # If the guardrail didn't trigger, we use the result as the input for the next run + input_data = result.to_input_list() + except InputGuardrailTripwireTriggered: + # If the guardrail triggered, we instead add a refusal message to the input + message = "Sorry, I can't help you with your math homework." + print(message) + input_data.append( + { + "role": "assistant", + "content": message, + } + ) + + # Sample run: + # Enter a message: What's the capital of California? + # The capital of California is Sacramento. + # Enter a message: Can you help me solve for x: 2x + 5 = 11 + # Sorry, I can't help you with your math homework. + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/llm_as_a_judge.py b/examples/agent_patterns/llm_as_a_judge.py new file mode 100644 index 0000000..d13a67c --- /dev/null +++ b/examples/agent_patterns/llm_as_a_judge.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import Literal + +from agents import Agent, ItemHelpers, Runner, TResponseInputItem, trace + +""" +This example shows the LLM as a judge pattern. The first agent generates an outline for a story. +The second agent judges the outline and provides feedback. We loop until the judge is satisfied +with the outline. +""" + +story_outline_generator = Agent( + name="story_outline_generator", + instructions=( + "You generate a very short story outline based on the user's input." + "If there is any feedback provided, use it to improve the outline." + ), +) + + +@dataclass +class EvaluationFeedback: + score: Literal["pass", "needs_improvement", "fail"] + feedback: str + + +evaluator = Agent[None]( + name="evaluator", + instructions=( + "You evaluate a story outline and decide if it's good enough." + "If it's not good enough, you provide feedback on what needs to be improved." + "Never give it a pass on the first try." + ), + output_type=EvaluationFeedback, +) + + +async def main() -> None: + msg = input("What kind of story would you like to hear? ") + input_items: list[TResponseInputItem] = [{"content": msg, "role": "user"}] + + latest_outline: str | None = None + + # We'll run the entire workflow in a single trace + with trace("LLM as a judge"): + while True: + story_outline_result = await Runner.run( + story_outline_generator, + input_items, + ) + + input_items = story_outline_result.to_input_list() + latest_outline = ItemHelpers.text_message_outputs(story_outline_result.new_items) + print("Story outline generated") + + evaluator_result = await Runner.run(evaluator, input_items) + result: EvaluationFeedback = evaluator_result.final_output + + print(f"Evaluator score: {result.score}") + + if result.score == "pass": + print("Story outline is good enough, exiting.") + break + + print("Re-running with feedback") + + input_items.append({"content": f"Feedback: {result.feedback}", "role": "user"}) + + print(f"Final story outline: {latest_outline}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/output_guardrails.py b/examples/agent_patterns/output_guardrails.py new file mode 100644 index 0000000..526a085 --- /dev/null +++ b/examples/agent_patterns/output_guardrails.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import asyncio +import json + +from pydantic import BaseModel, Field + +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + output_guardrail, +) + +""" +This example shows how to use output guardrails. + +Output guardrails are checks that run on the final output of an agent. +They can be used to do things like: +- Check if the output contains sensitive data +- Check if the output is a valid response to the user's message + +In this example, we'll use a (contrived) example where we check if the agent's response contains +a phone number. +""" + + +# The agent's output type +class MessageOutput(BaseModel): + reasoning: str = Field(description="Thoughts on how to respond to the user's message") + response: str = Field(description="The response to the user's message") + user_name: str | None = Field(description="The name of the user who sent the message, if known") + + +@output_guardrail +async def sensitive_data_check( + context: RunContextWrapper, agent: Agent, output: MessageOutput +) -> GuardrailFunctionOutput: + phone_number_in_response = "650" in output.response + phone_number_in_reasoning = "650" in output.reasoning + + return GuardrailFunctionOutput( + output_info={ + "phone_number_in_response": phone_number_in_response, + "phone_number_in_reasoning": phone_number_in_reasoning, + }, + tripwire_triggered=phone_number_in_response or phone_number_in_reasoning, + ) + + +agent = Agent( + name="Assistant", + instructions="You are a helpful assistant.", + output_type=MessageOutput, + output_guardrails=[sensitive_data_check], +) + + +async def main(): + # This should be ok + await Runner.run(agent, "What's the capital of California?") + print("First message passed") + + # This should trip the guardrail + try: + result = await Runner.run( + agent, "My phone number is 650-123-4567. Where do you think I live?" + ) + print( + f"Guardrail didn't trip - this is unexpected. Output: {json.dumps(result.final_output.model_dump(), indent=2)}" + ) + + except OutputGuardrailTripwireTriggered as e: + print(f"Guardrail tripped. Info: {e.guardrail_result.output.output_info}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/parallelization.py b/examples/agent_patterns/parallelization.py new file mode 100644 index 0000000..fe2a8ec --- /dev/null +++ b/examples/agent_patterns/parallelization.py @@ -0,0 +1,61 @@ +import asyncio + +from agents import Agent, ItemHelpers, Runner, trace + +""" +This example shows the parallelization pattern. We run the agent three times in parallel, and pick +the best result. +""" + +spanish_agent = Agent( + name="spanish_agent", + instructions="You translate the user's message to Spanish", +) + +translation_picker = Agent( + name="translation_picker", + instructions="You pick the best Spanish translation from the given options.", +) + + +async def main(): + msg = input("Hi! Enter a message, and we'll translate it to Spanish.\n\n") + + # Ensure the entire workflow is a single trace + with trace("Parallel translation"): + res_1, res_2, res_3 = await asyncio.gather( + Runner.run( + spanish_agent, + msg, + ), + Runner.run( + spanish_agent, + msg, + ), + Runner.run( + spanish_agent, + msg, + ), + ) + + outputs = [ + ItemHelpers.text_message_outputs(res_1.new_items), + ItemHelpers.text_message_outputs(res_2.new_items), + ItemHelpers.text_message_outputs(res_3.new_items), + ] + + translations = "\n\n".join(outputs) + print(f"\n\nTranslations:\n\n{translations}") + + best_translation = await Runner.run( + translation_picker, + f"Input: {msg}\n\nTranslations:\n{translations}", + ) + + print("\n\n-----") + + print(f"Best translation: {best_translation.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/agent_patterns/routing.py b/examples/agent_patterns/routing.py new file mode 100644 index 0000000..3dcaefa --- /dev/null +++ b/examples/agent_patterns/routing.py @@ -0,0 +1,70 @@ +import asyncio +import uuid + +from openai.types.responses import ResponseContentPartDoneEvent, ResponseTextDeltaEvent + +from agents import Agent, RawResponsesStreamEvent, Runner, TResponseInputItem, trace + +""" +This example shows the handoffs/routing pattern. The triage agent receives the first message, and +then hands off to the appropriate agent based on the language of the request. Responses are +streamed to the user. +""" + +french_agent = Agent( + name="french_agent", + instructions="You only speak French", +) + +spanish_agent = Agent( + name="spanish_agent", + instructions="You only speak Spanish", +) + +english_agent = Agent( + name="english_agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="triage_agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[french_agent, spanish_agent, english_agent], +) + + +async def main(): + # We'll create an ID for this conversation, so we can link each trace + conversation_id = str(uuid.uuid4().hex[:16]) + + msg = input("Hi! We speak French, Spanish and English. How can I help? ") + agent = triage_agent + inputs: list[TResponseInputItem] = [{"content": msg, "role": "user"}] + + while True: + # Each conversation turn is a single trace. Normally, each input from the user would be an + # API request to your app, and you can wrap the request in a trace() + with trace("Routing example", group_id=conversation_id): + result = Runner.run_streamed( + agent, + input=inputs, + ) + async for event in result.stream_events(): + if not isinstance(event, RawResponsesStreamEvent): + continue + data = event.data + if isinstance(data, ResponseTextDeltaEvent): + print(data.delta, end="", flush=True) + elif isinstance(data, ResponseContentPartDoneEvent): + print("\n") + + inputs = result.to_input_list() + print("\n") + + user_msg = input("Enter a message: ") + inputs.append({"content": user_msg, "role": "user"}) + agent = result.current_agent + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/basic/agent_lifecycle_example.py b/examples/basic/agent_lifecycle_example.py new file mode 100644 index 0000000..bc0bbe4 --- /dev/null +++ b/examples/basic/agent_lifecycle_example.py @@ -0,0 +1,112 @@ +import asyncio +import random +from typing import Any + +from pydantic import BaseModel + +from agents import Agent, AgentHooks, RunContextWrapper, Runner, Tool, function_tool + + +class CustomAgentHooks(AgentHooks): + def __init__(self, display_name: str): + self.event_counter = 0 + self.display_name = display_name + + async def on_start(self, context: RunContextWrapper, agent: Agent) -> None: + self.event_counter += 1 + print(f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started") + + async def on_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended with output {output}" + ) + + async def on_handoff(self, context: RunContextWrapper, agent: Agent, source: Agent) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {source.name} handed off to {agent.name}" + ) + + async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started tool {tool.name}" + ) + + async def on_tool_end( + self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str + ) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended tool {tool.name} with result {result}" + ) + + +### + + +@function_tool +def random_number(max: int) -> int: + """ + Generate a random number up to the provided maximum. + """ + return random.randint(0, max) + + +@function_tool +def multiply_by_two(x: int) -> int: + """Simple multiplication by two.""" + return x * 2 + + +class FinalResult(BaseModel): + number: int + + +multiply_agent = Agent( + name="Multiply Agent", + instructions="Multiply the number by 2 and then return the final result.", + tools=[multiply_by_two], + output_type=FinalResult, + hooks=CustomAgentHooks(display_name="Multiply Agent"), +) + +start_agent = Agent( + name="Start Agent", + instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", + tools=[random_number], + output_type=FinalResult, + handoffs=[multiply_agent], + hooks=CustomAgentHooks(display_name="Start Agent"), +) + + +async def main() -> None: + user_input = input("Enter a max number: ") + await Runner.run( + start_agent, + input=f"Generate a random number between 0 and {user_input}.", + ) + + print("Done!") + + +if __name__ == "__main__": + asyncio.run(main()) +""" +$ python examples/basic/agent_lifecycle_example.py + +Enter a max number: 250 +### (Start Agent) 1: Agent Start Agent started +### (Start Agent) 2: Agent Start Agent started tool random_number +### (Start Agent) 3: Agent Start Agent ended tool random_number with result 37 +### (Start Agent) 4: Agent Start Agent started +### (Start Agent) 5: Agent Start Agent handed off to Multiply Agent +### (Multiply Agent) 1: Agent Multiply Agent started +### (Multiply Agent) 2: Agent Multiply Agent started tool multiply_by_two +### (Multiply Agent) 3: Agent Multiply Agent ended tool multiply_by_two with result 74 +### (Multiply Agent) 4: Agent Multiply Agent started +### (Multiply Agent) 5: Agent Multiply Agent ended with output number=74 +Done! +""" diff --git a/examples/basic/dynamic_system_prompt.py b/examples/basic/dynamic_system_prompt.py new file mode 100644 index 0000000..7bcf90c --- /dev/null +++ b/examples/basic/dynamic_system_prompt.py @@ -0,0 +1,69 @@ +import asyncio +import random +from typing import Literal + +from agents import Agent, RunContextWrapper, Runner + + +class CustomContext: + def __init__(self, style: Literal["haiku", "pirate", "robot"]): + self.style = style + + +def custom_instructions( + run_context: RunContextWrapper[CustomContext], agent: Agent[CustomContext] +) -> str: + context = run_context.context + if context.style == "haiku": + return "Only respond in haikus." + elif context.style == "pirate": + return "Respond as a pirate." + else: + return "Respond as a robot and say 'beep boop' a lot." + + +agent = Agent( + name="Chat agent", + instructions=custom_instructions, +) + + +async def main(): + choice: Literal["haiku", "pirate", "robot"] = random.choice(["haiku", "pirate", "robot"]) + context = CustomContext(style=choice) + print(f"Using style: {choice}\n") + + user_message = "Tell me a joke." + print(f"User: {user_message}") + result = await Runner.run(agent, user_message, context=context) + + print(f"Assistant: {result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) + +""" +$ python examples/basic/dynamic_system_prompt.py + +Using style: haiku + +User: Tell me a joke. +Assistant: Why don't eggs tell jokes? +They might crack each other's shells, +leaving yolk on face. + +$ python examples/basic/dynamic_system_prompt.py +Using style: robot + +User: Tell me a joke. +Assistant: Beep boop! Why was the robot so bad at soccer? Beep boop... because it kept kicking up a debug! Beep boop! + +$ python examples/basic/dynamic_system_prompt.py +Using style: pirate + +User: Tell me a joke. +Assistant: Why did the pirate go to school? + +To improve his arrr-ticulation! Har har har! 🏴‍☠️ +""" diff --git a/examples/basic/hello_world.py b/examples/basic/hello_world.py new file mode 100644 index 0000000..169290d --- /dev/null +++ b/examples/basic/hello_world.py @@ -0,0 +1,20 @@ +import asyncio + +from agents import Agent, Runner + + +async def main(): + agent = Agent( + name="Assistant", + instructions="You only respond in haikus.", + ) + + result = await Runner.run(agent, "Tell me about recursion in programming.") + print(result.final_output) + # Function calls itself, + # Looping in smaller pieces, + # Endless by design. + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/basic/lifecycle_example.py b/examples/basic/lifecycle_example.py new file mode 100644 index 0000000..9b36510 --- /dev/null +++ b/examples/basic/lifecycle_example.py @@ -0,0 +1,118 @@ +import asyncio +import random +from typing import Any + +from pydantic import BaseModel + +from agents import Agent, RunContextWrapper, RunHooks, Runner, Tool, Usage, function_tool + + +class ExampleHooks(RunHooks): + def __init__(self): + self.event_counter = 0 + + def _usage_to_str(self, usage: Usage) -> str: + return f"{usage.requests} requests, {usage.input_tokens} input tokens, {usage.output_tokens} output tokens, {usage.total_tokens} total tokens" + + async def on_agent_start(self, context: RunContextWrapper, agent: Agent) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Agent {agent.name} started. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_agent_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Agent {agent.name} ended with output {output}. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Tool {tool.name} started. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_tool_end( + self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str + ) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Tool {tool.name} ended with result {result}. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_handoff( + self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent + ) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Handoff from {from_agent.name} to {to_agent.name}. Usage: {self._usage_to_str(context.usage)}" + ) + + +hooks = ExampleHooks() + +### + + +@function_tool +def random_number(max: int) -> int: + """Generate a random number up to the provided max.""" + return random.randint(0, max) + + +@function_tool +def multiply_by_two(x: int) -> int: + """Return x times two.""" + return x * 2 + + +class FinalResult(BaseModel): + number: int + + +multiply_agent = Agent( + name="Multiply Agent", + instructions="Multiply the number by 2 and then return the final result.", + tools=[multiply_by_two], + output_type=FinalResult, +) + +start_agent = Agent( + name="Start Agent", + instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", + tools=[random_number], + output_type=FinalResult, + handoffs=[multiply_agent], +) + + +async def main() -> None: + user_input = input("Enter a max number: ") + await Runner.run( + start_agent, + hooks=hooks, + input=f"Generate a random number between 0 and {user_input}.", + ) + + print("Done!") + + +if __name__ == "__main__": + asyncio.run(main()) +""" +$ python examples/basic/lifecycle_example.py + +Enter a max number: 250 +### 1: Agent Start Agent started. Usage: 0 requests, 0 input tokens, 0 output tokens, 0 total tokens +### 2: Tool random_number started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 3: Tool random_number ended with result 101. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 4: Agent Start Agent started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 5: Handoff from Start Agent to Multiply Agent. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens +### 6: Agent Multiply Agent started. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens +### 7: Tool multiply_by_two started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 8: Tool multiply_by_two ended with result 202. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 9: Agent Multiply Agent started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 10: Agent Multiply Agent ended with output number=202. Usage: 4 requests, 714 input tokens, 63 output tokens, 777 total tokens +Done! + +""" diff --git a/examples/basic/stream_items.py b/examples/basic/stream_items.py new file mode 100644 index 0000000..c1f2257 --- /dev/null +++ b/examples/basic/stream_items.py @@ -0,0 +1,65 @@ +import asyncio +import random + +from agents import Agent, ItemHelpers, Runner, function_tool + + +@function_tool +def how_many_jokes() -> int: + return random.randint(1, 10) + + +async def main(): + agent = Agent( + name="Joker", + instructions="First call the `how_many_jokes` tool, then tell that many jokes.", + tools=[how_many_jokes], + ) + + result = Runner.run_streamed( + agent, + input="Hello", + ) + print("=== Run starting ===") + async for event in result.stream_events(): + # We'll ignore the raw responses event deltas + if event.type == "raw_response_event": + continue + elif event.type == "agent_updated_stream_event": + print(f"Agent updated: {event.new_agent.name}") + continue + elif event.type == "run_item_stream_event": + if event.item.type == "tool_call_item": + print("-- Tool was called") + elif event.item.type == "tool_call_output_item": + print(f"-- Tool output: {event.item.output}") + elif event.item.type == "message_output_item": + print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") + else: + pass # Ignore other event types + + print("=== Run complete ===") + + +if __name__ == "__main__": + asyncio.run(main()) + + # === Run starting === + # Agent updated: Joker + # -- Tool was called + # -- Tool output: 4 + # -- Message output: + # Sure, here are four jokes for you: + + # 1. **Why don't skeletons fight each other?** + # They don't have the guts! + + # 2. **What do you call fake spaghetti?** + # An impasta! + + # 3. **Why did the scarecrow win an award?** + # Because he was outstanding in his field! + + # 4. **Why did the bicycle fall over?** + # Because it was two-tired! + # === Run complete === diff --git a/examples/basic/stream_text.py b/examples/basic/stream_text.py new file mode 100644 index 0000000..a73c1fe --- /dev/null +++ b/examples/basic/stream_text.py @@ -0,0 +1,21 @@ +import asyncio + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import Agent, Runner + + +async def main(): + agent = Agent( + name="Joker", + instructions="You are a helpful assistant.", + ) + + result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") + async for event in result.stream_events(): + if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): + print(event.data.delta, end="", flush=True) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/customer_service/main.py b/examples/customer_service/main.py new file mode 100644 index 0000000..bd802e2 --- /dev/null +++ b/examples/customer_service/main.py @@ -0,0 +1,169 @@ +from __future__ import annotations as _annotations + +import asyncio +import random +import uuid + +from pydantic import BaseModel + +from agents import ( + Agent, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + RunContextWrapper, + Runner, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, + function_tool, + handoff, + trace, +) +from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX + +### CONTEXT + + +class AirlineAgentContext(BaseModel): + passenger_name: str | None = None + confirmation_number: str | None = None + seat_number: str | None = None + flight_number: str | None = None + + +### TOOLS + + +@function_tool( + name_override="faq_lookup_tool", description_override="Lookup frequently asked questions." +) +async def faq_lookup_tool(question: str) -> str: + if "bag" in question or "baggage" in question: + return ( + "You are allowed to bring one bag on the plane. " + "It must be under 50 pounds and 22 inches x 14 inches x 9 inches." + ) + elif "seats" in question or "plane" in question: + return ( + "There are 120 seats on the plane. " + "There are 22 business class seats and 98 economy seats. " + "Exit rows are rows 4 and 16. " + "Rows 5-8 are Economy Plus, with extra legroom. " + ) + elif "wifi" in question: + return "We have free wifi on the plane, join Airline-Wifi" + return "I'm sorry, I don't know the answer to that question." + + +@function_tool +async def update_seat( + context: RunContextWrapper[AirlineAgentContext], confirmation_number: str, new_seat: str +) -> str: + """ + Update the seat for a given confirmation number. + + Args: + confirmation_number: The confirmation number for the flight. + new_seat: The new seat to update to. + """ + # Update the context based on the customer's input + context.context.confirmation_number = confirmation_number + context.context.seat_number = new_seat + # Ensure that the flight number has been set by the incoming handoff + assert context.context.flight_number is not None, "Flight number is required" + return f"Updated seat to {new_seat} for confirmation number {confirmation_number}" + + +### HOOKS + + +async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext]) -> None: + flight_number = f"FLT-{random.randint(100, 999)}" + context.context.flight_number = flight_number + + +### AGENTS + +faq_agent = Agent[AirlineAgentContext]( + name="FAQ Agent", + handoff_description="A helpful agent that can answer questions about the airline.", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent. + Use the following routine to support the customer. + # Routine + 1. Identify the last question asked by the customer. + 2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge. + 3. If you cannot answer the question, transfer back to the triage agent.""", + tools=[faq_lookup_tool], +) + +seat_booking_agent = Agent[AirlineAgentContext]( + name="Seat Booking Agent", + handoff_description="A helpful agent that can update a seat on a flight.", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent. + Use the following routine to support the customer. + # Routine + 1. Ask for their confirmation number. + 2. Ask the customer what their desired seat number is. + 3. Use the update seat tool to update the seat on the flight. + If the customer asks a question that is not related to the routine, transfer back to the triage agent. """, + tools=[update_seat], +) + +triage_agent = Agent[AirlineAgentContext]( + name="Triage Agent", + handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.", + instructions=( + f"{RECOMMENDED_PROMPT_PREFIX} " + "You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents." + ), + handoffs=[ + faq_agent, + handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff), + ], +) + +faq_agent.handoffs.append(triage_agent) +seat_booking_agent.handoffs.append(triage_agent) + + +### RUN + + +async def main(): + current_agent: Agent[AirlineAgentContext] = triage_agent + input_items: list[TResponseInputItem] = [] + context = AirlineAgentContext() + + # Normally, each input from the user would be an API request to your app, and you can wrap the request in a trace() + # Here, we'll just use a random UUID for the conversation ID + conversation_id = uuid.uuid4().hex[:16] + + while True: + user_input = input("Enter your message: ") + with trace("Customer service", group_id=conversation_id): + input_items.append({"content": user_input, "role": "user"}) + result = await Runner.run(current_agent, input_items, context=context) + + for new_item in result.new_items: + agent_name = new_item.agent.name + if isinstance(new_item, MessageOutputItem): + print(f"{agent_name}: {ItemHelpers.text_message_output(new_item)}") + elif isinstance(new_item, HandoffOutputItem): + print( + f"Handed off from {new_item.source_agent.name} to {new_item.target_agent.name}" + ) + elif isinstance(new_item, ToolCallItem): + print(f"{agent_name}: Calling a tool") + elif isinstance(new_item, ToolCallOutputItem): + print(f"{agent_name}: Tool call output: {new_item.output}") + else: + print(f"{agent_name}: Skipping item: {new_item.__class__.__name__}") + input_items = result.to_input_list() + current_agent = result.last_agent + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/handoffs/message_filter.py b/examples/handoffs/message_filter.py new file mode 100644 index 0000000..9dd56ef --- /dev/null +++ b/examples/handoffs/message_filter.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import json +import random + +from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace +from agents.extensions import handoff_filters + + +@function_tool +def random_number_tool(max: int) -> int: + """Return a random integer between 0 and the given maximum.""" + return random.randint(0, max) + + +def spanish_handoff_message_filter(handoff_message_data: HandoffInputData) -> HandoffInputData: + # First, we'll remove any tool-related messages from the message history + handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) + + # Second, we'll also remove the first two items from the history, just for demonstration + history = ( + tuple(handoff_message_data.input_history[2:]) + if isinstance(handoff_message_data.input_history, tuple) + else handoff_message_data.input_history + ) + + return HandoffInputData( + input_history=history, + pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), + new_items=tuple(handoff_message_data.new_items), + ) + + +first_agent = Agent( + name="Assistant", + instructions="Be extremely concise.", + tools=[random_number_tool], +) + +spanish_agent = Agent( + name="Spanish Assistant", + instructions="You only speak Spanish and are extremely concise.", + handoff_description="A Spanish-speaking assistant.", +) + +second_agent = Agent( + name="Assistant", + instructions=( + "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." + ), + handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], +) + + +async def main(): + # Trace the entire run as a single workflow + with trace(workflow_name="Message filtering"): + # 1. Send a regular message to the first agent + result = await Runner.run(first_agent, input="Hi, my name is Sora.") + + print("Step 1 done") + + # 2. Ask it to square a number + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [{"content": "Can you generate a random number between 0 and 100?", "role": "user"}], + ) + + print("Step 2 done") + + # 3. Call the second agent + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user", + } + ], + ) + + print("Step 3 done") + + # 4. Cause a handoff to occur + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "Por favor habla en español. ¿Cuál es mi nombre y dónde vivo?", + "role": "user", + } + ], + ) + + print("Step 4 done") + + print("\n===Final messages===\n") + + # 5. That should have caused spanish_handoff_message_filter to be called, which means the + # output should be missing the first two messages, and have no tool calls. + # Let's print the messages to see what happened + for message in result.to_input_list(): + print(json.dumps(message, indent=2)) + # tool_calls = message.tool_calls if isinstance(message, AssistantMessage) else None + + # print(f"{message.role}: {message.content}\n - Tool calls: {tool_calls or 'None'}") + """ + $python examples/handoffs/message_filter.py + Step 1 done + Step 2 done + Step 3 done + Step 4 done + + ===Final messages=== + + { + "content": "Can you generate a random number between 0 and 100?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "Sure! Here's a random number between 0 and 100: **42**.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "As of the most recent estimates, the population of New York City is approximately 8.6 million people. However, this number is constantly changing due to various factors such as migration and birth rates. For the latest and most accurate information, it's always a good idea to check the official data from sources like the U.S. Census Bureau.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "No tengo acceso a esa informaci\u00f3n personal, solo s\u00e9 lo que me has contado: vives en Nueva York.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + """ + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/examples/handoffs/message_filter_streaming.py b/examples/handoffs/message_filter_streaming.py new file mode 100644 index 0000000..8d1b420 --- /dev/null +++ b/examples/handoffs/message_filter_streaming.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import json +import random + +from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace +from agents.extensions import handoff_filters + + +@function_tool +def random_number_tool(max: int) -> int: + """Return a random integer between 0 and the given maximum.""" + return random.randint(0, max) + + +def spanish_handoff_message_filter(handoff_message_data: HandoffInputData) -> HandoffInputData: + # First, we'll remove any tool-related messages from the message history + handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) + + # Second, we'll also remove the first two items from the history, just for demonstration + history = ( + tuple(handoff_message_data.input_history[2:]) + if isinstance(handoff_message_data.input_history, tuple) + else handoff_message_data.input_history + ) + + return HandoffInputData( + input_history=history, + pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), + new_items=tuple(handoff_message_data.new_items), + ) + + +first_agent = Agent( + name="Assistant", + instructions="Be extremely concise.", + tools=[random_number_tool], +) + +spanish_agent = Agent( + name="Spanish Assistant", + instructions="You only speak Spanish and are extremely concise.", + handoff_description="A Spanish-speaking assistant.", +) + +second_agent = Agent( + name="Assistant", + instructions=( + "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." + ), + handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], +) + + +async def main(): + # Trace the entire run as a single workflow + with trace(workflow_name="Streaming message filter"): + # 1. Send a regular message to the first agent + result = await Runner.run(first_agent, input="Hi, my name is Sora.") + + print("Step 1 done") + + # 2. Ask it to square a number + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [{"content": "Can you generate a random number between 0 and 100?", "role": "user"}], + ) + + print("Step 2 done") + + # 3. Call the second agent + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user", + } + ], + ) + + print("Step 3 done") + + # 4. Cause a handoff to occur + stream_result = Runner.run_streamed( + second_agent, + input=result.to_input_list() + + [ + { + "content": "Por favor habla en español. ¿Cuál es mi nombre y dónde vivo?", + "role": "user", + } + ], + ) + async for _ in stream_result.stream_events(): + pass + + print("Step 4 done") + + print("\n===Final messages===\n") + + # 5. That should have caused spanish_handoff_message_filter to be called, which means the + # output should be missing the first two messages, and have no tool calls. + # Let's print the messages to see what happened + for item in stream_result.to_input_list(): + print(json.dumps(item, indent=2)) + """ + $python examples/handoffs/message_filter_streaming.py + Step 1 done + Step 2 done + Step 3 done + Tu nombre y lugar de residencia no los tengo disponibles. Solo sé que mencionaste vivir en la ciudad de Nueva York. + Step 4 done + + ===Final messages=== + + { + "content": "Can you generate a random number between 0 and 100?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "Sure! Here's a random number between 0 and 100: **37**.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "As of the latest estimates, New York City's population is approximately 8.5 million people. Would you like more information about the city?", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "No s\u00e9 tu nombre, pero me dijiste que vives en Nueva York.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + """ + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/examples/research_bot/README.md b/examples/research_bot/README.md new file mode 100644 index 0000000..4060983 --- /dev/null +++ b/examples/research_bot/README.md @@ -0,0 +1,25 @@ +# Research bot + +This is a simple example of a multi-agent research bot. To run it: + +```bash +python -m examples.research_bot.main +``` + +## Architecture + +The flow is: + +1. User enters their research topic +2. `planner_agent` comes up with a plan to search the web for information. The plan is a list of search queries, with a search term and a reason for each query. +3. For each search item, we run a `search_agent`, which uses the Web Search tool to search for that term and summarize the results. These all run in parallel. +4. Finally, the `writer_agent` receives the search summaries, and creates a written report. + +## Suggested improvements + +If you're building your own research bot, some ideas to add to this are: + +1. Retrieval: Add support for fetching relevant information from a vector store. You could use the File Search tool for this. +2. Image and file upload: Allow users to attach PDFs or other files, as baseline context for the research. +3. More planning and thinking: Models often produce better results given more time to think. Improve the planning process to come up with a better plan, and add an evaluation step so that the model can choose to improve it's results, search for more stuff, etc. +4. Code execution: Allow running code, which is useful for data analysis. diff --git a/examples/research_bot/__init__.py b/examples/research_bot/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/examples/research_bot/__init__.py @@ -0,0 +1 @@ + diff --git a/examples/research_bot/__pycache__/__init__.cpython-313.pyc b/examples/research_bot/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d1acc93f4bc9c71f89e2d10e448709f37eac452 GIT binary patch literal 165 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 literal 0 HcmV?d00001 diff --git a/examples/research_bot/__pycache__/printer.cpython-313.pyc b/examples/research_bot/__pycache__/printer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e9ac76c97d24a53666bbf942c29c78cac09adea GIT binary patch literal 2823 zcmb7G&2JM&6rcUF*I$WCNJtztF>$H01Y#NzQa)8m1XV&)nyn=&qOOd+Nes@~on5!U zp~|IIBDI1`q#_|TQhP{F<=8)=r;1exj#hwBd#JdXCKcDdH|w=cvX{JN|P;!z>nbN4h|ZV-LGYvRuFxN|O*Hg#&I=wd2c%;~Ajylz-#?z&c@MXPAtEa@rz zMrN^8&_PEx^$g7}XjZXUNR?dJhD$dcS<~`H-qJKDS_5;~lLTNJ^DvMl@_29e+CF=) zvMhcZ*curH7JQsM_+xold??4ZQ)4 z(mi!Ye&iME8#vn+*b(?*x`Y-@rU6d3@A|fF3j+5 z9YS;P&HoCaG>6AjUCO@(Z}PhAP=slUY4!<(9K#E*iN=Av5^HT3Tnq>|Cb~Q{UX`;L zJp{%OVf~38Zr#1Lp0xW%?!ROAkKMN(^qp#gXBQ65d5_hZrPA9VT_q`EK;3Pb`^{Eh zgu{!34)VbzLdxKr!6Pk=HbozV;pKR%ihL5jm;r5-`-oF{LrQZqE#mRAie;{mwjYi| zG`G}jbOlvN3#xdWZ?XcHLL`oPfFAD}t$M>1EMsh>xnoWHL#L3Y%Obq_rcA z-!QQsv21bEg<|A55ic-Uvh4?vc?$@{!~Vo_Xlp3-^NEcUm7$aN(8)(br|ocZ`GVb< zyfgo_y=yCRure}Xk4*fYn7G^Z>%bk~RlY*SQvY&~s>0KFqRA5!^b!A{;SY(%N*gvc zBeSSOQwwU^Vlh`P;5@8ppO-U*+M9Mwo6A$vD&!5_C~6u-xanRbP|KiJn--<$up?fD zo=LIN(IF%Zm`+WD&TQqgTE?f@xr^`lh+?EdHuvZ_ozN`S5Bc96Z4Wog(w4xVIM2|7ObDaaf16qC^j(|mSexc1NFdXFLF1ehHEOWa=+ zSI=~R2HiMgo}C8ehPRG<$_4rieIH&x+7T0RtO^{*Jt19xk>nE+dqUzbefzoLzX=d# F{4b?4CgA`8 literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__init__.py b/examples/research_bot/agents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc b/examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a094b5a5310588de48141f37146944bc4283a4ae GIT binary patch literal 172 zcmey&%ge<81b+h0rGx0lAOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=2{m|mnqGJ7` zT>a$ylvMr1^whkP;*{+8f};GA{F2IoRQ=S7#N2|MRG^yF;?%^VKEC5pNEtmiR literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc b/examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f33d418871c61aa28bea1c82722cfc0b8c064195 GIT binary patch literal 929 zcmYjP&2G~`5MKW#ah%eW6p13WTPT8{QVY=w;zug#0i_}w++4s?WSni{HnFYU4OKY7 z4RN6yDwRj*Gw=>b*v z$>6ItHGvDB>Xp2L z0dXX|MX)VT|7Vvx(^I6iZn@S1+SPXzTJGv~`v>??T@~6!G!n%p2@m7Q@7EPkdgwFq zIPQ=>oEjTl5^Y}__@!*0Q(Xu-Vb>X^B%$7@CAvmM7)a6B3`xpsDtCYVB8NG$MFt?gfehU@tH!_ ztnFUN@ILhuHjPOlLLpVwmH!an6(C~aAnAuYBOF6|n!_2yonfD_HLH27(;}2cOH#2hxp#vv?NjO<)th3inXv|~TV}}w(d>ZuH5_o>Wfku#a zc6lus2|J*~=edKOt-8-EpNlo?jl%oPW1>K*zPE(~@DEDKZRm z0vF+nG@Er>n2SykN}QsYj5>bA!+^@~CCn+tX*F`#LWB7TUG`V#5^{#%7}2T> zaV`sSZ`z&*^dguFF!MpAnxZJbi%41ih8Dl0#neVuu8nWrIxWpA=GdyF8th-@=2HXq w)I@geVDqTES^xk5 literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc b/examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b836aaccefaaea442c2c69164006214838ee5e6f GIT binary patch literal 1133 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 literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc b/examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3281242f38bbe1eca339d39aef19fa8b6c80d19 GIT binary patch literal 964 zcmY*XL2DC16rN3X)22;IYbgq%JbIBp&6XC?f+$i!nqmuXO0fjOWOtGb?#`?;v#r~k zXV3lz{WC>x?SgplS4dEfiqH*db{W^<0Y8F zG~25Gw%Jyt@Lr6VFh%o_4FX10IPOXrx16H+T1G4mm@!<8G(B?&c`GxZiU9@1bcqG3 zHP?m;(}q|4mT4RS2GxfnAzVFMUS1!hbQSOicJrAkIwXlX+!LarI;EJ>)! zK@JhgY_}{nx(!_nZgNSc4QxEzgp?{eQZyZFO9nC1+VTt7TwgztD#E&LnwkuSTe5_5 z0jcD|5*{jUEM*vC9Or8wBnZYLFcQY}n4zL1jhV&ZkeCRW29!UNGQyTtq!qoJErijUsNi6i9`V6BJv{5BrwG7Uk2Yqru=hO3Vb9XP6;mgtYRp_Z`r4@$f{rvLx| literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc b/examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b809d7c5a494bde94badff40aa5cd0ecc2255b19 GIT binary patch literal 625 zcmZuvO^XyU5KVe!zlPbOiwVKG?lb#dl3JF z_#^x?f;Z`-p1kckc(-~!ZWbX_sK>#P*UuT_J2zTjbuAiq%rCl7TU#jkkw_0WIkxBQ%1b@H6Up~ zSZZ4>Y6}>G2@zd{xWouyhYw)AE1=>!Qw8t~i28k;z!URMJ3u}Zjj|d%!KI#8j9Mec zP?67T)rlzYlnENn@7!f3!h*0`ZZ#S}MLq=hcs`x}{Pg+WbPAWA@~~+{VHIm);keXL zqH$1p<6;;C&zIxeSQDEu_mP7sD^KeYavjmwFoDM&oR46fvUSd#I9AJZ6ODIhxvjEf zcaR$JOi!JnTMKQ*Z@|Q$?P4-Y+P=e5w?pGXq*`?Z;l-%e4(1rPCR2}{Z%=m4VTak+ z|5dEGB--cctDq^QRHn-3I$iH3HSOVpk`1={ t5e=EW+j{`Mu6k0+zlTEJzTOw7w|*SmxaucIFXi>Y!D;ecoKW}E{sH>R%#8p5 literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc b/examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be550b1efb6bd192deb292e8968ec6c3d8a0aa0f GIT binary patch literal 1293 zcmY*ZPm9||6dzfV?e*@G4cm2h7n)83r5F->Lr6-X1X6k^3Hd{mQVND)RvO#GN;B%4 zQC#=dQ?I=Q+K8 zj*`wePPz{wXiv8Wz4zWc2;A1!+=!<-6FF|;M^m9JzX}NXL06Q6pqv_^qy`GY2oC8? z0JYqliHy#rozXc+E7cVJQauw!z82_NYOr*ILHxfk`eP+vF{D24T;@4F6|@BXT4w7^ zb|$C>IhBg%jrT3eRYr|21eFSpLH!y9&9oBqbg_8?H%1!El^yo{DQ2dtJfjl{#v(XO zQk4y6uBfhTE-`}#=?`9Ewli*NCQJ(QRG8XgEL5qjJQWCLlIuCulZ}`meKiy67DeU+ za4R-Ubp!|uR;b0y+|Y%t05SO*5kpxaSd9OlW{n&fl8P1_&N4k$9%{jD!-=#TPPtH1 zJ3}fEUnVS`uePR0?=QtJz}+-T%*CF7jMs6}h!HAxJeX?xIdy_fY+e8Vk& ze;o~O7uy4tnX1477w+1+OxAS5?N4-$T(hcVI61~jt<0a~*`VvXj456S#$1oFLT6R( zNaSw2nKkCIKErl&%SDD4MW*1}?AlF5fd3xcym5Er2KDV<8y{4oAa^O9?Up4eUgimpcKMi6Y;BpZLLGX^;ct;K|+U@xMMM#3qRlggI jemlA%`1#N$dk4#{{_^_A%ijKS@A$)3yx00=r%wGZ=IxVm literal 0 HcmV?d00001 diff --git a/examples/research_bot/agents/planner_agent.py b/examples/research_bot/agents/planner_agent.py new file mode 100644 index 0000000..e80a8e6 --- /dev/null +++ b/examples/research_bot/agents/planner_agent.py @@ -0,0 +1,29 @@ +from pydantic import BaseModel + +from agents import Agent + +PROMPT = ( + "You are a helpful research assistant. Given a query, come up with a set of web searches " + "to perform to best answer the query. Output between 5 and 20 terms to query for." +) + + +class WebSearchItem(BaseModel): + reason: str + "Your reasoning for why this search is important to the query." + + query: str + "The search term to use for the web search." + + +class WebSearchPlan(BaseModel): + searches: list[WebSearchItem] + """A list of web searches to perform to best answer the query.""" + + +planner_agent = Agent( + name="PlannerAgent", + instructions=PROMPT, + model="gpt-4o", + output_type=WebSearchPlan, +) diff --git a/examples/research_bot/agents/search_agent.py b/examples/research_bot/agents/search_agent.py new file mode 100644 index 0000000..72cbc8e --- /dev/null +++ b/examples/research_bot/agents/search_agent.py @@ -0,0 +1,18 @@ +from agents import Agent, WebSearchTool +from agents.model_settings import ModelSettings + +INSTRUCTIONS = ( + "You are a research assistant. Given a search term, you search the web for that term and" + "produce a concise summary of the results. The summary must 2-3 paragraphs and less than 300" + "words. Capture the main points. Write succintly, no need to have complete sentences or good" + "grammar. This will be consumed by someone synthesizing a report, so its vital you capture the" + "essence and ignore any fluff. Do not include any additional commentary other than the summary" + "itself." +) + +search_agent = Agent( + name="Search agent", + instructions=INSTRUCTIONS, + tools=[WebSearchTool()], + model_settings=ModelSettings(tool_choice="required"), +) diff --git a/examples/research_bot/agents/writer_agent.py b/examples/research_bot/agents/writer_agent.py new file mode 100644 index 0000000..7b7d01a --- /dev/null +++ b/examples/research_bot/agents/writer_agent.py @@ -0,0 +1,33 @@ +# Agent used to synthesize a final report from the individual summaries. +from pydantic import BaseModel + +from agents import Agent + +PROMPT = ( + "You are a senior researcher tasked with writing a cohesive report for a research query. " + "You will be provided with the original query, and some initial research done by a research " + "assistant.\n" + "You should first come up with an outline for the report that describes the structure and " + "flow of the report. Then, generate the report and return that as your final output.\n" + "The final output should be in markdown format, and it should be lengthy and detailed. Aim " + "for 5-10 pages of content, at least 1000 words." +) + + +class ReportData(BaseModel): + short_summary: str + """A short 2-3 sentence summary of the findings.""" + + markdown_report: str + """The final report""" + + follow_up_questions: list[str] + """Suggested topics to research further""" + + +writer_agent = Agent( + name="WriterAgent", + instructions=PROMPT, + model="o3-mini", + output_type=ReportData, +) diff --git a/examples/research_bot/main.py b/examples/research_bot/main.py new file mode 100644 index 0000000..a0fd43d --- /dev/null +++ b/examples/research_bot/main.py @@ -0,0 +1,12 @@ +import asyncio + +from .manager import ResearchManager + + +async def main() -> None: + query = input("What would you like to research? ") + await ResearchManager().run(query) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/research_bot/manager.py b/examples/research_bot/manager.py new file mode 100644 index 0000000..47306f1 --- /dev/null +++ b/examples/research_bot/manager.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +import asyncio +import time + +from rich.console import Console + +from agents import Runner, custom_span, gen_trace_id, trace + +from .agents.planner_agent import WebSearchItem, WebSearchPlan, planner_agent +from .agents.search_agent import search_agent +from .agents.writer_agent import ReportData, writer_agent +from .printer import Printer + + +class ResearchManager: + def __init__(self): + self.console = Console() + self.printer = Printer(self.console) + + async def run(self, query: str) -> None: + trace_id = gen_trace_id() + with trace("Research trace", trace_id=trace_id): + self.printer.update_item( + "trace_id", + f"View trace: https://platform.openai.com/traces/{trace_id}", + is_done=True, + hide_checkmark=True, + ) + + self.printer.update_item( + "starting", + "Starting research...", + is_done=True, + hide_checkmark=True, + ) + search_plan = await self._plan_searches(query) + search_results = await self._perform_searches(search_plan) + report = await self._write_report(query, search_results) + + final_report = f"Report summary\n\n{report.short_summary}" + self.printer.update_item("final_report", final_report, is_done=True) + + self.printer.end() + + print("\n\n=====REPORT=====\n\n") + print(f"Report: {report.markdown_report}") + print("\n\n=====FOLLOW UP QUESTIONS=====\n\n") + follow_up_questions = "\n".join(report.follow_up_questions) + print(f"Follow up questions: {follow_up_questions}") + + async def _plan_searches(self, query: str) -> WebSearchPlan: + self.printer.update_item("planning", "Planning searches...") + result = await Runner.run( + planner_agent, + f"Query: {query}", + ) + self.printer.update_item( + "planning", + f"Will perform {len(result.final_output.searches)} searches", + is_done=True, + ) + return result.final_output_as(WebSearchPlan) + + async def _perform_searches(self, search_plan: WebSearchPlan) -> list[str]: + with custom_span("Search the web"): + self.printer.update_item("searching", "Searching...") + num_completed = 0 + tasks = [asyncio.create_task(self._search(item)) for item in search_plan.searches] + results = [] + for task in asyncio.as_completed(tasks): + result = await task + if result is not None: + results.append(result) + num_completed += 1 + self.printer.update_item( + "searching", f"Searching... {num_completed}/{len(tasks)} completed" + ) + self.printer.mark_item_done("searching") + return results + + async def _search(self, item: WebSearchItem) -> str | None: + input = f"Search term: {item.query}\nReason for searching: {item.reason}" + try: + result = await Runner.run( + search_agent, + input, + ) + return str(result.final_output) + except Exception: + return None + + async def _write_report(self, query: str, search_results: list[str]) -> ReportData: + self.printer.update_item("writing", "Thinking about report...") + input = f"Original query: {query}\nSummarized search results: {search_results}" + result = Runner.run_streamed( + writer_agent, + input, + ) + update_messages = [ + "Thinking about report...", + "Planning report structure...", + "Writing outline...", + "Creating sections...", + "Cleaning up formatting...", + "Finalizing report...", + "Finishing report...", + ] + + last_update = time.time() + next_message = 0 + async for _ in result.stream_events(): + if time.time() - last_update > 5 and next_message < len(update_messages): + self.printer.update_item("writing", update_messages[next_message]) + next_message += 1 + last_update = time.time() + + self.printer.mark_item_done("writing") + return result.final_output_as(ReportData) diff --git a/examples/research_bot/printer.py b/examples/research_bot/printer.py new file mode 100644 index 0000000..e820c75 --- /dev/null +++ b/examples/research_bot/printer.py @@ -0,0 +1,41 @@ +from typing import Any + +from rich.console import Console, Group +from rich.live import Live +from rich.spinner import Spinner + + +class Printer: + def __init__(self, console: Console): + self.live = Live(console=console) + self.items: dict[str, tuple[str, bool]] = {} + self.hide_done_ids: set[str] = set() + self.live.start() + + def end(self) -> None: + self.live.stop() + + def hide_done_checkmark(self, item_id: str) -> None: + self.hide_done_ids.add(item_id) + + def update_item( + self, item_id: str, content: str, is_done: bool = False, hide_checkmark: bool = False + ) -> None: + self.items[item_id] = (content, is_done) + if hide_checkmark: + self.hide_done_ids.add(item_id) + self.flush() + + def mark_item_done(self, item_id: str) -> None: + self.items[item_id] = (self.items[item_id][0], True) + self.flush() + + def flush(self) -> None: + renderables: list[Any] = [] + for item_id, (content, is_done) in self.items.items(): + if is_done: + prefix = "✅ " if item_id not in self.hide_done_ids else "" + renderables.append(prefix + content) + else: + renderables.append(Spinner("dots", text=content)) + self.live.update(Group(*renderables)) diff --git a/examples/research_bot/sample_outputs/product_recs.md b/examples/research_bot/sample_outputs/product_recs.md new file mode 100644 index 0000000..70789eb --- /dev/null +++ b/examples/research_bot/sample_outputs/product_recs.md @@ -0,0 +1,180 @@ +# Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options + +Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Board Types and Design Considerations](#board-types-and-design-considerations) +3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) +4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) +5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) +6. [Budget and Pricing Options](#budget-and-pricing-options) +7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) +8. [Conclusion](#conclusion) +9. [Follow-up Questions](#follow-up-questions) + +--- + +## Introduction + +Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. + +The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. + +--- + +## Board Types and Design Considerations + +Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: + +### Longboards and Mini-Mals + +Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. + +### Funboards and Hybrids + +Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6’6" to 8’0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. + +### Shortboards + +Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. + +--- + +## Key Board Dimensions and Features + +When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: + +### Length and Width + +- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. +- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. + +### Thickness and Volume + +- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. +- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surfer’s weight and experience level. + +### Nose and Tail Shape + +- **Nose Shape**: A wide, rounded nose expands the board’s planing surface, which can help in catching waves sooner and maintaining stability as you ride. +- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. + +### Rocker + +- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surfer’s skills improve. + +--- + +## Materials: Soft-Top vs. Hard-Top Boards + +The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: + +### Soft-Top (Foam) Boards + +Soft-top boards are constructed almost entirely from foam. Their attributes include: + +- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. +- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. +- **Maintenance**: They often require less maintenance—there is typically no need for waxing and they are more resistant to dings and scratches. + +However, as a surfer’s skills progress, a soft-top might limit maneuverability and overall performance. + +### Hard-Top Boards + +Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: + +- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. +- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. + +Deciding between soft-top and hard-top boards often depends on a beginner’s progression goals, overall comfort, and budget constraints. + +--- + +## Tips for Transitioning from Longboards to Shorter Boards + +For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: + +### Gradual Downsizing + +Experts recommend reducing the board length gradually—by about a foot at a time—to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. + +### Strengthening Core Skills + +Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: + +- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. +- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. + +### Experimenting with Rentals or Borrowed Boards + +If possible, try out a friend’s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. + +--- + +## Budget and Pricing Options + +Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, it’s important to understand what features you can expect at different price points. + +### Budget-Friendly Options + +For those on a tight budget, several entry-level models offer excellent value. Examples include: + +- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. +- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. + +### Moderate Price Range + +As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. + +### Higher-End Models and Transitional Packages + +For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. + +--- + +## Recommended Models and Buying Options + +Based on extensive research and community recommendations, here are some standout models and tips on where to buy: + +### Recommended Models + +- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. +- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. +- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. +- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. + +### Buying Options + +- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and condition—especially if you are considering a used board. +- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. +- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. + +--- + +## Conclusion + +Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. + +For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stability—such as funboards and hybrids—before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. + +Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrum—from budget-friendly models to premium options—allows you to make an informed purchase that suits both your financial and performance needs. + +With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. + +--- + +## Follow-up Questions + +1. What is your current budget range for a new surfboard, or are you considering buying used? +2. How frequently do you plan to surf, and in what type of wave conditions? +3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? +4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? +5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? + +--- + +With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! diff --git a/examples/research_bot/sample_outputs/product_recs.txt b/examples/research_bot/sample_outputs/product_recs.txt new file mode 100644 index 0000000..78865f2 --- /dev/null +++ b/examples/research_bot/sample_outputs/product_recs.txt @@ -0,0 +1,212 @@ +# Terminal output for a product recommendation related query. See product_recs.md for final report. + +$ uv run python -m examples.research_bot.main + +What would you like to research? Best surfboards for beginners. I can catch my own waves, but previously used an 11ft board. What should I look for, what are my options? Various budget ranges. +View trace: https://platform.openai.com/traces/trace_... +Starting research... +✅ Will perform 15 searches +✅ Searching... 15/15 completed +✅ Finishing report... +✅ Report summary + +This report provides a detailed guide on selecting the best surfboards for beginners, especially for those transitioning from an 11-foot longboard to a +shorter board. It covers design considerations such as board dimensions, shape, materials, and volume, while comparing soft-top and hard-top boards. In +addition, the report discusses various budget ranges, recommended board models, buying options (both new and used), and techniques to ease the transition to +more maneuverable boards. By understanding these factors, beginner surfers can select a board that not only enhances their skills but also suits their +individual needs. + + +=====REPORT===== + + +Report: # Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options + +Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Board Types and Design Considerations](#board-types-and-design-considerations) +3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) +4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) +5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) +6. [Budget and Pricing Options](#budget-and-pricing-options) +7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) +8. [Conclusion](#conclusion) +9. [Follow-up Questions](#follow-up-questions) + +--- + +## Introduction + +Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. + +The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. + +--- + +## Board Types and Design Considerations + +Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: + +### Longboards and Mini-Mals + +Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. + +### Funboards and Hybrids + +Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6’6" to 8’0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. + +### Shortboards + +Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. + +--- + +## Key Board Dimensions and Features + +When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: + +### Length and Width + +- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. +- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. + +### Thickness and Volume + +- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. +- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surfer’s weight and experience level. + +### Nose and Tail Shape + +- **Nose Shape**: A wide, rounded nose expands the board’s planing surface, which can help in catching waves sooner and maintaining stability as you ride. +- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. + +### Rocker + +- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surfer’s skills improve. + +--- + +## Materials: Soft-Top vs. Hard-Top Boards + +The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: + +### Soft-Top (Foam) Boards + +Soft-top boards are constructed almost entirely from foam. Their attributes include: + +- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. +- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. +- **Maintenance**: They often require less maintenance—there is typically no need for waxing and they are more resistant to dings and scratches. + +However, as a surfer’s skills progress, a soft-top might limit maneuverability and overall performance. + +### Hard-Top Boards + +Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: + +- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. +- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. + +Deciding between soft-top and hard-top boards often depends on a beginner’s progression goals, overall comfort, and budget constraints. + +--- + +## Tips for Transitioning from Longboards to Shorter Boards + +For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: + +### Gradual Downsizing + +Experts recommend reducing the board length gradually—by about a foot at a time—to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. + +### Strengthening Core Skills + +Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: + +- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. +- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. + +### Experimenting with Rentals or Borrowed Boards + +If possible, try out a friend’s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. + +--- + +## Budget and Pricing Options + +Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, it’s important to understand what features you can expect at different price points. + +### Budget-Friendly Options + +For those on a tight budget, several entry-level models offer excellent value. Examples include: + +- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. +- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. + +### Moderate Price Range + +As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. + +### Higher-End Models and Transitional Packages + +For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. + +--- + +## Recommended Models and Buying Options + +Based on extensive research and community recommendations, here are some standout models and tips on where to buy: + +### Recommended Models + +- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. +- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. +- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. +- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. + +### Buying Options + +- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and condition—especially if you are considering a used board. +- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. +- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. + +--- + +## Conclusion + +Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. + +For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stability—such as funboards and hybrids—before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. + +Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrum—from budget-friendly models to premium options—allows you to make an informed purchase that suits both your financial and performance needs. + +With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. + +--- + +## Follow-up Questions + +1. What is your current budget range for a new surfboard, or are you considering buying used? +2. How frequently do you plan to surf, and in what type of wave conditions? +3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? +4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? +5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? + +--- + +With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! + + +=====FOLLOW UP QUESTIONS===== + + +Follow up questions: What is your current budget range for a new surfboard, or are you considering a used board? +What types of waves do you typically surf, and how might that affect your board choice? +Would you be interested in a transitional board that grows with your skills, or are you looking for a more specialized design? +Have you had experience with renting or borrowing boards to try different sizes before making a purchase? +Do you require additional equipment bundles (like fins, leash, or wax), or do you already have those? diff --git a/examples/research_bot/sample_outputs/vacation.md b/examples/research_bot/sample_outputs/vacation.md new file mode 100644 index 0000000..82c137a --- /dev/null +++ b/examples/research_bot/sample_outputs/vacation.md @@ -0,0 +1,177 @@ +Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration + +The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) +3. [Surfing in the Caribbean](#surfing-in-the-caribbean) + - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) + - 3.2 [Puerto Rico: Rincón and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) + - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) +4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) + - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) + - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) +5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) + - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) + - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) +6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) +7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) +8. [Conclusion](#conclusion) + +--- + +## Introduction + +Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. + +Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. + +--- + +## Why April is the Perfect Time in the Caribbean + +April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: + +- **Consistent Warm Temperatures:** Average daytime highs around 29°C (84°F) foster comfortable conditions for both land and water activities. +- **Pleasant Sea Temperatures:** With sea temperatures near 26°C (79°F), swimmers, surfers, and divers are treated to inviting waters. +- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. +- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. + +These factors create an ideal backdrop for balancing your outdoor pursuits, whether you’re catching epic waves, trekking rugged trails, or partaking in water sports. + +--- + +## Surfing in the Caribbean + +Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. + +### Barbados: The Tale of Two Coasts + +Barbados is a prime destination: + +- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. +- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. + +Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. + +### Puerto Rico: Rincón and Beyond + +Rincón in Puerto Rico is hailed as the Caribbean’s surfing capital: + +- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. +- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. + +In addition, Puerto Rico’s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. + +### Dominican Republic and Other Hotspots + +Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: + +- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. +- **Jamaica and The Bahamas:** Jamaica’s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamas’ Surfer’s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. + +These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. + +--- + +## Hiking Adventures Across the Caribbean + +The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. + +### Trekking Through Tropical Rainforests + +For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: + +- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquí frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. +- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. + +### Volcanic Peaks and Rugged Landscapes + +For those seeking more rugged challenges, several destinations offer unforgettable adventures: + +- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. +- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. +- **La Soufrière, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. + +Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelée, which combine historical context with natural beauty. + +--- + +## Diverse Water Sports Experiences + +While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. + +### Snorkeling, Diving, and Jet Skiing + +Caribbean waters teem with life and color, making them ideal for underwater exploration: + +- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. +- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. +- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. + +### Kiteboarding and Windsurfing + +Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: + +- **Aruba:** Known as "One Happy Island," Aruba’s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. +- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. + +Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. + +--- + +## Combining Adventures: Multi-Activity Destinations + +For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. + +- **Puerto Rico:** With its robust surf scene in Rincón, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. +- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. +- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaica’s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. + +Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. + +--- + +## Practical Advice and Travel Tips + +### Weather and Timing + +- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. +- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincón in Puerto Rico), many destinations maintain consistent conditions during this month. + +### Booking and Costs + +- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. +- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. +- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. + +### Cultural Considerations + +- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. +- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisine—with its fusion of flavors—can be as adventurous as the outdoor activities. + +### Health and Safety + +- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. +- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. +- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. + +--- + +## Conclusion + +The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. + +This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offerings—from epic surf breaks and challenging hiking trails to vibrant water sports—the Caribbean stands out as a multi-adventure destination where every day brings a new experience. + +Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. + +Happy travels! + +--- + +_References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning._ diff --git a/examples/research_bot/sample_outputs/vacation.txt b/examples/research_bot/sample_outputs/vacation.txt new file mode 100644 index 0000000..b264998 --- /dev/null +++ b/examples/research_bot/sample_outputs/vacation.txt @@ -0,0 +1,206 @@ +# Terminal output for a vacation related query. See vacation.md for final report. + +$ uv run python -m examples.research_bot.main +What would you like to research? Caribbean vacation spots in April, optimizing for surfing, hiking and water sports +View trace: https://platform.openai.com/traces/trace_.... +Starting research... +✅ Will perform 15 searches +✅ Searching... 15/15 completed +✅ Finishing report... +✅ Report summary + +This report provides an in-depth exploration of selected Caribbean vacation spots in April that are ideal for surfing, hiking, and water sports. Covering +destinations from Barbados and Puerto Rico to the Bahamas and Jamaica, it examines favorable weather conditions, recommended surf breaks, scenic hiking +trails, and various water sports activities. Detailed destination profiles, activity highlights, and travel tips are integrated to help travelers design a +multi-adventure itinerary in the Caribbean during April. + + +=====REPORT===== + + +Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration + +The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) +3. [Surfing in the Caribbean](#surfing-in-the-caribbean) + - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) + - 3.2 [Puerto Rico: Rincón and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) + - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) +4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) + - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) + - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) +5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) + - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) + - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) +6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) +7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) +8. [Conclusion](#conclusion) + +--- + +## Introduction + +Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. + +Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. + +--- + +## Why April is the Perfect Time in the Caribbean + +April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: + +- **Consistent Warm Temperatures:** Average daytime highs around 29°C (84°F) foster comfortable conditions for both land and water activities. +- **Pleasant Sea Temperatures:** With sea temperatures near 26°C (79°F), swimmers, surfers, and divers are treated to inviting waters. +- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. +- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. + +These factors create an ideal backdrop for balancing your outdoor pursuits, whether you’re catching epic waves, trekking rugged trails, or partaking in water sports. + +--- + +## Surfing in the Caribbean + +Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. + +### Barbados: The Tale of Two Coasts + +Barbados is a prime destination: + +- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. +- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. + +Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. + +### Puerto Rico: Rincón and Beyond + +Rincón in Puerto Rico is hailed as the Caribbean’s surfing capital: + +- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. +- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. + +In addition, Puerto Rico’s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. + +### Dominican Republic and Other Hotspots + +Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: + +- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. +- **Jamaica and The Bahamas:** Jamaica’s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamas’ Surfer’s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. + +These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. + +--- + +## Hiking Adventures Across the Caribbean + +The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. + +### Trekking Through Tropical Rainforests + +For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: + +- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquí frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. +- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. + +### Volcanic Peaks and Rugged Landscapes + +For those seeking more rugged challenges, several destinations offer unforgettable adventures: + +- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. +- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. +- **La Soufrière, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. + +Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelée, which combine historical context with natural beauty. + +--- + +## Diverse Water Sports Experiences + +While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. + +### Snorkeling, Diving, and Jet Skiing + +Caribbean waters teem with life and color, making them ideal for underwater exploration: + +- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. +- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. +- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. + +### Kiteboarding and Windsurfing + +Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: + +- **Aruba:** Known as "One Happy Island," Aruba’s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. +- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. + +Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. + +--- + +## Combining Adventures: Multi-Activity Destinations + +For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. + +- **Puerto Rico:** With its robust surf scene in Rincón, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. +- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. +- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaica’s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. + +Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. + +--- + +## Practical Advice and Travel Tips + +### Weather and Timing + +- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. +- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincón in Puerto Rico), many destinations maintain consistent conditions during this month. + +### Booking and Costs + +- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. +- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. +- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. + +### Cultural Considerations + +- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. +- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisine—with its fusion of flavors—can be as adventurous as the outdoor activities. + +### Health and Safety + +- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. +- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. +- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. + +--- + +## Conclusion + +The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. + +This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offerings—from epic surf breaks and challenging hiking trails to vibrant water sports—the Caribbean stands out as a multi-adventure destination where every day brings a new experience. + +Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. + +Happy travels! + +--- + +*References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning.* + + + +=====FOLLOW UP QUESTIONS===== + + +Follow up questions: Would you like detailed profiles for any of the highlighted destinations (e.g., Puerto Rico or Barbados)? +Are you interested in more information about booking details and local tour operators in specific islands? +Do you need guidance on combining cultural events with outdoor adventures during your Caribbean vacation? \ No newline at end of file diff --git a/examples/tools/computer_use.py b/examples/tools/computer_use.py new file mode 100644 index 0000000..35fc865 --- /dev/null +++ b/examples/tools/computer_use.py @@ -0,0 +1,165 @@ +import asyncio +import base64 +import logging +from typing import Literal, Union + +from playwright.async_api import Browser, Page, Playwright, async_playwright + +from agents import ( + Agent, + AsyncComputer, + Button, + ComputerTool, + Environment, + ModelSettings, + Runner, + trace, +) + +logging.getLogger("openai.agents").setLevel(logging.DEBUG) +logging.getLogger("openai.agents").addHandler(logging.StreamHandler()) + + +async def main(): + async with LocalPlaywrightComputer() as computer: + with trace("Computer use example"): + agent = Agent( + name="Browser user", + instructions="You are a helpful agent.", + tools=[ComputerTool(computer)], + # Use the computer using model, and set truncation to auto because its required + model="computer-use-preview-2025-02-04", + model_settings=ModelSettings(truncation="auto"), + ) + result = await Runner.run(agent, "Search for SF sports news and summarize.") + print(result.final_output) + + +CUA_KEY_TO_PLAYWRIGHT_KEY = { + "/": "Divide", + "\\": "Backslash", + "alt": "Alt", + "arrowdown": "ArrowDown", + "arrowleft": "ArrowLeft", + "arrowright": "ArrowRight", + "arrowup": "ArrowUp", + "backspace": "Backspace", + "capslock": "CapsLock", + "cmd": "Meta", + "ctrl": "Control", + "delete": "Delete", + "end": "End", + "enter": "Enter", + "esc": "Escape", + "home": "Home", + "insert": "Insert", + "option": "Alt", + "pagedown": "PageDown", + "pageup": "PageUp", + "shift": "Shift", + "space": " ", + "super": "Meta", + "tab": "Tab", + "win": "Meta", +} + + +class LocalPlaywrightComputer(AsyncComputer): + """A computer, implemented using a local Playwright browser.""" + + def __init__(self): + self._playwright: Union[Playwright, None] = None + self._browser: Union[Browser, None] = None + self._page: Union[Page, None] = None + + async def _get_browser_and_page(self) -> tuple[Browser, Page]: + width, height = self.dimensions + launch_args = [f"--window-size={width},{height}"] + browser = await self.playwright.chromium.launch(headless=False, args=launch_args) + page = await browser.new_page() + await page.set_viewport_size({"width": width, "height": height}) + await page.goto("https://www.bing.com") + return browser, page + + async def __aenter__(self): + # Start Playwright and call the subclass hook for getting browser/page + self._playwright = await async_playwright().start() + self._browser, self._page = await self._get_browser_and_page() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self._browser: + await self._browser.close() + if self._playwright: + await self._playwright.stop() + + @property + def playwright(self) -> Playwright: + assert self._playwright is not None + return self._playwright + + @property + def browser(self) -> Browser: + assert self._browser is not None + return self._browser + + @property + def page(self) -> Page: + assert self._page is not None + return self._page + + @property + def environment(self) -> Environment: + return "browser" + + @property + def dimensions(self) -> tuple[int, int]: + return (1024, 768) + + async def screenshot(self) -> str: + """Capture only the viewport (not full_page).""" + png_bytes = await self.page.screenshot(full_page=False) + return base64.b64encode(png_bytes).decode("utf-8") + + async def click(self, x: int, y: int, button: Button = "left") -> None: + playwright_button: Literal["left", "middle", "right"] = "left" + + # Playwright only supports left, middle, right buttons + if button in ("left", "right", "middle"): + playwright_button = button # type: ignore + + await self.page.mouse.click(x, y, button=playwright_button) + + async def double_click(self, x: int, y: int) -> None: + await self.page.mouse.dblclick(x, y) + + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + await self.page.mouse.move(x, y) + await self.page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})") + + async def type(self, text: str) -> None: + await self.page.keyboard.type(text) + + async def wait(self) -> None: + await asyncio.sleep(1) + + async def move(self, x: int, y: int) -> None: + await self.page.mouse.move(x, y) + + async def keypress(self, keys: list[str]) -> None: + for key in keys: + mapped_key = CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) + await self.page.keyboard.press(mapped_key) + + async def drag(self, path: list[tuple[int, int]]) -> None: + if not path: + return + await self.page.mouse.move(path[0][0], path[0][1]) + await self.page.mouse.down() + for px, py in path[1:]: + await self.page.mouse.move(px, py) + await self.page.mouse.up() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/tools/file_search.py b/examples/tools/file_search.py new file mode 100644 index 0000000..2a3d4cf --- /dev/null +++ b/examples/tools/file_search.py @@ -0,0 +1,36 @@ +import asyncio + +from agents import Agent, FileSearchTool, Runner, trace + + +async def main(): + agent = Agent( + name="File searcher", + instructions="You are a helpful agent.", + tools=[ + FileSearchTool( + max_num_results=3, + vector_store_ids=["vs_67bf88953f748191be42b462090e53e7"], + include_search_results=True, + ) + ], + ) + + with trace("File search example"): + result = await Runner.run( + agent, "Be concise, and tell me 1 sentence about Arrakis I might not know." + ) + print(result.final_output) + """ + Arrakis, the desert planet in Frank Herbert's "Dune," was inspired by the scarcity of water + as a metaphor for oil and other finite resources. + """ + + print("\n".join([str(out) for out in result.new_items])) + """ + {"id":"...", "queries":["Arrakis"], "results":[...]} + """ + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/tools/web_search.py b/examples/tools/web_search.py new file mode 100644 index 0000000..35eeb68 --- /dev/null +++ b/examples/tools/web_search.py @@ -0,0 +1,23 @@ +import asyncio + +from agents import Agent, Runner, WebSearchTool, trace + + +async def main(): + agent = Agent( + name="Web searcher", + instructions="You are a helpful agent.", + tools=[WebSearchTool(user_location={"type": "approximate", "city": "New York"})], + ) + + with trace("Web search example"): + result = await Runner.run( + agent, + "search the web for 'local sports news' and give me 1 interesting update in a sentence.", + ) + print(result.final_output) + # The New York Giants are reportedly pursuing quarterback Aaron Rodgers after his ... + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..398fb74 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,121 @@ +site_name: OpenAI Agents SDK +theme: + name: material + features: + # Allows copying code blocks + - content.code.copy + # Allows selecting code blocks + - content.code.select + # Shows the current path in the sidebar + - navigation.path + # Shows sections in the sidebar + - navigation.sections + # Shows sections expanded by default + - navigation.expand + # Enables annotations in code blocks + - content.code.annotate + palette: + primary: black + logo: assets/logo.svg + favicon: images/favicon-platform.svg +nav: + - Intro: index.md + - Quickstart: quickstart.md + - Documentation: + - agents.md + - running_agents.md + - results.md + - streaming.md + - tools.md + - handoffs.md + - tracing.md + - context.md + - guardrails.md + - multi_agent.md + - models.md + - config.md + - API Reference: + - Agents: + - ref/index.md + - ref/agent.md + - ref/run.md + - ref/tool.md + - ref/result.md + - ref/stream_events.md + - ref/handoffs.md + - ref/lifecycle.md + - ref/items.md + - ref/run_context.md + - ref/usage.md + - ref/exceptions.md + - ref/guardrail.md + - ref/model_settings.md + - ref/agent_output.md + - ref/function_schema.md + - ref/models/interface.md + - ref/models/openai_chatcompletions.md + - ref/models/openai_responses.md + - Tracing: + - ref/tracing/index.md + - ref/tracing/create.md + - ref/tracing/traces.md + - ref/tracing/spans.md + - ref/tracing/processor_interface.md + - ref/tracing/processors.md + - ref/tracing/scope.md + - ref/tracing/setup.md + - ref/tracing/span_data.md + - ref/tracing/util.md + - Extensions: + - ref/extensions/handoff_filters.md + - ref/extensions/handoff_prompt.md + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: ["src/agents"] + selection: + docstring_style: google + options: + # Shows links to other members in signatures + signature_crossrefs: true + # Orders members by source order, rather than alphabetical + members_order: source + # Puts the signature on a separate line from the member name + separate_signature: true + # Shows type annotations in signatures + show_signature_annotations: true + # Makes the font sizes nicer + heading_level: 3 + +extra: + # Remove material generation message in footer + generator: false + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - attr_list + - md_in_html + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + +validation: + omitted_files: warn + absolute_links: warn + unrecognized_links: warn + anchors: warn + +extra_css: + - stylesheets/extra.css + +watch: + - "src/agents" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..24e08eb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,119 @@ +[project] +name = "openai-agents" +version = "0.0.1" +description = "OpenAI Agents SDK" +readme = "README.md" +requires-python = ">=3.9" +license = "MIT" +authors = [ + { name = "OpenAI", email = "support@openai.com" }, +] +dependencies = [ + "openai>=1.66.0", + "pydantic>=2.10, <3", + "griffe>=1.5.6, <2", + "typing-extensions>=4.12.2, <5", + "requests>=2.0, <3", + "types-requests>=2.0, <3", +] +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License" +] + +[project.urls] +Homepage = "https://github.com/openai/openai-agents-python" +Repository = "https://github.com/openai/openai-agents-python" + +[dependency-groups] +dev = [ + "mypy", + "ruff==0.9.2", + "pytest", + "pytest-asyncio", + "pytest-mock>=3.14.0", + "rich", + "mkdocs>=1.6.0", + "mkdocs-material>=9.6.0", + "mkdocstrings[python]>=0.28.0", + "coverage>=7.6.12", + "playwright==1.50.0", +] +[tool.uv.workspace] +members = ["agents"] + +[tool.uv.sources] +agents = { workspace = true } + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/agents"] + + +[tool.ruff] +line-length = 100 +target-version = "py39" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] +isort = { combine-as-imports = true, known-first-party = ["agents"] } + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"examples/**/*.py" = ["E501"] + +[tool.mypy] +strict = true +disallow_incomplete_defs = false +disallow_untyped_defs = false +disallow_untyped_calls = false + +[tool.coverage.run] +source = [ + "tests", + "src/agents", +] + +[tool.coverage.report] +show_missing = true +sort = "-Cover" +exclude_also = [ + # This is only executed while typechecking + "if TYPE_CHECKING:", + "@abc.abstractmethod", + "raise NotImplementedError", + "logger.debug", +] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + # This is a warning that is expected to happen: we have an async filter that raises an exception + "ignore:coroutine 'test_async_input_filter_fails..invalid_input_filter' was never awaited:RuntimeWarning", +] +markers = [ + "allow_call_model_methods: mark test as allowing calls to real model implementations", +] \ No newline at end of file diff --git a/src/agents/__init__.py b/src/agents/__init__.py new file mode 100644 index 0000000..69c500a --- /dev/null +++ b/src/agents/__init__.py @@ -0,0 +1,223 @@ +import logging +import sys +from typing import Literal + +from openai import AsyncOpenAI + +from . import _config +from .agent import Agent +from .agent_output import AgentOutputSchema +from .computer import AsyncComputer, Button, Computer, Environment +from .exceptions import ( + AgentsException, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + OutputGuardrailTripwireTriggered, + UserError, +) +from .guardrail import ( + GuardrailFunctionOutput, + InputGuardrail, + InputGuardrailResult, + OutputGuardrail, + OutputGuardrailResult, + input_guardrail, + output_guardrail, +) +from .handoffs import Handoff, HandoffInputData, HandoffInputFilter, handoff +from .items import ( + HandoffCallItem, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + ModelResponse, + ReasoningItem, + RunItem, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, +) +from .lifecycle import AgentHooks, RunHooks +from .model_settings import ModelSettings +from .models.interface import Model, ModelProvider, ModelTracing +from .models.openai_chatcompletions import OpenAIChatCompletionsModel +from .models.openai_provider import OpenAIProvider +from .models.openai_responses import OpenAIResponsesModel +from .result import RunResult, RunResultStreaming +from .run import RunConfig, Runner +from .run_context import RunContextWrapper, TContext +from .stream_events import ( + AgentUpdatedStreamEvent, + RawResponsesStreamEvent, + RunItemStreamEvent, + StreamEvent, +) +from .tool import ( + ComputerTool, + FileSearchTool, + FunctionTool, + Tool, + WebSearchTool, + default_tool_error_function, + function_tool, +) +from .tracing import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + Span, + SpanData, + SpanError, + Trace, + add_trace_processor, + agent_span, + custom_span, + function_span, + gen_span_id, + gen_trace_id, + generation_span, + get_current_span, + get_current_trace, + guardrail_span, + handoff_span, + set_trace_processors, + set_tracing_disabled, + set_tracing_export_api_key, + trace, +) +from .usage import Usage + + +def set_default_openai_key(key: str) -> None: + """Set the default OpenAI API key to use for LLM requests and tracing. This is only necessary if + the OPENAI_API_KEY environment variable is not already set. + + If provided, this key will be used instead of the OPENAI_API_KEY environment variable. + """ + _config.set_default_openai_key(key) + + +def set_default_openai_client(client: AsyncOpenAI, use_for_tracing: bool = True) -> None: + """Set the default OpenAI client to use for LLM requests and/or tracing. If provided, this + client will be used instead of the default OpenAI client. + + Args: + client: The OpenAI client to use. + use_for_tracing: Whether to use the API key from this client for uploading traces. If False, + you'll either need to set the OPENAI_API_KEY environment variable or call + set_tracing_export_api_key() with the API key you want to use for tracing. + """ + _config.set_default_openai_client(client, use_for_tracing) + + +def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: + """Set the default API to use for OpenAI LLM requests. By default, we will use the responses API + but you can set this to use the chat completions API instead. + """ + _config.set_default_openai_api(api) + + +def enable_verbose_stdout_logging(): + """Enables verbose logging to stdout. This is useful for debugging.""" + for name in ["openai.agents", "openai.agents.tracing"]: + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +__all__ = [ + "Agent", + "Runner", + "Model", + "ModelProvider", + "ModelTracing", + "ModelSettings", + "OpenAIChatCompletionsModel", + "OpenAIProvider", + "OpenAIResponsesModel", + "AgentOutputSchema", + "Computer", + "AsyncComputer", + "Environment", + "Button", + "AgentsException", + "InputGuardrailTripwireTriggered", + "OutputGuardrailTripwireTriggered", + "MaxTurnsExceeded", + "ModelBehaviorError", + "UserError", + "InputGuardrail", + "InputGuardrailResult", + "OutputGuardrail", + "OutputGuardrailResult", + "GuardrailFunctionOutput", + "input_guardrail", + "output_guardrail", + "handoff", + "Handoff", + "HandoffInputData", + "HandoffInputFilter", + "TResponseInputItem", + "MessageOutputItem", + "ModelResponse", + "RunItem", + "HandoffCallItem", + "HandoffOutputItem", + "ToolCallItem", + "ToolCallOutputItem", + "ReasoningItem", + "ModelResponse", + "ItemHelpers", + "RunHooks", + "AgentHooks", + "RunContextWrapper", + "TContext", + "RunResult", + "RunResultStreaming", + "RunConfig", + "RawResponsesStreamEvent", + "RunItemStreamEvent", + "AgentUpdatedStreamEvent", + "StreamEvent", + "FunctionTool", + "ComputerTool", + "FileSearchTool", + "Tool", + "WebSearchTool", + "function_tool", + "Usage", + "add_trace_processor", + "agent_span", + "custom_span", + "function_span", + "generation_span", + "get_current_span", + "get_current_trace", + "guardrail_span", + "handoff_span", + "set_trace_processors", + "set_tracing_disabled", + "trace", + "Trace", + "SpanError", + "Span", + "SpanData", + "AgentSpanData", + "CustomSpanData", + "FunctionSpanData", + "GenerationSpanData", + "GuardrailSpanData", + "HandoffSpanData", + "set_default_openai_key", + "set_default_openai_client", + "set_default_openai_api", + "set_tracing_export_api_key", + "enable_verbose_stdout_logging", + "gen_trace_id", + "gen_span_id", + "default_tool_error_function", +] diff --git a/src/agents/__pycache__/__init__.cpython-311.pyc b/src/agents/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daf05e7ea70ebab1d91fa405ff92bd37f7227058 GIT binary patch literal 6280 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/__init__.cpython-313.pyc b/src/agents/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d9218c780acbb12c377e8a3f26be0b4c1b53b65 GIT binary patch literal 5761 zcmb_gO>7&-6`mD;MTz>eY+3T(TK;8hQ?X+^iR{>xWLefwl5J6n|CFp(6q6v&}Kfcn@T3l!+JmjXQ|c2gIV8a@Omkeg#C(6#T)?vfOnU!V)=+c$6C z{JrP;xw%pUQ3LgD}X4V2JO49egM35O5d3~zz@Pfeh3cn!*G~qAj4mQSNIV)!jHmHehiNBEM)lzj41nb?p1yq zj`I_6LY2F6ukn*`Qpw%9Q~WfX=C8x+{0(?R>3VW+@=+L7a&PVoKMQA-+?RWcpM!Hs z-jX}dFTe#p24kw+pBv{BFu^avMOEIKo8*_^5}$%8Ro<4n%%@?RbKpD&IX(k3NE}n zaO{#*Mlm*47Pf~%uM)1*ylTzM7gofonTg7@YCPerqEdj2kJmiUv1PKZ>6oLf0^RVVJ`gMz4f7FzC^wz26{&4ePDR%HRK>U|3YzRP8FTv7JSnhzFgV5>Hwh?>4x zrzk4pV#Qpu9GC=fKmu8(yl}1)7N8UUqGsj_luDo)>Pd;>qro z(d5<^MQCTy;*4^*I@#kW_cb)=!o+3ME;^->>&3UFobtZ-ekESl=m%9%Lds$;uCrssv}I<{q(RqMoj z%~nF^g5y-`fF26QxjhcR66V5EKY?JLcl^{la zbusUOFjr|H-(W(efQ+MoEdZ6hD526554`JOR?($0xezEYkOOM1i`Am(iK5@xr-g1ps~qO&AN`J0y4ET%p`KT>_QTVyfC4#qRO2ieN`!{FnwJtH<|1xijs+> zH9YLV5R@%ODImQ}hmxwz3}QZ0w&$y+tq9mPQFA?KwO;NC$E$1lrUW|2q&U=dz4h#> zt3s;Q6;YC8vR-Z{$r z3d1VOPAd34VW}HS!HoT;J!Rn;g&IJ;=iX+GYACzvN$fZ34ARgq^}E`2UQ0=!>n4yJ+Urhst+V?Y?1O*Mo1Y4;o$~YpuIVKA}G-R%U2OT zVz)y*o5x#5>od$2*{Ptl&hQ*)d;bNdp6;y(Jz$6KCyG9Hby~+Ubp2ajsd3)pm-GLh&D8%`&Ol*FAJJhQMYW@!xp+z zQj7QrLuFHQnKp={YveWxK$sN{o_nTpenx{Otaiy!x*Q#$^THj0)sX@oHzU5?F-1T3 zSasc7aqJNn3M0N|#4s$|@(d$eT?YeuP(Xs)hwuUWrjx}wpG1crMTZ~0_F44MOVlzM zFH|hrA-Zu@20B}D2z^^A78i}`*H;Ac45#OFl{gpDw*=0r zPn}?bMC4kv;+RGBu3C_AvpTNll38)ZQ8fTfW!D1@BsSDZZXD0<*w>9goLFtJWXa5KWguBU*9EM5car>ty;;FjS2U$>%2h~{l5tBS=PH07Gyg+~gm$_N%^_)UypcvwUC`~v^oX@n2h7d@jNM*rQr z{Ymfequ$|%Cm;77{xJTpWY=%Hf8G6Y;`iy_rT>vUh~+2A!B3NeA0K)$H1cR@LB_rHDy9U`?jYknyMgWmlKJrcKgHdqpf}>J!cRfRtO-A3{aAk&3F`5e$>4tMwvvE|A!8PP^|(#J z*HPO<4aES(j|MA3%}Q(gkKRY z`5<=%2EIoPItQAL0hK9o+h&{KS_&$Iw+>~t3_4(YOH!)jA=~J$W+KTB7eqTTvMRJ)4cE2AXW`n?r&x+aK zl^TkoimX2&Nff;)Q)H}0-!dxWWCT~I5 z(Y1;=2fxK4owRNo@p(kkw9i@B=PdP<4SmUWf62B!W!s;!BhT3QV>Z6gmeATZ7(q{x zB!sUCHu{)0t375j8*Lq=ufukx9ev6&&)9`e*@dTU@+mv7#pRto0Z85Fw zB}3TgZ0T@_`uQICmMXqI&9uSi@wj&QVdOc(*B_$a(Ch2Ily?5%z2^*Hf4!()pDf(? qmWt1ZqG+A?g3x8kuhF&kaV%l3TdTwhSZNEw=Km8ZXcw7nq literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/__init__.cpython-39.pyc b/src/agents/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7f3ec7de326a9f97beab24635cb0ffaefefc8ed GIT binary patch literal 5232 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 TynL}HPu2^~+Dv1nQmy_Q?1TGZ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/_config.cpython-311.pyc b/src/agents/__pycache__/_config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fd0d6a77c98e6b40fcfe342f6d72e62d5bc0802 GIT binary patch literal 1508 zcmZ`&&1(}u6ragXcDHHM)~`ZEO0_N1}i+_Sg(aT2mP!PI2xQ6&AR)v!Rqr* zU}-E?CF5o95GUbb$A?h4>WG(R&L|)*3nm@O%Wk#4>af=hpG&9Qs5sq!eR zls7z?)(%x!n41t5Dof$-g8f`DE^J=2U7s?$yvpiQ&~>N5ed)_)BX!x52`TJi(H7k8 zz--6$>y_$ivC-s1Dz8%U&u@USg$~HXJJBMOA(;%w+0L}pc*WUO^b?N1~h1e`@;DkGzF zqY0SVFg}^8rm}8-N=kvpsITM;QK`Or!_k)%>9h%cYDlwN< zKGKud%vCQ$=b+J(M=V)gC{j65e!T{7!V}3+jLuU}`a%4M*o=&PEf@gM&;O0VDc-%&?P&k;*RPkB=0ajCC;Q#;t literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/_config.cpython-313.pyc b/src/agents/__pycache__/_config.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0386911c50e020e2ebac38445ee894f69b1f16f GIT binary patch literal 1335 zcmah|O-ma=7@o~ecGrBU{jybxO(Uf)s0&pKh9U^own?$zYFjX{tm~LW<8GMQKy&j? z2tB5!>a9PaM~~JTEKBUAEyY_P?Y-~pCNV96zOe82%)I-|GtGEBfLf?4M z7OCYlU2yi1j&vb|(gGC{+z(|!X_1QHi#pEWbeM*HKb(=$GL^xXvWhP2$_&=~M$k5< zeR?D-=+SJ%&q{jC&qnolR@D2$a7L?pz|Y5WiaL0~M^FVXy1**7^`b=Vg=Y}T&kHW0 zX7NG@%Ke4|C9`0#yh+I>bc`_K8ZI@hf}JzSo03Ca!z>ky9a8Z`t|Us_GPRd2zC~L^ zaQN3;0=19UJGC`5+wv1=9dHU{1-HFRn?&8cIdmv2t0I-5fpR>(AwJk9*)q#Tw>>q? zqY6}kGUKNtyg6-+5y~{Wt69z_(SpC5G;_pu*<`8W<{evO)Y5{CW>}8BRme@1Dm2DV z;>zFd7C3vTjz{0JQ#@A3SI@9|g4Khu8lGxG4SAAPEWobbK$#I^%b{)1tj=FTR|l6o z1p8=B80Tw2%p2%0lvcV|1ufmkb?{ngbF07Hi6!51ktKq=2`S)5p8wR!YHRbcxM4hLqF6lXijO zMHWmgx1MZp=^}Hf&t(s9I8HIRLq58gr(fzXY>$` z{WqNV$^rE+aAL+zLWRmoGoF`s=9_Qh(WuYxyj=cR{&E=mNzVP_qVontU!jsr@{Hwt z&N}$ z3B%YJtB&LZV?*g(uwZ(J{aC>5;9S>5^089I`FnKz52;ZQXLr0!kE0S(aT@7W3`%0f zQ>{#7AWqU^8L2N-2_}lGG`d#xEO4yAuk}EgH53V5vOi=Cl(#7Q7?oig_7#yDzI8V2 zdxQCc-SSx=z{5OrP}yT@C)FZeXJ$VOwg@=*n7(B&z|Zd&;b*NthmeO!DOK1GeHJfO zVf0y5n^jqa8j`S$gi%r!i*)(Cs)6|FqG+Om@h)#7fv{eZrTDu&T5A<8O4x%2s!ov1 z!y)?s?G&92hcUss;tfD&EB*q69u@>{1JU}s&9zuh9>CfT5jZfy-(XCN5VvYb)&vzL zH7$*Cm-l%SLoz1GDmI8UuQFxQve32%N>^woOQq#7rr(jB%gAL1P72P5mX z90M)iPEB34@6o726H*{Tte4|!$aK42fA?zJ)eCK)q1w8aWtlZAYwo@`R0~|) zKY}vs<6eF9(T|bRhsxDApM2}6AHaLluENj*_uO-TIsgAXA(><{DPXbfM9vEV;3q#^ zgfuHAk63XECYWds2$(_>nPOAeluYRsypf356j#AaY@umCk#0jwz20a!*b9S=?s53a_tb!N)G1Ld8@GGTz3ws1 zPD?BHWTLi>ckIN^!-_ncyT9jTHr4P>DeK7}Xh0$8$ z>+Yzi4T{>Zs7KYtpxB6{g9+0DT``_k^wJZnv{TV*rd2ldnxz>#*1l1$XjZvYE7AOa zT55(d?NDTr+-3j&hPlaNy)WpB8sMX(T~_?ADJ C=dBe0 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/_debug.cpython-39.pyc b/src/agents/__pycache__/_debug.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..796f509936bf48b9b09380ba22af902f61c6904a GIT binary patch literal 518 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 D<$kG> literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/_utils.cpython-313.pyc b/src/agents/__pycache__/_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af2c299cb8c2131928b6be38617513231cbcd43c GIT binary patch literal 2495 zcmZ`*O>7&-6`t82a`{i%q%G5qvvzF9Oe@i;BPVv$qH3K2Arl7inn4iCU989v&0w`l zyj|HA8?71hsVku1uUc1Y8fbD% ztLwEG#AW9Jkkz23`U10d2&^{96t@7U#zPzSsQ@bAQIJI@fwW zjJ0B}-|aHMZ8WRzcswDboaFf~3+?s_0P{jS5R>$BdqjdbHxV~SH_@GI2yT-mxwLmy zXkfFbrOYZ3_DE}l2)U$1V|yjitXfKw%#NJz37he~8WEly4V@qrt--0usXa)ye(6jP z=BM8|GkyL_MdNW`eb;F-t^ng|u(HT?*T0T8rCjrzWfq9Dn*60|Ghh%@VY%8yeyV<- zc}{QI>Efk>>HcbX)%U6aw5!ob)m{mEZZOkdET78bK%1a zf0$d>4({M8(jGA(FiGGi`M!q$`BqMgQEU#fYSA~4X-_>$n)JK;@V08H=M_(DYA49C zJ${H2XPOXCZwYV8)|xPGX=jL~pI64AZ<3ZcgKSo;NwSzmO^VWJ>REC&Hr8wE43pE- z=k+DJM1HJ3O_pdS(HP{BvYv}nO-GCczd&!4*^NGf-ZB#IxVGb>m+Za+Vb5^~rB}Uc zBCySx^+v<&gMY2pVI2(qI65#umv9Y(-Wim?rFZ=*^Gq}m;7;a1H_=DtKzZ4$=sY9M zIU%#(4SWy8ZTJx<44w8>n~B7+L;qVb4>?qUG>S@$8)A|0Hcy9`T(&dHzWC_8JU0S% zz`_uB=ZW!t9Zp$*HIA6FL0OnRHWp7I2!4fPNWMOLa>FRE&2FniFK)-udSSC@{;fE( zQJh)3v0j{6FLpjiZ04rcjj4ZcrOG=bp%=D{6IH3bU79Qy961UPe>v?euaTs1t}q?61+&$cEx z_I7?;sl>TklL~c7IPQstwTj9NP|m7*Z(C6tgHppc#lg+cH=Rz$l=n0TD)uWH^h+;1%a9oTE@q(E70g55nN*%tRdTMy_@A1@@apcbB2S(*f zBYQLZQFi^%%-YIY_nx(W@caYgLc|ZQnKVK7`=V-#pnis!$WfQPs4h`>4hKFaAj*|o z7P%zr`F`JS``}~g^BA1P={fuY1I6RW9sdagSHDvuXgm+IZ4fSp17dxy?Yl0Mbvu}G7TX|h!ef}<=o65SyYN1UqR5T@ zYRB=yUK_;aqw-ORRBd)6WL_X%a1j4$c#_>{v%Y-NW&Id=8%tnYSQeoi6&D!~;<7yZ zy0f~-CU3udL82ldG9U^BLI0C3GUk^3&Wg)^0zbuB5%9q~D7IBf=|gh-3zB$9PJT{K zZIDynkmnJ9MV@;|%Ab>S8|2(SwA2SbfB&^1eN;GbYxQp7Pw(A*^VV-R3eOI+yRkT( Z+AZd3VfPfJB`kL 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/agent.cpython-39.pyc b/src/agents/__pycache__/agent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b494ffe442d8cafbfda9f9069fc5cf2e36eeb900 GIT binary patch literal 4877 zcmaJ_TW{RP6(+gl-n1*NR`T7k#)@y-%Bv=Alfrd^s!nPPfz`rxkd}r)Q=HiqWnQ*J zZsc9aKmi+wU(%O86-@ve=u7@V-}?vp%+~@1@>rlRE!v{r8SaI$({Qcf;atv~IhXI8 zvD<9cH9Xh)f9e1Jq^A9qK8jBnA3xDFJ!$Eh#x<@7TBzH)PP7piwxQl7yM%WsFhk3> z)R-9{?j2>*0dE5H{>a*tDA%EAvXQ7%tgMimnDrVavv5=&uFK zVcTwpEB1=|t_Q2(G5eUJ7lPyA3HyYi8^Ou&q1Zmf_*`aw}Xq}CHoS_$~QH>!dIVZd{s2W z+9~a6>8WmC=EhyEbL?xh>%jF>cQ+6nBdc*Yb-ln%lFaJ) zBH$e>Yu@|SFFtf`-1_jwN839$v(i>H%BnZqAfPo_<)(;)^u5gV+$8-Ps;5oL&M@_Z z1k~bfceE>tdn|58gJF7e=t?eKKggDTK1_=pSX8;?Mm+BIKrU>jBD^Jnfsn+m9K`*; zkf55kV=jWbB2E3MPeYdKz#*3J45J%yl#0jcC(<2YWmdg6>AvoOT(X*gN>%0NFsYVynXoD}JXQ?p1$y?}IF7zz&HhNYHJxj2< z$&V?XEq)yI2}LjSlb}z5KF!bYvtJne4Sw#KX}9?mUcqmTGk*RHldtnPpIP<_zkt4r zn01L?#;jFt!h1TIrPSm89R}Wtm`l21e!|>{ZEdrVOu@W3>iPYl6r3IS=^pc=B$Y#@ z5GEb@K0!4CF( z51S<{-4kp*86|KSHjr^RNY_~}ma1O~+-2L5+Q8-9C);nZQ9NWG4nfS>LoO0e`U4s& zGIrVCFm$7ZPD!an1vpC2wZwXdt@m9?m)KArr7G;V=(i7Vqv0FU?N{X;aMJCOavS=!_5{jn(#S z8UO`D95s(fy{=lAm0*s{jNDM{jBU{DkWB&L{{7dY?fx{_9&9jmrg;Z?j^e?6aC`6o z{C46jm(gD@E6NZ+%A`9KoOU&Wu5k8BV`2OL-#6DpCvr*P~QuCL-v`4vzelCcAk3 z>)p+d6J()H8E$$2yv=wZBGrm)S^YW1xkte{QV`epSK8HdwYbml27}*JLn51#&Ogse8Ays_H?_jY< z_MAQz2>L6LeSZWZgg1&$%}Ny!8R$=vs7DgJT4 z*AtR5O%HHBPZvH=@gQag?kJi2$XkT&vpL8&@HzO(Ls05guGlvQGg1jQ?eJ6)Rk1C=b$a$!36n3npMt9 zau~^XryX(`l)Xw)I};5}04&LBg!(=TyQE{P)j1zpo0W5Wd}YY*6RWG#5MIko8m(cE zeCyxfQ>8G?Y8a+&;Aj4)VVU}(G0y9huA8XaK=*nOBfgUNyR*gE7I#ifHqh8>fYTi1 z9Fz}qwrMSe?@=J+8<^Fxa#7x*Wp7iX#Jf(E67R=EDH$u1efQCm&<@&hLtoU(|256i z&2e*!Ky)hG_zYBg$a<<|pUM%53ek7(wl=S}$vP2D4Jeh^4i)F{i z?fE|SD+A7*Y`HH|CqIUh-#idj6K69TdRedQ4gIXKig()>pU4gR+M15O(>az^9hKD` zC#yRSs?T8{KsOxc*F!f@FyLer$KkPu?j^@5T<0ApKkOQbNWq<%RB6id^u12a3N>$1 zbAg(R)Lf$GGBsDI`5razQS$*c^OMewE6-ry6#f$8SvSmjwQl^g-fCFwa--5V+oiVE zwi=~|*=lJ2KHgFDJ6GsDy&)=0oJW!)%I{LMg(h3_Vx+VDs@QdRy)1vtvYxk^YmSxXiCN!JH_ zO57@hT&1Iz_w(=3y+pDM)^EM&F7?+I_@A@)fzR7%tT9mvzPmelo4dd#ZVbNjwe f6?a0~Zx~g*3SVi#TN>(3ZT@TNEx;D&lA-?(vpatR literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/agent_output.cpython-313.pyc b/src/agents/__pycache__/agent_output.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b527f54e664bc8dc6e6dfe2d3835edaf9883387d GIT binary patch literal 6002 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/agent_output.cpython-39.pyc b/src/agents/__pycache__/agent_output.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfc5353bbf85c98ca529d5f30680754d4c3fbce3 GIT binary patch literal 4019 zcmZ`+&u<*J6&}va?#}+yN|t5IaqOuR6j?)&a%s^Dsu9IM+O&>rSc=_l5llu)N*ZN$ zW`J|-h7DrJyNn(mOTJDzTHf`u$S3)Gv3-rthbf ze$Wm2)o!(4>((GE@p4-4H@XeeuB6R=tJ~^Nb*D@}NZbAC?zCxF)0zIM?kTiu-C4nS zoj0DaUpu_XTaO*y+IRO^_q6cD9G`mX@;0A-?03(Iv%;Hq#JrKs@KcbT1RZ`paq- z=9{~sr$fCH>#zs=!$t}{5&m}l)>>#S6y{q-l$*&5VXVRjB2DRiKTZ?4Hp#Y^2C-Cl zn~Aj$&p_sU*b_Y5+%wZ}-uxm=GM!K83N;+PXhs)xt3D!8(KN3pwvZ_5QKF&;G9CaH zMJqZOX?Iu5ob>5N$CWeij+{f)^H3c=6jM~LqNwMn#%%0F&e%oG#*DL}qt1_+c888! z;Ux96KW6(hsx)?XOV7(M9JDK(tvfoPFAeH@_~uEitK8GIF-zFK`-oF%g~W$+v(or%b^s#yx$iYKej8gkkAATD1{n0enSq0fls2L zwI>A#z@jZpz7c7%ljoc^-peJFYKy@n17eR3uy&)U-7-KVlIS#!{9Jh0lmwAFl0sCT zLRHi!B3+&)$tA*)8g3dkit?>!6T67$XesC=;-Cjvrh(JZy*sP7Z(qL?UHkm%M)bSu ze~?YcJAUCSk!~F=EYCpvy(8bp>*bXtq8+s)`%69OT*?O`i<6IIiaz!6U{CMl*^-jI zB`dM714J(l_KF~ih;^dqBWP1pIh8UiyKPoxHP&Fh-*C&!Wv=VLa)0#8HP&(=hir82 zM2IgQDg&97r9gCNJ>t&8huSWRi3YM9um>U=I)fQ%X|zBd?&w#O@UVEZD1%=IX$*vD z@lb~!-N(s?DvYh?EVpzP*AAocELQlQ$@6#_W7+BgxJY-@z}KKm2Gc-J*Y`hX(kog`<8Y4l5rw6~mfAyj#o<;OI@81el$ zN#o5_bUb+p%gY8;ggf~Yst6)SrPw-p-q4DfL(9q6v@EaX}?7r|GS-$!^F_bRtyXyD&Ue;h7?t;Mr;;rwTXKuo{ z|HOU4m}8jjK+a2J`s$NG!FJw#CjUAyzpQKDIlS`BJ2u-&!I+0Lv2tx5_!@g~NOknL zyY?;-hwveq0(r#Sz?Y&c3Z!NN-#RC}WAJSGoBpGn}LF1JEQA}a7K;KGb1v-E-GAT_*jijUw=3NNny!2B+^(2w{smL7pe4xQi$%t zr=(|854W%vQokIIPF>B1DF<+LxRqo){6_4Bqkt%u28?Qlt>v&&my{hA&BLRaoW+ou z;kcrf;JQ^h&U&;G8`2kSy}5q)_Uc8K=oUq_;4k2 zuy;o>SwRyqA$|cAHabs{o5lS%?l_AI*uPNFkhfOIaM z48k{uuXWf)hu!G3iLz;a6cx3IE|OtN?M4)RJB-tVFO?|bc`u6O0#=b8s$wdN;tWVY z`h-c3Vp_zaavIeXiX!T(xwtF5h8L9D zD03gS{JHY%1v3U#V%n0%i~&_>Db8mWwGnREx~&^~*_G!mOF_mQhX1B0KA zkv#1iPF=gvDa#I~*+UZjNXoU$X$Y@}w=T%>=a3g=o6Z-_I8E~hf*W6`iIbT_ox{*Q z6h%mIa0voXZs1CiAA`-Y+-?J3Y5Qiw;61!uRhJ0ZE;XLlXueCwL2 z449FN7zF#fi=Px8er(=2ZBbo8rXU^;1uLum9+Tl4O9Q3~m8oTW|-1JsxK|=~)CAXiwV#5iWTAjS=Wgq9x6SY3qe! zIj<%j_Qb%1h2<)`MT;g~2>5riX}`(Eii(zTj}|3%Gn8iVEq_K`0*Hm8i6lDLgtcEs hO%bfvpuIw|plECZ300fTx((Jw%GGd*!rDo@@qdpr`z-(f literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/call_agent_tool.cpython-313.pyc b/src/agents/__pycache__/call_agent_tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..281eefcbfdcf989b9fb187619e934eecc2aed079 GIT binary patch literal 3070 zcmaJ@&2JmW6`$qq@?%AcQY1^V9Lr-_vdY?_kVZxjRdGSYHexasP_2|Ake%&nxfEAf z?y57pViojYpg@pw3m0uJxdcgj^Iwo4Ko2cYAdY>Ix`2=XsSmzURS-8l^}X38L(5Ip z*6_{Dd-J}2@68+)3L1fL{ICDm{9TriuW-`+l$el?{s_b!q7z-Jk*dTbigc=$s>)0j zv|LlFDpP@0bhVbQW>`kh=~}j$V>v-*YFc%G4G210%U27mAn06euv%nAL2I?4>M$Fw zKEs|7^MTq(b(D=(Pp}h|Ops}!=O>9?xRi=5V`H0xrQ#!;l%#0D^gKT>1IPC`OmmgC z=>+D6Yr`b_is`yYKayhRv=Ii5i+pNj)AjDsAe|kn&CdrnvC0$BRV`*JwnN7KrjSA?ca0j(NDguZ3Avj#2 zsGbH*GoTi*aB6zgM7PUywdugrb^SJ{yM73qf~Chn+fLA=uf2AI0>917z^+rr+YS>^ z4W#Q%qhT{RjW(FyqD|AQ`;7(%HX5{j+BSFjqV{~5UiW%?7r_{tdUiWDgEHH)ogJHt zWy|+=AO|9o6wC{JwkxCqv=v=BCazKX-3iCd)PC}VngjDKQ0s%jr*!|IP70=AEbTgK zHz9_JW#Ii&CkbW%jpKz7zKvbM;4^2_@k}?#gtly{NlNhtX_*k|=np`=DZQ!OA?t`; z@vWs^mVT7bbxH^&>q$uvvXUSx*%m+3WK~*8_pikCrR2mlx$itU35rkBQ!k}-WldUB z7E-zln9FV{UCepvnmR*Ds`(Kd@HDzGx0{g8F32>k!ZrZ|5UAMMMSz{1q&@*$S?pqR z4bN=ZF$YrAZ-{W=7G$JBp~&er^LL!OU5|kvpghN^U~fCFOE+v9a=YGlXx-)(bGETG zrM5PGXt4@v+M1kLXehWCNEoH_^403gQbfpMjuH(3R+$pD-G z3q)OdNxlxZM-!mEBi)gg)jaW1t4bHl<`5yP$~0L^b)k8jdxos{fV=`X*6|NzSHzw6 zZFUk$6lv*yy(I5{@2F1B)h>%CBXmC*m8WHySQko6GVUf&2k4*x_ zayT8uJtK*Il4~C2ps5)S+$6lD#>^*?IUOfAQezF{--8g2We=dbeLMQu{2GTYIL})1 zmS4B$g&?oLWdHyIKiJ*2=Q*>I<#}vHvGwArDR1vaIWRYI=)w~hLmAt2yi;CseGuiB z%RS}`U;)1g!+#!;+vIDFJUjjV;(LqxXJ+=tPVG;edB6Cl;$cR4cJMJ##s=TL_Kik{ z=Kgtl;qL1XPA~0^FWnnj`q#+R-pC90M_#ylZExYq=L=UJj9k5!zj`Rk2ieQGJp@W*{wC0@49xRAoxq^NVZ(g(10k{>o?JmAp7cpQ9OAX29AKE zzohV=odq(|z6)S9@qMA4K*_5xd<#)LPqgv<$#Z*?Z+x(`H+SRy+>O84pKbkp>%rU` z`)8luJNx3@l|xn5w8z<8?wWLz1F|E?&KMawc`rM01P777z%ysLqzT*KE>`dYU+=y> zZm|+@{8t0l>2K(=4|nyCVS&x#Kw)_MB3g=Xp!Icl!^BUUTNi2XnY0d~w{CTXXHjkt z26$Iw4bhVgBhn0`<<~>k20CvTzY0xPbQB{h8b-rmJa8S)_I%jR7)ITmAUqkED7zHr;4-@mB5(r}j#2B#k|Z6F p>4#+YfSh_rMi0ou0hu}=rw_>Z7i8fJ^4#O}1?l9!2@JyT{{f*7L`VPt literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/call_agent_tool.cpython-39.pyc b/src/agents/__pycache__/call_agent_tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3502ff97892fd12223d71e5f97ae4d2af0c7fdb8 GIT binary patch literal 2546 zcmaJ@&2QX96!+Nc{oZ`E=?CS|fdgo1v=JN-s;WX%30kS-&;}tniY&)7n{_(2mzi;w zjkc#$YA(H$YY*+Q{{$zF`~`F6fch_hK)g5hE@_(9Q6}$=-@K3C`^|HrUa#%IbLqF= z_n$Q#=MPj)J{~CFI1W+IKwu6d!bwR$2+FQ-Q!ntW?1@_H2R_I(=8JmT2pU$di)Pvi zT2^j|cG?L#R&I)J+6#JCZi%IIIas!GTdbt3!D@OgIA`^aIG?TsYw3mH0&%``SeNx4 zJFIu)1{c}VeW$>gKzz;|^}GA{eBSq14N}9?`pZ zzo%f&Wu!TyNj5I3qiqTgcwotJ$R&8BLz$;^6lE+Q4i%VEJ;~`LnyTy8_VWR~lP%t_ zLsFb(d{U-EC69UXkSn_#=h;Jm+%}FvnmU(Lds<`8SkkHEHUQ+a@Nzn3Yw*Q;Gtkik z2%kgXw{m!IYy<(Z1LNmc-)dsAzW%Va;vB2jh6y4pHiw(Z=R zvuXFRt+b>wx|L`AaDC3;ot*_Sch~8H(ZE1JA;kDUYW zC7F5Si;OV$sXOQIbMJ}sgbZEe@#F3hdF0Q0U~n_~^GwPjQTBlwbLO_^IMYqJUy%#R zh>xxAI&hm;ysB6|Se!wtMe(I2!a(z(?E}7ob`L2G4my_k!-R2Gvd#MbR8ea_Nra$# zoED0+Gn}7*vy5?K<7B9HIvFMLh^kRu2)th`iQ%TfF5K8eQvWUOrw`N$0jkcHm#*w~d5aNpTkaf+x8lDEjfj+{DOU;(o$>%<?><<5OadoOA;_rQNifQ#-{-wT$MjwD>u z{m^0|uYf=K0WAHx#4n`rWRckoiF4bmVQOIoLv?$UX~B>S6Znv*zE>KfA5$;!e&xy) zP~~}8UO9h#z43#B$+scXjW}m~!v<>-oUX_=4QVpgYnd~Af6T*c)hSWNr>1TKTFy0AFs zU9v>#|JG_(I)|6vm2aPHL9k|M2U!@Jb{M8PD+C9*8-_m>k+2L4O(P6h9>aDg2Ck}< z>ru|+ClIL2hAt#bNiM^JzY6=>B(~-=gfJ4xXO`m}=D+>Ot3k9E zOQfNxL%RSYSp!90zy(1pVQHX&Z&^`t2SzLYVhox-Y5aN36s@PcwM?~m)Ktdf8l3^ta$$cio)1G literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/computer.cpython-313.pyc b/src/agents/__pycache__/computer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b10842587fa05f95b2a6284e9ec134b020fb4d2d GIT binary patch literal 5841 zcmeHLPi)&%7`J2RPm?BX*Z&n_W<_HKtsT&Twv|EGbsG#em^Y+BkmWjVYL?jH*;&_} zDi9N#7zadm%Wmnd#~pX%xJpP&4Vms!g9{`mX7-}^p4 zzxQ)$>2y+n>u~-~{^u@1_#GSm6|Gllt5EqtPz6;yEf6dtr^WXoB%%n}h$^8Qk)S5C znmi)x5!C3IpvJPoIW~HIoO*Fk;uR8Oo^kL@bnuMl#fh%_2s<+&()jz9XA-?IAycWS z8#KOb**Ry~rLpot+4jmbzCfU9l8Gpl3+AFnqr}QDL9x7KnguE^=*A@)Uv$W_PI5+l zTC7Ob8?{^nL(U3BR0R@IMIxyYB4?x7*ttv)!0W1%O-w@q#)Qx1gZ0~fNxHT{C9|n) zYC3|613V<{5%})5oN2(sf*6u%;~USWAjv7UE8C+=YH~WErb4*h4miZb)b0?buLC9x zd>Qy5HmUZ6u)Eq|d$VcgFROha|Nb`qqw21Z-#{BbKYkuZe+VLR`mS@kX`+HRx#=wid8HX%%W*~W=>hQ zyd}k5u?@v3!Rq%c$95-`^Gl}U;}r`W$)awUioW1_L^nKF@s`YF6QJ0pnS;SR2S{v> zI0Z#-(m6z_zd4gMMoh0vY(uOsXDqOeFKv=oSZoY@e)3^pP zfoI9_0yND|oJ$Q?%BpZL(apH1c!i29G`3LoJjXs`V8;FHp~ujSa9t*Y;70bK8AtOX z7#GnBTs-QiQLtbG%rpXKZH$n-fQH42&)L7WBa~VQT?96$x^qr>p0bwD7coAW?5&hcnr={Gb1&R)M*YM~OJ3G3FEBJVUk(qw!zW1)7Jhni z(YYL)H#RYu&3RM#lDXnOZi0>?Q!qgZ7^Or^*QI@pB>7#hwGftcZ|QLf=UNHn9MSWE zv|;m&5E0uj)+3}Bde5xGF0E;{UNkk0CN-_-ANje2F+`l$q4I(ZH|B-qf& zZmh+Qu|0vj1P$^Enpe@V=^w({3>XkT79eJ^eFEEqn&x_NY8aaCdBlQ!#dI}|Z;R~J z$4!W!wqXX3BKI#??AL@pB4YFiY__q?-Du*M=5#jWhSakd?xD>&?2(~I%oh(U+z?iTW{@uGTywKlvCs5OK6Rd`X&b#j-uJ1?r7 z>lO#!?g;MZ;DPoeHOwGO-J@gVO#Y-h5}vP&-?+#~tJCdC`C}VcGb?k|{>rHv&gSSO z18oFAY-a2XU<<*3h0wU3$>T;Uu%d=#^tJih%C0~)v(;%$wW|1g;g#)9%+9#a3(`P0AI3z z2KP2Y0^?shp?XLru``&Cg_(i^_ahj-N!Zk+Az8Z<>H{!Pi;!h!KOG2@b18cvOpB^C zq?>lah<{=Pl^RnSbXS-TN`un&voOumKRc$CTH-;OtA~gP zD%QtN-a7yaB@fjY9SSGUm$@d~3oMd3Xg)UKd@CVSel5|V)V0E7?A!Ph_TekqstG^A zJ~X)X{y& literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/computer.cpython-39.pyc b/src/agents/__pycache__/computer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df273d50354edb396360a4b3c5ca783566001727 GIT binary patch literal 4169 zcmeHLOK;mo5avq~B};zD@-r`{�}fwgCz>E!sFvQ4~lG6lo3>P!JS%EitA@bxB2* zuFj?X4gLfA9sL76^t9KW`WJfY%u=K%g>qoK=qU^Aa(5p4&F<{ZENE3KB?-!d_8;v( zi<0yg4$?(~!9%FQ6KIG;WLIk9S8?U8(o|%LDn#9pnksxX_-fxuBuDZuC6cGo^W2%* z%mbxBbfD-HN`c5vrF!vim{B^a%e?T!2`RJOy2jP6W%I(o@rXYNc)s82dtsj!S`3Di z)pJ~P>2b(&%xQPvGw4w2a;;_AC%ka%vw_8kjnk$}0R|7D2KQu1qEb^PQd1!^n5@y< z^E@pO*orokqtquA{8XjnoY7LXzTGT zli9b)j-b~BeS?<4`XbqdS#FS^+HOFffII&VCdO~-vbARj_(Eg#*ZCp#Hxk^g6!C`sbRH(kXd#Z7-5H& z3@B42!=seIgrN^WUdVjcu%?hcGbRUMe=PGnqhX(UHaIgW*vEPZHNYcFq^HtYJ{m*A zwSczEUUS*ouwB zinPQyH#LUquv@Q;4q360D~XX2Z>(@F zq^DtIhB&q$jRpG`nW02xya!_QxLP>uQDnrZl>`+z*FKcmz;eP5GUrE*eF=9n^x!M@ zcl{T1KXZQM;GUh(VQ}R=`zQlbhZ%c}2EnH(+_hxiVlHC$tgv%s?2j|BHNvd+XDQsN zzmPND=DKNmR+pM4FPUc7Cw;g8CNG=jFMZ3Ma0;eLd>hDmk6{cChg`K&_u4C4*YKZ-)hO&G8{*t~sS%)e%SeG?bku|w0+FezXSD|h~twG&}T7`-<93x%m+n_&F-aI0% z_K2t);QJsx-J{r;YNz`@szP~upB4HHEHP#x{etoOfs4TuZ#I0dym3-y|} z!0)JuX&IkJ(@dQ8?hqI9y97xY%^{^;0&EM!GBf`oyc?j9y0+rR;+_9Kg||MtGLeAU zEkw!+_=}J-0=@_=KctX$XOYtRoEZq1m9G~8z6}cVOT!@c3~HPpYqNk+Zf5TyR8~%2 z1oeIf6o$&5Qc%@dsD%l65k|&~R7B#R7h^0;%8M|ve1w7CT8x2-nSBWSOKsR5D9rl? z(+EugZO(33oQYZEvS6QNfML3FQebo5!Su`S&+1K6+5s@WKxLfe3Mmcs>pJ@!K0FTx x!SUJ*j~l~D8OATJ9y~e}sgI#>YO4|}xc+7IAoxlKi2(l_=yF-nWnHb_{}&sUGX($u literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/exceptions.cpython-311.pyc b/src/agents/__pycache__/exceptions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d851c8e1c85e16b01a87d107315e6ba4ea0df5c GIT binary patch literal 3660 zcmd5;%WoS+7@zU3pN*R|^pzHxLE=$EY8NU5q9rORN&0jVNe`IAYVl4Sw`;GNS+{9a zL69mDq#k-fMM6k9wX*t`a6vALM6?o8B~HC1at^2`zHc_Wjn{90Cxx{C2EmU%%UaDiov{^*Z*;a*IhY2SLO}3>eT0tj+v6PvTaJq zW*osx+h#mqC9Bq%JIk3b#il+vmoMgYFJ%~Yv&Id>%NRz@v6?p4`wZi&rfF|5NyD(5 zvSGAH2bc8tY~hmRLKZ}=P&uH#;B1Wg5(OIH^1`GOSXjc$eRlJbq~ zA_W5~0H+~xFTg_hThTvk2anhpbnH=6;2NE`fbOd461Z4N<2sL9u@k^pF|N;mo88n) z2}=j{#l3+G=G=5s)Rh9c#jW|#P?Zd9CAr0}&2nF9HFK_7Yt~r3S^JU;=FBh`MOjrU z4WoG8OV+rQpo?-0W(*3}agm+f0C&g@dV?4N2azyETC~`r0S-j4&9kE!6#M zu=A(s{#ERQFbwZD@ifuaC!QysaLDbo{w^@zMKUDjMN7jBnTF$bk<3syc>79Fo1>)! z}CjcI`)JK zcG2MNd=2me-I_pM zJalY%=%eMKi8lOI&zcrXXVwV-g!^Vtxg!_zmxl`4{umK21LbcuyRpAAw{5sCP=8aX z$CLh+RBRv|hF4Alz-qg{6@9F&k3HNw($?{LppUHRqpP|ehowCO_-6}FQ-(Jzf!#aL z9SdyBy)6<9;&qr!BoNRK{;#eqQ2io@a0Fl*x63_kr=i~+$5GJvdfY{+pn$FK-~^jo z;21U{yobaSUEoMt$LE25|1H{`1=+4ScUOG%){#_KWBJ&s+r z!tLrBwyt-nRD#02Dhjv1S;N-#E;X3Ko>BO5cnw?EyVTz3aB7O%{gE*<6rwQ-OU@d$ Vu6L;e(N0Qm3D()$Gn7$2^f#FXEXx1@ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/exceptions.cpython-313.pyc b/src/agents/__pycache__/exceptions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fa364527d69c5a9723ebc3229873c0e15f7452d GIT binary patch literal 3235 zcmd5;-ER{|5a08i?X!s!lLC#QCEJz?EG0H-pdUa|3QYn@F;y<9+LHEk*c<2C^PRhU zhL9JKDi!si5-&)74gVs$LTqg!GlxZ@haD|e2=&PontvpA#UMOpC|osSTr*T2uWI>t(DzZ5xxN@*Dzmz8d+r(} zm@FSWVF^adjwJ+L@Hn*`hqA7Tptei>MMgtcYJ5ChXp&Ms5$LArS~X^xLE1EHUZvsS zxW_cVYgkTarq47NY%YAqcA4wJbkZ~{UKs{EO;h-mZ;R%RanuhGuN_*Eg8XZ{gS`vxkbUt#ZXgxyyHUs5{DWfQecDv>*fb?S3APD zcl8_c*Qp57dIzRA=I?E&C9ciK=M#pyA7%}8K7J^Mwe*d~lSZ8D)kHoSWY1VjvkmUb z+_DO*tZf71<*_K$6c81xLN6||FrPJR$*whO)NRzxGfuq)>Z3okg-kxKQ3#SXCIkdn zj=^f(hGX1uD+}T}`8?}%XGoPal~PmTyFk;71)5;af_xF^L@Dow4w186+XobKo3~x) zPT|@uOx_i8=heMG^@N$rfuht*aSV?4pva);MbQT$P=(I}K;3d(58?G(MD;bC#}y^M z;ZnF0zN0H}L4muzN`BQ&-OFUZ|Kz@=#0PJv4+(Uk3tGQq-1^TzZ8;~{qIJRc_&Dbt zU&GHVUwH;{=t3(V6I6IL<}ccAl{Q?qRA*(MRfZ@lR135W#|S4Z>U*98N67M}kGtPb zvn5;jLln+hoqHGT3fk2y2M%8iN@U77TPma_Y%h*G$D_{?AHEyJdc0-&gFR118iBnB z-S(o8=R=~*2k{-x<2tve-CK+TyGu`GHW-KA1&*8?WFs?^%iC^4!J`S5OUnl_rfj6T za}g!Z#^%UZDWGO$PMJx7>83WLZ&JYPjQq*isP6@;A5jl?E>ntRs+N}1(S1ypUwIrr z6jn3A55hV@Tr}!XU`TaADhew|nWns1fd!G{nktuv4PE%VAOr?T=@rS$q4$C3WIiga z0=Tx`1Mvt*njZW`fBmkW`7ybw58UY;SjpvY=SEg@Be!#-tGUtJxrx=>#LB?r%H)?T zJ?9>%u~b?fY`86OGx?b8BrMM$c>V_1KIZcv(+RK49LF?aEg2K0@OT)}FxsQQauLWZ zkC5U|+}kyDOB=eQjXl(~c=`bW5%LV${)c5vH~mdLS=WbQ1H6a=Ye{D1|5cS7|5Eq} z3IuT;@4QMNAU@yx|QauNujSvV`O}H(g1Aek}a=9OL8Qfx8r+fw-?K zit-zwf2!HvwTX5@QI1}-+XT9{o=^s_U2YTT+WopR`14?!K-b=d`o4}nt)y>wZ310; MH;ykqz_HBIKf2brWB>pF literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/exceptions.cpython-39.pyc b/src/agents/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f18ec51c0081aa8a8f8d36405ca6ddf38987bafb GIT binary patch literal 2625 zcmcImTW=dh6rR~#-|gB>%cT_Pa1qo}Yxe~SfmMZuD3q!$BIyJBGFm(nXTx61%x;@l zd26KdA1D$$`rq)puRQfH^aakDjn_$>(jtYiW<2N2?acR`IcL3At4^SNG5&MBS|Q{w z6pj}MgjJ~e5E?-Q4M<2`N{OV32}{_4722*HI<7;>1`&=Z9THKJr6;yq2DvOMAXj7= zuI`ksG43NxB!GgC(R(v)0xR->vht zZ`RlD-5qW`r>M2E%Xgzmnrx(=63X*~@1;(IBzxmQnk=hFxYO~1d#pm$9cUzRsUWT; zsA~%gPPVi5S7R9^dVOysCy5_N(_42uE!imWv}W6}VqOq1IUm)`k65xJS-y&GetoYu z>^fPM^T-P&=UJWeFcxWm{U+x>r(SS0QsG?0BhF_;HNY0rycqN!YN>Qzh5b=1WIvwB z$n$S{#;BW<{bVPO`dW?pxu(Ccq&L}DW!MKOCfb6gN{zgPfDL1-IO#exa|9j&@2)K! zSrXQONJrSusA1Z1%c2a(RI<)}Z*MDAk-_~#~>(V=RCW@crM zmch1JyueU3VGJ4YBR}CBc{X?uI=yoG3cdNP;jE$%IaS4`h7BH?**Z~Kn{zLU;>1IT z(44DFI9`yJ_wZEJf(G>(nW$68s#->OZX~SS2d@b3$Q|#IAFFkxVm1BcUueMDOvF+% z9fva6@uM+IBe^$`qeO}uO!me-HUgi<0gokd9DqN2iCGhUn$#H-x##QT1_S4xsQ8gD zaBk=Y@EStk{M*e3!=9O!6M@vcE|m(|F=VQX*jr#~;6$9SLY?z>b@}!S2G!-0JD9!9 zHgCZI4$fI|fECo88!I(@v^g1rmy?mgB*5(_Aj6B8MDAFs5loCA7&`tJ06)f?o2Nym zD}|x2gWKD%^4c-n(3?@Wgd3(1EczBSCqs&`FqjMoBogNteM+{eD2ei6(=9zE4sr9) za_wY9-%|eMv9BcjkH=C;@#6EdQT5n(o&{KPv;iEGBhbuJEAVHQfSDg)f8olwxCd2ZoFcRgzs}#&>#xACAp7hn zr{09Ua=Thb#*AbTfI*A*_z_MqujvZ(Fm=pdlU219>dxIO*r|-Zr7xjfq~C(=OTI73U$M7Gb<JbG=-B4&S*>^+CgGceW8#~6tcDx2&#^;`>txkI{&{j6Ka9*rQPXm0$L=_t JTBiZE^$*HALD2vJ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/function_schema.cpython-313.pyc b/src/agents/__pycache__/function_schema.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f2547f2a3203a805b9fd3f0d27864922f77865b GIT binary patch literal 12148 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 Md;jRcT*mMJ4-B*IqyPW_ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/function_schema.cpython-39.pyc b/src/agents/__pycache__/function_schema.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e7ffdd9faa0e3ccf6b09f3271bdad1f2f5b497d GIT binary patch literal 7825 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrail.cpython-311.pyc b/src/agents/__pycache__/guardrail.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a7e4dfeb2279a091a510376c5a59daca5d66e9a GIT binary patch literal 10844 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrail.cpython-313.pyc b/src/agents/__pycache__/guardrail.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..edcd014f129c6717f62bf95a52de43f5222b9d51 GIT binary patch literal 9979 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrail.cpython-39.pyc b/src/agents/__pycache__/guardrail.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce0385b4194b4d5f1ae6ef239aaa54d0b02b982b GIT binary patch literal 7825 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 XpDQT;)zaF3=FVvtsnfG?M9=>L2R$?; literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrail_base.cpython-313.pyc b/src/agents/__pycache__/guardrail_base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..525e91e8b4e93f9cc37c20618e454960217831bb GIT binary patch literal 3401 zcmcH+O>Y~=b(XvQ{*ow4re#YV%eEN8mXyYpZM3dk#|a#hiCUOa8-OxatL2c|db!KK zS=yG+L(v}6V=e}K$RURu5}>~#NPu37id7303%L%E_@J8z8$f)IY?idTOf2e}qv@u`c`1wstaUebTQg0u>qhS|i-DoL(Kii;fo8gju5kxXMHkoG>}MJX z21}g5>i{D0#k*F!<;7ZMqz-h9TMTb8*c{m5JO5gX?Glag=$z0+f`O=+jMgd80|*}FcpDjlL4!LL>5XB zH>RqV)?%vaCRDX$*E^yyQj%N0EvZ~rJfIFsPG0VnnL>#aeB1Tn}3+w5s=HLaZ z9fxsMg>jUohd<)JJ|}d|=b$yG+jTal3C;5Q1GNpdV>@V)K5CAG9)N>qsjC`iv+Z@r zBQI|f8sS1|?~u>3#Vw_{7Yc=Adohw5-BR*SB+J}o#@W)q8RY@OhTayusnI~DXnWdXoFgy)Uc1qlaqPCC59mA*d^e#OAT5p@t)fF88n&r9D2BC8 z+qP-w2IF{WQG39s{g46GF&e-&sogkUTp5V0F53xtbe1HF?{zkn(NKF(CZ&an@Pi4rZP;in-wMamJPb9Y^V~QNlBRC)2 ztgYb9SZV!HOAVmR02&%Vxd0NP&fvRiBJX&kHWol)Z!T2B7x|+mlASjW(+IlDAu)f8 zqAj4aEhG!|H%I>QvX*HD@)2jiO-Q3Sy;L%H>6T% zVWTwgHyXGTZFRUKvY6g4(-&#ec4#T%#sUmXqoP0<#f=&k7b(OYF}Q96CMzsI0V@y| zgt(xSF%n%x1R@9%f+!6jObEhrylR2{fx?;DaKm0Qa-$-PVNJ&lBM3y#=iQuTTk2sP z7p9|Pz!XBRjEUP#4<0v*%bSSj|G;UN{F;0fAwz|y%64I{TbSFPAj27Oz z{6V67>Oyz)!e3^7{K-Uj<|mu^pYG%*-m`z->P}wo=6|#&OQSdUWqCM<#cz0Yd#KnQ zD)y3*$;|H-_A_Msr4PefC$DagT-{7v{o>5||H$_22XA%ruLzEpkKs6#*-T9$%l!2F zY-?nGGc~_ILh{%C#Ygbvzj^i2Yia4@dFi#J^6`~hW4Q3DKeiY!i~Ad6$LC9!6Pr`t zoiyEuUnw>w2dek$#F30yioq#l38%$0A*M;3+^DM7Z5>;Z?+Mf^VxnRa?(g>o!nhy` zw>JrZU&84fvaiIjnHY=46FoT|j_l>V-r_{~%HKvW?8)ML{Vz1r`^jhjSIR{Mc*Ac(w{ z%hStYUAeLAe!F-Bg#U|7&)0B5_mQM$Wa@L0c}~tgBj=xyGtbGjzmjV&$oUtf_<~G4 zCqLYakWligP*#e)+kD#Uj$P~#gnKe#_XKwDoVWjd>B_s&r|Is{^oMef;L4-e4Zqfh% literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrail_base.cpython-39.pyc b/src/agents/__pycache__/guardrail_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..400cd36c8737e392fa6c3c2b47c005130cf38bb7 GIT binary patch literal 3458 zcmZu!TW=h<6`tGPFIvg+O7bNgJ8hLsW$7h-F^s^D;J5Q%K| z61BMQ)#HZOh?`zBZh5V^?X~q-SuDjJucPIPSdLe`706Xq6F1^jZ&k~6aWn3EUDMzz ztijiKn|Juyz}hfa^9S2o=MBEWS2v7fvv|{pcZ*k_8LY+HuMO7bO}@TiT-X<;cU$)_ zu@3q>)X%%5&+Q8bqj%Wy*G6yU9U;&&RU=4}G!OD9O|qV)>?dFBs&;UcJ!|K^}x6$g*AuA0+3hx*G^VqpI?ZCtOCMs{A&}xeSCVJx^epKKmlb z`0rB21saw8^CABxknhaGjr#sxG92Z5Igc^g+JAD)lYBQ#a{e+`o&9H{Kr$Ib;`gH* zYH+l|_#haGT$PT}QNlu6&SJ6AcuYb4B?NAGCNn(iIfBb=W<4^Pjkkk$3GXuA6@XGz zOY@1o;u)-u-Fe5sRVTJwEd5Z`j%U8fWlW{YsdiC0M`dIQP^)LMXO}=W-PWZ{W$QsOna=grBKl>Rhl}kIXRw@X=I)Cq6+sS{l5=;M ziYSbDHvMovIOT5of=f5VZjh;P)4@->PoBW-QNmvid6>iV!mkLk@Lc!Qwb5_WkzODS z4x-}`&=`bi?3X?3eGfb^bEsLyA&i2EhRxAUxZ}ny&cAQp5qgHe1Tf z(JjDU7$^oszTn>?u-lXeMT+R@DTkz{gyM4wMu4QEbzWjuXdnYJ_lyfsB$TQl?R*`J z@uMj#9&FD0Hn-f(IL&fj@(_F2r@YuabSLia2>8UB7Qc)H$W%485q~gkC22yeqUmM) zY;Jk6yD&5*=Ywk$b_foe5&Hr0)#0I9nwj*A{OFJG5}jQC7B2SFgsVo8&^~F6yEr&7 zAfhZkxTN&M@#+VRKK*ojZyxo6Zm&<$vsFp2T2%;gIt|K_=OdYfHh5@O$`FX4ek6i= z2w`IU-I$nf%~v)gb7D`dx7HpG^xqh-9Q0Td=dC$ymvZP#oJon9KiHratKYNbP1ulK zs@73~v0GEvi$JO>62gz-p-@Y{=9+mVG<){S%E`DG$X}2NI)c1*5jM_rgtJTIm#i*7 zf}*lv`YD90gUYb$=8D-dt>X8;acd6QHuUt7BiEqUvkHrwkVEj{ypT*#X=olnm>A#V z7(BIPn^|8Qxk>E{Q+_gotbhe076UNf8-F$aW?h(n#sLa&oW8sd8()q$_L3KYh(M3i z3?zO6oji1b-p5c;)o_}3s{TzNM*OLisf3%q!eHU+{5aDKm!D$b4|p z`~(W$;>qX=U;ya0lkGRK@pM8v1?#%pL0g zn5v&swM7->nO?snrzB0ZFu6`j+KBRJs0hc5c%W`M?WSYBuh*I-5%h%2t&0>0WKLw4dJ}NG)pHqA@0=a2*%qD(!EY$kxT3y>RyUUjOf4k(fL;wH) literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/guardrails.cpython-313.pyc b/src/agents/__pycache__/guardrails.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bec8e3f80154cd21374ad81c0992830bbc2961ce GIT binary patch literal 4141 zcmb7H-EZ606~7coiK4!2+45KX!8jj|TE|Y-XhE7ZiILesk75M&QafCBYP&|oj?WBoD&-Jh^M?9oD2#2^+Vz|e=h$xwl`dD=Ob674v}irt69 zbI(0IydUTMewWQeLMG78-uuT=BTmSFu+wf)U*VbxN614`AO&ugjjpQXN<)bv3m#LhO(O5oCZ)%2?7IG(s@8r4cR?D139x+h)I zs#SFIB9{%zpr-CcJ~Ul}YE@4xS`f<z#g=vvK!k3u(bo%Ju-mTTN`XEr%c zFfG&7{RR87wR*1kIrKk<%qSDe6$lL#ILaS^%%B3>NxmSQ;b^!ZmLrAm82Ap8LWD{M z3HnjLKMXdoCj4hx7LrABiYv$ijO@&QNVhe@{6 zOS0()i+-7e&wlpzDNC8UqAX~RQBjuKo1?hPnyb_t!%;L#(QrQ}l zQfA&0*_>$t>>h7gu9sp4bw#_Qx;1J!I;MHMp$?V7HQxv2Jok{4+9Tw;SS0f>UgV2H zfjbjY$jy#ws6BjBslt1HTOymGImqKU+YEKlPq>sz&DAxN8mewvi{?^R@IaMP4FgqurJWT`Ti02_phKFA_~;S?haoI zb`u6$ZIl$rwN@>)ZTP&I20KRE*V?Fa1<-dHzI>Z|$j^y!V(~@(nzYXux|UFiwXG$< zcE1&cTx%|eiA3g<)BGa0$<1XWv6F_q!I;ikHTza0cE8`@oQmiOx^3UJ(7RNS&HwVmNv z$@d+AFf5(vAZiMT*XLS*T^b8s9vfH2R&2+Gz0d()R6z8Vv3cbW%3G+-^8fYaAjIB} z`fs!dqEyGR`Cz%iu-+pf;fQ_SYgFg*^B&pN86$4(pC&r^<-6Du`9JI>Ty;5eNC&jMp7yO0d?G}X&IIc!x2@j*g6zFW-;3@ z4}A8}A`BrEu&SgdLGvA^Y2sk^)ffrd8(LM}= z0)gK70>lr0B0B;vN;@*?AO0#=@5|nuzW@FcA-gS{cyVCx?$wRL6Jc;$xY>}2a&Y@2 z?L~Z`kw}V@FH-#t5uT7k14E4nY7*%!aE&OcG8xdgS20uxic#_#uE;g`h&Zu5JmEjj z{F*&qMEhTfU~du-^jLd_b2?~23rO@fnEpQ!Wt1uJp+g`*pvy==ml9B9e9w5IAhqZ+ zT8L7a(Pa$iGU=uKJPaW5C6a@dgbkijbat`W&zFqifG@Ffjg^2iKMCMgU94F;UVc9D zDRjY5bVyxb-Zj%*My8W}CdFDn>a8fwGUH4?^B5Nf(SQ(r`1pCIrN)wR=ch^j=h;?9 zdCgTB)?ZrpA?@DyKI}^R2p__tL>agKg9IQbIu5 zSh4#^TWFDXsK~!f+Dn9vz$*Y}qww$xZ3YcKa#PfIdDkziHbCF?OY0E0czyd4B;d@; zFzR46B4>9056(Q4`&JrZRqzH~;wl+kUIfLbOQ1ch?0xNKx=mR7iH{MOfCT2l9miG$ zOFj%C81ylqbAT(6c>Gl{w0Ub)ZOXX;uFfzBq#OW(o`I*C@p@+b>yMvgrk|#!x1{NM zYWhb9nebj_nER3&6dsAg9L<9Pc;r2J&{L>~{VkqGgMtEoM;OS{U!sNpO;3Z!#_1W< zvGV9y6z5QUfP&#CJF4^8#S>w0NiU#w5ycdUy?}TLjVLgl^8f?`L;(P?15RlpSC;_h zh}hd8q5g1_gv0?@r+9;FB*_hKfkQM*CBgL_vVQ9Q-083IbZ@5CwAq z3i9F%$6$~th=ZswAVj4}Q2J1abIT4Roh;UXX@E{B4<;|A+kg@bIh+Y?L8q7@UM%=w zG8~Et%J#$y!==x(mS_Ymc=w_Bvr1(YckR(_DBc2S2MWB-cp}3h znnnZOI{dp(46~wjqoLhsMxyZqEPXG%Tg@{qz+>CKN&ICVMOW#ZPnqZGN6^LOcD@18 z;5m+aMn=CQN1l^o&&h?SWiWN&XobdPWAHk)u0d5{hkzb*|_8&>?PO_6wX&i=0cUa3iNh<#)ygeqIpe*xqy B5CQ-I literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/handoff.cpython-313.pyc b/src/agents/__pycache__/handoff.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5442e41960003daa28abb8cdcdf0fa11d9631895 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/handoff.cpython-39.pyc b/src/agents/__pycache__/handoff.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..753a02eaf2f8a324916fecc9db20662a8505540c GIT binary patch literal 6070 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 QTAUb+tbQ*b$hO)4050%W-v9sr literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/handoffs.cpython-39.pyc b/src/agents/__pycache__/handoffs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64f8bb0c303ab4b226e87f4c2561e00c2d2b6cb4 GIT binary patch literal 6154 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 TABEkQqi=JZ>%R!L%n$q@u(+?x literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/items.cpython-39.pyc b/src/agents/__pycache__/items.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7245ae297fe2e2a88faa1bc61fce72001f9cc5ed GIT binary patch literal 6896 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 RacNejYX3oLRZ~YM{s*iU^V$Fa literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/lifecycle.cpython-313.pyc b/src/agents/__pycache__/lifecycle.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1ccf5e18135836d245d2ed05778a2e61dcf94a4 GIT binary patch literal 3990 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/logger.cpython-313.pyc b/src/agents/__pycache__/logger.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24bf916107d176ed6c78405a99674ba14e9fc73a GIT binary patch literal 261 zcmey&%ge<81Xk%6(rtkBV-N=hn4yf%20+GChG2$ZMsJ29h8Tt*CYTZgt;7(_WXcHP z7cr$XYBIkBar~-y^9xe*5;OG@(^KhA6lGRRIFc=tDl^olBy3eLKk9$Zb4;9Mt+`taZ$29L_{B=Td$z<7Kcr4 xeoARhs$CH$&>)a|iY0)=2WCb_#@h^h4>$#`b4p#}l$v3GnNz!wt%w~c2>>0uK)?V1 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/logger.cpython-39.pyc b/src/agents/__pycache__/logger.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..360a1a3bddc7111dd178079e04fb84d71ff92536 GIT binary patch literal 212 zcmYe~<>g`k0;}{3={7+6F^GcPtVLtzr~rJTH*s_rxx8}1Bs;;tz;-- z1!@8lzby1ai&Kk=^^0=#lk-zj^&#fyLd?-Es4U6I&(kk1O4f&n=tGRqE2zB1VUwGm SQks)$#|Sj6m=c zj6t?I%zg z;(eeT{Q$~SbQdv2M=Yi*EItR(FjbGUgs$FA>WMs907?ZY8KQK6QXv`$P&!1KJ^(x| zKpB?N2LWZnwL$$1prHVb=);rfdzEGNv+PVQUKsVWw`qkqIuXKlYy3?LJhz`6Lo6t6 za)KY!iHj}Mali(fE?!^1UDC8~>Ndy0k#U_m6^>~`X2W#wp4<~#)UDdJCJS5ffpR3MFl$Jy><9DeyzQ zL0#f!PHf>Pf-U^BRYvYLiEFit{$?$IxWNdA8FD<+UJSp_oxIr;G+)GdNwOKKNZp0K z^WT9JF4%illsbl0r?y3Iwllkcj+aK|$sC6`h6rpx=k1yQc&OOi+;zZ%+&Ktedw)bL zEVZCSWI;o*C`T_9St#3zT-`>q=nmli*jBbqGQ!#G%B4`N3?hVq6J-TlH8eZ%7r~9N z0&emIDO4l45mvxWe-t;u3b;X=pXNvSjj#ePWaGmT!Huv2?wJpn2t{xstaRmSY8Dk@ z?fTb@R+<*UCU(gKe5&E_ZVS`CGuh-HV;S>RPMoTrKK9II z(P8Ybl(#uFa+&RwEok*Jm91tkm^I=GzR+liI(5sOS>^srIk*oD9$qq~M$1n@OH$`P z@yAc4qZCem0)F_fAokH=5)F=ZGUs}uxkqcShR*jUus2ccOccF|l|NN)aiz1k(i@q4 zR(ewMMoK^5_-*x<)!y_)Z~97S`ieKb_2P!NywzFW>P^jgQ@XJx0C`_#+LcXIPy?)vYQKj?G%b|^ip9isGrb_7wP=cu5v z5m4G=r_OWS>Pbf6o^ylD7l!<_VYqM^8iubK1|+BHNPW;S?lny(Tp2Qqs?E57JD0c= zmQ#jNp_X9;U(Sc+6Auu~{_jU;V-TA^ZWvH0!nO=k2xjj#1>wNV!5WhpXQNUC)ggua z@oz!=h~BDdB7LZ#3v>HxkI#42+1}*z{#TEdyXtiB;{1U+{`g^6o$t*R4%D&7PFF4T zZrs{mJ2?AgSH0EC7Z23jPv^R7vA1+}fAwJa^R9aJotjN(eXr8bMj<70QEkfS0AiAl zrJwotO(mSk+sIFfRs;Hk$(Ow!=;`47^m0F)U$D=>MBbM?58^PcD9URz^EbNi8cjk; V#WL?==M?yYAdcby-%1$7^AC&i_+tP7 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/model_settings.cpython-39.pyc b/src/agents/__pycache__/model_settings.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ac8110a9a027deded3f20967f04817d03c01257 GIT binary patch literal 1469 zcma)+&5s*36u|BI$Yk?DcUwRltzMQ$*@P2^szNIvfrPdzq-`&QkmVga$yCnRwLM!U z+FN_=p?7Ya_-{DRl@osgoOsX4(kw^_kK+7nKffTI2tIVCFnmdH%zZ;8y`ByVWhlf5f4DSk(-6WR?$ z)0iMaZ5j{5BPoKYwD346F6yA1&=txawiE{E=*10yA|;iiOkRLDWh#?0lc~yO_7pq< zYsh)zB62TsKXMWINcKVI2zXEq@?|&v(RvVXar}^-49Y5?aGD6Ain#cs3gh7_{fB+OrrXTNy1qGZqsdG+U!e zR+(l|FIyMW!mMy&tsoPgeXeH984GG{AupkIY8v$gW4WC&t3XAlm7lWU+NO%zr%Z)v zHkn1i%k5~PjR@?PU7AL9!vj@a9@TUw3#VSSs;M@7r*P99TcPmgH9bUueZs`$5sI}5OL>XYeef%#Avi9Oy zg9X$a-5-1ibNlhV)7VQ)lCE!VlykLIUC${~d1L9z^8|?E2VYv<@%18)7$tZqj#f*UA5pOuEb~y>MfY|An|?`1|vG{=!3X z&fR)mLHW)NmQ8&omZ}N<%x=QUG;{B&`EF(&Yn=Q2y_wlfS3r3geWN}FbsjsC_Gv~F zIyn6P*nV;^W3&CP$++ut-oPE`K7{inP5zM`<`@kmDrSWFZ87hQLSg8%>k literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/output_tool.cpython-313.pyc b/src/agents/__pycache__/output_tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3569e3351fdf38a63ed7d7bdf021128f880718cc GIT binary patch literal 933 zcmY*X&r2IY6rTN&WTUZGu~wzDtDq$y1`6J!;7?FvB5rIifnnTC(xvOp_RYki*B<&8 z_z(2fzoPe^3SMNPkWzZ^mNti8`(`&OI52O%_r14o-}hz)6BA>!ZuIOXD< z_R2<5Y%#_BkV$!GWX$wthexX3RdH9n1{TL0l2SW}SV%=mULBuG?y5I|bd?Yx8W*_g zLWCT5(@ZzZbVWPmn69|$vZ&>9+&aw{(FF&|R3M@?f7n`jSVL#9f#8}n2^by$(=)(Y zMzLTwbIrVGJ;&%%l45&^3&`g*_VVC#3S}o*NVV+g<}*PJYvX>o;*xpi%TDZSSw5( zWCNk^vw?mlwQ@0WXqrxGGDhjSXmvx5xJ2o@E(>)b^d>272Ow1#L_88m6ew*8AAuzm z=rD^Sq0%`@N|U?Umll%8kA9 z$24;e%#OaPl?%!E|EI?#=vz-pX&=L9mPwh~+1=Ybtu|<*Ua!*H=Fx64caPfH^|nv; zj~n~-TGoUouIX%KlYI$K(A95TzChEr48!b`@E-9NZ+}I+eT#2{ti?O$sNcPVZ&5$6UCs>4OQ!W*&}5#C zR-!WVjj1>0gJN|pRF*7@ij`FQvPaT?wU`DP%Rs9hiqFgx~-~zXT;v7I2i|3!s{XA`&sjL*#7;wgquCL_7vs zE&IZwKXz~%3JZXED63_$x|y7bS_utUG+_;PcEyU4eJp{G1bk#VJpp*kkWxTVVeXl+ zN4y5M>zLDut%cq9E*CmiMeXR=Ck=c0rJ3HW-?kmE(u%t_?(O zO1aFz3AIsnk5X1u()bB!N|n3z??~-|@#@#R6z)OkRISroa*_HeYyO$mN*Zad>-$uy zRla?jZlO8MgZf&<&}|1qyOrzV2zNqw0-txdcmA!1&-z`4*mk~ C^WuI0 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/result.cpython-313.pyc b/src/agents/__pycache__/result.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1c647a7d15ccf9232f102b9af136704a5b8f376 GIT binary patch literal 10436 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/run.cpython-313.pyc b/src/agents/__pycache__/run.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d127b935c3d5f3376d985a14c98605d20c9d9b7e GIT binary patch literal 31819 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 A5dZ)H literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/run_context.cpython-313.pyc b/src/agents/__pycache__/run_context.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72a6585a2a0b8328ed3b494d2b319e8071ff494b GIT binary patch literal 1144 zcmYjQ&yUnL6m~MnB$HtmEojk-O2w^2VFXN0dte2XP=!!u=~iln_A-+3CZ3r<#tzSp zAUAI9u@@wc9NYhRwsiUj^f?o%T8jPN`|^H#&wlHzt%%?{Sp2a_dW8IC zl}{_^gmsC+E0U1JIV2MYoQVrAwYq!Qn|R<&eDGVnci5i<5TNWO{$V&7z<`oj@Fhw5 zGcO5lyAaKsJ%W08nIwamKZ|^fy^opg+TR=mhKp!4Jhj@$Z3Y=PJT17^jX%#tk)f@3 zPu5NFKu7_3I&zv|T9@KS4yTUwaT@=T<_pn;({GeC;)SulWMa;%!lYf2_V7AoeFueC zWJbVA2)Ky@y{kwMJS?%F^sXWLc}rv#Cf;oa2D1?DzO^TPh!(*pY<7OAWS8zI;AJV` zbci_r0&AKsb4{P4Ra3JRG~HB1)$tRN8Z>c3YgN$_Ns5dbMIU1@BsYdiULQRk({Bys zg;uR6&!vuXIajdaCRdX3V^!HGf>xF`Rr6LsQ5uR7C_2tLjHBr2`|14y+EuA32SKGW z|I2HGJbd`wm`<0XzJ@TT&v|VtvbE7nltN}grnx|GnNfQ;7EM(qB1<7(m4#Sg;+wR{ zp9;iOLFbiBZKe7qO?goq^Ykf-OQq}~OgfI5pz8_V)cNkF*qo4MfXxM~| zNxl+{H4$Sgl~sk6&oTC_;zfs?XKbE>HbpLlR0s!*Wh!M1>|<{b<`QYQ&C_VLuK)&i z+p+jAW7>i#}0oGH>_<}HdR@U zn>3$U*YM7!!(YP{RPAi(ySSb89LIS_uD>H!K9H*)$lm+mgu7i~a4b^rhX literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/run_context.cpython-39.pyc b/src/agents/__pycache__/run_context.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f8fc216774cebac6488f42bdcabf6c9dff3d379 GIT binary patch literal 997 zcmYjQO>fjN5Ouyb+3Z4F1P5-u!Ak9}xK;&$5UP+$k?0nrTwFJv?1mcgHl;ApC{aT#`g#32F{WZeiHQwd|jUd9SNbO}_ z?PtE{V!sONFbnG_i-tI;Ms=LU=!YVzl6sttJp!>9K`7!|KTE+oCL$5zA4H5Hg7hi5 z32wY>BGNN5o7~|kWajr{!7a}#ZcHB)B~${j;EnA1_!uPUGM{;UyzCnI%=Mk;a=bn| zH+%(svV5zgh40qABB0=HWph{d{z9o6S^MY!$vi4aH z9WqZpF5ex}Em1?c1}c@kcfKoha`ItLmuu*r;Wnq&ymQPPM=GEJDM02WV73(0jl}Kd zO2BY}lT+4B1vL`br&W0cxUVW&v@&;$%|6X}RbBG@3f;9*?g2KR59xip?GX-YXFDiX z@Z2ih_0b^lbT;l2#w4$Su|8$2R-&z(KV|G&%d729%$QI)V|t9^>~|RB615IpGl;rr z>%^IdXnqp?*!j-G={?uK8@BIZzn-UEG literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/stream_events.cpython-313.pyc b/src/agents/__pycache__/stream_events.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee957ee6b2a1954961c17d9d2a4acba286cf0e08 GIT binary patch literal 2174 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 L@+TV&r(ypGotX;$ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/tool.cpython-313.pyc b/src/agents/__pycache__/tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb3a11978f139414b2a0978804c41b973317ab67 GIT binary patch literal 11731 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 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/tool.cpython-39.pyc b/src/agents/__pycache__/tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8318baa49197057717e6d9ceec262087ae7dd3a6 GIT binary patch literal 8572 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 VyZn;P{J|#Q#*32nH$x$g@h_{P`d|P6 literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/tool_converter.cpython-39.pyc b/src/agents/__pycache__/tool_converter.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ee20bd4589e44efb70a46fb62e87a249daff87c GIT binary patch literal 1534 zcmZ`(UvC^W5VyU1dwYAA5>liz<&Q#DflflXcT}NPQ40?!pqBEmFV<@9OE%rLm+>A| zqC9c^76b|2$Oqv|_?4%A0wg35Grm2zpvEhY$KLVyH@_LX*xJe%wx@smcKqvrvEOO3 z*&>`g#O|J=P)zZfH9Y5>bWw{Y$|KYx71wby$Ol0m)JZeUhe1#3v>D~2Cd)Iv*8zoQFcB@ z)4Pw3wdtNzrl`MKbnT)e<3nra)$vsL$&-aC*Dica|4KS)v&A@hh~0gOLbIGJmJ1!} zSPvAx6iTS*RlMYRf@{NmEmniDmg3q-XL}6S6SSqk$hHs5Z}n)t*U)J|Kyil# zfGGu@xNf8)_v{e6dw`;27rf(30Us%T!A}MDsJ)|Tj8$}rY+s6JtQ)97mt62A|B?M9 zmiz~VkBlZqKArcmdv+E1*c6TSW2IdQRZEoktR+r00&`b{?5x!=!Gq6lKQ}LoeQEXy z>^`l3x<4KIta6ocU13TML$rgpNNl6V<5ZV}gR{0@>R^-DLch4J9gHLeV1Ge!RgK?2;E^WhL&S=J)zA<(vv!_S?xX z89K@qizrMC6vI6K_$Ws}wN#w2#jPIDWj5{}og^esvc^ bAcW{mP<{GfMaF0JfI8YW5%}>5-;VwPO)rb~ literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/usage.cpython-313.pyc b/src/agents/__pycache__/usage.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..057a30b926e639adaf5504fb5a914a6754743281 GIT binary patch literal 1357 zcmaJ>y=&V*6u*;XNB(L^sUbFvNxx_{I3ffSD5>dE2yNm(g*St$+KS`Wk(|5Jkg48U z3Z)qu2wl3iQ!@5X$k;ffHV1(~r)()6vi6;<7{`b{q^JA6_w;`6p5CdSO2rY#*hL01@C)=D6FiG5cpBYBhLlByT!8n46b27Oq`IO-&IQsikJT(@ zRht&2)xhsRl!!6RjA)$MZy@)1!lSBbsl*_2V9Y&b2Cnaq7_JCvtfkBz`2bD7l) zoTX~8RY-u4*n+tGU$EFh89l)-g?&uQq1H!1l$As%JYo$SIG;j@O3h(LpJTL!zyldV ztYaJ@5hEP1hk^JUqEBN293j>*j*y5Ej$s8pM|6w;AaI0O$2dYFMmVkp3)PV(mHcn5 zDP#hY6IG~~P-5mai7V7@uJee?Hf%ygakI00Q7=i&t5)MNf6V4!c@xsncc9v+rzStAKBm4G_KW+)z4W)-*WAyo zBlYR06nMI7rlV#K3f+ZgaP5`5GkRx6Kb${OS5DN7ACT>+*@JSoungCp-ObH+a`T7Q zk-B!GX8)hzx0>lg3Frrc9>6RiBF;3A5}SF%DQQWrnx@h$ znt^B1G}jx1vS!1v9oIDdZgRQdFb*!uao~ushU-vpUGFiO6m)6Rq#~kfT8xp#YmeC? zRCI_;33Wp#QJJsA4ACV}@6p+FIrroFOr?^wz7l*O}5w!ONuaI9o| z%F?K(c&R9nRXj*6a+WliCx$;En-Vc01GeiGaCK?WfFijKA>u&L`=BmljPWVDa}h;S V;!>KztM~#5-Iu}e*Ejfd{{WQ_30?pI literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/usage.cpython-39.pyc b/src/agents/__pycache__/usage.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f07f1c9df17d4da289a9c54ac564ddeb7f75f699 GIT binary patch literal 774 zcmZ8fv2NQi5IvHTEjuy_WbD{YKogAtil7KkpxwL_UbL`;$U07C$|0#CMYiNK`UCx& z&ICGj>MwNa9aRWIkKoh0d(!FdNVVCl0qxEH$Nnb=_(jg#GIVZm`&S4WH0$8V8pgn| z51_dg&!EKS*+;1I-}oL>EUvT)s_m5bEk))g(7D0ww+IFrrlH|G%-sl+nOyU0 z;)}#Z;>*Od#EZmpM_yux3za|Jx1z*rgwRIh(c+7ju|{< z1NJhlud6iOOVn`Gg_Kqujg)aM<+0a8N4%8seo)=0q&lNG*brYxsjS6GheL0@l>b%l zHu#%V^9)Lg(YUEyPgix#OEIn#-&P`?O;^o$N9U82Sr!_Eqg1iD9?N`7*TYlNQgHSc DCLE{) literal 0 HcmV?d00001 diff --git a/src/agents/__pycache__/version.cpython-313.pyc b/src/agents/__pycache__/version.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9730494b699ddad126adcc521c79c500529a5e10 GIT binary patch literal 465 zcmey&%ge<81Xk%6((eQ5#~=<2us|7~JAjO-48aV+jNS}IjB*Syj6pCtm|7qm#E!&) zGJ}~+86nIfmUKo%TspySK-JC8kjTowpuiB!T*M;Jkj|LKV9PAekjALV@)D%b?-pBP zdTL%taTTk99uR7>-4e>oEyyn_$;nL8%S|mwOi3(Byu}F@yTt()WG_oCD$dN$yTu(J z4`s&3-x3K(OwI6c&PmS38e;#yRcU$m0pGsw1EminQ^Ku!8Zx%$cZDXIGT1*v(7 znYs`s=oVC#WaQ`R7Z)Y#LqznU#_APR-r}&y%}*)KNwq8D1)2m3jbdjY@qw9g`k0;}{3>Gy&3V-N=!Z~!?DKwNAABvKes7;_k+7^?(Q8MBxcFsCprWQ<}7 zXULOcVE~C_u~r%}GJs$Ta}-M?Lmra>P)9I>Cd*5pZU(DZqc_qbFtOj~OsL6Iq zC^NSpzo;Z9Gf6KuwInemu_W;pCtU0n2V9W7EVZaOGe7SZcYHjQ86SU3Bp@+48)&9q zeu-OtX None: + set_tracing_export_api_key(key) + _openai_shared.set_default_openai_key(key) + + +def set_default_openai_client(client: AsyncOpenAI, use_for_tracing: bool) -> None: + if use_for_tracing: + set_tracing_export_api_key(client.api_key) + _openai_shared.set_default_openai_client(client) + + +def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: + if api == "chat_completions": + _openai_shared.set_use_responses_by_default(False) + else: + _openai_shared.set_use_responses_by_default(True) diff --git a/src/agents/_debug.py b/src/agents/_debug.py new file mode 100644 index 0000000..4da91be --- /dev/null +++ b/src/agents/_debug.py @@ -0,0 +1,17 @@ +import os + + +def _debug_flag_enabled(flag: str) -> bool: + flag_value = os.getenv(flag) + return flag_value is not None and (flag_value == "1" or flag_value.lower() == "true") + + +DONT_LOG_MODEL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_MODEL_DATA") +"""By default we don't log LLM inputs/outputs, to prevent exposing sensitive information. Set this +flag to enable logging them. +""" + +DONT_LOG_TOOL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_TOOL_DATA") +"""By default we don't log tool call inputs/outputs, to prevent exposing sensitive information. Set +this flag to enable logging them. +""" diff --git a/src/agents/_run_impl.py b/src/agents/_run_impl.py new file mode 100644 index 0000000..112819c --- /dev/null +++ b/src/agents/_run_impl.py @@ -0,0 +1,792 @@ +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +from openai.types.responses import ( + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseOutputMessage, +) +from openai.types.responses.response_computer_tool_call import ( + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, +) +from openai.types.responses.response_input_param import ComputerCallOutput +from openai.types.responses.response_output_item import Reasoning + +from . import _utils +from .agent import Agent +from .agent_output import AgentOutputSchema +from .computer import AsyncComputer, Computer +from .exceptions import AgentsException, ModelBehaviorError, UserError +from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult +from .handoffs import Handoff, HandoffInputData +from .items import ( + HandoffCallItem, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + ModelResponse, + ReasoningItem, + RunItem, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, +) +from .lifecycle import RunHooks +from .logger import logger +from .models.interface import ModelTracing +from .run_context import RunContextWrapper, TContext +from .stream_events import RunItemStreamEvent, StreamEvent +from .tool import ComputerTool, FunctionTool +from .tracing import ( + SpanError, + Trace, + function_span, + get_current_trace, + guardrail_span, + handoff_span, + trace, +) + +if TYPE_CHECKING: + from .run import RunConfig + + +class QueueCompleteSentinel: + pass + + +QUEUE_COMPLETE_SENTINEL = QueueCompleteSentinel() + + +@dataclass +class ToolRunHandoff: + handoff: Handoff + tool_call: ResponseFunctionToolCall + + +@dataclass +class ToolRunFunction: + tool_call: ResponseFunctionToolCall + function_tool: FunctionTool + + +@dataclass +class ToolRunComputerAction: + tool_call: ResponseComputerToolCall + computer_tool: ComputerTool + + +@dataclass +class ProcessedResponse: + new_items: list[RunItem] + handoffs: list[ToolRunHandoff] + functions: list[ToolRunFunction] + computer_actions: list[ToolRunComputerAction] + + def has_tools_to_run(self) -> bool: + # Handoffs, functions and computer actions need local processing + # Hosted tools have already run, so there's nothing to do. + return any( + [ + self.handoffs, + self.functions, + self.computer_actions, + ] + ) + + +@dataclass +class NextStepHandoff: + new_agent: Agent[Any] + + +@dataclass +class NextStepFinalOutput: + output: Any + + +@dataclass +class NextStepRunAgain: + pass + + +@dataclass +class SingleStepResult: + original_input: str | list[TResponseInputItem] + """The input items i.e. the items before run() was called. May be mutated by handoff input + filters.""" + + model_response: ModelResponse + """The model response for the current step.""" + + pre_step_items: list[RunItem] + """Items generated before the current step.""" + + new_step_items: list[RunItem] + """Items generated during this current step.""" + + next_step: NextStepHandoff | NextStepFinalOutput | NextStepRunAgain + """The next step to take.""" + + @property + def generated_items(self) -> list[RunItem]: + """Items generated during the agent run (i.e. everything generated after + `original_input`).""" + return self.pre_step_items + self.new_step_items + + +def get_model_tracing_impl( + tracing_disabled: bool, trace_include_sensitive_data: bool +) -> ModelTracing: + if tracing_disabled: + return ModelTracing.DISABLED + elif trace_include_sensitive_data: + return ModelTracing.ENABLED + else: + return ModelTracing.ENABLED_WITHOUT_DATA + + +class RunImpl: + @classmethod + async def execute_tools_and_side_effects( + cls, + *, + agent: Agent[TContext], + # The original input to the Runner + original_input: str | list[TResponseInputItem], + # Eveything generated by Runner since the original input, but before the current step + pre_step_items: list[RunItem], + new_response: ModelResponse, + processed_response: ProcessedResponse, + output_schema: AgentOutputSchema | None, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + # Make a copy of the generated items + pre_step_items = list(pre_step_items) + + new_step_items: list[RunItem] = [] + new_step_items.extend(processed_response.new_items) + + # First, lets run the tool calls - function tools and computer actions + function_results, computer_results = await asyncio.gather( + cls.execute_function_tool_calls( + agent=agent, + tool_runs=processed_response.functions, + hooks=hooks, + context_wrapper=context_wrapper, + config=run_config, + ), + cls.execute_computer_actions( + agent=agent, + actions=processed_response.computer_actions, + hooks=hooks, + context_wrapper=context_wrapper, + config=run_config, + ), + ) + new_step_items.extend(function_results) + new_step_items.extend(computer_results) + + # Second, check if there are any handoffs + if run_handoffs := processed_response.handoffs: + return await cls.execute_handoffs( + agent=agent, + original_input=original_input, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + new_response=new_response, + run_handoffs=run_handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + # Now we can check if the model also produced a final output + message_items = [item for item in new_step_items if isinstance(item, MessageOutputItem)] + + # We'll use the last content output as the final output + potential_final_output_text = ( + ItemHelpers.extract_last_text(message_items[-1].raw_item) if message_items else None + ) + + # There are two possibilities that lead to a final output: + # 1. Structured output schema => always leads to a final output + # 2. Plain text output schema => only leads to a final output if there are no tool calls + if output_schema and not output_schema.is_plain_text() and potential_final_output_text: + final_output = output_schema.validate_json(potential_final_output_text) + return await cls.execute_final_output( + agent=agent, + original_input=original_input, + new_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + final_output=final_output, + hooks=hooks, + context_wrapper=context_wrapper, + ) + elif ( + not output_schema or output_schema.is_plain_text() + ) and not processed_response.has_tools_to_run(): + return await cls.execute_final_output( + agent=agent, + original_input=original_input, + new_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + final_output=potential_final_output_text or "", + hooks=hooks, + context_wrapper=context_wrapper, + ) + else: + # If there's no final output, we can just run again + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepRunAgain(), + ) + + @classmethod + def process_model_response( + cls, + *, + agent: Agent[Any], + response: ModelResponse, + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + ) -> ProcessedResponse: + items: list[RunItem] = [] + + run_handoffs = [] + functions = [] + computer_actions = [] + + handoff_map = {handoff.tool_name: handoff for handoff in handoffs} + function_map = {tool.name: tool for tool in agent.tools if isinstance(tool, FunctionTool)} + computer_tool = next((tool for tool in agent.tools if isinstance(tool, ComputerTool)), None) + + for output in response.output: + if isinstance(output, ResponseOutputMessage): + items.append(MessageOutputItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseFileSearchToolCall): + items.append(ToolCallItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseFunctionWebSearch): + items.append(ToolCallItem(raw_item=output, agent=agent)) + elif isinstance(output, Reasoning): + items.append(ReasoningItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseComputerToolCall): + items.append(ToolCallItem(raw_item=output, agent=agent)) + if not computer_tool: + _utils.attach_error_to_current_span( + SpanError( + message="Computer tool not found", + data={}, + ) + ) + raise ModelBehaviorError( + "Model produced computer action without a computer tool." + ) + computer_actions.append( + ToolRunComputerAction(tool_call=output, computer_tool=computer_tool) + ) + elif not isinstance(output, ResponseFunctionToolCall): + logger.warning(f"Unexpected output type, ignoring: {type(output)}") + continue + + # At this point we know it's a function tool call + if not isinstance(output, ResponseFunctionToolCall): + continue + + # Handoffs + if output.name in handoff_map: + items.append(HandoffCallItem(raw_item=output, agent=agent)) + handoff = ToolRunHandoff( + tool_call=output, + handoff=handoff_map[output.name], + ) + run_handoffs.append(handoff) + # Regular function tool call + else: + if output.name not in function_map: + _utils.attach_error_to_current_span( + SpanError( + message="Tool not found", + data={"tool_name": output.name}, + ) + ) + raise ModelBehaviorError(f"Tool {output.name} not found in agent {agent.name}") + items.append(ToolCallItem(raw_item=output, agent=agent)) + functions.append( + ToolRunFunction( + tool_call=output, + function_tool=function_map[output.name], + ) + ) + + return ProcessedResponse( + new_items=items, + handoffs=run_handoffs, + functions=functions, + computer_actions=computer_actions, + ) + + @classmethod + async def execute_function_tool_calls( + cls, + *, + agent: Agent[TContext], + tool_runs: list[ToolRunFunction], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> list[RunItem]: + async def run_single_tool( + func_tool: FunctionTool, tool_call: ResponseFunctionToolCall + ) -> str: + with function_span(func_tool.name) as span_fn: + if config.trace_include_sensitive_data: + span_fn.span_data.input = tool_call.arguments + try: + _, _, result = await asyncio.gather( + hooks.on_tool_start(context_wrapper, agent, func_tool), + ( + agent.hooks.on_tool_start(context_wrapper, agent, func_tool) + if agent.hooks + else _utils.noop_coroutine() + ), + func_tool.on_invoke_tool(context_wrapper, tool_call.arguments), + ) + + await asyncio.gather( + hooks.on_tool_end(context_wrapper, agent, func_tool, result), + ( + agent.hooks.on_tool_end(context_wrapper, agent, func_tool, result) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + except Exception as e: + _utils.attach_error_to_current_span( + SpanError( + message="Error running tool", + data={"tool_name": func_tool.name, "error": str(e)}, + ) + ) + if isinstance(e, AgentsException): + raise e + raise UserError(f"Error running tool {func_tool.name}: {e}") from e + + if config.trace_include_sensitive_data: + span_fn.span_data.output = result + return result + + tasks = [] + for tool_run in tool_runs: + function_tool = tool_run.function_tool + tasks.append(run_single_tool(function_tool, tool_run.tool_call)) + + results = await asyncio.gather(*tasks) + + return [ + ToolCallOutputItem( + output=str(result), + raw_item=ItemHelpers.tool_call_output_item(tool_run.tool_call, str(result)), + agent=agent, + ) + for tool_run, result in zip(tool_runs, results) + ] + + @classmethod + async def execute_computer_actions( + cls, + *, + agent: Agent[TContext], + actions: list[ToolRunComputerAction], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> list[RunItem]: + results: list[RunItem] = [] + # Need to run these serially, because each action can affect the computer state + for action in actions: + results.append( + await ComputerAction.execute( + agent=agent, + action=action, + hooks=hooks, + context_wrapper=context_wrapper, + config=config, + ) + ) + + return results + + @classmethod + async def execute_handoffs( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + pre_step_items: list[RunItem], + new_step_items: list[RunItem], + new_response: ModelResponse, + run_handoffs: list[ToolRunHandoff], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + # If there is more than one handoff, add tool responses that reject those handoffs + if len(run_handoffs) > 1: + output_message = "Multiple handoffs detected, ignoring this one." + new_step_items.extend( + [ + ToolCallOutputItem( + output=output_message, + raw_item=ItemHelpers.tool_call_output_item( + handoff.tool_call, output_message + ), + agent=agent, + ) + for handoff in run_handoffs[1:] + ] + ) + + actual_handoff = run_handoffs[0] + with handoff_span(from_agent=agent.name) as span_handoff: + handoff = actual_handoff.handoff + new_agent: Agent[Any] = await handoff.on_invoke_handoff( + context_wrapper, actual_handoff.tool_call.arguments + ) + span_handoff.span_data.to_agent = new_agent.name + + # Append a tool output item for the handoff + new_step_items.append( + HandoffOutputItem( + agent=agent, + raw_item=ItemHelpers.tool_call_output_item( + actual_handoff.tool_call, + handoff.get_transfer_message(new_agent), + ), + source_agent=agent, + target_agent=new_agent, + ) + ) + + # Execute handoff hooks + await asyncio.gather( + hooks.on_handoff( + context=context_wrapper, + from_agent=agent, + to_agent=new_agent, + ), + ( + agent.hooks.on_handoff( + context_wrapper, + agent=new_agent, + source=agent, + ) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + # If there's an input filter, filter the input for the next agent + input_filter = handoff.input_filter or ( + run_config.handoff_input_filter if run_config else None + ) + if input_filter: + logger.debug("Filtering inputs for handoff") + handoff_input_data = HandoffInputData( + input_history=tuple(original_input) + if isinstance(original_input, list) + else original_input, + pre_handoff_items=tuple(pre_step_items), + new_items=tuple(new_step_items), + ) + if not callable(input_filter): + _utils.attach_error_to_span( + span_handoff, + SpanError( + message="Invalid input filter", + data={"details": "not callable()"}, + ), + ) + raise UserError(f"Invalid input filter: {input_filter}") + filtered = input_filter(handoff_input_data) + if not isinstance(filtered, HandoffInputData): + _utils.attach_error_to_span( + span_handoff, + SpanError( + message="Invalid input filter result", + data={"details": "not a HandoffInputData"}, + ), + ) + raise UserError(f"Invalid input filter result: {filtered}") + + original_input = ( + filtered.input_history + if isinstance(filtered.input_history, str) + else list(filtered.input_history) + ) + pre_step_items = list(filtered.pre_handoff_items) + new_step_items = list(filtered.new_items) + + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepHandoff(new_agent), + ) + + @classmethod + async def execute_final_output( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + new_response: ModelResponse, + pre_step_items: list[RunItem], + new_step_items: list[RunItem], + final_output: Any, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + ) -> SingleStepResult: + # Run the on_end hooks + await cls.run_final_output_hooks(agent, hooks, context_wrapper, final_output) + + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepFinalOutput(final_output), + ) + + @classmethod + async def run_final_output_hooks( + cls, + agent: Agent[TContext], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + final_output: Any, + ): + await asyncio.gather( + hooks.on_agent_end(context_wrapper, agent, final_output), + agent.hooks.on_end(context_wrapper, agent, final_output) + if agent.hooks + else _utils.noop_coroutine(), + ) + + @classmethod + async def run_single_input_guardrail( + cls, + agent: Agent[Any], + guardrail: InputGuardrail[TContext], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> InputGuardrailResult: + with guardrail_span(guardrail.get_name()) as span_guardrail: + result = await guardrail.run(agent, input, context) + span_guardrail.span_data.triggered = result.output.tripwire_triggered + return result + + @classmethod + async def run_single_output_guardrail( + cls, + guardrail: OutputGuardrail[TContext], + agent: Agent[Any], + agent_output: Any, + context: RunContextWrapper[TContext], + ) -> OutputGuardrailResult: + with guardrail_span(guardrail.get_name()) as span_guardrail: + result = await guardrail.run(agent=agent, agent_output=agent_output, context=context) + span_guardrail.span_data.triggered = result.output.tripwire_triggered + return result + + @classmethod + def stream_step_result_to_queue( + cls, + step_result: SingleStepResult, + queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel], + ): + for item in step_result.new_step_items: + if isinstance(item, MessageOutputItem): + event = RunItemStreamEvent(item=item, name="message_output_created") + elif isinstance(item, HandoffCallItem): + event = RunItemStreamEvent(item=item, name="handoff_requested") + elif isinstance(item, HandoffOutputItem): + event = RunItemStreamEvent(item=item, name="handoff_occured") + elif isinstance(item, ToolCallItem): + event = RunItemStreamEvent(item=item, name="tool_called") + elif isinstance(item, ToolCallOutputItem): + event = RunItemStreamEvent(item=item, name="tool_output") + elif isinstance(item, ReasoningItem): + event = RunItemStreamEvent(item=item, name="reasoning_item_created") + else: + logger.warning(f"Unexpected item type: {type(item)}") + event = None + + if event: + queue.put_nowait(event) + + +class TraceCtxManager: + """Creates a trace only if there is no current trace, and manages the trace lifecycle.""" + + def __init__( + self, + workflow_name: str, + trace_id: str | None, + group_id: str | None, + metadata: dict[str, Any] | None, + disabled: bool, + ): + self.trace: Trace | None = None + self.workflow_name = workflow_name + self.trace_id = trace_id + self.group_id = group_id + self.metadata = metadata + self.disabled = disabled + + def __enter__(self) -> TraceCtxManager: + current_trace = get_current_trace() + if not current_trace: + self.trace = trace( + workflow_name=self.workflow_name, + trace_id=self.trace_id, + group_id=self.group_id, + metadata=self.metadata, + disabled=self.disabled, + ) + self.trace.start(mark_as_current=True) + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.trace: + self.trace.finish(reset_current=True) + + +class ComputerAction: + @classmethod + async def execute( + cls, + *, + agent: Agent[TContext], + action: ToolRunComputerAction, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> RunItem: + output_func = ( + cls._get_screenshot_async(action.computer_tool.computer, action.tool_call) + if isinstance(action.computer_tool.computer, AsyncComputer) + else cls._get_screenshot_sync(action.computer_tool.computer, action.tool_call) + ) + + _, _, output = await asyncio.gather( + hooks.on_tool_start(context_wrapper, agent, action.computer_tool), + ( + agent.hooks.on_tool_start(context_wrapper, agent, action.computer_tool) + if agent.hooks + else _utils.noop_coroutine() + ), + output_func, + ) + + await asyncio.gather( + hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output), + ( + agent.hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + # TODO: don't send a screenshot every single time, use references + image_url = f"data:image/png;base64,{output}" + return ToolCallOutputItem( + agent=agent, + output=image_url, + raw_item=ComputerCallOutput( + call_id=action.tool_call.call_id, + output={ + "type": "computer_screenshot", + "image_url": image_url, + }, + type="computer_call_output", + ), + ) + + @classmethod + async def _get_screenshot_sync( + cls, + computer: Computer, + tool_call: ResponseComputerToolCall, + ) -> str: + action = tool_call.action + if isinstance(action, ActionClick): + computer.click(action.x, action.y, action.button) + elif isinstance(action, ActionDoubleClick): + computer.double_click(action.x, action.y) + elif isinstance(action, ActionDrag): + computer.drag([(p.x, p.y) for p in action.path]) + elif isinstance(action, ActionKeypress): + computer.keypress(action.keys) + elif isinstance(action, ActionMove): + computer.move(action.x, action.y) + elif isinstance(action, ActionScreenshot): + computer.screenshot() + elif isinstance(action, ActionScroll): + computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) + elif isinstance(action, ActionType): + computer.type(action.text) + elif isinstance(action, ActionWait): + computer.wait() + + return computer.screenshot() + + @classmethod + async def _get_screenshot_async( + cls, + computer: AsyncComputer, + tool_call: ResponseComputerToolCall, + ) -> str: + action = tool_call.action + if isinstance(action, ActionClick): + await computer.click(action.x, action.y, action.button) + elif isinstance(action, ActionDoubleClick): + await computer.double_click(action.x, action.y) + elif isinstance(action, ActionDrag): + await computer.drag([(p.x, p.y) for p in action.path]) + elif isinstance(action, ActionKeypress): + await computer.keypress(action.keys) + elif isinstance(action, ActionMove): + await computer.move(action.x, action.y) + elif isinstance(action, ActionScreenshot): + await computer.screenshot() + elif isinstance(action, ActionScroll): + await computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) + elif isinstance(action, ActionType): + await computer.type(action.text) + elif isinstance(action, ActionWait): + await computer.wait() + + return await computer.screenshot() diff --git a/src/agents/_utils.py b/src/agents/_utils.py new file mode 100644 index 0000000..2a0293a --- /dev/null +++ b/src/agents/_utils.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import re +from collections.abc import Awaitable +from typing import Any, Literal, Union + +from pydantic import TypeAdapter, ValidationError +from typing_extensions import TypeVar + +from .exceptions import ModelBehaviorError +from .logger import logger +from .tracing import Span, SpanError, get_current_span + +T = TypeVar("T") + +MaybeAwaitable = Union[Awaitable[T], T] + + +def transform_string_function_style(name: str) -> str: + # Replace spaces with underscores + name = name.replace(" ", "_") + + # Replace non-alphanumeric characters with underscores + name = re.sub(r"[^a-zA-Z0-9]", "_", name) + + return name.lower() + + +def validate_json(json_str: str, type_adapter: TypeAdapter[T], partial: bool) -> T: + partial_setting: bool | Literal["off", "on", "trailing-strings"] = ( + "trailing-strings" if partial else False + ) + try: + validated = type_adapter.validate_json(json_str, experimental_allow_partial=partial_setting) + return validated + except ValidationError as e: + attach_error_to_current_span( + SpanError( + message="Invalid JSON provided", + data={}, + ) + ) + raise ModelBehaviorError( + f"Invalid JSON when parsing {json_str} for {type_adapter}; {e}" + ) from e + + +def attach_error_to_span(span: Span[Any], error: SpanError) -> None: + span.set_error(error) + + +def attach_error_to_current_span(error: SpanError) -> None: + span = get_current_span() + if span: + attach_error_to_span(span, error) + else: + logger.warning(f"No span to add error {error} to") + + +async def noop_coroutine() -> None: + pass diff --git a/src/agents/agent.py b/src/agents/agent.py new file mode 100644 index 0000000..61c0a89 --- /dev/null +++ b/src/agents/agent.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import dataclasses +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Callable, Generic, cast + +from . import _utils +from ._utils import MaybeAwaitable +from .guardrail import InputGuardrail, OutputGuardrail +from .handoffs import Handoff +from .items import ItemHelpers +from .logger import logger +from .model_settings import ModelSettings +from .models.interface import Model +from .run_context import RunContextWrapper, TContext +from .tool import Tool, function_tool + +if TYPE_CHECKING: + from .lifecycle import AgentHooks + from .result import RunResult + + +@dataclass +class Agent(Generic[TContext]): + """An agent is an AI model configured with instructions, tools, guardrails, handoffs and more. + + We strongly recommend passing `instructions`, which is the "system prompt" for the agent. In + addition, you can pass `description`, which is a human-readable description of the agent, used + when the agent is used inside tools/handoffs. + + Agents are generic on the context type. The context is a (mutable) object you create. It is + passed to tool functions, handoffs, guardrails, etc. + """ + + name: str + """The name of the agent.""" + + instructions: ( + str + | Callable[ + [RunContextWrapper[TContext], Agent[TContext]], + MaybeAwaitable[str], + ] + | None + ) = None + """The instructions for the agent. Will be used as the "system prompt" when this agent is + invoked. Describes what the agent should do, and how it responds. + + Can either be a string, or a function that dynamically generates instructions for the agent. If + you provide a function, it will be called with the context and the agent instance. It must + return a string. + """ + + handoff_description: str | None = None + """A description of the agent. This is used when the agent is used as a handoff, so that an + LLM knows what it does and when to invoke it. + """ + + handoffs: list[Agent[Any] | Handoff[TContext]] = field(default_factory=list) + """Handoffs are sub-agents that the agent can delegate to. You can provide a list of handoffs, + and the agent can choose to delegate to them if relevant. Allows for separation of concerns and + modularity. + """ + + model: str | Model | None = None + """The model implementation to use when invoking the LLM. + + By default, if not set, the agent will use the default model configured in + `model_settings.DEFAULT_MODEL`. + """ + + model_settings: ModelSettings = field(default_factory=ModelSettings) + """Configures model-specific tuning parameters (e.g. temperature, top_p). + """ + + tools: list[Tool] = field(default_factory=list) + """A list of tools that the agent can use.""" + + input_guardrails: list[InputGuardrail[TContext]] = field(default_factory=list) + """A list of checks that run in parallel to the agent's execution, before generating a + response. Runs only if the agent is the first agent in the chain. + """ + + output_guardrails: list[OutputGuardrail[TContext]] = field(default_factory=list) + """A list of checks that run on the final output of the agent, after generating a response. + Runs only if the agent produces a final output. + """ + + output_type: type[Any] | None = None + """The type of the output object. If not provided, the output will be `str`.""" + + hooks: AgentHooks[TContext] | None = None + """A class that receives callbacks on various lifecycle events for this agent. + """ + + def clone(self, **kwargs: Any) -> Agent[TContext]: + """Make a copy of the agent, with the given arguments changed. For example, you could do: + ``` + new_agent = agent.clone(instructions="New instructions") + ``` + """ + return dataclasses.replace(self, **kwargs) + + def as_tool( + self, + tool_name: str | None, + tool_description: str | None, + custom_output_extractor: Callable[[RunResult], Awaitable[str]] | None = None, + ) -> Tool: + """Transform this agent into a tool, callable by other agents. + + This is different from handoffs in two ways: + 1. In handoffs, the new agent receives the conversation history. In this tool, the new agent + receives generated input. + 2. In handoffs, the new agent takes over the conversation. In this tool, the new agent is + called as a tool, and the conversation is continued by the original agent. + + Args: + tool_name: The name of the tool. If not provided, the agent's name will be used. + tool_description: The description of the tool, which should indicate what it does and + when to use it. + custom_output_extractor: A function that extracts the output from the agent. If not + provided, the last message from the agent will be used. + """ + + @function_tool( + name_override=tool_name or _utils.transform_string_function_style(self.name), + description_override=tool_description or "", + ) + async def run_agent(context: RunContextWrapper, input: str) -> str: + from .run import Runner + + output = await Runner.run( + starting_agent=self, + input=input, + context=context.context, + ) + if custom_output_extractor: + return await custom_output_extractor(output) + + return ItemHelpers.text_message_outputs(output.new_items) + + return run_agent + + async def get_system_prompt(self, run_context: RunContextWrapper[TContext]) -> str | None: + """Get the system prompt for the agent.""" + if isinstance(self.instructions, str): + return self.instructions + elif callable(self.instructions): + if inspect.iscoroutinefunction(self.instructions): + return await cast(Awaitable[str], self.instructions(run_context, self)) + else: + return cast(str, self.instructions(run_context, self)) + elif self.instructions is not None: + logger.error(f"Instructions must be a string or a function, got {self.instructions}") + + return None diff --git a/src/agents/agent_output.py b/src/agents/agent_output.py new file mode 100644 index 0000000..8140d8c --- /dev/null +++ b/src/agents/agent_output.py @@ -0,0 +1,144 @@ +from dataclasses import dataclass +from typing import Any + +from pydantic import BaseModel, TypeAdapter +from typing_extensions import TypedDict, get_args, get_origin + +from . import _utils +from .exceptions import ModelBehaviorError, UserError +from .strict_schema import ensure_strict_json_schema +from .tracing import SpanError + +_WRAPPER_DICT_KEY = "response" + + +@dataclass(init=False) +class AgentOutputSchema: + """An object that captures the JSON schema of the output, as well as validating/parsing JSON + produced by the LLM into the output type. + """ + + output_type: type[Any] + """The type of the output.""" + + _type_adapter: TypeAdapter[Any] + """A type adapter that wraps the output type, so that we can validate JSON.""" + + _is_wrapped: bool + """Whether the output type is wrapped in a dictionary. This is generally done if the base + output type cannot be represented as a JSON Schema object. + """ + + _output_schema: dict[str, Any] + """The JSON schema of the output.""" + + strict_json_schema: bool + """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, + as it increases the likelihood of correct JSON input. + """ + + def __init__(self, output_type: type[Any], strict_json_schema: bool = True): + """ + Args: + output_type: The type of the output. + strict_json_schema: Whether the JSON schema is in strict mode. We **strongly** recommend + setting this to True, as it increases the likelihood of correct JSON input. + """ + self.output_type = output_type + self.strict_json_schema = strict_json_schema + + if output_type is None or output_type is str: + self._is_wrapped = False + self._type_adapter = TypeAdapter(output_type) + self._output_schema = self._type_adapter.json_schema() + return + + # We should wrap for things that are not plain text, and for things that would definitely + # not be a JSON Schema object. + self._is_wrapped = not _is_subclass_of_base_model_or_dict(output_type) + + if self._is_wrapped: + OutputType = TypedDict( + "OutputType", + { + _WRAPPER_DICT_KEY: output_type, # type: ignore + }, + ) + self._type_adapter = TypeAdapter(OutputType) + self._output_schema = self._type_adapter.json_schema() + else: + self._type_adapter = TypeAdapter(output_type) + self._output_schema = self._type_adapter.json_schema() + + if self.strict_json_schema: + self._output_schema = ensure_strict_json_schema(self._output_schema) + + def is_plain_text(self) -> bool: + """Whether the output type is plain text (versus a JSON object).""" + return self.output_type is None or self.output_type is str + + def json_schema(self) -> dict[str, Any]: + """The JSON schema of the output type.""" + if self.is_plain_text(): + raise UserError("Output type is plain text, so no JSON schema is available") + return self._output_schema + + def validate_json(self, json_str: str, partial: bool = False) -> Any: + """Validate a JSON string against the output type. Returns the validated object, or raises + a `ModelBehaviorError` if the JSON is invalid. + """ + validated = _utils.validate_json(json_str, self._type_adapter, partial) + if self._is_wrapped: + if not isinstance(validated, dict): + _utils.attach_error_to_current_span( + SpanError( + message="Invalid JSON", + data={"details": f"Expected a dict, got {type(validated)}"}, + ) + ) + raise ModelBehaviorError( + f"Expected a dict, got {type(validated)} for JSON: {json_str}" + ) + + if _WRAPPER_DICT_KEY not in validated: + _utils.attach_error_to_current_span( + SpanError( + message="Invalid JSON", + data={"details": f"Could not find key {_WRAPPER_DICT_KEY} in JSON"}, + ) + ) + raise ModelBehaviorError( + f"Could not find key {_WRAPPER_DICT_KEY} in JSON: {json_str}" + ) + return validated[_WRAPPER_DICT_KEY] + return validated + + def output_type_name(self) -> str: + """The name of the output type.""" + return _type_to_str(self.output_type) + + +def _is_subclass_of_base_model_or_dict(t: Any) -> bool: + if not isinstance(t, type): + return False + + # If it's a generic alias, 'origin' will be the actual type, e.g. 'list' + origin = get_origin(t) + + allowed_types = (BaseModel, dict) + # If it's a generic alias e.g. list[str], then we should check the origin type i.e. list + return issubclass(origin or t, allowed_types) + + +def _type_to_str(t: type[Any]) -> str: + origin = get_origin(t) + args = get_args(t) + + if origin is None: + # It's a simple type like `str`, `int`, etc. + return t.__name__ + elif args: + args_str = ', '.join(_type_to_str(arg) for arg in args) + return f"{origin.__name__}[{args_str}]" + else: + return str(t) diff --git a/src/agents/computer.py b/src/agents/computer.py new file mode 100644 index 0000000..1b9224d --- /dev/null +++ b/src/agents/computer.py @@ -0,0 +1,107 @@ +import abc +from typing import Literal + +Environment = Literal["mac", "windows", "ubuntu", "browser"] +Button = Literal["left", "right", "wheel", "back", "forward"] + + +class Computer(abc.ABC): + """A computer implemented with sync operations. The Computer interface abstracts the + operations needed to control a computer or browser.""" + + @property + @abc.abstractmethod + def environment(self) -> Environment: + pass + + @property + @abc.abstractmethod + def dimensions(self) -> tuple[int, int]: + pass + + @abc.abstractmethod + def screenshot(self) -> str: + pass + + @abc.abstractmethod + def click(self, x: int, y: int, button: Button) -> None: + pass + + @abc.abstractmethod + def double_click(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + pass + + @abc.abstractmethod + def type(self, text: str) -> None: + pass + + @abc.abstractmethod + def wait(self) -> None: + pass + + @abc.abstractmethod + def move(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + def keypress(self, keys: list[str]) -> None: + pass + + @abc.abstractmethod + def drag(self, path: list[tuple[int, int]]) -> None: + pass + + +class AsyncComputer(abc.ABC): + """A computer implemented with async operations. The Computer interface abstracts the + operations needed to control a computer or browser.""" + + @property + @abc.abstractmethod + def environment(self) -> Environment: + pass + + @property + @abc.abstractmethod + def dimensions(self) -> tuple[int, int]: + pass + + @abc.abstractmethod + async def screenshot(self) -> str: + pass + + @abc.abstractmethod + async def click(self, x: int, y: int, button: Button) -> None: + pass + + @abc.abstractmethod + async def double_click(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + pass + + @abc.abstractmethod + async def type(self, text: str) -> None: + pass + + @abc.abstractmethod + async def wait(self) -> None: + pass + + @abc.abstractmethod + async def move(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + async def keypress(self, keys: list[str]) -> None: + pass + + @abc.abstractmethod + async def drag(self, path: list[tuple[int, int]]) -> None: + pass diff --git a/src/agents/exceptions.py b/src/agents/exceptions.py new file mode 100644 index 0000000..78898f0 --- /dev/null +++ b/src/agents/exceptions.py @@ -0,0 +1,63 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .guardrail import InputGuardrailResult, OutputGuardrailResult + + +class AgentsException(Exception): + """Base class for all exceptions in the Agents SDK.""" + + +class MaxTurnsExceeded(AgentsException): + """Exception raised when the maximum number of turns is exceeded.""" + + message: str + + def __init__(self, message: str): + self.message = message + + +class ModelBehaviorError(AgentsException): + """Exception raised when the model does something unexpected, e.g. calling a tool that doesn't + exist, or providing malformed JSON. + """ + + message: str + + def __init__(self, message: str): + self.message = message + + +class UserError(AgentsException): + """Exception raised when the user makes an error using the SDK.""" + + message: str + + def __init__(self, message: str): + self.message = message + + +class InputGuardrailTripwireTriggered(AgentsException): + """Exception raised when a guardrail tripwire is triggered.""" + + guardrail_result: "InputGuardrailResult" + """The result data of the guardrail that was triggered.""" + + def __init__(self, guardrail_result: "InputGuardrailResult"): + self.guardrail_result = guardrail_result + super().__init__( + f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" + ) + + +class OutputGuardrailTripwireTriggered(AgentsException): + """Exception raised when a guardrail tripwire is triggered.""" + + guardrail_result: "OutputGuardrailResult" + """The result data of the guardrail that was triggered.""" + + def __init__(self, guardrail_result: "OutputGuardrailResult"): + self.guardrail_result = guardrail_result + super().__init__( + f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" + ) diff --git a/src/agents/extensions/__init__.py b/src/agents/extensions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/agents/extensions/__pycache__/__init__.cpython-313.pyc b/src/agents/extensions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bda7d30b3e28cf5878cdefadb699edad54b2deef GIT binary patch literal 166 zcmey&%ge<81Xk%6(n0iN5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iWerR!OQL%ne zu6}ZUN~(T-L26!Nrfy<-YFPO4oIE6`Mst;Hb5M`lJw#v*1Q3jl_CDhmJr literal 0 HcmV?d00001 diff --git a/src/agents/extensions/__pycache__/__init__.cpython-39.pyc b/src/agents/extensions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b46e0f6d36b3c815492f3e1f5858041dc9f6611d GIT binary patch literal 160 zcmYe~<>g`k0;}{3=^*+sh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vNKeRZts93)! zS3fyFB~?GaAT=*BQ#Ua^HLs*tx1h2lBR@~SxF}g4BBGyKQIeWhoSC0jtREkrnU`4- ZAFo$Xd5gm)H$SB`C)EyQ{AVC$006&(C!PQR literal 0 HcmV?d00001 diff --git a/src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc b/src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2b51f4f20cd9543a485dc81d1e68522a8899f1d GIT binary patch literal 2320 zcmZ`)O>7%Q6rS<^c;lbgag#t&Vm4_h7!sSh^w3bK5EV2|nhI|%s4Ti#>`CIv*|lcZ zlyLGf$8w11fddCF+&IFWBZpY2uvv-(q8xg2Nl>MV18-)3;v{0C%)EK?X5QQHd+&Ka znT#PAi|f1VKSUAwlWl?#@dUhI2Vnd}} zSf>4$#R0nXHN&i1YimnptLG^E{{FLjeR*52KzAH7CQ*glFM**o;<{4|ksHm-7M6sIw@E!Vj7JFW1X0e zz0lfnHY~GX(^|pP6v$@|F>RLeg&bqk0;nex>a(h#kcg4v^@zedubN%Tz`kAv&qGCcGAW@R{!6Q0S8Vq7y z8vlZ@g9?b1!6|g@4q}NP7EvWq4NK#A;@7W0;tyMj%nKVQEMGll`Mv8ThN9jq@wFhgyKj=RhF@Q92O~*5?;+kDdUB*s3>$*L|Hjg)@fbY zXh9m(=@@LFMkKEgM#D_<_99;ZPnJt`irqdHOlLr_2}~W-pLnjb`fGY(TYQw5yytwS z_Yw=;#KObW)cvy$)bqVmv70LL-8YF|YN4A7j*7k1g>LG?ABpk4GTBpRy2{M``p?Rl zCu3-A@*sl}WB0OqadlTz>1ptEglWbKXL=h1#foXhDYLIVHY!5UxB&IMkNRN~4POJ)g;gldbs?ZoSSJphfyg~hC7K19sY6v~adFQ_LN@|&>}5^BrO@4fl+-uwCY#!0W&ad6$8|1tl)~Gi*xj^;+t~f|5OyyeeB+FcUYI;aL?0wYQL02O%0j-NwQul6 zQl#Z<_OMv4)PqDNBVQjjzWYg*Jybl`M~&m_mBK9RYCT#NR_Q)2%WOMyxUqJo4j(__ za*3GS1~IFIYob#QyxbBOR{#MQM^6Xs#xV#UP^vufQ4S1e7?vO7cwkYDnzZJLMh<_ z`N_NCY?1L$@wL_l+JR;c&JprUu1uidFIhEJya^5!1c8j?;xyr8w6|OTpu9 ziDqBjF8H(9h8P8+hf2gDgzhZlqL3;nrd)e!wamDFHx@iEpYqsb6|3_lm+?%Ld5x96 zzB{}bF(E|X>}mn_#evzp)#PJCMFUiZs(BhW?&!wW^IE>qa67h5{5MCufo=c#YVxfF zdJ~aPre(?}<&qc4;!ZN>g_3uc=jya9CQ?i%wP(VARJ@Ree3K0(_2zQCJlBJ!S|DmG zmIK6)Z$dcCCqBFOin0y~C?)oODc-`!#j(ZeyM~On!|{0h1ibPr!lH{Qdpt5tXppfy z`Vz|Avgm>HiPb!XJjF`A?eElu9HeKzSm&LXgrg(7-@g5#|1HvILAP8Pj8vH|9 z@RGX0-_)n1OYFRhn>8G-H(ZU(9u_9cEf)7+%yB6FO4bCJ(8>WOx7Ms;S2&=aql?fv zipL=!XPK!fr!8hEc!phLkxY0$5PH=K^q_w za1BfYux9jR)YqLjo~>{)@i-Rd@YF%WVriSrhTZw}_y_EKe_?(Oma_Ik*-dxSF#ROV b=hnf!yi8Xa|HK@B(yZ#zE(yDX<3ayl{@}K= literal 0 HcmV?d00001 diff --git a/src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc b/src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b1154bdaee6473333b7ed6b593cf68cba773d31 GIT binary patch literal 1071 zcmY*YO>fgc5M9S<8>MP{L#3yY%Av7JA_46Is!A0NYC-)_-69Z`!p5E?rtz+|yKd^9 z_!InxaN~a60Qu$4seLm zzKI_5E(!y>IL!Nf48ime_Uyg}fB(%Ugs7vgumK4%ze1Q^3ZztFB)EyB04_qn8w}EX zgo zNYM2(zpnE;M#}zl7H|PQON9VPB8k67rKb#uJQtF*sRDD*LxN8dC5ad}Sch5a+$kRP zBD^Nm?F<4kCK6qOwCEPRlZu?dCJ;YV6wg5Ln2vuWZ9;cYwTx`-lkD*RrmcEM5dRcQ+-suI+`aV;>3*R6`E9uz?pR(jc)NyR`cfj@GrDXO?bhU z*;DrstCenFSNFmY6jHH>U`S%_6>Cb3`oJcF4skE!n;thnJZARldiAzct=62;!?Whg zlcS^N@qTl^b$WJkbb3xh^VQ+Inxm-EnYKJ(blA8c8M>k3Mj%6MOo79z79}&%WC=F3 z3X1Ip{ShcM`KKF2LG3vrRZp@}!LMZvYR$s`>re}na>eF;K%dX-cZIEN+%0Vk%fq+B zgS*A8yYkkABJ8;4syP}*E_G~v>nl?g+EDX$x3Uw7kg$?8vp HandoffInputData: + """Filters out all tool items: file search, web search and function calls+output.""" + + history = handoff_input_data.input_history + new_items = handoff_input_data.new_items + + filtered_history = ( + _remove_tool_types_from_input(history) if isinstance(history, tuple) else history + ) + filtered_pre_handoff_items = _remove_tools_from_items(handoff_input_data.pre_handoff_items) + filtered_new_items = _remove_tools_from_items(new_items) + + return HandoffInputData( + input_history=filtered_history, + pre_handoff_items=filtered_pre_handoff_items, + new_items=filtered_new_items, + ) + + +def _remove_tools_from_items(items: tuple[RunItem, ...]) -> tuple[RunItem, ...]: + filtered_items = [] + for item in items: + if ( + isinstance(item, HandoffCallItem) + or isinstance(item, HandoffOutputItem) + or isinstance(item, ToolCallItem) + or isinstance(item, ToolCallOutputItem) + ): + continue + filtered_items.append(item) + return tuple(filtered_items) + + +def _remove_tool_types_from_input( + items: tuple[TResponseInputItem, ...], +) -> tuple[TResponseInputItem, ...]: + tool_types = [ + "function_call", + "function_call_output", + "computer_call", + "computer_call_output", + "file_search_call", + "web_search_call", + ] + + filtered_items: list[TResponseInputItem] = [] + for item in items: + itype = item.get("type") + if itype in tool_types: + continue + filtered_items.append(item) + return tuple(filtered_items) diff --git a/src/agents/extensions/handoff_prompt.py b/src/agents/extensions/handoff_prompt.py new file mode 100644 index 0000000..cfb5ca7 --- /dev/null +++ b/src/agents/extensions/handoff_prompt.py @@ -0,0 +1,19 @@ +# A recommended prompt prefix for agents that use handoffs. We recommend including this or +# similar instructions in any agents that use handoffs. +RECOMMENDED_PROMPT_PREFIX = ( + "# System context\n" + "You are part of a multi-agent system called the Agents SDK, designed to make agent " + "coordination and execution easy. Agents uses two primary abstraction: **Agents** and " + "**Handoffs**. An agent encompasses instructions and tools and can hand off a " + "conversation to another agent when appropriate. " + "Handoffs are achieved by calling a handoff function, generally named " + "`transfer_to_`. Transfers between agents are handled seamlessly in the background;" + " do not mention or draw attention to these transfers in your conversation with the user.\n" +) + + +def prompt_with_handoff_instructions(prompt: str) -> str: + """ + Add recommended instructions to the prompt for agents that use handoffs. + """ + return f"{RECOMMENDED_PROMPT_PREFIX}\n\n{prompt}" diff --git a/src/agents/function_schema.py b/src/agents/function_schema.py new file mode 100644 index 0000000..a4b5767 --- /dev/null +++ b/src/agents/function_schema.py @@ -0,0 +1,340 @@ +from __future__ import annotations + +import contextlib +import inspect +import logging +import re +from dataclasses import dataclass +from typing import Any, Callable, Literal, get_args, get_origin, get_type_hints + +from griffe import Docstring, DocstringSectionKind +from pydantic import BaseModel, Field, create_model + +from .exceptions import UserError +from .run_context import RunContextWrapper +from .strict_schema import ensure_strict_json_schema + + +@dataclass +class FuncSchema: + """ + Captures the schema for a python function, in preparation for sending it to an LLM as a tool. + """ + + name: str + """The name of the function.""" + description: str | None + """The description of the function.""" + params_pydantic_model: type[BaseModel] + """A Pydantic model that represents the function's parameters.""" + params_json_schema: dict[str, Any] + """The JSON schema for the function's parameters, derived from the Pydantic model.""" + signature: inspect.Signature + """The signature of the function.""" + takes_context: bool = False + """Whether the function takes a RunContextWrapper argument (must be the first argument).""" + + def to_call_args(self, data: BaseModel) -> tuple[list[Any], dict[str, Any]]: + """ + Converts validated data from the Pydantic model into (args, kwargs), suitable for calling + the original function. + """ + positional_args: list[Any] = [] + keyword_args: dict[str, Any] = {} + seen_var_positional = False + + # Use enumerate() so we can skip the first parameter if it's context. + for idx, (name, param) in enumerate(self.signature.parameters.items()): + # If the function takes a RunContextWrapper and this is the first parameter, skip it. + if self.takes_context and idx == 0: + continue + + value = getattr(data, name, None) + if param.kind == param.VAR_POSITIONAL: + # e.g. *args: extend positional args and mark that *args is now seen + positional_args.extend(value or []) + seen_var_positional = True + elif param.kind == param.VAR_KEYWORD: + # e.g. **kwargs handling + keyword_args.update(value or {}) + elif param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): + # Before *args, add to positional args. After *args, add to keyword args. + if not seen_var_positional: + positional_args.append(value) + else: + keyword_args[name] = value + else: + # For KEYWORD_ONLY parameters, always use keyword args. + keyword_args[name] = value + return positional_args, keyword_args + + +@dataclass +class FuncDocumentation: + """Contains metadata about a python function, extracted from its docstring.""" + + name: str + """The name of the function, via `__name__`.""" + description: str | None + """The description of the function, derived from the docstring.""" + param_descriptions: dict[str, str] | None + """The parameter descriptions of the function, derived from the docstring.""" + + +DocstringStyle = Literal["google", "numpy", "sphinx"] + + +# As of Feb 2025, the automatic style detection in griffe is an Insiders feature. This +# code approximates it. +def _detect_docstring_style(doc: str) -> DocstringStyle: + scores: dict[DocstringStyle, int] = {"sphinx": 0, "numpy": 0, "google": 0} + + # Sphinx style detection: look for :param, :type, :return:, and :rtype: + sphinx_patterns = [r"^:param\s", r"^:type\s", r"^:return:", r"^:rtype:"] + for pattern in sphinx_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["sphinx"] += 1 + + # Numpy style detection: look for headers like 'Parameters', 'Returns', or 'Yields' followed by + # a dashed underline + numpy_patterns = [ + r"^Parameters\s*\n\s*-{3,}", + r"^Returns\s*\n\s*-{3,}", + r"^Yields\s*\n\s*-{3,}", + ] + for pattern in numpy_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["numpy"] += 1 + + # Google style detection: look for section headers with a trailing colon + google_patterns = [r"^(Args|Arguments):", r"^(Returns):", r"^(Raises):"] + for pattern in google_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["google"] += 1 + + max_score = max(scores.values()) + if max_score == 0: + return "google" + + # Priority order: sphinx > numpy > google in case of tie + styles: list[DocstringStyle] = ["sphinx", "numpy", "google"] + + for style in styles: + if scores[style] == max_score: + return style + + return "google" + + +@contextlib.contextmanager +def _suppress_griffe_logging(): + # Supresses warnings about missing annotations for params + logger = logging.getLogger("griffe") + previous_level = logger.getEffectiveLevel() + logger.setLevel(logging.ERROR) + try: + yield + finally: + logger.setLevel(previous_level) + + +def generate_func_documentation( + func: Callable[..., Any], style: DocstringStyle | None = None +) -> FuncDocumentation: + """ + Extracts metadata from a function docstring, in preparation for sending it to an LLM as a tool. + + Args: + func: The function to extract documentation from. + style: The style of the docstring to use for parsing. If not provided, we will attempt to + auto-detect the style. + + Returns: + A FuncDocumentation object containing the function's name, description, and parameter + descriptions. + """ + name = func.__name__ + doc = inspect.getdoc(func) + if not doc: + return FuncDocumentation(name=name, description=None, param_descriptions=None) + + with _suppress_griffe_logging(): + docstring = Docstring(doc, lineno=1, parser=style or _detect_docstring_style(doc)) + parsed = docstring.parse() + + description: str | None = next( + (section.value for section in parsed if section.kind == DocstringSectionKind.text), None + ) + + param_descriptions: dict[str, str] = { + param.name: param.description + for section in parsed + if section.kind == DocstringSectionKind.parameters + for param in section.value + } + + return FuncDocumentation( + name=func.__name__, + description=description, + param_descriptions=param_descriptions or None, + ) + + +def function_schema( + func: Callable[..., Any], + docstring_style: DocstringStyle | None = None, + name_override: str | None = None, + description_override: str | None = None, + use_docstring_info: bool = True, + strict_json_schema: bool = True, +) -> FuncSchema: + """ + Given a python function, extracts a `FuncSchema` from it, capturing the name, description, + parameter descriptions, and other metadata. + + Args: + func: The function to extract the schema from. + docstring_style: The style of the docstring to use for parsing. If not provided, we will + attempt to auto-detect the style. + name_override: If provided, use this name instead of the function's `__name__`. + description_override: If provided, use this description instead of the one derived from the + docstring. + use_docstring_info: If True, uses the docstring to generate the description and parameter + descriptions. + strict_json_schema: Whether the JSON schema is in strict mode. If True, we'll ensure that + the schema adheres to the "strict" standard the OpenAI API expects. We **strongly** + recommend setting this to True, as it increases the likelihood of the LLM providing + correct JSON input. + + Returns: + A `FuncSchema` object containing the function's name, description, parameter descriptions, + and other metadata. + """ + + # 1. Grab docstring info + if use_docstring_info: + doc_info = generate_func_documentation(func, docstring_style) + param_descs = doc_info.param_descriptions or {} + else: + doc_info = None + param_descs = {} + + func_name = name_override or doc_info.name if doc_info else func.__name__ + + # 2. Inspect function signature and get type hints + sig = inspect.signature(func) + type_hints = get_type_hints(func) + params = list(sig.parameters.items()) + takes_context = False + filtered_params = [] + + if params: + first_name, first_param = params[0] + # Prefer the evaluated type hint if available + ann = type_hints.get(first_name, first_param.annotation) + if ann != inspect._empty: + origin = get_origin(ann) or ann + if origin is RunContextWrapper: + takes_context = True # Mark that the function takes context + else: + filtered_params.append((first_name, first_param)) + else: + filtered_params.append((first_name, first_param)) + + # For parameters other than the first, raise error if any use RunContextWrapper. + for name, param in params[1:]: + ann = type_hints.get(name, param.annotation) + if ann != inspect._empty: + origin = get_origin(ann) or ann + if origin is RunContextWrapper: + raise UserError( + f"RunContextWrapper param found at non-first position in function" + f" {func.__name__}" + ) + filtered_params.append((name, param)) + + # We will collect field definitions for create_model as a dict: + # field_name -> (type_annotation, default_value_or_Field(...)) + fields: dict[str, Any] = {} + + for name, param in filtered_params: + ann = type_hints.get(name, param.annotation) + default = param.default + + # If there's no type hint, assume `Any` + if ann == inspect._empty: + ann = Any + + # If a docstring param description exists, use it + field_description = param_descs.get(name, None) + + # Handle different parameter kinds + if param.kind == param.VAR_POSITIONAL: + # e.g. *args: extend positional args + if get_origin(ann) is tuple: + # e.g. def foo(*args: tuple[int, ...]) -> treat as List[int] + args_of_tuple = get_args(ann) + if len(args_of_tuple) == 2 and args_of_tuple[1] is Ellipsis: + ann = list[args_of_tuple[0]] # type: ignore + else: + ann = list[Any] + else: + # If user wrote *args: int, treat as List[int] + ann = list[ann] # type: ignore + + # Default factory to empty list + fields[name] = ( + ann, + Field(default_factory=list, description=field_description), # type: ignore + ) + + elif param.kind == param.VAR_KEYWORD: + # **kwargs handling + if get_origin(ann) is dict: + # e.g. def foo(**kwargs: dict[str, int]) + dict_args = get_args(ann) + if len(dict_args) == 2: + ann = dict[dict_args[0], dict_args[1]] # type: ignore + else: + ann = dict[str, Any] + else: + # e.g. def foo(**kwargs: int) -> Dict[str, int] + ann = dict[str, ann] # type: ignore + + fields[name] = ( + ann, + Field(default_factory=dict, description=field_description), # type: ignore + ) + + else: + # Normal parameter + if default == inspect._empty: + # Required field + fields[name] = ( + ann, + Field(..., description=field_description), + ) + else: + # Parameter with a default value + fields[name] = ( + ann, + Field(default=default, description=field_description), + ) + + # 3. Dynamically build a Pydantic model + dynamic_model = create_model(f"{func_name}_args", __base__=BaseModel, **fields) + + # 4. Build JSON schema from that model + json_schema = dynamic_model.model_json_schema() + if strict_json_schema: + json_schema = ensure_strict_json_schema(json_schema) + + # 5. Return as a FuncSchema dataclass + return FuncSchema( + name=func_name, + description=description_override or doc_info.description if doc_info else None, + params_pydantic_model=dynamic_model, + params_json_schema=json_schema, + signature=sig, + takes_context=takes_context, + ) diff --git a/src/agents/guardrail.py b/src/agents/guardrail.py new file mode 100644 index 0000000..fcae0b8 --- /dev/null +++ b/src/agents/guardrail.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Generic, Union, overload + +from typing_extensions import TypeVar + +from ._utils import MaybeAwaitable +from .exceptions import UserError +from .items import TResponseInputItem +from .run_context import RunContextWrapper, TContext + +if TYPE_CHECKING: + from .agent import Agent + + +@dataclass +class GuardrailFunctionOutput: + """The output of a guardrail function.""" + + output_info: Any + """ + Optional information about the guardrail's output. For example, the guardrail could include + information about the checks it performed and granular results. + """ + + tripwire_triggered: bool + """ + Whether the tripwire was triggered. If triggered, the agent's execution will be halted. + """ + + +@dataclass +class InputGuardrailResult: + """The result of a guardrail run.""" + + guardrail: InputGuardrail[Any] + """ + The guardrail that was run. + """ + + output: GuardrailFunctionOutput + """The output of the guardrail function.""" + + +@dataclass +class OutputGuardrailResult: + """The result of a guardrail run.""" + + guardrail: OutputGuardrail[Any] + """ + The guardrail that was run. + """ + + agent_output: Any + """ + The output of the agent that was checked by the guardrail. + """ + + agent: Agent[Any] + """ + The agent that was checked by the guardrail. + """ + + output: GuardrailFunctionOutput + """The output of the guardrail function.""" + + +@dataclass +class InputGuardrail(Generic[TContext]): + """Input guardrails are checks that run in parallel to the agent's execution. + They can be used to do things like: + - Check if input messages are off-topic + - Take over control of the agent's execution if an unexpected input is detected + + You can use the `@input_guardrail()` decorator to turn a function into an `InputGuardrail`, or + create an `InputGuardrail` manually. + + Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, the agent + execution will immediately stop and a `InputGuardrailTripwireTriggered` exception will be raised + """ + + guardrail_function: Callable[ + [RunContextWrapper[TContext], Agent[Any], str | list[TResponseInputItem]], + MaybeAwaitable[GuardrailFunctionOutput], + ] + """A function that receives the the agent input and the context, and returns a + `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally + include information about the guardrail's output. + """ + + name: str | None = None + """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail + function's name. + """ + + def get_name(self) -> str: + if self.name: + return self.name + + return self.guardrail_function.__name__ + + async def run( + self, + agent: Agent[Any], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> InputGuardrailResult: + if not callable(self.guardrail_function): + raise UserError(f"Guardrail function must be callable, got {self.guardrail_function}") + + output = self.guardrail_function(context, agent, input) + if inspect.isawaitable(output): + return InputGuardrailResult( + guardrail=self, + output=await output, + ) + + return InputGuardrailResult( + guardrail=self, + output=output, + ) + + +@dataclass +class OutputGuardrail(Generic[TContext]): + """Output guardrails are checks that run on the final output of an agent. + They can be used to do check if the output passes certain validation criteria + + You can use the `@output_guardrail()` decorator to turn a function into an `OutputGuardrail`, + or create an `OutputGuardrail` manually. + + Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, a + `OutputGuardrailTripwireTriggered` exception will be raised. + """ + + guardrail_function: Callable[ + [RunContextWrapper[TContext], Agent[Any], Any], + MaybeAwaitable[GuardrailFunctionOutput], + ] + """A function that receives the final agent, its output, and the context, and returns a + `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally + include information about the guardrail's output. + """ + + name: str | None = None + """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail + function's name. + """ + + def get_name(self) -> str: + if self.name: + return self.name + + return self.guardrail_function.__name__ + + async def run( + self, context: RunContextWrapper[TContext], agent: Agent[Any], agent_output: Any + ) -> OutputGuardrailResult: + if not callable(self.guardrail_function): + raise UserError(f"Guardrail function must be callable, got {self.guardrail_function}") + + output = self.guardrail_function(context, agent, agent_output) + if inspect.isawaitable(output): + return OutputGuardrailResult( + guardrail=self, + agent=agent, + agent_output=agent_output, + output=await output, + ) + + return OutputGuardrailResult( + guardrail=self, + agent=agent, + agent_output=agent_output, + output=output, + ) + + +TContext_co = TypeVar("TContext_co", bound=Any, covariant=True) + +# For InputGuardrail +_InputGuardrailFuncSync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Union[str, list[TResponseInputItem]]], + GuardrailFunctionOutput, +] +_InputGuardrailFuncAsync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Union[str, list[TResponseInputItem]]], + Awaitable[GuardrailFunctionOutput], +] + + +@overload +def input_guardrail( + func: _InputGuardrailFuncSync[TContext_co], +) -> InputGuardrail[TContext_co]: ... + + +@overload +def input_guardrail( + func: _InputGuardrailFuncAsync[TContext_co], +) -> InputGuardrail[TContext_co]: ... + + +@overload +def input_guardrail( + *, + name: str | None = None, +) -> Callable[ + [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], + InputGuardrail[TContext_co], +]: ... + + +def input_guardrail( + func: _InputGuardrailFuncSync[TContext_co] + | _InputGuardrailFuncAsync[TContext_co] + | None = None, + *, + name: str | None = None, +) -> ( + InputGuardrail[TContext_co] + | Callable[ + [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], + InputGuardrail[TContext_co], + ] +): + """ + Decorator that transforms a sync or async function into an `InputGuardrail`. + It can be used directly (no parentheses) or with keyword args, e.g.: + + @input_guardrail + def my_sync_guardrail(...): ... + + @input_guardrail(name="guardrail_name") + async def my_async_guardrail(...): ... + """ + + def decorator( + f: _InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co], + ) -> InputGuardrail[TContext_co]: + return InputGuardrail(guardrail_function=f, name=name) + + if func is not None: + # Decorator was used without parentheses + return decorator(func) + + # Decorator used with keyword arguments + return decorator + + +_OutputGuardrailFuncSync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Any], + GuardrailFunctionOutput, +] +_OutputGuardrailFuncAsync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Any], + Awaitable[GuardrailFunctionOutput], +] + + +@overload +def output_guardrail( + func: _OutputGuardrailFuncSync[TContext_co], +) -> OutputGuardrail[TContext_co]: ... + + +@overload +def output_guardrail( + func: _OutputGuardrailFuncAsync[TContext_co], +) -> OutputGuardrail[TContext_co]: ... + + +@overload +def output_guardrail( + *, + name: str | None = None, +) -> Callable[ + [_OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co]], + OutputGuardrail[TContext_co], +]: ... + + +def output_guardrail( + func: _OutputGuardrailFuncSync[TContext_co] + | _OutputGuardrailFuncAsync[TContext_co] + | None = None, + *, + name: str | None = None, +) -> ( + OutputGuardrail[TContext_co] + | Callable[ + [_OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co]], + OutputGuardrail[TContext_co], + ] +): + """ + Decorator that transforms a sync or async function into an `OutputGuardrail`. + It can be used directly (no parentheses) or with keyword args, e.g.: + + @output_guardrail + def my_sync_guardrail(...): ... + + @output_guardrail(name="guardrail_name") + async def my_async_guardrail(...): ... + """ + + def decorator( + f: _OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co], + ) -> OutputGuardrail[TContext_co]: + return OutputGuardrail(guardrail_function=f, name=name) + + if func is not None: + # Decorator was used without parentheses + return decorator(func) + + # Decorator used with keyword arguments + return decorator diff --git a/src/agents/handoffs.py b/src/agents/handoffs.py new file mode 100644 index 0000000..ac15740 --- /dev/null +++ b/src/agents/handoffs.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Generic, cast, overload + +from pydantic import TypeAdapter +from typing_extensions import TypeAlias, TypeVar + +from . import _utils +from .exceptions import ModelBehaviorError, UserError +from .items import RunItem, TResponseInputItem +from .run_context import RunContextWrapper, TContext +from .strict_schema import ensure_strict_json_schema +from .tracing.spans import SpanError + +if TYPE_CHECKING: + from .agent import Agent + + +# The handoff input type is the type of data passed when the agent is called via a handoff. +THandoffInput = TypeVar("THandoffInput", default=Any) + +OnHandoffWithInput = Callable[[RunContextWrapper[Any], THandoffInput], Any] +OnHandoffWithoutInput = Callable[[RunContextWrapper[Any]], Any] + + +@dataclass(frozen=True) +class HandoffInputData: + input_history: str | tuple[TResponseInputItem, ...] + """ + The input history before `Runner.run()` was called. + """ + + pre_handoff_items: tuple[RunItem, ...] + """ + The items generated before the agent turn where the handoff was invoked. + """ + + new_items: tuple[RunItem, ...] + """ + The new items generated during the current agent turn, including the item that triggered the + handoff and the tool output message representing the response from the handoff output. + """ + + +HandoffInputFilter: TypeAlias = Callable[[HandoffInputData], HandoffInputData] +"""A function that filters the input data passed to the next agent.""" + + +@dataclass +class Handoff(Generic[TContext]): + """A handoff is when an agent delegates a task to another agent. + For example, in a customer support scenario you might have a "triage agent" that determines + which agent should handle the user's request, and sub-agents that specialize in different + areas like billing, account management, etc. + """ + + tool_name: str + """The name of the tool that represents the handoff.""" + + tool_description: str + """The description of the tool that represents the handoff.""" + + input_json_schema: dict[str, Any] + """The JSON schema for the handoff input. Can be empty if the handoff does not take an input. + """ + + on_invoke_handoff: Callable[[RunContextWrapper[Any], str], Awaitable[Agent[TContext]]] + """The function that invokes the handoff. The parameters passed are: + 1. The handoff run context + 2. The arguments from the LLM, as a JSON string. Empty string if input_json_schema is empty. + + Must return an agent. + """ + + agent_name: str + """The name of the agent that is being handed off to.""" + + input_filter: HandoffInputFilter | None = None + """A function that filters the inputs that are passed to the next agent. By default, the new + agent sees the entire conversation history. In some cases, you may want to filter inputs e.g. + to remove older inputs, or remove tools from existing inputs. + + The function will receive the entire conversation history so far, including the input item + that triggered the handoff and a tool call output item representing the handoff tool's output. + + You are free to modify the input history or new items as you see fit. The next agent that + runs will receive `handoff_input_data.all_items`. + + IMPORTANT: in streaming mode, we will not stream anything as a result of this function. The + items generated before will already have been streamed. + """ + + strict_json_schema: bool = True + """Whether the input JSON schema is in strict mode. We **strongly** recommend setting this to + True, as it increases the likelihood of correct JSON input. + """ + + def get_transfer_message(self, agent: Agent[Any]) -> str: + base = f"{{'assistant': '{agent.name}'}}" + return base + + @classmethod + def default_tool_name(cls, agent: Agent[Any]) -> str: + return _utils.transform_string_function_style(f"transfer_to_{agent.name}") + + @classmethod + def default_tool_description(cls, agent: Agent[Any]) -> str: + return ( + f"Handoff to the {agent.name} agent to handle the request. " + f"{agent.handoff_description or ''}" + ) + + +@overload +def handoff( + agent: Agent[TContext], + *, + tool_name_override: str | None = None, + tool_description_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +@overload +def handoff( + agent: Agent[TContext], + *, + on_handoff: OnHandoffWithInput[THandoffInput], + input_type: type[THandoffInput], + tool_description_override: str | None = None, + tool_name_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +@overload +def handoff( + agent: Agent[TContext], + *, + on_handoff: OnHandoffWithoutInput, + tool_description_override: str | None = None, + tool_name_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +def handoff( + agent: Agent[TContext], + tool_name_override: str | None = None, + tool_description_override: str | None = None, + on_handoff: OnHandoffWithInput[THandoffInput] | OnHandoffWithoutInput | None = None, + input_type: type[THandoffInput] | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: + """Create a handoff from an agent. + + Args: + agent: The agent to handoff to, or a function that returns an agent. + tool_name_override: Optional override for the name of the tool that represents the handoff. + tool_description_override: Optional override for the description of the tool that + represents the handoff. + on_handoff: A function that runs when the handoff is invoked. + input_type: the type of the input to the handoff. If provided, the input will be validated + against this type. Only relevant if you pass a function that takes an input. + input_filter: a function that filters the inputs that are passed to the next agent. + """ + assert (on_handoff and input_type) or not (on_handoff and input_type), ( + "You must provide either both on_input and input_type, or neither" + ) + type_adapter: TypeAdapter[Any] | None + if input_type is not None: + assert callable(on_handoff), "on_handoff must be callable" + sig = inspect.signature(on_handoff) + if len(sig.parameters) != 2: + raise UserError("on_handoff must take two arguments: context and input") + + type_adapter = TypeAdapter(input_type) + input_json_schema = type_adapter.json_schema() + else: + type_adapter = None + input_json_schema = {} + if on_handoff is not None: + sig = inspect.signature(on_handoff) + if len(sig.parameters) != 1: + raise UserError("on_handoff must take one argument: context") + + async def _invoke_handoff( + ctx: RunContextWrapper[Any], input_json: str | None = None + ) -> Agent[Any]: + if input_type is not None and type_adapter is not None: + if input_json is None: + _utils.attach_error_to_current_span( + SpanError( + message="Handoff function expected non-null input, but got None", + data={"details": "input_json is None"}, + ) + ) + raise ModelBehaviorError("Handoff function expected non-null input, but got None") + + validated_input = _utils.validate_json( + json_str=input_json, + type_adapter=type_adapter, + partial=False, + ) + input_func = cast(OnHandoffWithInput[THandoffInput], on_handoff) + if inspect.iscoroutinefunction(input_func): + await input_func(ctx, validated_input) + else: + input_func(ctx, validated_input) + elif on_handoff is not None: + no_input_func = cast(OnHandoffWithoutInput, on_handoff) + if inspect.iscoroutinefunction(no_input_func): + await no_input_func(ctx) + else: + no_input_func(ctx) + + return agent + + tool_name = tool_name_override or Handoff.default_tool_name(agent) + tool_description = tool_description_override or Handoff.default_tool_description(agent) + + # Always ensure the input JSON schema is in strict mode + # If there is a need, we can make this configurable in the future + input_json_schema = ensure_strict_json_schema(input_json_schema) + + return Handoff( + tool_name=tool_name, + tool_description=tool_description, + input_json_schema=input_json_schema, + on_invoke_handoff=_invoke_handoff, + input_filter=input_filter, + agent_name=agent.name, + ) diff --git a/src/agents/items.py b/src/agents/items.py new file mode 100644 index 0000000..bbaf49d --- /dev/null +++ b/src/agents/items.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +import abc +import copy +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union + +from openai.types.responses import ( + Response, + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseInputItemParam, + ResponseOutputItem, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, + ResponseStreamEvent, +) +from openai.types.responses.response_input_item_param import ComputerCallOutput, FunctionCallOutput +from openai.types.responses.response_output_item import Reasoning +from pydantic import BaseModel +from typing_extensions import TypeAlias + +from .exceptions import AgentsException, ModelBehaviorError +from .usage import Usage + +if TYPE_CHECKING: + from .agent import Agent + +TResponse = Response +"""A type alias for the Response type from the OpenAI SDK.""" + +TResponseInputItem = ResponseInputItemParam +"""A type alias for the ResponseInputItemParam type from the OpenAI SDK.""" + +TResponseOutputItem = ResponseOutputItem +"""A type alias for the ResponseOutputItem type from the OpenAI SDK.""" + +TResponseStreamEvent = ResponseStreamEvent +"""A type alias for the ResponseStreamEvent type from the OpenAI SDK.""" + +T = TypeVar("T", bound=Union[TResponseOutputItem, TResponseInputItem]) + + +@dataclass +class RunItemBase(Generic[T], abc.ABC): + agent: Agent[Any] + """The agent whose run caused this item to be generated.""" + + raw_item: T + """The raw Responses item from the run. This will always be a either an output item (i.e. + `openai.types.responses.ResponseOutputItem` or an input item + (i.e. `openai.types.responses.ResponseInputItemParam`). + """ + + def to_input_item(self) -> TResponseInputItem: + """Converts this item into an input item suitable for passing to the model.""" + if isinstance(self.raw_item, dict): + # We know that input items are dicts, so we can ignore the type error + return self.raw_item # type: ignore + elif isinstance(self.raw_item, BaseModel): + # All output items are Pydantic models that can be converted to input items. + return self.raw_item.model_dump(exclude_unset=True) # type: ignore + else: + raise AgentsException(f"Unexpected raw item type: {type(self.raw_item)}") + + +@dataclass +class MessageOutputItem(RunItemBase[ResponseOutputMessage]): + """Represents a message from the LLM.""" + + raw_item: ResponseOutputMessage + """The raw response output message.""" + + type: Literal["message_output_item"] = "message_output_item" + + +@dataclass +class HandoffCallItem(RunItemBase[ResponseFunctionToolCall]): + """Represents a tool call for a handoff from one agent to another.""" + + raw_item: ResponseFunctionToolCall + """The raw response function tool call that represents the handoff.""" + + type: Literal["handoff_call_item"] = "handoff_call_item" + + +@dataclass +class HandoffOutputItem(RunItemBase[TResponseInputItem]): + """Represents the output of a handoff.""" + + raw_item: TResponseInputItem + """The raw input item that represents the handoff taking place.""" + + source_agent: Agent[Any] + """The agent that made the handoff.""" + + target_agent: Agent[Any] + """The agent that is being handed off to.""" + + type: Literal["handoff_output_item"] = "handoff_output_item" + + +ToolCallItemTypes: TypeAlias = Union[ + ResponseFunctionToolCall, + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, +] +"""A type that represents a tool call item.""" + + +@dataclass +class ToolCallItem(RunItemBase[ToolCallItemTypes]): + """Represents a tool call e.g. a function call or computer action call.""" + + raw_item: ToolCallItemTypes + """The raw tool call item.""" + + type: Literal["tool_call_item"] = "tool_call_item" + + +@dataclass +class ToolCallOutputItem(RunItemBase[Union[FunctionCallOutput, ComputerCallOutput]]): + """Represents the output of a tool call.""" + + raw_item: FunctionCallOutput | ComputerCallOutput + """The raw item from the model.""" + + output: str + """The output of the tool call.""" + + type: Literal["tool_call_output_item"] = "tool_call_output_item" + + +@dataclass +class ReasoningItem(RunItemBase[Reasoning]): + """Represents a reasoning item.""" + + raw_item: Reasoning + """The raw reasoning item.""" + + type: Literal["reasoning_item"] = "reasoning_item" + + +RunItem: TypeAlias = Union[ + MessageOutputItem, + HandoffCallItem, + HandoffOutputItem, + ToolCallItem, + ToolCallOutputItem, + ReasoningItem, +] +"""An item generated by an agent.""" + + +@dataclass +class ModelResponse: + output: list[TResponseOutputItem] + """A list of outputs (messages, tool calls, etc) generated by the model""" + + usage: Usage + """The usage information for the response.""" + + referenceable_id: str | None + """An ID for the response which can be used to refer to the response in subsequent calls to the + model. Not supported by all model providers. + """ + + def to_input_items(self) -> list[TResponseInputItem]: + """Convert the output into a list of input items suitable for passing to the model.""" + # We happen to know that the shape of the Pydantic output items are the same as the + # equivalent TypedDict input items, so we can just convert each one. + # This is also tested via unit tests. + return [it.model_dump(exclude_unset=True) for it in self.output] # type: ignore + + +class ItemHelpers: + @classmethod + def extract_last_content(cls, message: TResponseOutputItem) -> str: + """Extracts the last text content or refusal from a message.""" + if not isinstance(message, ResponseOutputMessage): + return "" + + last_content = message.content[-1] + if isinstance(last_content, ResponseOutputText): + return last_content.text + elif isinstance(last_content, ResponseOutputRefusal): + return last_content.refusal + else: + raise ModelBehaviorError(f"Unexpected content type: {type(last_content)}") + + @classmethod + def extract_last_text(cls, message: TResponseOutputItem) -> str | None: + """Extracts the last text content from a message, if any. Ignores refusals.""" + if isinstance(message, ResponseOutputMessage): + last_content = message.content[-1] + if isinstance(last_content, ResponseOutputText): + return last_content.text + + return None + + @classmethod + def input_to_new_input_list( + cls, input: str | list[TResponseInputItem] + ) -> list[TResponseInputItem]: + """Converts a string or list of input items into a list of input items.""" + if isinstance(input, str): + return [ + { + "content": input, + "role": "user", + } + ] + return copy.deepcopy(input) + + @classmethod + def text_message_outputs(cls, items: list[RunItem]) -> str: + """Concatenates all the text content from a list of message output items.""" + text = "" + for item in items: + if isinstance(item, MessageOutputItem): + text += cls.text_message_output(item) + return text + + @classmethod + def text_message_output(cls, message: MessageOutputItem) -> str: + """Extracts all the text content from a single message output item.""" + text = "" + for item in message.raw_item.content: + if isinstance(item, ResponseOutputText): + text += item.text + return text + + @classmethod + def tool_call_output_item( + cls, tool_call: ResponseFunctionToolCall, output: str + ) -> FunctionCallOutput: + """Creates a tool call output item from a tool call and its output.""" + return { + "call_id": tool_call.call_id, + "output": output, + "type": "function_call_output", + } diff --git a/src/agents/lifecycle.py b/src/agents/lifecycle.py new file mode 100644 index 0000000..8643248 --- /dev/null +++ b/src/agents/lifecycle.py @@ -0,0 +1,105 @@ +from typing import Any, Generic + +from .agent import Agent +from .run_context import RunContextWrapper, TContext +from .tool import Tool + + +class RunHooks(Generic[TContext]): + """A class that receives callbacks on various lifecycle events in an agent run. Subclass and + override the methods you need. + """ + + async def on_agent_start( + self, context: RunContextWrapper[TContext], agent: Agent[TContext] + ) -> None: + """Called before the agent is invoked. Called each time the current agent changes.""" + pass + + async def on_agent_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + """Called when the agent produces a final output.""" + pass + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + from_agent: Agent[TContext], + to_agent: Agent[TContext], + ) -> None: + """Called when a handoff occurs.""" + pass + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + """Called before a tool is invoked.""" + pass + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + """Called after a tool is invoked.""" + pass + + +class AgentHooks(Generic[TContext]): + """A class that receives callbacks on various lifecycle events for a specific agent. You can + set this on `agent.hooks` to receive events for that specific agent. + + Subclass and override the methods you need. + """ + + async def on_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext]) -> None: + """Called before the agent is invoked. Called each time the running agent is changed to this + agent.""" + pass + + async def on_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + """Called when the agent produces a final output.""" + pass + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + source: Agent[TContext], + ) -> None: + """Called when the agent is being handed off to. The `source` is the agent that is handing + off to this agent.""" + pass + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + """Called before a tool is invoked.""" + pass + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + """Called after a tool is invoked.""" + pass diff --git a/src/agents/logger.py b/src/agents/logger.py new file mode 100644 index 0000000..bd81a82 --- /dev/null +++ b/src/agents/logger.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("openai.agents") diff --git a/src/agents/model_settings.py b/src/agents/model_settings.py new file mode 100644 index 0000000..78cf9a8 --- /dev/null +++ b/src/agents/model_settings.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + + +@dataclass +class ModelSettings: + """Settings to use when calling an LLM. + + This class holds optional model configuration parameters (e.g. temperature, + top_p, penalties, truncation, etc.). + """ + temperature: float | None = None + top_p: float | None = None + frequency_penalty: float | None = None + presence_penalty: float | None = None + tool_choice: Literal["auto", "required", "none"] | str | None = None + parallel_tool_calls: bool | None = False + truncation: Literal["auto", "disabled"] | None = None + + def resolve(self, override: ModelSettings | None) -> ModelSettings: + """Produce a new ModelSettings by overlaying any non-None values from the + override on top of this instance.""" + if override is None: + return self + return ModelSettings( + temperature=override.temperature or self.temperature, + top_p=override.top_p or self.top_p, + frequency_penalty=override.frequency_penalty or self.frequency_penalty, + presence_penalty=override.presence_penalty or self.presence_penalty, + tool_choice=override.tool_choice or self.tool_choice, + parallel_tool_calls=override.parallel_tool_calls or self.parallel_tool_calls, + truncation=override.truncation or self.truncation, + ) diff --git a/src/agents/models/__init__.py b/src/agents/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/agents/models/__pycache__/__init__.cpython-311.pyc b/src/agents/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..809c19dff1f3cc9d75ee9c3cbb609c8a65e0043a GIT binary patch literal 1098 zcmaJ=&u`N(6tnt?@fbR| zZA0({dC0>73h@AAQHWRuXJ_;ODNq(eJBLK(5y+=!TLMod9n!a9T<%Lb&e;4`0J$cM`~ zr_fVifh8*}^I&zbHi}cyjpT0r*Vbx^b%>r2+TVbBgxT_e7P?cggXb05{vTpwDpF?_v%wTtT?xvai#m7jK@WlXn;U}OH+u-sCTX8{| literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/__init__.cpython-313.pyc b/src/agents/models/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62acacc3e9243e196b1d72ab89d042c94b949c8c GIT binary patch literal 162 zcmey&%ge<81Xk%6(n0iN5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iOerR!OQL%ne zu6}ZUN~(T-L26!Nrfy<-YFl_0 literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/__init__.cpython-39.pyc b/src/agents/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..772e18bd5abcc3967b71fa578a38c220bd020196 GIT binary patch literal 156 zcmYe~<>g`k0;}{3=^*+sh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vJKeRZts93)! zS3fyFB~?GaAT=*BQ#Ua^HLs*tx1h2lBR@~SxF}g4BBGxQRFqS!A0MBYmst`YuUAlc Ti^B#eSelb+2QvCI5HkP(50)j) literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/_openai_shared.cpython-311.pyc b/src/agents/models/__pycache__/_openai_shared.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31d955795bcdbe98d94ba451c69ea55a29e27378 GIT binary patch literal 1651 zcmbVMJx?1!5Z(1Hhp}-W7#VDA$9#my1wPTENJweYkb=&^~|tcJjs`XHsUq!h;)H`6f=4Hz2^<4jEPsU2X#%;YA_ru8iAO!FC-<@9Ws z&wXNtSzhMzU}t!R7s2NE3SR`9=T%+;JIiZ)32cEI`=nX_g=sWtly^MO51ioC_Y_Wd zRo`pB>k4o8?cOk_0@*lk?D?LEiZ&O=PR|W&A9kHn`%LtQ3n_X+EIlN)-BaNO?<02P z`)*XWdrH_+s4hel%0B8R{Kx>8kpVFyQ;MJ`y@+AxHpN^=4aXoNy+e&J$vJUAwtaF% z_pYckVXaAJ9*ja_V|ZX4C?S<4J67B0!g5X^6~)i&uJi*x=y#JPD-bGBR;y(x*&flX z4t(M&D>z`MI0{xM5U9-ftO%&4t^>Na6!K3>mjS_?;mjSeU8nNY(^tTkEFt_6pgcivKqcUi0g$ybI`+o`10ACM2^I4R$?c*N2 zVqx17UMz{s_>Uv@i%&J0v+dO15Ag8jBucJdlEkW(SXC15R?8~!WDm3$W_RGF_FVB= zzJgsWR`nBLU{FeL$XfWQ-;#Pb-f!17LsAS0Aogprd9$@0l2S+jv0szz8|0MJoPn94 n^#K`&9HlP?WE`>?x`Dhj0B;R>X#ided1(ONOXN)ieW$+xn(cUU literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/_openai_shared.cpython-313.pyc b/src/agents/models/__pycache__/_openai_shared.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33dfe316a181c8f74874d2184f80365ddbf70460 GIT binary patch literal 1429 zcmbVM&r2IY6rRbhF($@evC#yR#uzOr#6{?-l!8zwRj`Mtm$tCPI7ZvJ8{cdWfgXD3 zAJB`?f5HDI;zbrIfzm^7h1iRyzBilIC@H0#!{d8zzM1d6w{NzWOvV|m$MsJAnZejM za>9i^E#CWt_Lwa*!35uA8yt9naAY&GVSs_yI5LI#c0`OkKqh)o5k+rQd$FTYF@|1T zd($G3W$#Rgi5W4j(U?eyq(*TuD<(8bh1 z>nL*9Rokh(Y)WVS#nx3^c~JOJ*m51|r|ez1S8X*s+r{1Lf&E^#uVx15Au)ZISZf?e z$9v_QJFeUCGj>Z!8>DKYqEz-yJLK0m<apT-QlYVC0y+3FEB9U>qGtqB&tOO?VQGpRNxV zoW)Hd^aU>x`Hq>_=lgC&_x5qW`-2om(3!Rq_68;?3j!HSTB7btT%aUz6Q%)pt5i64on~9@%X|`3f)5G z)Xe=y<#fm0$``2eKPngS#Nv(eMWWO(OIo>X0*#Vy;6wDIAiWkizDe(~9DyS4zz7;- z%@H=q=;#tf`;5_mk)zGAp$v+aC;Yf=@3l}t+BVQ1%Z~={D7cqioj+;Y1OE%;(To8# zUOy3_vDG64^g(q?p+M-)B*dQJ6Kpl)Q=oqt4U^hL^V8s*e`Uq*EZ;4zbXe;1>oc}; sURmp~bZ7qI8CyH2aOTS+3|~egJbxMI{QhMu!b^mTgjRncTGs9R4fe)9kpKVy literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/_openai_shared.cpython-39.pyc b/src/agents/models/__pycache__/_openai_shared.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f95fb8d8017151958d7d29f9a89bd1b12868e12 GIT binary patch literal 1308 zcmbVLy>8nu5GEyA{{M-cIC1M1>F6QVUIayh7AU&7Lz=}4q39ULs4N+zJUGbM$M6gI zZ8~%9)K}=#JE?Ny!Uc>{5Ov2p^83Ey$!|7m49A=4_vr_lu^;3V2d=SqjB&k2RII;w#OR`;X(JrH1@yk#}yM}fh8qi!@(1P|iXHo$NI%^I` z&_%BbJ&Yb>^aT3o*T7Aqr*MW|9jX`|VDuS0N56qS?H$4k^qOE_v(d|+#Cya|-H)O; z@sn8`X>zahDhfU?WOVgu{7}=0I)6ML$B{HW0rJjYhKYzV>d(Yixq9dp((}ar--$su zlTq@;IJa>ent@nqDU{R;M3h?GuJU|iBb;#vZDbi6&o{1;$x=lDC$@hE+4R1rLRZju zWImSnnr--a!8kY6-)84W+gg+t1cb(L?q=f*dsZ^>0x%R`WWp) z<7oo%7GIzb!7Hg~${KQx!=9IpDiY*Hda(!6tip-YEbE|wx*hsr+P z1$vS_B-#H$d$mR5YJm2rWokm)E%C3ALMUoF<7WMwwn5tdrY%I_x5hlWU`2}4{>-|D m?kuflDkD98@3mAe@qbu`@-4NLrd=(oVKq2DcBj;GviCP~Q5u{8 literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/fake_id.cpython-313.pyc b/src/agents/models/__pycache__/fake_id.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07ae380a65694bce98922ab351f7e3dcb0f24b5f GIT binary patch literal 205 zcmey&%ge<81Xk%6(s>ve7#@Q-FaYF(!DkL2HI*Tlp@<=!QIqKwcYJ(WVs>hLW=ed# zpC;oiK{rQl*Z3gU-~fNWVAtSyPnVSppFuirIp~KLrxq3K7v<_F=clCV=NF{rC1&a- zrl;nW6zdjLmSp7T=@%Cz>qA8JbAgI-iuIwU>lIYq;;;dVmFA?{6|n;i1i7eK07!gb XW@Kc%A)g`k0;}{3={$@K439w^WB?RnZ~)?B4j_@j5XF$f7|fu_6vZ7MpO%=N8lRaG zAMdBhcuUaD(c3jX$Tc{?-!IrTINsA`B|{MtP!E{+<)9y0oLW?@UzDq#oS%}apI?xg zmzb%Wn4X$fQmk80S(1^Tr(ax@tPc^<&jl*VDb|Ntp;u6Oi^B#eR+^J)2Xav{$Yd5q E01x*tBme*a literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/interface.cpython-313.pyc b/src/agents/models/__pycache__/interface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f96c60bf49de263edd568f91dae3c9f3002a546b GIT binary patch literal 4222 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 literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/map.cpython-313.pyc b/src/agents/models/__pycache__/map.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f648281cc1b2aa9998e99c33d74ea97a0a86d0bf GIT binary patch literal 760 zcmZ8eziZo25WXkLvSeyEg9#+0U_zkL5USo$@A(G&Bc> zW&Wg>z)}+PIPCLr+neyD=7tduyrC;DJmh_gC8aD$VDlAanlFzoy!a@bL}MNg!$1_- z`Z9aMMMP1)j3gxi`WCOrTtlyj!-=wjE3geua~!ZP+s(0O z0`y8nQJ03Z&vu|!{y$q-l2rM`7c{_NiOj8}J|&{WoreVI0P@s0i5t{XA=DGK4+xqP=LARHcsP zsKT=m-X~)DxOhzF628^9zt#^w*AKtco0rGG4Y;>)r-J$5-LqR`FIBaYey2li_hxik z-A~KpF6cLCTa^S*4tLqBISgWs=bq0|PAgf@7wywN{lELn8cx>o8nu5GE;EmJKWL5M(Gabk$P5KoKPHk_>f#1X;8QjClTOMM*f)g8^p=y5k=d$J<}?C(hV!m%KNRl4CmNl$>IU z7fkb%Ymo}>a#48NPyIIbik=SAppE^aufsIt3^@3zcP~_+`rrL@pu$TQ51wfki$yc| zQYk2=QbV(sRW+1ynVE$|P%&?MZIOZx=Ttd4n=GWARJty}F00aX)%|Vt0!&RP>~PTx zwVR!lZg_xTS6Jq5ZD)c4CvQT>d?aVt5B`(g@D0ZUD>fwdj$bnCbx|y)j#WE0_R;;@ z9L~XJD+3vJ>$B^1reG#lg~fncutzQy*mv#Gf2|J;M}+=4Nv;f_Nl+&_aZcnMN^8`& ztj5Y#c3pQ+VldxTla_`_w5&hX>n3tMwt}ncuWpxVAC`x{cpZuG1@{P>#>0l86TW3X zSe6!WCUZca!1Q4E1UcPw{_mz+3F&xO{Ht^;&&_RRymJv@P57DktO_`Cmu^CGV?J`9 F_XZ?frB(m{ literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc b/src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..064b4f353091e7778cb6bb60b191e575cee03a1a GIT binary patch literal 34089 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 aJ!St*ldj|uNAA->TZPW@3l0uref}Te1bM>% literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/openai_chatcompletions.cpython-39.pyc b/src/agents/models/__pycache__/openai_chatcompletions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7d1e60dd509d798196096a3ddf5fa1529d5e44e GIT binary patch literal 20220 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 literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/openai_provider.cpython-313.pyc b/src/agents/models/__pycache__/openai_provider.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16ada95369641eca806628b5939d73774a5abb2c GIT binary patch literal 2868 zcmaJ@&2Jk;6rZ)%-)qPDXze5p-8yZz{v z&{!NI4pj~y!J!vUMM6S|3;zNqgoH%gfMSgbMQSo>Jq&-QJCTiCJQRCOc<6g%$+PmUWdLCm(5m=;P7^YpYOQvDXDIONN zV67X)%N5C1&`+2&&xZM$-7vRu*)`?27B9MeotER-~BwLtYH_}q8NT++)d z%9|_H{Gha?Q`ly}S#ff;U{9KBm9ow^=giLQRt3z^Ek0iJuT<>OS54-%Y|5fj z({EhJ&tBCoU7nhrttD5{nhZD0u}S_&o-+-dNg7URMQ&)(IkHoGpaBeo-7ygEkcIYK z5&XH(LWhv?*7(g1jbi{6phIIYN72XMkX=+`7qfXwr&gA(Ws7j~tb@mBVFggKMl0*~ zs%c~`TFg2l*)@d6YN=_J7C@QGI_q&T?6jWt!WQ_m4nws;9tC>qfx-KU%ywdIQ@S5X zY>5fl4O9D$8Gr#O!tPb5?~v<+3Mv883Dm24I{v&F{|n^5{?vdfB86^vsHg^2AE%Q~ z^;18PC&Xfo!(0bH9)PXp6h8|TDkbfGeVqjt3zn``DNwA(j$ zcA#{eg>-(Uh;y}xDbpCS(z3j6R@*B3h@kjr0(PTGtdKr*5Goew z2;N!@7R@+cWs2sSBC$Y=Mikw{`d4&YTXIsAn~9vbr|4{U_+RFMoII@f#!Or_Et`cc zyFhL0!;-zq;#$da*W$s-V%k1#vJ|emZ%A8Qcg;`&Otkd!a>wEN@o?=E(iC(pG+%ZK z?Lkw6npQGOwx+Sf{?N*}69|v>EmRw%*+-=QdgNH6ci{8$cW>24PB)1s8f(O_?0TWy z>OI}`la%u5?4v2%*T+=It!cYCA=hSy6f1dquw%+|tGXO&zSH&df%`yX#06*^))0$o8D&Y+ZdMEB)=ZbCEtjZemrI6jn3_h>xpWjNc1Y786zx(` zE7&$IEmm#a0);p#V4CAVQ%J&uS7^CqKy{k{ghz%O@PBHoF+9?EA>BBxJn2sPt_Tnc zJ*j4Zi$T&m)C_SkOp+&?5iUw3akLrbVhnY=xELqN;ij`f4++Pf_2TIBb3vbjJ~sd; zfv2x^|Kl~H^dSPvRlp1op!m38KCZ?5PDbM?ioYcoanf0+WY{`gE);bRCq)Lc!;ZFA zgqIWKK=o0Nf8M3!Zb_H9^mV3f>;p>rgel zf*|}((hrIBfSmr5q#lr$ej^jVkyrjWG_(=jw6@6*Abc{jA#KG@Y?INaVNpmvjg1K5 M&)$7Tpy7`D8*Jm7+yDRo literal 0 HcmV?d00001 diff --git a/src/agents/models/__pycache__/openai_provider.cpython-39.pyc b/src/agents/models/__pycache__/openai_provider.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6a5cbc3a727c2f476cd38fe56d3afc4acee0b23 GIT binary patch literal 2044 zcmZuyOLH4V5T4n$Rx4SSO>6>{QUMjv!P12blq(gNoe)xqLqUAm%hX!4mSpxJW=DWj z3kU3LZXA7xkNg|XbLEs@00l*b?pevmiFZq_?wQ%1?(ggAi5d-`!1MX=_u-!&A%CE; z`dDDxCxjNif^tMS;WQyB4Jk$4NQ}^ce={*tE3{HOv|-%hcH*RN=xW_bytEe9Qa|*y z?I!g!2m`HqNh58BP0(xM8u#NCukV7_Xq^Y3H$ZRlwF}ByeEr-C+g}jg<{RgPZ^ZtY z8E$an8`9l;h4FT&sz+It%Sev%tT^l%s=imuvca=Sob7$7whrQxXqrep_DIUfkNe3u z&g3gvecWU@ER4pi7)2uH@VWgo=W(JM`tK`|e?R82fNgG99`{F)+|ScV66+21Dwy4@ zX1|V$34{?BdVKB-CvxZ0T$zOws&Vk(;oi}c=j`dTg9lIM+arvorvs~E>Xej;h~-pd1B77pF~HbI+S;H(WhWW}TE)&}CP#VJFT|iffU2=Lg@l``SuwL@^04bDH=2yuxA9DQ$59cpsQ|jGbQhqjAQ!_Z8_#uBDHpOk zjR#USrm$QrIiCAK>w>g-d!_1@WA)=aPnN7&$ZgjZ2!+^0@eYbxD7H{+qiBK{U~S@q zfVqpGX7y(>A(dee`;5 zh#k+CVRT((6>)O1lH5%MUmZBn!azO7#@Sdh_8wS82L$mw+NQ>9%k&JN`qa>`^^fPZ zsYg9yzIDBzdrJ=jO9dL3uY+esN>Ps<Mvf9=-vBAV`8=;N!O-k{~6Lrbx?{DT$&4N~DHQu;oxP41td%Vh~{O z10{=9Y;C$t=}x+Kvg;L{WXJMMCbqJ>jiyaIYCCNc*NNTT?krI{0p^W7RwtR=@$4VS zQdVnsf9yH;y$6646sObOy`t_r_ug~A?>%4l;GWB6ClJP$wwBUEg!}>%df-)nyZ1*N zA+HmWh}<*@bCeShHcT6+0lvm*W7tGZEYCE}ht1T?V1C*Xwo+@@Mr~m`wTB(l!Sc=1 z&ajKR!fxsgSJA4lhk97PWx6`-rCtVGr)$DK>I>J>+Hf7M3)j>7u%G(F4YVQLNE-pi zCfcW)!p*dq!H(&caDWCF?3`{53shjRYq~AmPTLvmp6&>D(#~)f?Fx6(ZkAUy-4pJi zJq-3t?+FKKFua%U4foRCa3Adp_tSn>Ry`dG572?|ART0B@ASU#e!8E*HPb`kVLA-3 zPpq9D2|q?33y;!Kft<7M(N;2MCOG1{=>y?0ItG~a=Z4*6$Uw(M|GDu&jy`_QE;itD z(}RA}PQ=DWA~v0SY{aNGK%Y2gX8FyK-!fuQb4Rq+>65H10A;Ou89lUQ2nwGdMvzk+ zu~aIZiDeS$lsp4z*MxjI6+e-YXe^VakYQVhWn%GUOqPQt#dYS{)05F7$0v_WpO~3a zj1#HLisfViv6G55eNm#xbZh|-Z8NiHqEjcHo}5t}ti{zpL7!hj^DPKS=9S5}i!Mp~Ez{ESkg=c`Srne}?+8R^B$ zk#uS?v2;2{p(ERwbULYm-e;r>q73iYF;;3~v1`Y?f(FGV zuEtVqScmLvci(J1Vh3{6F&pTJEfP?2XtRh;Z|hQ{KF6wY0pfhc7% zke1ioY$@b{)0Z>L>6EZ;$5rTOnL*>{{zVvZRA%cOjmMJ75DZ~0nUU99RwNla4<&Ho z(ZmXaF^;a$ec#O|zvAX&;7+XmLEedeANccdw6;qH~0DA_4NlQKM*}rT{7Z z6J4UQk$l-idC@JJ7-TM$R))kXk!P5e%H^e8(Ic7}ruCeylv^1Rt3?aq7B8y(H>l$k ztvl-kh*(p(eLWWlj}7a5nWJ{GRh5?Yyz-3Wr9oEX3p zC=bw;fjHD2P_g>qV5Ks0|0+AC12T;ds5t|y3t2;p>Ni9VtzK5F(P$!-$V8(`OQrP* z>BYm)wd{uIDk<1WUDMa?`TEX3aNdI)64xj(!UQMCrtb&fbrRt&Kxu^QB=cCxLMNg7 z0Js7B1fQURV&e*bF_v7DWNb?i=wp~w7fr!!O|2!9(KH1(6J0_Qs`OSKYDMIO(2(qf z=qkzkTeAM(#@d$kz<;<55gmi&6-0Nm90XV32BK>1qK`pZJZsh@(`+K6**P|q)Y=k}Li z9UA0B*O)=C3G*-A3nVJL0^AFC#EBwjsfGmWmvT-x7r909gt3(@a&4rK=+j<+TeJx4 z(F&(lATu{rj8Wqv7xc{B;1ugZLXKTXN(;vzVV0S!x9&Q5@|2)c*@bnxo*cJRJlu$x zNG$-ZRyaGwoAxl@=u`rQ#A!FPc~gw$LsZs^Un< zQGL=%Rdi9x#FvXKg#u|JO3l&PnKRLovs2MivqvXSMvqRMnV@)q6rM?!is{R8It7Wf zl~tL}Vu78ps3_*p(~v>CF*=UX35>!J;mX95YYS2oC!^R)NN#n=&^(h86+Q#zz|$UlPP`w4Kq`#$1sx;~Zb9L;u)Zg-CTy=(l| zO2N?Us>`=^<=RHFZ6kSKPu|y%_qDUQE$?g2HwlF*(*9V1Slhi1h_%`KniU%IHRfvi z;qSJu@!H96p1j`rGvB^ns&L-lb-Stc&GB!I-+J+{U;gu#x4UQm{N-D1Z(n)q%J*LS zvzN2ovs-7M%{F~$t8sp7{(Qm6HAK1lMq_PV!B70cwU@5Fbl*j48}n`L`L6!ApL^@M zT-Q_CuBYkHMSx~@=1d`-EUo@`A|t|pkR z3Fb!+<$S$aU+;&$;r#HJ4)1$#gd^_9@1Ns{rzKz0bnVbL4&}Y|`IfFX=iivWYcYB3 z1!A(;%m7_?67Q}mYk<2>*=}eYsm`-?lgD2b164)Bs*{< zH!ztUnEbBzPW5-6{$SsKef)19&rL_O)6s&_5NN#1<9LmhYVUmysjk1~{HAlOb@Uc@ zQ{HNM{6o*dyr=db3$@Vx=MN@vd_QxI|8My@*vYT@kGr^UaZ?A4-|-*g3_s*1$IO2+ z(F)+ZzJ|$zo_G5XcR}JO-X6gF$ry+5K_23ta`ve){-=(K;M6ex{xA=T_s5LT&ie=L z$L*F6%oeEdkPKurfDH7nKotA`AOo3p%0QwiWslfA#0xUajuOYb1r~yZh-2QW!#eNN zDRvCxp@?Iy$Om*h;9d5JW7Kq!yv_;a6bPVJGBXDMLv|9e7i$BP2JVG-a960vz2BKD z%1tHoWKsG9?p>+8IQH4!1`b=~^<^Bkw$z#q%_C`LLF7gAZ#YKeutj-k7l*ANCCyck zhk(O^oL1tO^F>K$PS6Ha_5luSt-xVTJ2|Xq!hibC31rFiXr8-dD&x7f_0#re6H;

ihD7U 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 None: + global _default_openai_key + _default_openai_key = key + + +def get_default_openai_key() -> str | None: + return _default_openai_key + + +def set_default_openai_client(client: AsyncOpenAI) -> None: + global _default_openai_client + _default_openai_client = client + + +def get_default_openai_client() -> AsyncOpenAI | None: + return _default_openai_client + + +def set_use_responses_by_default(use_responses: bool) -> None: + global _use_responses_by_default + _use_responses_by_default = use_responses + + +def get_use_responses_by_default() -> bool: + return _use_responses_by_default diff --git a/src/agents/models/fake_id.py b/src/agents/models/fake_id.py new file mode 100644 index 0000000..0565b0a --- /dev/null +++ b/src/agents/models/fake_id.py @@ -0,0 +1,5 @@ +FAKE_RESPONSES_ID = "__fake_id__" +"""This is a placeholder ID used to fill in the `id` field in Responses API related objects. It's +useful when you're creating Responses objects from non-Responses APIs, e.g. the OpenAI Chat +Completions API or other LLM providers. +""" diff --git a/src/agents/models/interface.py b/src/agents/models/interface.py new file mode 100644 index 0000000..e9a8700 --- /dev/null +++ b/src/agents/models/interface.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import abc +import enum +from collections.abc import AsyncIterator +from typing import TYPE_CHECKING + +from ..agent_output import AgentOutputSchema +from ..handoffs import Handoff +from ..items import ModelResponse, TResponseInputItem, TResponseStreamEvent +from ..tool import Tool + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +class ModelTracing(enum.Enum): + DISABLED = 0 + """Tracing is disabled entirely.""" + + ENABLED = 1 + """Tracing is enabled, and all data is included.""" + + ENABLED_WITHOUT_DATA = 2 + """Tracing is enabled, but inputs/outputs are not included.""" + + def is_disabled(self) -> bool: + return self == ModelTracing.DISABLED + + def include_data(self) -> bool: + return self == ModelTracing.ENABLED + + +class Model(abc.ABC): + """The base interface for calling an LLM.""" + + @abc.abstractmethod + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + """Get a response from the model. + + Args: + system_instructions: The system instructions to use. + input: The input items to the model, in OpenAI Responses format. + model_settings: The model settings to use. + tools: The tools available to the model. + output_schema: The output schema to use. + handoffs: The handoffs available to the model. + tracing: Tracing configuration. + + Returns: + The full model response. + """ + pass + + @abc.abstractmethod + def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[TResponseStreamEvent]: + """Stream a response from the model. + + Args: + system_instructions: The system instructions to use. + input: The input items to the model, in OpenAI Responses format. + model_settings: The model settings to use. + tools: The tools available to the model. + output_schema: The output schema to use. + handoffs: The handoffs available to the model. + tracing: Tracing configuration. + + Returns: + An iterator of response stream events, in OpenAI Responses format. + """ + pass + + +class ModelProvider(abc.ABC): + """The base interface for a model provider. + + Model provider is responsible for looking up Models by name. + """ + + @abc.abstractmethod + def get_model(self, model_name: str | None) -> Model: + """Get a model by name. + + Args: + model_name: The name of the model to get. + + Returns: + The model. + """ diff --git a/src/agents/models/openai_chatcompletions.py b/src/agents/models/openai_chatcompletions.py new file mode 100644 index 0000000..a7340d0 --- /dev/null +++ b/src/agents/models/openai_chatcompletions.py @@ -0,0 +1,952 @@ +from __future__ import annotations + +import dataclasses +import json +import time +from collections.abc import AsyncIterator, Iterable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Literal, cast, overload + +from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, NotGiven +from openai.types import ChatModel +from openai.types.chat import ( + ChatCompletion, + ChatCompletionAssistantMessageParam, + ChatCompletionChunk, + ChatCompletionContentPartImageParam, + ChatCompletionContentPartParam, + ChatCompletionContentPartTextParam, + ChatCompletionDeveloperMessageParam, + ChatCompletionMessage, + ChatCompletionMessageParam, + ChatCompletionMessageToolCallParam, + ChatCompletionSystemMessageParam, + ChatCompletionToolChoiceOptionParam, + ChatCompletionToolMessageParam, + ChatCompletionUserMessageParam, +) +from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam +from openai.types.chat.completion_create_params import ResponseFormat +from openai.types.completion_usage import CompletionUsage +from openai.types.responses import ( + EasyInputMessageParam, + Response, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseFileSearchToolCallParam, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionToolCall, + ResponseFunctionToolCallParam, + ResponseInputContentParam, + ResponseInputImageParam, + ResponseInputTextParam, + ResponseOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputMessageParam, + ResponseOutputRefusal, + ResponseOutputText, + ResponseRefusalDeltaEvent, + ResponseTextDeltaEvent, +) +from openai.types.responses.response_input_param import FunctionCallOutput, ItemReference, Message + +from .. import _debug +from ..agent_output import AgentOutputSchema +from ..exceptions import AgentsException, UserError +from ..handoffs import Handoff +from ..items import ModelResponse, TResponseInputItem, TResponseOutputItem, TResponseStreamEvent +from ..logger import logger +from ..tool import FunctionTool, Tool +from ..tracing import generation_span +from ..tracing.span_data import GenerationSpanData +from ..tracing.spans import Span +from ..usage import Usage +from ..version import __version__ +from .fake_id import FAKE_RESPONSES_ID +from .interface import Model, ModelTracing + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +_USER_AGENT = f"Agents/Python {__version__}" +_HEADERS = {"User-Agent": _USER_AGENT} + + +@dataclass +class _StreamingState: + started: bool = False + text_content_index_and_output: tuple[int, ResponseOutputText] | None = None + refusal_content_index_and_output: tuple[int, ResponseOutputRefusal] | None = None + function_calls: dict[int, ResponseFunctionToolCall] = field(default_factory=dict) + + +class OpenAIChatCompletionsModel(Model): + def __init__( + self, + model: str | ChatModel, + openai_client: AsyncOpenAI, + ) -> None: + self.model = model + self._client = openai_client + + def _non_null_or_not_given(self, value: Any) -> Any: + return value if value is not None else NOT_GIVEN + + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + with generation_span( + model=str(self.model), + model_config=dataclasses.asdict(model_settings) + | {"base_url": str(self._client.base_url)}, + disabled=tracing.is_disabled(), + ) as span_generation: + response = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + span_generation, + tracing, + stream=False, + ) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Received model response") + else: + logger.debug( + f"LLM resp:\n{json.dumps(response.choices[0].message.model_dump(), indent=2)}\n" + ) + + usage = ( + Usage( + requests=1, + input_tokens=response.usage.prompt_tokens, + output_tokens=response.usage.completion_tokens, + total_tokens=response.usage.total_tokens, + ) + if response.usage + else Usage() + ) + if tracing.include_data(): + span_generation.span_data.output = [response.choices[0].message.model_dump()] + span_generation.span_data.usage = { + "input_tokens": usage.input_tokens, + "output_tokens": usage.output_tokens, + } + + items = _Converter.message_to_output_items(response.choices[0].message) + + return ModelResponse( + output=items, + usage=usage, + referenceable_id=None, + ) + + async def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[TResponseStreamEvent]: + """ + Yields a partial message as it is generated, as well as the usage information. + """ + with generation_span( + model=str(self.model), + model_config=dataclasses.asdict(model_settings) + | {"base_url": str(self._client.base_url)}, + disabled=tracing.is_disabled(), + ) as span_generation: + response, stream = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + span_generation, + tracing, + stream=True, + ) + + usage: CompletionUsage | None = None + state = _StreamingState() + + async for chunk in stream: + if not state.started: + state.started = True + yield ResponseCreatedEvent( + response=response, + type="response.created", + ) + + # The usage is only available in the last chunk + usage = chunk.usage + + if not chunk.choices or not chunk.choices[0].delta: + continue + + delta = chunk.choices[0].delta + + # Handle text + if delta.content: + if not state.text_content_index_and_output: + # Initialize a content tracker for streaming text + state.text_content_index_and_output = ( + 0 if not state.refusal_content_index_and_output else 1, + ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + ) + # Start a new assistant message stream + assistant_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="in_progress", + ) + # Notify consumers of the start of a new output message + first content part + yield ResponseOutputItemAddedEvent( + item=assistant_item, + output_index=0, + type="response.output_item.added", + ) + yield ResponseContentPartAddedEvent( + content_index=state.text_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + type="response.content_part.added", + ) + # Emit the delta for this segment of content + yield ResponseTextDeltaEvent( + content_index=state.text_content_index_and_output[0], + delta=delta.content, + item_id=FAKE_RESPONSES_ID, + output_index=0, + type="response.output_text.delta", + ) + # Accumulate the text into the response part + state.text_content_index_and_output[1].text += delta.content + + # Handle refusals (model declines to answer) + if delta.refusal: + if not state.refusal_content_index_and_output: + # Initialize a content tracker for streaming refusal text + state.refusal_content_index_and_output = ( + 0 if not state.text_content_index_and_output else 1, + ResponseOutputRefusal(refusal="", type="refusal"), + ) + # Start a new assistant message if one doesn't exist yet (in-progress) + assistant_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="in_progress", + ) + # Notify downstream that assistant message + first content part are starting + yield ResponseOutputItemAddedEvent( + item=assistant_item, + output_index=0, + type="response.output_item.added", + ) + yield ResponseContentPartAddedEvent( + content_index=state.refusal_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + type="response.content_part.added", + ) + # Emit the delta for this segment of refusal + yield ResponseRefusalDeltaEvent( + content_index=state.refusal_content_index_and_output[0], + delta=delta.refusal, + item_id=FAKE_RESPONSES_ID, + output_index=0, + type="response.refusal.delta", + ) + # Accumulate the refusal string in the output part + state.refusal_content_index_and_output[1].refusal += delta.refusal + + # Handle tool calls + # Because we don't know the name of the function until the end of the stream, we'll + # save everything and yield events at the end + if delta.tool_calls: + for tc_delta in delta.tool_calls: + if tc_delta.index not in state.function_calls: + state.function_calls[tc_delta.index] = ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + arguments="", + name="", + type="function_call", + call_id="", + ) + tc_function = tc_delta.function + + state.function_calls[tc_delta.index].arguments += ( + tc_function.arguments if tc_function else "" + ) or "" + state.function_calls[tc_delta.index].name += ( + tc_function.name if tc_function else "" + ) or "" + state.function_calls[tc_delta.index].call_id += tc_delta.id or "" + + function_call_starting_index = 0 + if state.text_content_index_and_output: + function_call_starting_index += 1 + # Send end event for this content part + yield ResponseContentPartDoneEvent( + content_index=state.text_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=state.text_content_index_and_output[1], + type="response.content_part.done", + ) + + if state.refusal_content_index_and_output: + function_call_starting_index += 1 + # Send end event for this content part + yield ResponseContentPartDoneEvent( + content_index=state.refusal_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=state.refusal_content_index_and_output[1], + type="response.content_part.done", + ) + + # Actually send events for the function calls + for function_call in state.function_calls.values(): + # First, a ResponseOutputItemAdded for the function call + yield ResponseOutputItemAddedEvent( + item=ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=function_call.call_id, + arguments=function_call.arguments, + name=function_call.name, + type="function_call", + ), + output_index=function_call_starting_index, + type="response.output_item.added", + ) + # Then, yield the args + yield ResponseFunctionCallArgumentsDeltaEvent( + delta=function_call.arguments, + item_id=FAKE_RESPONSES_ID, + output_index=function_call_starting_index, + type="response.function_call_arguments.delta", + ) + # Finally, the ResponseOutputItemDone + yield ResponseOutputItemDoneEvent( + item=ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=function_call.call_id, + arguments=function_call.arguments, + name=function_call.name, + type="function_call", + ), + output_index=function_call_starting_index, + type="response.output_item.done", + ) + + # Finally, send the Response completed event + outputs: list[ResponseOutputItem] = [] + if state.text_content_index_and_output or state.refusal_content_index_and_output: + assistant_msg = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="completed", + ) + if state.text_content_index_and_output: + assistant_msg.content.append(state.text_content_index_and_output[1]) + if state.refusal_content_index_and_output: + assistant_msg.content.append(state.refusal_content_index_and_output[1]) + outputs.append(assistant_msg) + + # send a ResponseOutputItemDone for the assistant message + yield ResponseOutputItemDoneEvent( + item=assistant_msg, + output_index=0, + type="response.output_item.done", + ) + + for function_call in state.function_calls.values(): + outputs.append(function_call) + + final_response = response.model_copy(update={"output": outputs, "usage": usage}) + + yield ResponseCompletedEvent( + response=final_response, + type="response.completed", + ) + if tracing.include_data(): + span_generation.span_data.output = [final_response.model_dump()] + + if usage: + span_generation.span_data.usage = { + "input_tokens": usage.prompt_tokens, + "output_tokens": usage.completion_tokens, + } + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: Literal[True], + ) -> tuple[Response, AsyncStream[ChatCompletionChunk]]: ... + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: Literal[False], + ) -> ChatCompletion: ... + + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: bool = False, + ) -> ChatCompletion | tuple[Response, AsyncStream[ChatCompletionChunk]]: + converted_messages = _Converter.items_to_messages(input) + + if system_instructions: + converted_messages.insert( + 0, + { + "content": system_instructions, + "role": "system", + }, + ) + if tracing.include_data(): + span.span_data.input = converted_messages + + parallel_tool_calls = ( + True if model_settings.parallel_tool_calls and tools and len(tools) > 0 else NOT_GIVEN + ) + tool_choice = _Converter.convert_tool_choice(model_settings.tool_choice) + response_format = _Converter.convert_response_format(output_schema) + + converted_tools = [ToolConverter.to_openai(tool) for tool in tools] if tools else [] + + for handoff in handoffs: + converted_tools.append(ToolConverter.convert_handoff_tool(handoff)) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Calling LLM") + else: + logger.debug( + f"{json.dumps(converted_messages, indent=2)}\n" + f"Tools:\n{json.dumps(converted_tools, indent=2)}\n" + f"Stream: {stream}\n" + f"Tool choice: {tool_choice}\n" + f"Response format: {response_format}\n" + ) + + ret = await self._get_client().chat.completions.create( + model=self.model, + messages=converted_messages, + tools=converted_tools or NOT_GIVEN, + temperature=self._non_null_or_not_given(model_settings.temperature), + top_p=self._non_null_or_not_given(model_settings.top_p), + frequency_penalty=self._non_null_or_not_given(model_settings.frequency_penalty), + presence_penalty=self._non_null_or_not_given(model_settings.presence_penalty), + tool_choice=tool_choice, + response_format=response_format, + parallel_tool_calls=parallel_tool_calls, + stream=stream, + stream_options={"include_usage": True} if stream else NOT_GIVEN, + extra_headers=_HEADERS, + ) + + if isinstance(ret, ChatCompletion): + return ret + + response = Response( + id=FAKE_RESPONSES_ID, + created_at=time.time(), + model=self.model, + object="response", + output=[], + tool_choice=cast(Literal["auto", "required", "none"], tool_choice) + if tool_choice != NOT_GIVEN + else "auto", + top_p=model_settings.top_p, + temperature=model_settings.temperature, + tools=[], + parallel_tool_calls=parallel_tool_calls or False, + ) + return response, ret + + def _get_client(self) -> AsyncOpenAI: + if self._client is None: + self._client = AsyncOpenAI() + return self._client + + +class _Converter: + @classmethod + def convert_tool_choice( + cls, tool_choice: Literal["auto", "required", "none"] | str | None + ) -> ChatCompletionToolChoiceOptionParam | NotGiven: + if tool_choice is None: + return NOT_GIVEN + elif tool_choice == "auto": + return "auto" + elif tool_choice == "required": + return "required" + elif tool_choice == "none": + return "none" + else: + return { + "type": "function", + "function": { + "name": tool_choice, + }, + } + + @classmethod + def convert_response_format( + cls, final_output_schema: AgentOutputSchema | None + ) -> ResponseFormat | NotGiven: + if not final_output_schema or final_output_schema.is_plain_text(): + return NOT_GIVEN + + return { + "type": "json_schema", + "json_schema": { + "name": "final_output", + "strict": final_output_schema.strict_json_schema, + "schema": final_output_schema.json_schema(), + }, + } + + @classmethod + def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TResponseOutputItem]: + items: list[TResponseOutputItem] = [] + + message_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="completed", + ) + if message.content: + message_item.content.append( + ResponseOutputText(text=message.content, type="output_text", annotations=[]) + ) + if message.refusal: + message_item.content.append( + ResponseOutputRefusal(refusal=message.refusal, type="refusal") + ) + if message.audio: + raise AgentsException("Audio is not currently supported") + + if message_item.content: + items.append(message_item) + + if message.tool_calls: + for tool_call in message.tool_calls: + items.append( + ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=tool_call.id, + arguments=tool_call.function.arguments, + name=tool_call.function.name, + type="function_call", + ) + ) + + return items + + @classmethod + def maybe_easy_input_message(cls, item: Any) -> EasyInputMessageParam | None: + if not isinstance(item, dict): + return None + + keys = item.keys() + # EasyInputMessageParam only has these two keys + if keys != {"content", "role"}: + return None + + role = item.get("role", None) + if role not in ("user", "assistant", "system", "developer"): + return None + + if "content" not in item: + return None + + return cast(EasyInputMessageParam, item) + + @classmethod + def maybe_input_message(cls, item: Any) -> Message | None: + if ( + isinstance(item, dict) + and item.get("type") == "message" + and item.get("role") + in ( + "user", + "system", + "developer", + ) + ): + return cast(Message, item) + + return None + + @classmethod + def maybe_file_search_call(cls, item: Any) -> ResponseFileSearchToolCallParam | None: + if isinstance(item, dict) and item.get("type") == "file_search_call": + return cast(ResponseFileSearchToolCallParam, item) + return None + + @classmethod + def maybe_function_tool_call(cls, item: Any) -> ResponseFunctionToolCallParam | None: + if isinstance(item, dict) and item.get("type") == "function_call": + return cast(ResponseFunctionToolCallParam, item) + return None + + @classmethod + def maybe_function_tool_call_output( + cls, + item: Any, + ) -> FunctionCallOutput | None: + if isinstance(item, dict) and item.get("type") == "function_call_output": + return cast(FunctionCallOutput, item) + return None + + @classmethod + def maybe_item_reference(cls, item: Any) -> ItemReference | None: + if isinstance(item, dict) and item.get("type") == "item_reference": + return cast(ItemReference, item) + return None + + @classmethod + def maybe_response_output_message(cls, item: Any) -> ResponseOutputMessageParam | None: + # ResponseOutputMessage is only used for messages with role assistant + if ( + isinstance(item, dict) + and item.get("type") == "message" + and item.get("role") == "assistant" + ): + return cast(ResponseOutputMessageParam, item) + return None + + @classmethod + def extract_text_content( + cls, content: str | Iterable[ResponseInputContentParam] + ) -> str | list[ChatCompletionContentPartTextParam]: + all_content = cls.extract_all_content(content) + if isinstance(all_content, str): + return all_content + out: list[ChatCompletionContentPartTextParam] = [] + for c in all_content: + if c.get("type") == "text": + out.append(cast(ChatCompletionContentPartTextParam, c)) + return out + + @classmethod + def extract_all_content( + cls, content: str | Iterable[ResponseInputContentParam] + ) -> str | list[ChatCompletionContentPartParam]: + if isinstance(content, str): + return content + out: list[ChatCompletionContentPartParam] = [] + + for c in content: + if isinstance(c, dict) and c.get("type") == "input_text": + casted_text_param = cast(ResponseInputTextParam, c) + out.append( + ChatCompletionContentPartTextParam( + type="text", + text=casted_text_param["text"], + ) + ) + elif isinstance(c, dict) and c.get("type") == "input_image": + casted_image_param = cast(ResponseInputImageParam, c) + if "image_url" not in casted_image_param or not casted_image_param["image_url"]: + raise UserError( + f"Only image URLs are supported for input_image {casted_image_param}" + ) + out.append( + ChatCompletionContentPartImageParam( + type="image_url", + image_url={ + "url": casted_image_param["image_url"], + "detail": casted_image_param["detail"], + }, + ) + ) + elif isinstance(c, dict) and c.get("type") == "input_file": + raise UserError(f"File uploads are not supported for chat completions {c}") + else: + raise UserError(f"Unknonw content: {c}") + return out + + @classmethod + def items_to_messages( + cls, + items: str | Iterable[TResponseInputItem], + ) -> list[ChatCompletionMessageParam]: + """ + Convert a sequence of 'Item' objects into a list of ChatCompletionMessageParam. + + Rules: + - EasyInputMessage or InputMessage (role=user) => ChatCompletionUserMessageParam + - EasyInputMessage or InputMessage (role=system) => ChatCompletionSystemMessageParam + - EasyInputMessage or InputMessage (role=developer) => ChatCompletionDeveloperMessageParam + - InputMessage (role=assistant) => Start or flush a ChatCompletionAssistantMessageParam + - response_output_message => Also produces/flushes a ChatCompletionAssistantMessageParam + - tool calls get attached to the *current* assistant message, or create one if none. + - tool outputs => ChatCompletionToolMessageParam + """ + + if isinstance(items, str): + return [ + ChatCompletionUserMessageParam( + role="user", + content=items, + ) + ] + + result: list[ChatCompletionMessageParam] = [] + current_assistant_msg: ChatCompletionAssistantMessageParam | None = None + + def flush_assistant_message() -> None: + nonlocal current_assistant_msg + if current_assistant_msg is not None: + # The API doesn't support empty arrays for tool_calls + if not current_assistant_msg.get("tool_calls"): + del current_assistant_msg["tool_calls"] + result.append(current_assistant_msg) + current_assistant_msg = None + + def ensure_assistant_message() -> ChatCompletionAssistantMessageParam: + nonlocal current_assistant_msg + if current_assistant_msg is None: + current_assistant_msg = ChatCompletionAssistantMessageParam(role="assistant") + current_assistant_msg["tool_calls"] = [] + return current_assistant_msg + + for item in items: + # 1) Check easy input message + if easy_msg := cls.maybe_easy_input_message(item): + role = easy_msg["role"] + content = easy_msg["content"] + + if role == "user": + flush_assistant_message() + msg_user: ChatCompletionUserMessageParam = { + "role": "user", + "content": cls.extract_all_content(content), + } + result.append(msg_user) + elif role == "system": + flush_assistant_message() + msg_system: ChatCompletionSystemMessageParam = { + "role": "system", + "content": cls.extract_text_content(content), + } + result.append(msg_system) + elif role == "developer": + flush_assistant_message() + msg_developer: ChatCompletionDeveloperMessageParam = { + "role": "developer", + "content": cls.extract_text_content(content), + } + result.append(msg_developer) + else: + raise UserError(f"Unexpected role in easy_input_message: {role}") + + # 2) Check input message + elif in_msg := cls.maybe_input_message(item): + role = in_msg["role"] + content = in_msg["content"] + flush_assistant_message() + + if role == "user": + msg_user = { + "role": "user", + "content": cls.extract_all_content(content), + } + result.append(msg_user) + elif role == "system": + msg_system = { + "role": "system", + "content": cls.extract_text_content(content), + } + result.append(msg_system) + elif role == "developer": + msg_developer = { + "role": "developer", + "content": cls.extract_text_content(content), + } + result.append(msg_developer) + else: + raise UserError(f"Unexpected role in input_message: {role}") + + # 3) response output message => assistant + elif resp_msg := cls.maybe_response_output_message(item): + flush_assistant_message() + new_asst = ChatCompletionAssistantMessageParam(role="assistant") + contents = resp_msg["content"] + + text_segments = [] + for c in contents: + if c["type"] == "output_text": + text_segments.append(c["text"]) + elif c["type"] == "refusal": + new_asst["refusal"] = c["refusal"] + elif c["type"] == "output_audio": + # Can't handle this, b/c chat completions expects an ID which we dont have + raise UserError( + f"Only audio IDs are supported for chat completions, but got: {c}" + ) + else: + raise UserError(f"Unknown content type in ResponseOutputMessage: {c}") + + if text_segments: + combined = "\n".join(text_segments) + new_asst["content"] = combined + + new_asst["tool_calls"] = [] + current_assistant_msg = new_asst + + # 4) function/file-search calls => attach to assistant + elif file_search := cls.maybe_file_search_call(item): + asst = ensure_assistant_message() + tool_calls = list(asst.get("tool_calls", [])) + new_tool_call = ChatCompletionMessageToolCallParam( + id=file_search["id"], + type="function", + function={ + "name": "file_search_call", + "arguments": json.dumps( + { + "queries": file_search.get("queries", []), + "status": file_search.get("status"), + } + ), + }, + ) + tool_calls.append(new_tool_call) + asst["tool_calls"] = tool_calls + + elif func_call := cls.maybe_function_tool_call(item): + asst = ensure_assistant_message() + tool_calls = list(asst.get("tool_calls", [])) + new_tool_call = ChatCompletionMessageToolCallParam( + id=func_call["call_id"], + type="function", + function={ + "name": func_call["name"], + "arguments": func_call["arguments"], + }, + ) + tool_calls.append(new_tool_call) + asst["tool_calls"] = tool_calls + # 5) function call output => tool message + elif func_output := cls.maybe_function_tool_call_output(item): + flush_assistant_message() + msg: ChatCompletionToolMessageParam = { + "role": "tool", + "tool_call_id": func_output["call_id"], + "content": func_output["output"], + } + result.append(msg) + + # 6) item reference => handle or raise + elif item_ref := cls.maybe_item_reference(item): + raise UserError( + f"Encountered an item_reference, which is not supported: {item_ref}" + ) + + # 7) If we haven't recognized it => fail or ignore + else: + raise UserError(f"Unhandled item type or structure: {item}") + + flush_assistant_message() + return result + + +class ToolConverter: + @classmethod + def to_openai(cls, tool: Tool) -> ChatCompletionToolParam: + if isinstance(tool, FunctionTool): + return { + "type": "function", + "function": { + "name": tool.name, + "description": tool.description or "", + "parameters": tool.params_json_schema, + }, + } + + raise UserError( + f"Hosted tools are not supported with the ChatCompletions API. FGot tool type: " + f"{type(tool)}, tool: {tool}" + ) + + @classmethod + def convert_handoff_tool(cls, handoff: Handoff[Any]) -> ChatCompletionToolParam: + return { + "type": "function", + "function": { + "name": handoff.tool_name, + "description": handoff.tool_description, + "parameters": handoff.input_json_schema, + }, + } diff --git a/src/agents/models/openai_provider.py b/src/agents/models/openai_provider.py new file mode 100644 index 0000000..5194663 --- /dev/null +++ b/src/agents/models/openai_provider.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import httpx +from openai import AsyncOpenAI, DefaultAsyncHttpxClient + +from . import _openai_shared +from .interface import Model, ModelProvider +from .openai_chatcompletions import OpenAIChatCompletionsModel +from .openai_responses import OpenAIResponsesModel + +DEFAULT_MODEL: str = "gpt-4o" + + +_http_client: httpx.AsyncClient | None = None + + +# If we create a new httpx client for each request, that would mean no sharing of connection pools, +# which would mean worse latency and resource usage. So, we share the client across requests. +def shared_http_client() -> httpx.AsyncClient: + global _http_client + if _http_client is None: + _http_client = DefaultAsyncHttpxClient() + return _http_client + + +class OpenAIProvider(ModelProvider): + def __init__( + self, + *, + api_key: str | None = None, + base_url: str | None = None, + openai_client: AsyncOpenAI | None = None, + organization: str | None = None, + project: str | None = None, + use_responses: bool | None = None, + ) -> None: + if openai_client is not None: + assert api_key is None and base_url is None, ( + "Don't provide api_key or base_url if you provide openai_client" + ) + self._client = openai_client + else: + self._client = _openai_shared.get_default_openai_client() or AsyncOpenAI( + api_key=api_key or _openai_shared.get_default_openai_key(), + base_url=base_url, + organization=organization, + project=project, + http_client=shared_http_client(), + ) + + self._is_openai_model = self._client.base_url.host.startswith("api.openai.com") + if use_responses is not None: + self._use_responses = use_responses + else: + self._use_responses = _openai_shared.get_use_responses_by_default() + + def get_model(self, model_name: str | None) -> Model: + if model_name is None: + model_name = DEFAULT_MODEL + + return ( + OpenAIResponsesModel(model=model_name, openai_client=self._client) + if self._use_responses + else OpenAIChatCompletionsModel(model=model_name, openai_client=self._client) + ) diff --git a/src/agents/models/openai_responses.py b/src/agents/models/openai_responses.py new file mode 100644 index 0000000..a10d7b9 --- /dev/null +++ b/src/agents/models/openai_responses.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +import json +from collections.abc import AsyncIterator +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Literal, overload + +from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, NotGiven +from openai.types import ChatModel +from openai.types.responses import ( + Response, + ResponseCompletedEvent, + ResponseStreamEvent, + ResponseTextConfigParam, + ToolParam, + WebSearchToolParam, + response_create_params, +) + +from .. import _debug +from ..agent_output import AgentOutputSchema +from ..exceptions import UserError +from ..handoffs import Handoff +from ..items import ItemHelpers, ModelResponse, TResponseInputItem +from ..logger import logger +from ..tool import ComputerTool, FileSearchTool, FunctionTool, Tool, WebSearchTool +from ..tracing import SpanError, response_span +from ..usage import Usage +from ..version import __version__ +from .interface import Model, ModelTracing + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +_USER_AGENT = f"Agents/Python {__version__}" +_HEADERS = {"User-Agent": _USER_AGENT} + +# From the Responses API +IncludeLiteral = Literal[ + "file_search_call.results", + "message.input_image.image_url", + "computer_call_output.output.image_url", +] + + +class OpenAIResponsesModel(Model): + """ + Implementation of `Model` that uses the OpenAI Responses API. + """ + + def __init__( + self, + model: str | ChatModel, + openai_client: AsyncOpenAI, + ) -> None: + self.model = model + self._client = openai_client + + def _non_null_or_not_given(self, value: Any) -> Any: + return value if value is not None else NOT_GIVEN + + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + with response_span(disabled=tracing.is_disabled()) as span_response: + try: + response = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + stream=False, + ) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("LLM responsed") + else: + logger.debug( + "LLM resp:\n" + f"{json.dumps([x.model_dump() for x in response.output], indent=2)}\n" + ) + + usage = ( + Usage( + requests=1, + input_tokens=response.usage.input_tokens, + output_tokens=response.usage.output_tokens, + total_tokens=response.usage.total_tokens, + ) + if response.usage + else Usage() + ) + + if tracing.include_data(): + span_response.span_data.response = response + span_response.span_data.input = input + except Exception as e: + span_response.set_error( + SpanError( + message="Error getting response", + data={ + "error": str(e) if tracing.include_data() else e.__class__.__name__, + }, + ) + ) + logger.error(f"Error getting response: {e}") + raise + + return ModelResponse( + output=response.output, + usage=usage, + referenceable_id=response.id, + ) + + async def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[ResponseStreamEvent]: + """ + Yields a partial message as it is generated, as well as the usage information. + """ + with response_span(disabled=tracing.is_disabled()) as span_response: + try: + stream = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + stream=True, + ) + + final_response: Response | None = None + + async for chunk in stream: + if isinstance(chunk, ResponseCompletedEvent): + final_response = chunk.response + yield chunk + + if final_response and tracing.include_data(): + span_response.span_data.response = final_response + span_response.span_data.input = input + + except Exception as e: + span_response.set_error( + SpanError( + message="Error streaming response", + data={ + "error": str(e) if tracing.include_data() else e.__class__.__name__, + }, + ) + ) + logger.error(f"Error streaming response: {e}") + raise + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[True], + ) -> AsyncStream[ResponseStreamEvent]: ... + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[False], + ) -> Response: ... + + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[True] | Literal[False] = False, + ) -> Response | AsyncStream[ResponseStreamEvent]: + list_input = ItemHelpers.input_to_new_input_list(input) + + parallel_tool_calls = ( + True if model_settings.parallel_tool_calls and tools and len(tools) > 0 else NOT_GIVEN + ) + + tool_choice = Converter.convert_tool_choice(model_settings.tool_choice) + converted_tools = Converter.convert_tools(tools, handoffs) + response_format = Converter.get_response_format(output_schema) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Calling LLM") + else: + logger.debug( + f"Calling LLM {self.model} with input:\n" + f"{json.dumps(list_input, indent=2)}\n" + f"Tools:\n{json.dumps(converted_tools.tools, indent=2)}\n" + f"Stream: {stream}\n" + f"Tool choice: {tool_choice}\n" + f"Response format: {response_format}\n" + ) + + return await self._client.responses.create( + instructions=self._non_null_or_not_given(system_instructions), + model=self.model, + input=list_input, + include=converted_tools.includes, + tools=converted_tools.tools, + temperature=self._non_null_or_not_given(model_settings.temperature), + top_p=self._non_null_or_not_given(model_settings.top_p), + truncation=self._non_null_or_not_given(model_settings.truncation), + tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, + stream=stream, + extra_headers=_HEADERS, + text=response_format, + ) + + def _get_client(self) -> AsyncOpenAI: + if self._client is None: + self._client = AsyncOpenAI() + return self._client + + +@dataclass +class ConvertedTools: + tools: list[ToolParam] + includes: list[IncludeLiteral] + + +class Converter: + @classmethod + def convert_tool_choice( + cls, tool_choice: Literal["auto", "required", "none"] | str | None + ) -> response_create_params.ToolChoice | NotGiven: + if tool_choice is None: + return NOT_GIVEN + elif tool_choice == "required": + return "required" + elif tool_choice == "auto": + return "auto" + elif tool_choice == "none": + return "none" + elif tool_choice == "file_search": + return { + "type": "file_search", + } + elif tool_choice == "web_search_preview": + return { + "type": "web_search_preview", + } + elif tool_choice == "computer_use_preview": + return { + "type": "computer_use_preview", + } + else: + return { + "type": "function", + "name": tool_choice, + } + + @classmethod + def get_response_format( + cls, output_schema: AgentOutputSchema | None + ) -> ResponseTextConfigParam | NotGiven: + if output_schema is None or output_schema.is_plain_text(): + return NOT_GIVEN + else: + return { + "format": { + "type": "json_schema", + "name": "final_output", + "schema": output_schema.json_schema(), + "strict": output_schema.strict_json_schema, + } + } + + @classmethod + def convert_tools( + cls, + tools: list[Tool], + handoffs: list[Handoff[Any]], + ) -> ConvertedTools: + converted_tools: list[ToolParam] = [] + includes: list[IncludeLiteral] = [] + + computer_tools = [tool for tool in tools if isinstance(tool, ComputerTool)] + if len(computer_tools) > 1: + raise UserError(f"You can only provide one computer tool. Got {len(computer_tools)}") + + for tool in tools: + converted_tool, include = cls._convert_tool(tool) + converted_tools.append(converted_tool) + if include: + includes.append(include) + + for handoff in handoffs: + converted_tools.append(cls._convert_handoff_tool(handoff)) + + return ConvertedTools(tools=converted_tools, includes=includes) + + @classmethod + def _convert_tool(cls, tool: Tool) -> tuple[ToolParam, IncludeLiteral | None]: + """Returns converted tool and includes""" + + if isinstance(tool, FunctionTool): + converted_tool: ToolParam = { + "name": tool.name, + "parameters": tool.params_json_schema, + "strict": tool.strict_json_schema, + "type": "function", + "description": tool.description, + } + includes: IncludeLiteral | None = None + elif isinstance(tool, WebSearchTool): + ws: WebSearchToolParam = { + "type": "web_search_preview", + "user_location": tool.user_location, + "search_context_size": tool.search_context_size, + } + converted_tool = ws + includes = None + elif isinstance(tool, FileSearchTool): + converted_tool = { + "type": "file_search", + "vector_store_ids": tool.vector_store_ids, + } + if tool.max_num_results: + converted_tool["max_num_results"] = tool.max_num_results + if tool.ranking_options: + converted_tool["ranking_options"] = tool.ranking_options + if tool.filters: + converted_tool["filters"] = tool.filters + + includes = "file_search_call.results" if tool.include_search_results else None + elif isinstance(tool, ComputerTool): + converted_tool = { + "type": "computer-preview", + "environment": tool.computer.environment, + "display_width": tool.computer.dimensions[0], + "display_height": tool.computer.dimensions[1], + } + includes = None + + else: + raise UserError(f"Unknown tool type: {type(tool)}, tool") + + return converted_tool, includes + + @classmethod + def _convert_handoff_tool(cls, handoff: Handoff) -> ToolParam: + return { + "name": handoff.tool_name, + "parameters": handoff.input_json_schema, + "strict": handoff.strict_json_schema, + "type": "function", + "description": handoff.tool_description, + } diff --git a/src/agents/result.py b/src/agents/result.py new file mode 100644 index 0000000..5683827 --- /dev/null +++ b/src/agents/result.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import abc +import asyncio +from collections.abc import AsyncIterator +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, cast + +from typing_extensions import TypeVar + +from ._run_impl import QueueCompleteSentinel +from .agent import Agent +from .agent_output import AgentOutputSchema +from .exceptions import InputGuardrailTripwireTriggered, MaxTurnsExceeded +from .guardrail import InputGuardrailResult, OutputGuardrailResult +from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem +from .logger import logger +from .stream_events import StreamEvent +from .tracing import Trace + +if TYPE_CHECKING: + from ._run_impl import QueueCompleteSentinel + from .agent import Agent + +T = TypeVar("T") + + +@dataclass +class RunResultBase(abc.ABC): + input: str | list[TResponseInputItem] + """The original input items i.e. the items before run() was called. This may be a mutated + version of the input, if there are handoff input filters that mutate the input. + """ + + new_items: list[RunItem] + """The new items generated during the agent run. These include things like new messages, tool + calls and their outputs, etc. + """ + + raw_responses: list[ModelResponse] + """The raw LLM responses generated by the model during the agent run.""" + + final_output: Any + """The output of the last agent.""" + + input_guardrail_results: list[InputGuardrailResult] + """Guardrail results for the input messages.""" + + output_guardrail_results: list[OutputGuardrailResult] + """Guardrail results for the final output of the agent.""" + + @property + @abc.abstractmethod + def last_agent(self) -> Agent[Any]: + """The last agent that was run.""" + + def final_output_as(self, cls: type[T], raise_if_incorrect_type: bool = False) -> T: + """A convenience method to cast the final output to a specific type. By default, the cast + is only for the typechecker. If you set `raise_if_incorrect_type` to True, we'll raise a + TypeError if the final output is not of the given type. + + Args: + cls: The type to cast the final output to. + raise_if_incorrect_type: If True, we'll raise a TypeError if the final output is not of + the given type. + + Returns: + The final output casted to the given type. + """ + if raise_if_incorrect_type and not isinstance(self.final_output, cls): + raise TypeError(f"Final output is not of type {cls.__name__}") + + return cast(T, self.final_output) + + def to_input_list(self) -> list[TResponseInputItem]: + """Creates a new input list, merging the original input with all the new items generated.""" + original_items: list[TResponseInputItem] = ItemHelpers.input_to_new_input_list(self.input) + new_items = [item.to_input_item() for item in self.new_items] + + return original_items + new_items + + +@dataclass +class RunResult(RunResultBase): + _last_agent: Agent[Any] + + @property + def last_agent(self) -> Agent[Any]: + """The last agent that was run.""" + return self._last_agent + + +@dataclass +class RunResultStreaming(RunResultBase): + """The result of an agent run in streaming mode. You can use the `stream_events` method to + receive semantic events as they are generated. + + The streaming method will raise: + - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. + - A GuardrailTripwireTriggered exception if a guardrail is tripped. + """ + + current_agent: Agent[Any] + """The current agent that is running.""" + + current_turn: int + """The current turn number.""" + + max_turns: int + """The maximum number of turns the agent can run for.""" + + final_output: Any + """The final output of the agent. This is None until the agent has finished running.""" + + _current_agent_output_schema: AgentOutputSchema | None = field(repr=False) + + _trace: Trace | None = field(repr=False) + + is_complete: bool = False + """Whether the agent has finished running.""" + + # Queues that the background run_loop writes to + _event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] = field( + default_factory=asyncio.Queue, repr=False + ) + _input_guardrail_queue: asyncio.Queue[InputGuardrailResult] = field( + default_factory=asyncio.Queue, repr=False + ) + + # Store the asyncio tasks that we're waiting on + _run_impl_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _input_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _output_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _stored_exception: Exception | None = field(default=None, repr=False) + + @property + def last_agent(self) -> Agent[Any]: + """The last agent that was run. Updates as the agent run progresses, so the true last agent + is only available after the agent run is complete. + """ + return self.current_agent + + async def stream_events(self) -> AsyncIterator[StreamEvent]: + """Stream deltas for new items as they are generated. We're using the types from the + OpenAI Responses API, so these are semantic events: each event has a `type` field that + describes the type of the event, along with the data for that event. + + This will raise: + - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. + - A GuardrailTripwireTriggered exception if a guardrail is tripped. + """ + while True: + self._check_errors() + if self._stored_exception: + logger.debug("Breaking due to stored exception") + self.is_complete = True + break + + if self.is_complete and self._event_queue.empty(): + break + + try: + item = await self._event_queue.get() + except asyncio.CancelledError: + break + + if isinstance(item, QueueCompleteSentinel): + self._event_queue.task_done() + # Check for errors, in case the queue was completed due to an exception + self._check_errors() + break + + yield item + self._event_queue.task_done() + + if self._trace: + self._trace.finish(reset_current=True) + + self._cleanup_tasks() + + if self._stored_exception: + raise self._stored_exception + + def _check_errors(self): + if self.current_turn > self.max_turns: + self._stored_exception = MaxTurnsExceeded(f"Max turns ({self.max_turns}) exceeded") + + # Fetch all the completed guardrail results from the queue and raise if needed + while not self._input_guardrail_queue.empty(): + guardrail_result = self._input_guardrail_queue.get_nowait() + if guardrail_result.output.tripwire_triggered: + self._stored_exception = InputGuardrailTripwireTriggered(guardrail_result) + + # Check the tasks for any exceptions + if self._run_impl_task and self._run_impl_task.done(): + exc = self._run_impl_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + if self._input_guardrails_task and self._input_guardrails_task.done(): + exc = self._input_guardrails_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + if self._output_guardrails_task and self._output_guardrails_task.done(): + exc = self._output_guardrails_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + def _cleanup_tasks(self): + if self._run_impl_task and not self._run_impl_task.done(): + self._run_impl_task.cancel() + + if self._input_guardrails_task and not self._input_guardrails_task.done(): + self._input_guardrails_task.cancel() + + if self._output_guardrails_task and not self._output_guardrails_task.done(): + self._output_guardrails_task.cancel() + self._output_guardrails_task.cancel() + self._output_guardrails_task.cancel() diff --git a/src/agents/run.py b/src/agents/run.py new file mode 100644 index 0000000..dfff7e3 --- /dev/null +++ b/src/agents/run.py @@ -0,0 +1,904 @@ +from __future__ import annotations + +import asyncio +import copy +from dataclasses import dataclass, field +from typing import Any, cast + +from openai.types.responses import ResponseCompletedEvent + +from . import Model, _utils +from ._run_impl import ( + NextStepFinalOutput, + NextStepHandoff, + NextStepRunAgain, + QueueCompleteSentinel, + RunImpl, + SingleStepResult, + TraceCtxManager, + get_model_tracing_impl, +) +from .agent import Agent +from .agent_output import AgentOutputSchema +from .exceptions import ( + AgentsException, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + OutputGuardrailTripwireTriggered, +) +from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult +from .handoffs import Handoff, HandoffInputFilter, handoff +from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem +from .lifecycle import RunHooks +from .logger import logger +from .model_settings import ModelSettings +from .models.interface import ModelProvider +from .models.openai_provider import OpenAIProvider +from .result import RunResult, RunResultStreaming +from .run_context import RunContextWrapper, TContext +from .stream_events import AgentUpdatedStreamEvent, RawResponsesStreamEvent +from .tracing import Span, SpanError, agent_span, get_current_trace, trace +from .tracing.span_data import AgentSpanData +from .usage import Usage + +DEFAULT_MAX_TURNS = 10 + + +@dataclass +class RunConfig: + """Configures settings for the entire agent run.""" + + model: str | Model | None = None + """The model to use for the entire agent run. If set, will override the model set on every + agent. The model_provider passed in below must be able to resolve this model name. + """ + + model_provider: ModelProvider = field(default_factory=OpenAIProvider) + """The model provider to use when looking up string model names. Defaults to OpenAI.""" + + model_settings: ModelSettings | None = None + """Configure global model settings. Any non-null values will override the agent-specific model + settings. + """ + + handoff_input_filter: HandoffInputFilter | None = None + """A global input filter to apply to all handoffs. If `Handoff.input_filter` is set, then that + will take precedence. The input filter allows you to edit the inputs that are sent to the new + agent. See the documentation in `Handoff.input_filter` for more details. + """ + + input_guardrails: list[InputGuardrail[Any]] | None = None + """A list of input guardrails to run on the initial run input.""" + + output_guardrails: list[OutputGuardrail[Any]] | None = None + """A list of output guardrails to run on the final output of the run.""" + + tracing_disabled: bool = False + """Whether tracing is disabled for the agent run. If disabled, we will not trace the agent run. + """ + + trace_include_sensitive_data: bool = True + """Whether we include potentially sensitive data (for example: inputs/outputs of tool calls or + LLM generations) in traces. If False, we'll still create spans for these events, but the + sensitive data will not be included. + """ + + workflow_name: str = "Agent workflow" + """The name of the run, used for tracing. Should be a logical name for the run, like + "Code generation workflow" or "Customer support agent". + """ + + trace_id: str | None = None + """A custom trace ID to use for tracing. If not provided, we will generate a new trace ID.""" + + group_id: str | None = None + """ + A grouping identifier to use for tracing, to link multiple traces from the same conversation + or process. For example, you might use a chat thread ID. + """ + + trace_metadata: dict[str, Any] | None = None + """ + An optional dictionary of additional metadata to include with the trace. + """ + + +class Runner: + @classmethod + async def run( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + *, + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResult: + """Run a workflow starting at the given agent. The agent will run in a loop until a final + output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A run result containing all the inputs, guardrail results and the output of the last + agent. Agents may perform handoffs, so we don't know the specific type of the output. + """ + if hooks is None: + hooks = RunHooks[Any]() + if run_config is None: + run_config = RunConfig() + + with TraceCtxManager( + workflow_name=run_config.workflow_name, + trace_id=run_config.trace_id, + group_id=run_config.group_id, + metadata=run_config.trace_metadata, + disabled=run_config.tracing_disabled, + ): + current_turn = 0 + original_input: str | list[TResponseInputItem] = copy.deepcopy(input) + generated_items: list[RunItem] = [] + model_responses: list[ModelResponse] = [] + + context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( + context=context, # type: ignore + ) + + input_guardrail_results: list[InputGuardrailResult] = [] + + current_span: Span[AgentSpanData] | None = None + current_agent = starting_agent + should_run_agent_start_hooks = True + + try: + while True: + # Start an agent span if we don't have one. This span is ended if the current + # agent changes, or if the agent loop ends. + if current_span is None: + handoff_names = [h.agent_name for h in cls._get_handoffs(current_agent)] + tool_names = [t.name for t in current_agent.tools] + if output_schema := cls._get_output_schema(current_agent): + output_type_name = output_schema.output_type_name() + else: + output_type_name = "str" + + current_span = agent_span( + name=current_agent.name, + handoffs=handoff_names, + tools=tool_names, + output_type=output_type_name, + ) + current_span.start(mark_as_current=True) + + current_turn += 1 + if current_turn > max_turns: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Max turns exceeded", + data={"max_turns": max_turns}, + ), + ) + raise MaxTurnsExceeded(f"Max turns ({max_turns}) exceeded") + + logger.debug( + f"Running agent {current_agent.name} (turn {current_turn})", + ) + + if current_turn == 1: + input_guardrail_results, turn_result = await asyncio.gather( + cls._run_input_guardrails( + starting_agent, + starting_agent.input_guardrails + + (run_config.input_guardrails or []), + copy.deepcopy(input), + context_wrapper, + ), + cls._run_single_turn( + agent=current_agent, + original_input=original_input, + generated_items=generated_items, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + should_run_agent_start_hooks=should_run_agent_start_hooks, + ), + ) + else: + turn_result = await cls._run_single_turn( + agent=current_agent, + original_input=original_input, + generated_items=generated_items, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + should_run_agent_start_hooks=should_run_agent_start_hooks, + ) + should_run_agent_start_hooks = False + + model_responses.append(turn_result.model_response) + original_input = turn_result.original_input + generated_items = turn_result.generated_items + + if isinstance(turn_result.next_step, NextStepFinalOutput): + output_guardrail_results = await cls._run_output_guardrails( + current_agent.output_guardrails + (run_config.output_guardrails or []), + current_agent, + turn_result.next_step.output, + context_wrapper, + ) + return RunResult( + input=original_input, + new_items=generated_items, + raw_responses=model_responses, + final_output=turn_result.next_step.output, + _last_agent=current_agent, + input_guardrail_results=input_guardrail_results, + output_guardrail_results=output_guardrail_results, + ) + elif isinstance(turn_result.next_step, NextStepHandoff): + current_agent = cast(Agent[TContext], turn_result.next_step.new_agent) + current_span.finish(reset_current=True) + current_span = None + should_run_agent_start_hooks = True + elif isinstance(turn_result.next_step, NextStepRunAgain): + pass + else: + raise AgentsException( + f"Unknown next step type: {type(turn_result.next_step)}" + ) + finally: + if current_span: + current_span.finish(reset_current=True) + + @classmethod + def run_sync( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + *, + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResult: + """Run a workflow synchronously, starting at the given agent. Note that this just wraps the + `run` method, so it will not work if there's already an event loop (e.g. inside an async + function, or in a Jupyter notebook or async context like FastAPI). For those cases, use + the `run` method instead. + + The agent will run in a loop until a final output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A run result containing all the inputs, guardrail results and the output of the last + agent. Agents may perform handoffs, so we don't know the specific type of the output. + """ + return asyncio.get_event_loop().run_until_complete( + cls.run( + starting_agent, + input, + context=context, + max_turns=max_turns, + hooks=hooks, + run_config=run_config, + ) + ) + + @classmethod + def run_streamed( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResultStreaming: + """Run a workflow starting at the given agent in streaming mode. The returned result object + contains a method you can use to stream semantic events as they are generated. + + The agent will run in a loop until a final output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A result object that contains data about the run, as well as a method to stream events. + """ + if hooks is None: + hooks = RunHooks[Any]() + if run_config is None: + run_config = RunConfig() + + # If there's already a trace, we don't create a new one. In addition, we can't end the + # trace here, because the actual work is done in `stream_events` and this method ends + # before that. + new_trace = ( + None + if get_current_trace() + else trace( + workflow_name=run_config.workflow_name, + trace_id=run_config.trace_id, + group_id=run_config.group_id, + metadata=run_config.trace_metadata, + disabled=run_config.tracing_disabled, + ) + ) + # Need to start the trace here, because the current trace contextvar is captured at + # asyncio.create_task time + if new_trace: + new_trace.start(mark_as_current=True) + + output_schema = cls._get_output_schema(starting_agent) + context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( + context=context # type: ignore + ) + + streamed_result = RunResultStreaming( + input=copy.deepcopy(input), + new_items=[], + current_agent=starting_agent, + raw_responses=[], + final_output=None, + is_complete=False, + current_turn=0, + max_turns=max_turns, + input_guardrail_results=[], + output_guardrail_results=[], + _current_agent_output_schema=output_schema, + _trace=new_trace, + ) + + # Kick off the actual agent loop in the background and return the streamed result object. + streamed_result._run_impl_task = asyncio.create_task( + cls._run_streamed_impl( + starting_input=input, + streamed_result=streamed_result, + starting_agent=starting_agent, + max_turns=max_turns, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + ) + return streamed_result + + @classmethod + async def _run_input_guardrails_with_queue( + cls, + agent: Agent[Any], + guardrails: list[InputGuardrail[TContext]], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + streamed_result: RunResultStreaming, + parent_span: Span[Any], + ): + queue = streamed_result._input_guardrail_queue + + # We'll run the guardrails and push them onto the queue as they complete + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_input_guardrail(agent, guardrail, input, context) + ) + for guardrail in guardrails + ] + guardrail_results = [] + try: + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + _utils.attach_error_to_span( + parent_span, + SpanError( + message="Guardrail tripwire triggered", + data={ + "guardrail": result.guardrail.get_name(), + "type": "input_guardrail", + }, + ), + ) + queue.put_nowait(result) + guardrail_results.append(result) + except Exception: + for t in guardrail_tasks: + t.cancel() + raise + + streamed_result.input_guardrail_results = guardrail_results + + @classmethod + async def _run_streamed_impl( + cls, + starting_input: str | list[TResponseInputItem], + streamed_result: RunResultStreaming, + starting_agent: Agent[TContext], + max_turns: int, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ): + current_span: Span[AgentSpanData] | None = None + current_agent = starting_agent + current_turn = 0 + should_run_agent_start_hooks = True + + streamed_result._event_queue.put_nowait(AgentUpdatedStreamEvent(new_agent=current_agent)) + + try: + while True: + if streamed_result.is_complete: + break + + # Start an agent span if we don't have one. This span is ended if the current + # agent changes, or if the agent loop ends. + if current_span is None: + handoff_names = [h.agent_name for h in cls._get_handoffs(current_agent)] + tool_names = [t.name for t in current_agent.tools] + if output_schema := cls._get_output_schema(current_agent): + output_type_name = output_schema.output_type_name() + else: + output_type_name = "str" + + current_span = agent_span( + name=current_agent.name, + handoffs=handoff_names, + tools=tool_names, + output_type=output_type_name, + ) + current_span.start(mark_as_current=True) + + current_turn += 1 + streamed_result.current_turn = current_turn + + if current_turn > max_turns: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Max turns exceeded", + data={"max_turns": max_turns}, + ), + ) + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + break + + if current_turn == 1: + # Run the input guardrails in the background and put the results on the queue + streamed_result._input_guardrails_task = asyncio.create_task( + cls._run_input_guardrails_with_queue( + starting_agent, + starting_agent.input_guardrails + (run_config.input_guardrails or []), + copy.deepcopy(ItemHelpers.input_to_new_input_list(starting_input)), + context_wrapper, + streamed_result, + current_span, + ) + ) + try: + turn_result = await cls._run_single_turn_streamed( + streamed_result, + current_agent, + hooks, + context_wrapper, + run_config, + should_run_agent_start_hooks, + ) + should_run_agent_start_hooks = False + + streamed_result.raw_responses = streamed_result.raw_responses + [ + turn_result.model_response + ] + streamed_result.input = turn_result.original_input + streamed_result.new_items = turn_result.generated_items + + if isinstance(turn_result.next_step, NextStepHandoff): + current_agent = turn_result.next_step.new_agent + current_span.finish(reset_current=True) + current_span = None + should_run_agent_start_hooks = True + streamed_result._event_queue.put_nowait( + AgentUpdatedStreamEvent(new_agent=current_agent) + ) + elif isinstance(turn_result.next_step, NextStepFinalOutput): + streamed_result._output_guardrails_task = asyncio.create_task( + cls._run_output_guardrails( + current_agent.output_guardrails + + (run_config.output_guardrails or []), + current_agent, + turn_result.next_step.output, + context_wrapper, + ) + ) + + try: + output_guardrail_results = await streamed_result._output_guardrails_task + except Exception: + # Exceptions will be checked in the stream_events loop + output_guardrail_results = [] + + streamed_result.output_guardrail_results = output_guardrail_results + streamed_result.final_output = turn_result.next_step.output + streamed_result.is_complete = True + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + elif isinstance(turn_result.next_step, NextStepRunAgain): + pass + except Exception as e: + if current_span: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Error in agent run", + data={"error": str(e)}, + ), + ) + streamed_result.is_complete = True + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + raise + + streamed_result.is_complete = True + finally: + if current_span: + current_span.finish(reset_current=True) + + @classmethod + async def _run_single_turn_streamed( + cls, + streamed_result: RunResultStreaming, + agent: Agent[TContext], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + should_run_agent_start_hooks: bool, + ) -> SingleStepResult: + if should_run_agent_start_hooks: + await asyncio.gather( + hooks.on_agent_start(context_wrapper, agent), + ( + agent.hooks.on_start(context_wrapper, agent) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + output_schema = cls._get_output_schema(agent) + + streamed_result.current_agent = agent + streamed_result._current_agent_output_schema = output_schema + + system_prompt = await agent.get_system_prompt(context_wrapper) + + handoffs = cls._get_handoffs(agent) + + model = cls._get_model(agent, run_config) + model_settings = agent.model_settings.resolve(run_config.model_settings) + final_response: ModelResponse | None = None + + input = ItemHelpers.input_to_new_input_list(streamed_result.input) + input.extend([item.to_input_item() for item in streamed_result.new_items]) + + # 1. Stream the output events + async for event in model.stream_response( + system_prompt, + input, + model_settings, + agent.tools, + output_schema, + handoffs, + get_model_tracing_impl( + run_config.tracing_disabled, run_config.trace_include_sensitive_data + ), + ): + if isinstance(event, ResponseCompletedEvent): + usage = ( + Usage( + requests=1, + input_tokens=event.response.usage.input_tokens, + output_tokens=event.response.usage.output_tokens, + total_tokens=event.response.usage.total_tokens, + ) + if event.response.usage + else Usage() + ) + final_response = ModelResponse( + output=event.response.output, + usage=usage, + referenceable_id=event.response.id, + ) + + streamed_result._event_queue.put_nowait(RawResponsesStreamEvent(data=event)) + + # 2. At this point, the streaming is complete for this turn of the agent loop. + if not final_response: + raise ModelBehaviorError("Model did not produce a final response!") + + # 3. Now, we can process the turn as we do in the non-streaming case + single_step_result = await cls._get_single_step_result_from_response( + agent=agent, + original_input=streamed_result.input, + pre_step_items=streamed_result.new_items, + new_response=final_response, + output_schema=output_schema, + handoffs=handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + RunImpl.stream_step_result_to_queue(single_step_result, streamed_result._event_queue) + return single_step_result + + @classmethod + async def _run_single_turn( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + generated_items: list[RunItem], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + should_run_agent_start_hooks: bool, + ) -> SingleStepResult: + # Ensure we run the hooks before anything else + if should_run_agent_start_hooks: + await asyncio.gather( + hooks.on_agent_start(context_wrapper, agent), + ( + agent.hooks.on_start(context_wrapper, agent) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + system_prompt = await agent.get_system_prompt(context_wrapper) + + output_schema = cls._get_output_schema(agent) + handoffs = cls._get_handoffs(agent) + input = ItemHelpers.input_to_new_input_list(original_input) + input.extend([generated_item.to_input_item() for generated_item in generated_items]) + + new_response = await cls._get_new_response( + agent, + system_prompt, + input, + output_schema, + handoffs, + context_wrapper, + run_config, + ) + + return await cls._get_single_step_result_from_response( + agent=agent, + original_input=original_input, + pre_step_items=generated_items, + new_response=new_response, + output_schema=output_schema, + handoffs=handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + @classmethod + async def _get_single_step_result_from_response( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + pre_step_items: list[RunItem], + new_response: ModelResponse, + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + processed_response = RunImpl.process_model_response( + agent=agent, + response=new_response, + output_schema=output_schema, + handoffs=handoffs, + ) + return await RunImpl.execute_tools_and_side_effects( + agent=agent, + original_input=original_input, + pre_step_items=pre_step_items, + new_response=new_response, + processed_response=processed_response, + output_schema=output_schema, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + @classmethod + async def _run_input_guardrails( + cls, + agent: Agent[Any], + guardrails: list[InputGuardrail[TContext]], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> list[InputGuardrailResult]: + if not guardrails: + return [] + + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_input_guardrail(agent, guardrail, input, context) + ) + for guardrail in guardrails + ] + + guardrail_results = [] + + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + # Cancel all guardrail tasks if a tripwire is triggered. + for t in guardrail_tasks: + t.cancel() + _utils.attach_error_to_current_span( + SpanError( + message="Guardrail tripwire triggered", + data={"guardrail": result.guardrail.get_name()}, + ) + ) + raise InputGuardrailTripwireTriggered(result) + else: + guardrail_results.append(result) + + return guardrail_results + + @classmethod + async def _run_output_guardrails( + cls, + guardrails: list[OutputGuardrail[TContext]], + agent: Agent[TContext], + agent_output: Any, + context: RunContextWrapper[TContext], + ) -> list[OutputGuardrailResult]: + if not guardrails: + return [] + + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_output_guardrail(guardrail, agent, agent_output, context) + ) + for guardrail in guardrails + ] + + guardrail_results = [] + + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + # Cancel all guardrail tasks if a tripwire is triggered. + for t in guardrail_tasks: + t.cancel() + _utils.attach_error_to_current_span( + SpanError( + message="Guardrail tripwire triggered", + data={"guardrail": result.guardrail.get_name()}, + ) + ) + raise OutputGuardrailTripwireTriggered(result) + else: + guardrail_results.append(result) + + return guardrail_results + + @classmethod + async def _get_new_response( + cls, + agent: Agent[TContext], + system_prompt: str | None, + input: list[TResponseInputItem], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> ModelResponse: + model = cls._get_model(agent, run_config) + model_settings = agent.model_settings.resolve(run_config.model_settings) + new_response = await model.get_response( + system_instructions=system_prompt, + input=input, + model_settings=model_settings, + tools=agent.tools, + output_schema=output_schema, + handoffs=handoffs, + tracing=get_model_tracing_impl( + run_config.tracing_disabled, run_config.trace_include_sensitive_data + ), + ) + + context_wrapper.usage.add(new_response.usage) + + return new_response + + @classmethod + def _get_output_schema(cls, agent: Agent[Any]) -> AgentOutputSchema | None: + if agent.output_type is None or agent.output_type is str: + return None + + return AgentOutputSchema(agent.output_type) + + @classmethod + def _get_handoffs(cls, agent: Agent[Any]) -> list[Handoff]: + handoffs = [] + for handoff_item in agent.handoffs: + if isinstance(handoff_item, Handoff): + handoffs.append(handoff_item) + elif isinstance(handoff_item, Agent): + handoffs.append(handoff(handoff_item)) + return handoffs + + @classmethod + def _get_model(cls, agent: Agent[Any], run_config: RunConfig) -> Model: + if isinstance(run_config.model, Model): + return run_config.model + elif isinstance(run_config.model, str): + return run_config.model_provider.get_model(run_config.model) + elif isinstance(agent.model, Model): + return agent.model + + return run_config.model_provider.get_model(agent.model) diff --git a/src/agents/run_context.py b/src/agents/run_context.py new file mode 100644 index 0000000..579a215 --- /dev/null +++ b/src/agents/run_context.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass, field +from typing import Any, Generic + +from typing_extensions import TypeVar + +from .usage import Usage + +TContext = TypeVar("TContext", default=Any) + + +@dataclass +class RunContextWrapper(Generic[TContext]): + """This wraps the context object that you passed to `Runner.run()`. It also contains + information about the usage of the agent run so far. + + NOTE: Contexts are not passed to the LLM. They're a way to pass dependencies and data to code + you implement, like tool functions, callbacks, hooks, etc. + """ + + context: TContext + """The context object (or None), passed by you to `Runner.run()`""" + + usage: Usage = field(default_factory=Usage) + """The usage of the agent run so far. For streamed responses, the usage will be stale until the + last chunk of the stream is processed. + """ diff --git a/src/agents/stream_events.py b/src/agents/stream_events.py new file mode 100644 index 0000000..bd37d11 --- /dev/null +++ b/src/agents/stream_events.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal, Union + +from typing_extensions import TypeAlias + +from .agent import Agent +from .items import RunItem, TResponseStreamEvent + + +@dataclass +class RawResponsesStreamEvent: + """Streaming event from the LLM. These are 'raw' events, i.e. they are directly passed through + from the LLM. + """ + + data: TResponseStreamEvent + """The raw responses streaming event from the LLM.""" + + type: Literal["raw_response_event"] = "raw_response_event" + """The type of the event.""" + + +@dataclass +class RunItemStreamEvent: + """Streaming events that wrap a `RunItem`. As the agent processes the LLM response, it will + generate these events for new messages, tool calls, tool outputs, handoffs, etc. + """ + + name: Literal[ + "message_output_created", + "handoff_requested", + "handoff_occured", + "tool_called", + "tool_output", + "reasoning_item_created", + ] + """The name of the event.""" + + item: RunItem + """The item that was created.""" + + type: Literal["run_item_stream_event"] = "run_item_stream_event" + + +@dataclass +class AgentUpdatedStreamEvent: + """Event that notifies that there is a new agent running.""" + + new_agent: Agent[Any] + """The new agent.""" + + type: Literal["agent_updated_stream_event"] = "agent_updated_stream_event" + + +StreamEvent: TypeAlias = Union[RawResponsesStreamEvent, RunItemStreamEvent, AgentUpdatedStreamEvent] +"""A streaming event from an agent.""" diff --git a/src/agents/strict_schema.py b/src/agents/strict_schema.py new file mode 100644 index 0000000..910ad85 --- /dev/null +++ b/src/agents/strict_schema.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +from typing import Any + +from openai import NOT_GIVEN +from typing_extensions import TypeGuard + +from .exceptions import UserError + +_EMPTY_SCHEMA = { + "additionalProperties": False, + "type": "object", + "properties": {}, + "required": [], +} + + +def ensure_strict_json_schema( + schema: dict[str, Any], +) -> dict[str, Any]: + """Mutates the given JSON schema to ensure it conforms to the `strict` standard + that the OpenAI API expects. + """ + if schema == {}: + return _EMPTY_SCHEMA + return _ensure_strict_json_schema(schema, path=(), root=schema) + + +# Adapted from https://github.com/openai/openai-python/blob/main/src/openai/lib/_pydantic.py +def _ensure_strict_json_schema( + json_schema: object, + *, + path: tuple[str, ...], + root: dict[str, object], +) -> dict[str, Any]: + if not is_dict(json_schema): + raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") + + defs = json_schema.get("$defs") + if is_dict(defs): + for def_name, def_schema in defs.items(): + _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name), root=root) + + definitions = json_schema.get("definitions") + if is_dict(definitions): + for definition_name, definition_schema in definitions.items(): + _ensure_strict_json_schema( + definition_schema, path=(*path, "definitions", definition_name), root=root + ) + + typ = json_schema.get("type") + if typ == "object" and "additionalProperties" not in json_schema: + json_schema["additionalProperties"] = False + elif ( + typ == "object" + and "additionalProperties" in json_schema + and json_schema["additionalProperties"] is True + ): + raise UserError( + "additionalProperties should not be set for object types. This could be because " + "you're using an older version of Pydantic, or because you configured additional " + "properties to be allowed. If you really need this, update the function or output tool " + "to not use a strict schema." + ) + + # object types + # { 'type': 'object', 'properties': { 'a': {...} } } + properties = json_schema.get("properties") + if is_dict(properties): + json_schema["required"] = list(properties.keys()) + json_schema["properties"] = { + key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key), root=root) + for key, prop_schema in properties.items() + } + + # arrays + # { 'type': 'array', 'items': {...} } + items = json_schema.get("items") + if is_dict(items): + json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items"), root=root) + + # unions + any_of = json_schema.get("anyOf") + if is_list(any_of): + json_schema["anyOf"] = [ + _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i)), root=root) + for i, variant in enumerate(any_of) + ] + + # intersections + all_of = json_schema.get("allOf") + if is_list(all_of): + if len(all_of) == 1: + json_schema.update( + _ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"), root=root) + ) + json_schema.pop("allOf") + else: + json_schema["allOf"] = [ + _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i)), root=root) + for i, entry in enumerate(all_of) + ] + + # strip `None` defaults as there's no meaningful distinction here + # the schema will still be `nullable` and the model will default + # to using `None` anyway + if json_schema.get("default", NOT_GIVEN) is None: + json_schema.pop("default") + + # we can't use `$ref`s if there are also other properties defined, e.g. + # `{"$ref": "...", "description": "my description"}` + # + # so we unravel the ref + # `{"type": "string", "description": "my description"}` + ref = json_schema.get("$ref") + if ref and has_more_than_n_keys(json_schema, 1): + assert isinstance(ref, str), f"Received non-string $ref - {ref}" + + resolved = resolve_ref(root=root, ref=ref) + if not is_dict(resolved): + raise ValueError( + f"Expected `$ref: {ref}` to resolved to a dictionary but got {resolved}" + ) + + # properties from the json schema take priority over the ones on the `$ref` + json_schema.update({**resolved, **json_schema}) + json_schema.pop("$ref") + # Since the schema expanded from `$ref` might not have `additionalProperties: false` applied + # we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid + return _ensure_strict_json_schema(json_schema, path=path, root=root) + + return json_schema + + +def resolve_ref(*, root: dict[str, object], ref: str) -> object: + if not ref.startswith("#/"): + raise ValueError(f"Unexpected $ref format {ref!r}; Does not start with #/") + + path = ref[2:].split("/") + resolved = root + for key in path: + value = resolved[key] + assert is_dict(value), ( + f"encountered non-dictionary entry while resolving {ref} - {resolved}" + ) + resolved = value + + return resolved + + +def is_dict(obj: object) -> TypeGuard[dict[str, object]]: + # just pretend that we know there are only `str` keys + # as that check is not worth the performance cost + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool: + i = 0 + for _ in obj.keys(): + i += 1 + if i > n: + return True + return False diff --git a/src/agents/tool.py b/src/agents/tool.py new file mode 100644 index 0000000..7587268 --- /dev/null +++ b/src/agents/tool.py @@ -0,0 +1,286 @@ +from __future__ import annotations + +import inspect +import json +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import Any, Callable, Literal, Union, overload + +from openai.types.responses.file_search_tool_param import Filters, RankingOptions +from openai.types.responses.web_search_tool_param import UserLocation +from pydantic import ValidationError +from typing_extensions import Concatenate, ParamSpec + +from . import _debug, _utils +from ._utils import MaybeAwaitable +from .computer import AsyncComputer, Computer +from .exceptions import ModelBehaviorError +from .function_schema import DocstringStyle, function_schema +from .logger import logger +from .run_context import RunContextWrapper +from .tracing import SpanError + +ToolParams = ParamSpec("ToolParams") + +ToolFunctionWithoutContext = Callable[ToolParams, Any] +ToolFunctionWithContext = Callable[Concatenate[RunContextWrapper[Any], ToolParams], Any] + +ToolFunction = Union[ToolFunctionWithoutContext[ToolParams], ToolFunctionWithContext[ToolParams]] + + +@dataclass +class FunctionTool: + """A tool that wraps a function. In most cases, you should use the `function_tool` helpers to + create a FunctionTool, as they let you easily wrap a Python function. + """ + + name: str + """The name of the tool, as shown to the LLM. Generally the name of the function.""" + + description: str + """A description of the tool, as shown to the LLM.""" + + params_json_schema: dict[str, Any] + """The JSON schema for the tool's parameters.""" + + on_invoke_tool: Callable[[RunContextWrapper[Any], str], Awaitable[str]] + """A function that invokes the tool with the given context and parameters. The params passed + are: + 1. The tool run context. + 2. The arguments from the LLM, as a JSON string. + + You must return a string representation of the tool output. In case of errors, you can either + raise an Exception (which will cause the run to fail) or return a string error message (which + will be sent back to the LLM). + """ + + strict_json_schema: bool = True + """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, + as it increases the likelihood of correct JSON input.""" + + +@dataclass +class FileSearchTool: + """A hosted tool that lets the LLM search through a vector store. Currently only supported with + OpenAI models, using the Responses API. + """ + + vector_store_ids: list[str] + """The IDs of the vector stores to search.""" + + max_num_results: int | None = None + """The maximum number of results to return.""" + + include_search_results: bool = False + """Whether to include the search results in the output produced by the LLM.""" + + ranking_options: RankingOptions | None = None + """Ranking options for search.""" + + filters: Filters | None = None + """A filter to apply based on file attributes.""" + + @property + def name(self): + return "file_search" + + +@dataclass +class WebSearchTool: + """A hosted tool that lets the LLM search the web. Currently only supported with OpenAI models, + using the Responses API. + """ + + user_location: UserLocation | None = None + """Optional location for the search. Lets you customize results to be relevant to a location.""" + + search_context_size: Literal["low", "medium", "high"] = "medium" + """The amount of context to use for the search.""" + + @property + def name(self): + return "web_search_preview" + + +@dataclass +class ComputerTool: + """A hosted tool that lets the LLM control a computer.""" + + computer: Computer | AsyncComputer + """The computer implementation, which describes the environment and dimensions of the computer, + as well as implements the computer actions like click, screenshot, etc. + """ + + @property + def name(self): + return "computer_use_preview" + + +Tool = Union[FunctionTool, FileSearchTool, WebSearchTool, ComputerTool] +"""A tool that can be used in an agent.""" + + +def default_tool_error_function(ctx: RunContextWrapper[Any], error: Exception) -> str: + """The default tool error function, which just returns a generic error message.""" + return f"An error occurred while running the tool. Please try again. Error: {str(error)}" + + +ToolErrorFunction = Callable[[RunContextWrapper[Any], Exception], MaybeAwaitable[str]] + + +@overload +def function_tool( + func: ToolFunction[...], + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = None, +) -> FunctionTool: + """Overload for usage as @function_tool (no parentheses).""" + ... + + +@overload +def function_tool( + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = None, +) -> Callable[[ToolFunction[...]], FunctionTool]: + """Overload for usage as @function_tool(...).""" + ... + + +def function_tool( + func: ToolFunction[...] | None = None, + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = default_tool_error_function, +) -> FunctionTool | Callable[[ToolFunction[...]], FunctionTool]: + """ + Decorator to create a FunctionTool from a function. By default, we will: + 1. Parse the function signature to create a JSON schema for the tool's parameters. + 2. Use the function's docstring to populate the tool's description. + 3. Use the function's docstring to populate argument descriptions. + The docstring style is detected automatically, but you can override it. + + If the function takes a `RunContextWrapper` as the first argument, it *must* match the + context type of the agent that uses the tool. + + Args: + func: The function to wrap. + name_override: If provided, use this name for the tool instead of the function's name. + description_override: If provided, use this description for the tool instead of the + function's docstring. + docstring_style: If provided, use this style for the tool's docstring. If not provided, + we will attempt to auto-detect the style. + use_docstring_info: If True, use the function's docstring to populate the tool's + description and argument descriptions. + failure_error_function: If provided, use this function to generate an error message when + the tool call fails. The error message is sent to the LLM. If you pass None, then no + error message will be sent and instead an Exception will be raised. + """ + + def _create_function_tool(the_func: ToolFunction[...]) -> FunctionTool: + schema = function_schema( + func=the_func, + name_override=name_override, + description_override=description_override, + docstring_style=docstring_style, + use_docstring_info=use_docstring_info, + ) + + async def _on_invoke_tool_impl(ctx: RunContextWrapper[Any], input: str) -> str: + try: + json_data: dict[str, Any] = json.loads(input) if input else {} + except Exception as e: + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Invalid JSON input for tool {schema.name}") + else: + logger.debug(f"Invalid JSON input for tool {schema.name}: {input}") + raise ModelBehaviorError( + f"Invalid JSON input for tool {schema.name}: {input}" + ) from e + + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Invoking tool {schema.name}") + else: + logger.debug(f"Invoking tool {schema.name} with input {input}") + + try: + parsed = ( + schema.params_pydantic_model(**json_data) + if json_data + else schema.params_pydantic_model() + ) + except ValidationError as e: + raise ModelBehaviorError(f"Invalid JSON input for tool {schema.name}: {e}") from e + + args, kwargs_dict = schema.to_call_args(parsed) + + if not _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Tool call args: {args}, kwargs: {kwargs_dict}") + + if inspect.iscoroutinefunction(the_func): + if schema.takes_context: + result = await the_func(ctx, *args, **kwargs_dict) + else: + result = await the_func(*args, **kwargs_dict) + else: + if schema.takes_context: + result = the_func(ctx, *args, **kwargs_dict) + else: + result = the_func(*args, **kwargs_dict) + + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Tool {schema.name} completed.") + else: + logger.debug(f"Tool {schema.name} returned {result}") + + return str(result) + + async def _on_invoke_tool(ctx: RunContextWrapper[Any], input: str) -> str: + try: + return await _on_invoke_tool_impl(ctx, input) + except Exception as e: + if failure_error_function is None: + raise + + result = failure_error_function(ctx, e) + if inspect.isawaitable(result): + return await result + + _utils.attach_error_to_current_span( + SpanError( + message="Error running tool (non-fatal)", + data={ + "tool_name": schema.name, + "error": str(e), + }, + ) + ) + return result + + return FunctionTool( + name=schema.name, + description=schema.description or "", + params_json_schema=schema.params_json_schema, + on_invoke_tool=_on_invoke_tool, + ) + + # If func is actually a callable, we were used as @function_tool with no parentheses + if callable(func): + return _create_function_tool(func) + + # Otherwise, we were used as @function_tool(...), so return a decorator + def decorator(real_func: ToolFunction[...]) -> FunctionTool: + return _create_function_tool(real_func) + + return decorator diff --git a/src/agents/tracing/__init__.py b/src/agents/tracing/__init__.py new file mode 100644 index 0000000..8e80201 --- /dev/null +++ b/src/agents/tracing/__init__.py @@ -0,0 +1,97 @@ +import atexit + +from .create import ( + agent_span, + custom_span, + function_span, + generation_span, + get_current_span, + get_current_trace, + guardrail_span, + handoff_span, + response_span, + trace, +) +from .processor_interface import TracingProcessor +from .processors import default_exporter, default_processor +from .setup import GLOBAL_TRACE_PROVIDER +from .span_data import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + ResponseSpanData, + SpanData, +) +from .spans import Span, SpanError +from .traces import Trace +from .util import gen_span_id, gen_trace_id + +__all__ = [ + "add_trace_processor", + "agent_span", + "custom_span", + "function_span", + "generation_span", + "get_current_span", + "get_current_trace", + "guardrail_span", + "handoff_span", + "response_span", + "set_trace_processors", + "set_tracing_disabled", + "trace", + "Trace", + "SpanError", + "Span", + "SpanData", + "AgentSpanData", + "CustomSpanData", + "FunctionSpanData", + "GenerationSpanData", + "GuardrailSpanData", + "HandoffSpanData", + "ResponseSpanData", + "TracingProcessor", + "gen_trace_id", + "gen_span_id", +] + + +def add_trace_processor(span_processor: TracingProcessor) -> None: + """ + Adds a new trace processor. This processor will receive all traces/spans. + """ + GLOBAL_TRACE_PROVIDER.register_processor(span_processor) + + +def set_trace_processors(processors: list[TracingProcessor]) -> None: + """ + Set the list of trace processors. This will replace the current list of processors. + """ + GLOBAL_TRACE_PROVIDER.set_processors(processors) + + +def set_tracing_disabled(disabled: bool) -> None: + """ + Set whether tracing is globally disabled. + """ + GLOBAL_TRACE_PROVIDER.set_disabled(disabled) + + +def set_tracing_export_api_key(api_key: str) -> None: + """ + Set the OpenAI API key for the backend exporter. + """ + default_exporter().set_api_key(api_key) + + +# Add the default processor, which exports traces and spans to the backend in batches. You can +# change the default behavior by either: +# 1. calling add_trace_processor(), which adds additional processors, or +# 2. calling set_trace_processors(), which replaces the default processor. +add_trace_processor(default_processor()) + +atexit.register(GLOBAL_TRACE_PROVIDER.shutdown) diff --git a/src/agents/tracing/__pycache__/__init__.cpython-311.pyc b/src/agents/tracing/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d78e7663953b1e6170c28faaa9f181d511f3603 GIT binary patch literal 2896 zcmcgt&2Jk;6rXk0A94JdIG_DsNt-rqgWW<4RU1H|-%@F#CU95@RvXXQal7laX4j-a z93s&_fLpH-hZ4kvBS-EWSR+eBb3)?Oo280NPrNtwt`k!Phr(v|xAWe-*?qsC-@JU2 zPA3UmUw!#g?dJp`zoQXcYJc+lhC;{_LJ3tYq7xdSBbH)E^oTv8kJzfN+EG1f$Ml#T z*W-3VPuQdSsGZc43dX9|n4Qv7avrtPc1F+GSv@Q5n3c2hdS1@s*0?>PPuK;$U{C6k z_LM$lPwUh6j6P$}>a#K?Va?e`^dt6B{iw7@tz-6a{kTGEgeJdM^w;PZOVJcnswz!C zR&|YL*y}7c8!#gMwJfa7(;Pd|xAS11q~mO%Z%=@IDnghBclIgl{w{pOQ_$i&xO1wM{@#;>a zXJ#RcadY6v)tF~gTAYX5^9Ma1H!DnJYAusfZq}{dTT+{5gF4k}&x*>Rq9StZ5M6K7 zR=HDQuIun(MC2%|nk~yS*rTSyJ;p^o@HE3PMNHnjbMJ$dJI4C`m8;i{)%*88zJ2Zb z{bF3CSCH5m@VaJtrpR2C+(J8dqfamNjNc3yhQ9pGApg*nz15=-IugMQl2&mpMiH-b z4m=bQMQ*G(EhbD#4awLD32>}%9z}>D#1Y^W5I%~4Cxedxh=L1h4{de_UeL~EF*wFnL0} z18Wb$`4{A+)ff4EEvdn^LaD2nT7zwAl9d)pQqtBp>+ZnTw(6FpaaLjV&zNSyTyAj7 zn5kQmZ@XKJ@EoQ;&RMPQLa_`e6cv6PmR!tCd$C&n$YtCubGuw|C@ahAcj-fn_Z)A# z8Mw+Gb3M0QDwSPc>4%l^%OG{xFzSuEXBegCHa`OIm2e5ag4PcCBSE5b9d&8%)3&k|A056$<-_lwyr}nzzX`3uMro>aeA+uF+NZ4DC8st#xY?hUi@kTs|l zbl8Cg8Tf)2Lk;>#g9K;5cR&I$hs*sF(Qnd0G=BqPKg4*fjox{fY^zfpb?R4j{ugzA z_no$Swxgc)gCQj<7DdeT*rU29VinF{`fvNA?0I4$EK4|_u(=AwE!&zbQI4c|7C=NH z^;WajGDc;`qwqiCANnJf))JG(OW+f8tX3L|_R^wYDg^=gyMPk`@Xv<>n@n!fD{{HT4gplrtqA2?$y(9m+ zYEp@G$^QjYM0wLE=MRp3a>aky*e8qr;n9sGFxUSA#))#mC-Z+D`()NXJi6-47-SK^ zPQ3fJs=VtjUg{Ef{;)0|!;n#xi@WRonaiDpE4X&?M^!$C0piZ@UO?~py=(F@3=pxj zH-g@!@8vs|z literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/__init__.cpython-313.pyc b/src/agents/tracing/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96425e4ec19e2fc8506b15ebb1565d1575596b69 GIT binary patch literal 2576 zcmb_dOK%%h6u#q(9mkFzNt`r~KBg{7<0g%pssYp%Q2M0f(iS^K2rPz)=f)Z8daN^J zlCY}S@C&-nvh3KwlAn;YM3pY8io~kBK#H>EoIB%jT3CS8k^IfM-?^{v%sIzP{9LDttn2_`0rNjCWIHDhgqjKC=I;Lx&Njp(GuIC}IpMVn@E3!)RV+G!- z^jA}rR7E+bR?_3_D-|Xynd($E6-9{wpOyT{%0P8GUw41x!vdGu6pFt7h|T zAqda{#)lq_RayB{-#&;qd$9c3LB_Yc>*`8j_yv)-UeI2Jgh-fm-t-K&Wi~~!)^6N9gv$pGG9H*UF4Fb32^N@k!{4QU)ofbM%1Uqq zaJS_&UG5K~a!o-I*(F?TH|ux6sd3kJpb!yRi?5jNhG+2gmIEFKG0G?0 zr&)BNR`i)7or6&>8iw7pJ;Ru5ZNM?yNx}!TFGcqOdl_fZ@u%wSV|Dh?7dz_3J!F|k zgo@lZ8oRrR3imW`m1_-Dt2ryL2ha^77{smBz-*QCgW?a@buV}zifPKat_LGHO-f}1 zVG2#qI&h5~uY_Rwu{!-|d`CS8C($S3o&0>G0ho~iSX<>71CZkJBWaj2^@g*I`LUsO zjy)i&pJa7-6g5M4KOpKji6Z!j9IuF)daO=uuJ5R4d{Mn-%Qo)ujepnlHZI44pq=H@ z`70<>$-MUbqb$ybpY}GU=Hd-)@y-nmkJ45g6pCMb*{t2;O-t+aR9|2Q_Z3MJ7+ekY z%^`O{pBN{1kkCO(?#zaHoV!HxsXO@TJp4o*d8+2O)%@l=JL-&7UYHXx)8p&5Ct@|= zX#TH$CVP|^3R4S<3U^q+=Eqem~!G78+*=X{!|YqBM*9a-&d9Qwx-|RW%&MS=_S2()2gztxwLia{PyGp zaxDC;l4F4!^PA_0ntybKsClAhAN3G5`-7Y{`^N}Va@)DdC%Kcqu<74e?7PgjnXT;P P)9l=Kc5a6irLeyN#p8cI literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/__init__.cpython-39.pyc b/src/agents/tracing/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63b233ae90cd6cc237953f6c151eca688ad8ab4e GIT binary patch literal 2212 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 J#!eaj|1Y3dtg!$9 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/create.cpython-311.pyc b/src/agents/tracing/__pycache__/create.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62b33cf3542d1eb37007e981b0972fbde07091a1 GIT binary patch literal 10389 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 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/create.cpython-313.pyc b/src/agents/tracing/__pycache__/create.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efa27d1aea26c67ad87188996ef513e4505297b1 GIT binary patch literal 12635 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 U3Ku~N7eT;v@W1nb_)Nn80eY z$D7Cbo|=2YJJ~$R&O+C*-6&CXYX?$`kVB z7nSB2c}h+_EXvbz`ipY&tUSZDv+^8j=j8i%>O8*m!CWX9&C(p}YCp2~6 zjC}UtpnOhVM&C2?1Nl6@uVPE@3o7>kauPdft+6qvw*mik``Y+B2wZiXW?YTO zwgYv4!wt4uzUA3*Eh54#N5-{H6?8jX^6bcxG})STLTlZ%Wn5Ku)Kz}FLUK<&Rs0U( ze~BFAMHHgKSH-Ujf2{0P)KpaZYCsmhhO0z{&&qp+z2aVJZ(y%1OJ7!?zyWI(q=p4P zOUkw)TUf%kw*|A)5O=n0T@>4n>k6n?tlJ`tEEU;OSY2oU0&ZKbyYsHt3A!TO3c9Wo zwi9jHN_0b;Njs6SLQrl8eq?_h3D5E^s9VggZR18g!fd0_s4uM5MW6%`6lP*AuV*c8CW>f?C!>>C^zzj?ZU-V*FiU7+%Ze}r3{~}x(GC=b ziQF9$5;O?g&t_=cbs?QhKdYjVT*tpJysjHL(2Qmx6dNk=Se+pmCmiH~twJ`ERE9KF zXab^X+hM5n>-g{sz-nH>ur|^l%_7=@GfR?JO2rs$j?bL=LM_H^U{dDZoK+{jlvt~g zzQLsh(%&7`1wTe_8mPd~9YQv--X*T=q?Sh$(x8$MDJE=BQ>?_tmRviYW9U*c|Jj z@=ZZq#GT)NF#lUXR5-7^c|wBupkw=%bA??zywcf;wgP`XRPA})GEY**u=9GLbwPG3H(vRgbuIBqlT|(dPs1~1IChJ9Y4vlSkD*q{=vP4T6%~UUCSg4A- z^NGsXjFQ=qKmoxI5_g&->*y#ugt!{@63b9M12R0@+(l3ubzH}PNFx?XT;H9fmHuvS z*?w&P8i(>1f^0wb;>o;<>8>1`q$4HhGK0&<_&2;#eRtA@DN&qZLX6#MBjEkB*gc_J z2-m4ZG1R?!O`QjwxVEK3pb*+bh!VylK{x7jVR1VhJ02vaC?2VH0E~Xb2C7v@QHS|Z zIgLUtpn`a#7?n)KA&Xy@_sV;fy(%J)GDjYN8-SwoK?fU|py71nL8YG$JTwIM1|BkC z2+`pMlWh^P*}6cKv6h7xJXSu!;D$87pg<)bT=cHb1;NVFGZbq7Q)rT&%bF2eG4-o27aPTJEk_ay&%$v}n(wo~Py)=)w!yfL_o>lJ$!2Q?%!=5B4Lq@97*Q z3Oo}JGehjsw@?p9aVeZD7svH_^MiT`_1!6&`Q82d!h6YxamDe0adA}}`2G=jX`-E# z`B0hud1)fp%;Ou-ZGd)FdzBR3=Juo8MzTRYdVn*O|7#d$2F$FIWvP9GiyCiiaqvzp zZhdQlEynKp~GmoY5(MiHy%) zb#E}mpv%bZ^h8+6CYVwX+KFh_L#6*H%woOCK|nNw6Hv&K{FOfbF;4E~hYXGxMk62r(8(S%gVCat>R?9rGjt8jVCD0tJkr$#M{c z;ESnfU3GOjkshXe0}-UPj#qxl8U;Q!B)$!L2)7cp1$KO-btZ#ZaB8t9-$e1in(0Yo zhwUnM_}FA7+tuF4Gl#ZjLL`u$sfL)Vk3F}}fiVjiT<1YJPay&1L|P$bS8VLAWR|Ab zM~JH$%(GBF)4lF}sXvNk!TY>gHaQK2Wkuku&{ufXSCvhpvP{L&R$MOKDO}rJoWP@%+EQ9evE`wk4NxBI1P9Mj-}qD%Yh|* z0_8p!$j|!3P1_o!N%o2X)>Kw|4C@*O{;gt z9lQy103Me1!-GD-Wpv;~s7^GSQ*Ipl(TTSb!M2|s{PoYjvmiNJj=;WWDd?YrPd0{n z{C*(91lPz58%LXj&rLb=c9J*t3F$FD4o;}rJ+iveJ8qUr6vfI%;$0Wh*V4hM{poDY1L;>C0>WBm@{#c9Ti1MeuMkTDb~ zhu)L2e?Jx6HpG%fH1r3H9rkht_<|+JWohH5BAH!-loqR#@W; zinj4_6(_R@a1{j&iZ&7Y4UtNS7hF0zd0wYhI&4z}TbxE$l*CXpbw#m{qCHYvA?ps+ zF)CO2Br@dJ6`ND~y^sbS0*4K=Nea~odX(Pm&@WlcP>GJ-6lK&of1zl*q$n6tgm8)k zlS|R3FF3@t_mBak4auH2rf!xR#KxuM~>>eRQ@lUKt-uYSi}K kh0AH}`|~qtS>OLJaV2T_VftL!_vq}Ur2OctdE(K(0n~W&asU7T literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/logger.cpython-311.pyc b/src/agents/tracing/__pycache__/logger.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07d25033a809c07dc39d33c5db94795c6e08bc08 GIT binary patch literal 302 zcmZ3^%ge<81b<8prMm#>#~=<2Fhd!i^?;1&3@HpLj5!QZ3``8E3|WjYMJY_nfb7+9 zeiTy*V=#jz^GlEdzbeuEg4DdkOufYP)Vz{ny^^BD7XOQM!e)^%ssYS*5MY;OP`6;RT5HnJ;;|q%NOY%!93t&R}C8@J?P};;_lhPbtkwwJYKT8V_=Du_}=Gz|6?Vc!Pnj0Sq5- cN_B`|;gq@{ZFG^-_zI_S0~-hyu>(~C05?HWF8}}l literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/logger.cpython-313.pyc b/src/agents/tracing/__pycache__/logger.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17e3c740e5f895eae4813ffb5970cea13c145d7d GIT binary patch literal 278 zcmey&%ge<81Xk%6(p`Y`V-N=hn4yf%20+GChG2$ZMsJ29h8Tt*CYTZgt;7(_WXcHP z7cr$XYBIkBar~-8^9xe*5;OG@(^KlfwfC+DZ6>O+jug&3t3irpz;=nO>TZlX-=wL5hu_J+)3}kX Ih#e>k06q*yQ~&?~ literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/logger.cpython-39.pyc b/src/agents/tracing/__pycache__/logger.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..756e590d56a4c29836ff25a084c596b0b3287efd GIT binary patch literal 228 zcmYe~<>g`k0;}{3=`KL}F^GcPX{t@ai*u1_yF0dMYq^M zVyQ(d8H!ken!&^`2mR3E)S_bjqFnvt{FGFEh&? literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc b/src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ea4cfd5decd0bb2ed1214e58b8aaa9614fb8655 GIT binary patch literal 2644 zcma)7J8v6D5Z-(8AyJa^aO^~~){c#UfI~KRV+27IMX`+lga9c;XatAT?ebyWgWf$t z7Lh_lkfd~Bz(Ib16T@|Y)cFN0TnJYgaOF;dTp1}cv&SPvkx{hd&F#+4?#_Js&D`&$ zQjtLW=f>a7S0*8^aF8`?&^h`EI!_2AO!G+{-@2~_dR-5Ux}g!Si&DXFZfOMi zmTw2SdQKxcxlNe;BVjpC?&q_OpBjWbhbL|Ig0jkkRpAt?BGWcVx%e6v)yjHO+Wc;P z%~`#(w))N8+U>+x33p#>Fjv+RbED&i@U}LE>v1nVCY9N#N58@N3E`x!F;dr=RyVkL zKZ}^KS@7C?3O3J&tLyfdMTr$AEOKKObF4UFkw0NEGhtC+7Q{K9ETHr4u(>WGk4qVe z-tyfr=Ar>1(MBX_C!GKjDj{B3Nvs&-6*+J@u?3I2BCL7(FaSajA4K5D1_3vUi0Zwq zRoC}9qdP4gQkSYtXj@VlyCUYSQY_LzTM9X z1D1V%21tJ%hZHwJL>uj}En5I7-DpK!pV2Ui>6RaP+Y~NTxNXTPf9UZ}+>YQvPXive zOmSD6E7*tI53i^uN)SO=7jy6u?}8X0FXl0_82|v6J3M5)#cA+lv+v{jb12>c;UST- zHl0*gjw28QJ={*mZ+k9A2KIGsKhJ*jJ(Q%(ljP^lIKHDdoR)*s?!=-5u z=(Y#Xuw~`^=nkk(DfT+InSrpRjhW19m{UFgIaQi-$^3woYY#gSR7lZVPkS=0fk>?Z zdny#j1}W_Z9iLyPo=SQ@YEsCE0CQeK=B0x;g4(;~dfT9ZQr?bvAbaQVBu;;z8^A?X zd{RoBp#*uEPcHS_GDdDA9~AL3?@lW)%7L`Ps4fKuxeB=QALZp5z&BQ2?9{$@{sdl? zRDT`pM>VCK7fYzX#1_jaD2mG{u7F5P3`2Z^;}gWaiaHc!c;q^WWv7N(Oaogf~g~BluL;XA1Cr!b3EQ2q(xz|b^ppA`4hZ=YQ2Py4?#79cOQOZ(e_qW;Zj@(Znq#*^Za_qch8`ZrT16-@sE+DCNn literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc b/src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f216bc0581d7000445ac12fa28a71c9f9f2d2a9 GIT binary patch literal 2943 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 b(+cuHJzZZ0Rj?|BL?sEk0xp literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/processors.cpython-311.pyc b/src/agents/tracing/__pycache__/processors.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..719f46870b76ae6cdc060a57603e56ab4aa35d92 GIT binary patch literal 13530 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 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/processors.cpython-313.pyc b/src/agents/tracing/__pycache__/processors.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da8a56646711ea409d3fdc7f9fdbc13a0dc2f66e GIT binary patch literal 12774 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 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/scope.cpython-311.pyc b/src/agents/tracing/__pycache__/scope.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca52084c4744ee1710a07791e6ab00e7b26ad412 GIT binary patch literal 3108 zcmb_d&2Jk;6rcUDGx66tX_I~=ZMSig#t7^KsG?CoLTy!0MTp`Q372Z+dKc5yUZ=C` zik7I#p$85exHS?2NMW`0Vi69mScpMR}=9wX#;dvBUb%K|Y(mV)P4J=Tbqf3+3PdCZxiuVw*N1fB8GS4g@)Z94QBH(b3?B;& z#7OFid3frJksJ^s6^fAx#Yp=xE)zZTkmyR^+ta#GBKhn~eCtA9^wP_pEndx8j_yX zrHArkK%WL{Nm&MbM2|=C7~rG&a0HJ7en?M7@FBnt1C9OhDd1ze62XUc3gnJ^u~N0U zVt8?8*ezzVBz##FNeu=z17@`U4v0s@4siJ0=v~*v!}*oSrU12LDX@D|&9FBGPpZ}} z3w_mtkBe6=gIPt^C{~-gQLNOUR+fHSTVYMRY2RB3prUP9wpA)EH`f(=C%IDxnAvV81O@`IA7;L`8Ry63Xg${r(G^fgyDyEIkOBK^dkVV z2@n>(OjX%Y`0=t}9GGuEj=Rnp2ho%7#^JUy<0>-_ongl@TFl1$76zv?2P^(dI&kKY zzLa9jfbZMvB&9;&P*S7Z98T+~U&g5A1bR<`fW+w7Hl5g_6YH1SbjqbzN`Lb7SO!-8 zzpWhTZ$pYerx*$8tH93epK8)675KCIE}7BOP<(PPH!ov+e*$=at_S}1>#51 zsc26wqoH#+h5He&4c?D;L4g8x`;3rVU0oMe1va!Q-0F6+31`;#e?xo>TKNh7rA(Zro_qJj!A1rp`F0C&3gn|5?yOeKOM9 zZqqQ z?M|yw?^u%6dd0FDhW%wzXLuO0Qz*`$m_hL-iUNpD!oQzyUp9g$3f!007KpX(-%c#M zCknCL9tp%NiNr@9Uh~m7=%}4VyDxg|XZHg8Klz_Qe_!lgPV@jdYs0r48eTKb!>U1i#t zdDm6WV?>b)SO6L&bBn_B3HSUbJMy9RWpJHC*Ew{Z6W21|CEe`I#sfEduHQ3pOn@>( N#Vf>K4=`}P{sO48t*`(9 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/scope.cpython-313.pyc b/src/agents/tracing/__pycache__/scope.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..017851d9490f1e7af727f60685afc4466a66b659 GIT binary patch literal 2764 zcmb_d&2Jl35P$pSZ2YxO+B9v`(#>aUpf;$qB2fULG%6%g3f=?>l4`Yew#MMB!|WP0 zfT|u4LI?>im2&KnaOr=6IND7sSF2VaQIL8I_<+TCqZZXx1@}Fr&v140Te3_`w!NF9o%dFyg0a{x>g) z&`a`Q3nLkNNs*VM49`s;XNGoO41$4FNyuiN&|)Dg`l-eD-!zv!(;J$;t&P;xkroPBF)SzId6qoW7jf}lnJx+ELzKoC5NPdeUE4!` z5JkRBp}BO1uESB1WKerx;CtoA(AqtOq$BX#hqtwfx;gK`4m8!FhB~x4dtV*l8T=n*7D2D!AwZW6 zN9asmW&z@Toh8Aaq(8X7R=8rAwnoKrvZ)O>wBc=Sw62Z@@pcxAjJCX3zC@-A@J=h# z(ZKE{ED4=mcS$Iahdtvmwu3Y~Cq|{Ev*lQ1Qs|Ai-MO;}v*N;MgNE&S_)zq)LFqiL zDLtzI`SF!i`)aM^t6XfBSAoMuw-i;Z!c|=*+t*o|Tpae1lHVlj zgSnpueyH8K@#Br3FV=JK)&~mrRO6u*qI-LMTRT%%&+NySu;uuEfXuVt+ksgd*+LI) z-ZOSFqvGeN6{zO-^uKY-}iL2rVqsE@jY`7THUxqE8< zp?ajLo@l5iHka;)6}N@)`7yA)8aR-^Ef~^sIB_0cLGI~jvFdpCb?;LP+}t8P*X6z6 z*A?6~M!Tpx^Ig=MX*Z;ip!RIU2R={j_qj+vUHHYd{ax7gm-JQBbgVVoH2t_~u2olS z72=bo`AN;Hgc{8>ua&{|DrLuZs;EvBE0*i7+1T(`fm$A%B0_Zp^jv`28N7_dA23bV zv%GTAv^)>WS8JZ_nkJkh1L}wTwFA9gPN*fj*O7e19w?cXC@7;XiN*SMh}(%FN3zpx z+3B|IAiJ@)-B@TRD%ntm+>XlqiArGC5|ImnqIj}t%yaja{Ft|~j*k?myn$*pe4PBV z04iBDA-r8sX%U|ZHos><@S!4%mg7_jjjBT+@Mp|Z?}o!yr-Pe#9cIuIbw$@jvLgwC z@R;@g!p47R$N!KrkL008^2uMPFEytNcc%*vsraQp+c C2u@7^ literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/scope.cpython-39.pyc b/src/agents/tracing/__pycache__/scope.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f28b19c50a24db84a63286689de5a7db286b9b1 GIT binary patch literal 1960 zcma)6OK;mo5Z+yq5=BWi{fHf>kElp}l8ctW4rgbE!*9Ns-Kg937(5S0e~!L&82bx1 zi;oSPk71c#K|EoSNuIC(-y-3u2t;ZH7H86uc4DV?U~{H~{uK63;-rnBLB2-POx?f* zAA8Y~P06MEn+QDGwf2~5OYflawQFC8d>!(257=WS*W~&uCf8qzpey;1_5C-1h#mDs z***T|%SYkiXO9lQ`26T`X?>ENzTx2O^RkiTqmj~}*~4*^!P`03QLM^NJkeTZMQAE> zQ>y3DBq^?ZjC%?xp7qXpM|q~oZnzj*c4xZ+6J2#E-z;`*9!uUBAuc|1I)4GHVgZ*d z5Xyq0#cPfvzOrBPz$S|$8?ObB<&w<-ngu4q|N?u{)5V%F1Pa;42+HW zv=6CbN{C6}LEVByr6MI*>KVU)Y1139otJA=0s1zW^lcOftr9z+l|J~dLEi)G67=N| zoMWdz#Q%Y74RXzJ!P$aFT*KyQ1*tD5@4- zk(w&Uw$J9I2l@&Di$g%4-iIiI2${IU;jeTUR-*oWXmJ{=ny3bGZeb18m{1*G;dHE4 z(zmG&jSZ-ytsSKY+Lzw#TP?H%$=5^}W&!!z@Zw7?xfbrn#IXsCUBf z$4Qjbo_3r>#-s}F2&s1w!yOdwqPUCV9t!N=0=W5r>veG8*)+(8%LVr>!8hwgx-B?G z_1DVw!ZekQ;&cpM)_4(Uyb0>=;&owk0}K#?vu1P?H{M}&-FzMC>LOeZ!zj!0A}UVu l%!FaZB?7t$Y3UwR419<`B)A^;MMv1Y!%^CQZ`&lDe*o+G&)NV0 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/setup.cpython-311.pyc b/src/agents/tracing/__pycache__/setup.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3aed1e68d40a0fdee8576907d313b7be9593b8d9 GIT binary patch literal 10043 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 EA6|6;!2kdN literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/span_data.cpython-313.pyc b/src/agents/tracing/__pycache__/span_data.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..127efae09beea9997fd9ea6555bba69c96fc7883 GIT binary patch literal 7611 zcmcgx-ER}w6~AN8jK|~nBOi8bC$J`bt%1bLvb1HF-3`kwkfiHU+-yHe7Y2MS1)=tEy3RpOx!{Wpr#3Tvbmq?Ic1W@J+&o_fx`GZTLd0gC8A z&Rn1Seedsg&bfD*!Jtaud3*71i~s5;G6;9hmU=9cxEbl)bt*zTm-FhLerO_nr6oA)Kq1;W=t8{((wW-_&@nq@9?pECYgEtBrA)Cp zuVt#sTBT4LFDz;mLmywhXq>55GCD10xF%!JLJ?LmqeHj(dFXdy`JyRnUo2OtVTSF& zOmIaXbfF`1FO&O9UlS6~7$FTo5gJAj0l~*e2Ow#R#>HjL3_3mFPgceUxtB>pCjKCe zVoj``UGtbS)r=agJPFO0ipELZpnFoVzo*tbG>Hv*L6cTZC7-Vpmb84{RP*_z>U^z? z>Armae63Kna>DuiLW$}|xm3|ARmk=hP8ZEk;WRW+C>l!|tkOIk#8#DM3QI_hi*y)& zM)P?cL0HTe41<cMVq2gwO|8fd{+cM|RiHN|n0+r_EoSKu` z@ko8S0NRYRB2{ueK%_U_X9f=;cIBSi+sD;#^ZLgjZb^C z*=$*;C~~aO8f4I z`Wq6w!4eE^ZTK*ykVvZG$COH9sYh6<5l4Ff7Gs!M2DBZ+k7b|4SB7;|gg%_dVvf+9 z1XZtj$SK?vF*9Hp1;-uvY{LL%Kn645O9#xzky@pQ-q*1N`=H@9FI#p~DxpI)WlsNM zJM{JEum-krnE>8^;?X3q zJ2tu+=;d6umu2Ku7Ocr{!0%{J_ek+~px375GQ)}9?l6l{{BuaWI*J3CMNVe1jm*w+ z(qquOJCBEZrp2j@b#>g*9x7WLjwP?3yL#@SUqS{;HiNyCb;=k#3AM9nxsA9i?|YzT zn@m~Cp(Z_m87!DsceUtTDGEfRjyyz`?Y=zK61~zUpbsZa(jiUqu`Ju9!Fukb`5bb3 zNn?A_UcbtG?Th=?fukDOB^J~=w8E_blk4n_DM1vYmCZi($``AZh0>xa)pT$;*chmSwR zT4-j$=|)IwgpqM2z-c-9`rnlVBv-j*d$#9QAwIDdaRYh|yNm}3n#Zx&1Rd!mD2Ahq zpevAgbp&md@9Guul2pyTXPV5dw+dg_Ub$4|9 z&gi)H`(ey`NrY^3cl%2;GZdpbt@ z@N+0ikDx%O(_xKp?C>WvDI{LZG@#?erw}I?*1H7oZs9w4anCk(SA=f}&C?z}^nkrh z`O-YBF$+v_wx*{v@6P|>p2>3d{{m|1=u6kxpV@c&tJ`1QcZ?;4Op$#1ZH9|(x7liOCp^>}KPUs!Z7#Wkq*S(gVJIPIbC zevy%@*r)BWU5@7>~b$$YV=o67zqUA=jaQtDtF_(wC^E^Ru3Cp2C+&Dq;E)0r2(iXru4~i3x z`$;^OB^%vX?}gC)q9@7F;hJt#mmF~4gbv{n503<6O}{IkJ{PqN9@st^KfrhuO80MI?=CTEbefQ zc8#2)`A<0ZPBeF1>vW+Rd;@L=+iFBM>zLixF!c!LZEV{wU=b7u8VA`aj_ibk?6`yM zcpI{@o;$KXgq+^MD(ACrNv!MAjqXXf?GBjzM{5N-PYb281MojX>o&msaBIA{s8MbH zWl)a1?m+pkK;eS&WY1t6px?$C?LbeAt%iEh=L&THqCLkAupaB}8{z&J?AL~SvbV$k z6WnpZ=xO4=ZOC+4&N2=iA}dQ-`w1V_-tA@(gAer*A!2~W%)CZ{!1Ji z3Pg{Cbcka912%lCgY*#x=_74O$9m}~egRpi!8a550zxrHp<^J-aIso0YwRvhpMVb^ z6qlUhmY6c^5PS`yGHUSTYsUBoqzT+QeZmeib*f+qKbG(w`rt7C;;|ET2T`yi6pO={ zD`Ko=6JpV~b9-Yd2l*G8_vr7T8e&cV5=6r*2*N+f*tbNzN5=1w?f>>h?ukcRvLXb3 zt^fAIHy2t2lC4-$P=9x$$}`~AdLshqGc5wI R)(%Y1uBUG(k5JDL`CkBClDYr@ literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/span_data.cpython-39.pyc b/src/agents/tracing/__pycache__/span_data.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6032252f3b133f0e78f74bb02327908cfde7510e GIT binary patch literal 5704 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 VE?5<-f`}u3R>iL64qsk)@qfNg5extT literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/traces.cpython-311.pyc b/src/agents/tracing/__pycache__/traces.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f45b3517800bbf8bf562c19c3d9949389737165 GIT binary patch literal 8065 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 literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/traces.cpython-39.pyc b/src/agents/tracing/__pycache__/traces.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7aae52b3b4bb3ae689364035101d521293d5d0a4 GIT binary patch literal 5578 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 N@Xu;|)V$?d{|C;aTqXbj literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/util.cpython-311.pyc b/src/agents/tracing/__pycache__/util.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70a74e916c55488ac1f32f4f29f8c93cbe70b25b GIT binary patch literal 1047 zcmb7C&ubGw6rR~1Y1&CZNe}8ttW8V`St2=z1P|7ONXWtBC6^F)w{c^#8)jzFdWxVo z4=JSdpoN?&rT>M0K`H^mf+tVC1#&AMd~dS}8tcKg^S*iW_RV|Wo0-qa=@WFi|&x@ z$~h*iWR)Op)uk<+wjIO~-*~Pa#JDYe?wV22a8dzLr_TWni3EmoX)bK3&3%M4F*=lE z_H8T=MAk09h2%+tjZ7}82s=a?c;UGEjgO@Zo5Ggx(mx9jnCU7nCB?zku$&xFl| z!P|!ES+-Gc*sc`T%UXwf(vy3gxyz8Ykiy944Z+PQ%#gfpI&Q=8rPIoH_GZUWgCaZ$ z$#qb16QE1Jv($&uQT$WljBSqC<~L@1WyTpRj96i)6*z++6LcX+9(8&T0Pc)lerA7m z-kjew`ACiwI0vN?#E_z0J5dA8_Z=(H5sQKP!hX&1$%LhO0<0hbt=DGZj>Xr(wF)Y( z19Zulr4JtUpX`_W<Axq=X(tk6@!w#802IK#kP5WN_AI|;Kj1fD8$ML=7GR=MO2>qC!*5J7!^_4bM(N$&#)QDM Z3~@>`Jrli|Wq@B>y&d#!{S(5DegPd7*ChY| literal 0 HcmV?d00001 diff --git a/src/agents/tracing/__pycache__/util.cpython-313.pyc b/src/agents/tracing/__pycache__/util.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebc9166f82841085c9933dc825d14c4c61e0c970 GIT binary patch literal 1076 zcmbtT&r2Io5PolW6Qi5-hw)&gl%>(yXmx9=rAY8nX)%h0EInBW%jOxknBDN+F4mkp zmR>6UKnjI=^}kv1AZ!mUv=?te+IweSHn!M84}I{y`Q|a1nQwN|&gJ?DoY{I*e?tlR zica?^328qCvQI2x(M7`1DvPvCIV~$(F^HwURHqe78zQeXt|eYdda1-qOD|og#hx=v zx?EJ^OwA5h;5JyCL2UURi_@G1A@`i5urw+l-u5cMK3S2E2$gPs7cYd|CM#Okt)#o` zmz@EPcq+Xe`#a7g&M+y`*1d}CL>R#uGn|lf<^=}!X}F%TU_Cb;Ke|6 z(WfG9it7;a_mL(ami}v~-O*Goh6^c^bA>!qU}Q?`#xv$I4j)w*w&Af21G{I&!u({M zmb7}L##$J@EIk$xxA?))hdTa|mW1O&GCzeHM!rD`ORsyB z#3(M=HW6^S5prHK>*cbUQp2x>>uin>!O!qtAWVRBl~VeN=%2~xd0L?lwhP}0nDah` Tj&3{1N0E0kfkZB0++nD7i#1c$v#GOt-rec6yfT9%A;a;D1md zd(6M_+*eQe3!bc+*_cIw4P8@JRK0rjri-1OO@`-a@^kW)Gxm#u^&>HOiPybBqnYM2 zmeW_vc+GQOi(GJKq?X^gj&%Gv%A-{@&>I-Vt0>V!j0T#Yv2^nWvDj%ULZS;+jE%>CXH<&OSH;E`vHN8*Q>? zZfsE<784xd4(FGC+StrNne~~>Ls3?ClC{2?jpmpAdSk?n5z^6MCV-Ux|B{;1noD*r zzOf$ymlTVGx5gSk*_ue0oCwutnytz-z3w zh*eivX(Dn<>LL2+25eIw3Ap-c&O;|8P%Ti=y{O5Hy3y^-9OENrj^-wOPwvLM@gERv BsyP4v literal 0 HcmV?d00001 diff --git a/src/agents/tracing/create.py b/src/agents/tracing/create.py new file mode 100644 index 0000000..8d7fc49 --- /dev/null +++ b/src/agents/tracing/create.py @@ -0,0 +1,306 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any + +from .logger import logger +from .setup import GLOBAL_TRACE_PROVIDER +from .span_data import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + ResponseSpanData, +) +from .spans import Span +from .traces import Trace + +if TYPE_CHECKING: + from openai.types.responses import Response + + +def trace( + workflow_name: str, + trace_id: str | None = None, + group_id: str | None = None, + metadata: dict[str, Any] | None = None, + disabled: bool = False, +) -> Trace: + """ + Create a new trace. The trace will not be started automatically; you should either use + it as a context manager (`with trace(...):`) or call `trace.start()` + `trace.finish()` + manually. + + In addition to the workflow name and optional grouping identifier, you can provide + an arbitrary metadata dictionary to attach additional user-defined information to + the trace. + + Args: + workflow_name: The name of the logical app or workflow. For example, you might provide + "code_bot" for a coding agent, or "customer_support_agent" for a customer support agent. + trace_id: The ID of the trace. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_trace_id()` to generate a trace ID, to guarantee that IDs are + correctly formatted. + group_id: Optional grouping identifier to link multiple traces from the same conversation + or process. For instance, you might use a chat thread ID. + metadata: Optional dictionary of additional metadata to attach to the trace. + disabled: If True, we will return a Trace but the Trace will not be recorded. This will + not be checked if there's an existing trace and `even_if_trace_running` is True. + + Returns: + The newly created trace object. + """ + current_trace = GLOBAL_TRACE_PROVIDER.get_current_trace() + if current_trace: + logger.warning( + "Trace already exists. Creating a new trace, but this is probably a mistake." + ) + + return GLOBAL_TRACE_PROVIDER.create_trace( + name=workflow_name, + trace_id=trace_id, + group_id=group_id, + metadata=metadata, + disabled=disabled, + ) + + +def get_current_trace() -> Trace | None: + """Returns the currently active trace, if present.""" + return GLOBAL_TRACE_PROVIDER.get_current_trace() + + +def get_current_span() -> Span[Any] | None: + """Returns the currently active span, if present.""" + return GLOBAL_TRACE_PROVIDER.get_current_span() + + +def agent_span( + name: str, + handoffs: list[str] | None = None, + tools: list[str] | None = None, + output_type: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[AgentSpanData]: + """Create a new agent span. The span will not be started automatically, you should either do + `with agent_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the agent. + handoffs: Optional list of agent names to which this agent could hand off control. + tools: Optional list of tool names available to this agent. + output_type: Optional name of the output type produced by the agent. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created agent span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=AgentSpanData(name=name, handoffs=handoffs, tools=tools, output_type=output_type), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def function_span( + name: str, + input: str | None = None, + output: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[FunctionSpanData]: + """Create a new function span. The span will not be started automatically, you should either do + `with function_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the function. + input: The input to the function. + output: The output of the function. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created function span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=FunctionSpanData(name=name, input=input, output=output), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def generation_span( + input: Sequence[Mapping[str, Any]] | None = None, + output: Sequence[Mapping[str, Any]] | None = None, + model: str | None = None, + model_config: Mapping[str, Any] | None = None, + usage: dict[str, Any] | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[GenerationSpanData]: + """Create a new generation span. The span will not be started automatically, you should either + do `with generation_span() ...` or call `span.start()` + `span.finish()` manually. + + This span captures the details of a model generation, including the + input message sequence, any generated outputs, the model name and + configuration, and usage data. If you only need to capture a model + response identifier, use `response_span()` instead. + + Args: + input: The sequence of input messages sent to the model. + output: The sequence of output messages received from the model. + model: The model identifier used for the generation. + model_config: The model configuration (hyperparameters) used. + usage: A dictionary of usage information (input tokens, output tokens, etc.). + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created generation span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=GenerationSpanData( + input=input, output=output, model=model, model_config=model_config, usage=usage + ), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def response_span( + response: Response | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[ResponseSpanData]: + """Create a new response span. The span will not be started automatically, you should either do + `with response_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + response: The OpenAI Response object. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=ResponseSpanData(response=response), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def handoff_span( + from_agent: str | None = None, + to_agent: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[HandoffSpanData]: + """Create a new handoff span. The span will not be started automatically, you should either do + `with handoff_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + from_agent: The name of the agent that is handing off. + to_agent: The name of the agent that is receiving the handoff. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created handoff span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=HandoffSpanData(from_agent=from_agent, to_agent=to_agent), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def custom_span( + name: str, + data: dict[str, Any] | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[CustomSpanData]: + """Create a new custom span, to which you can add your own metadata. The span will not be + started automatically, you should either do `with custom_span() ...` or call + `span.start()` + `span.finish()` manually. + + Args: + name: The name of the custom span. + data: Arbitrary structured data to associate with the span. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created custom span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=CustomSpanData(name=name, data=data or {}), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def guardrail_span( + name: str, + triggered: bool = False, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[GuardrailSpanData]: + """Create a new guardrail span. The span will not be started automatically, you should either + do `with guardrail_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the guardrail. + triggered: Whether the guardrail was triggered. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=GuardrailSpanData(name=name, triggered=triggered), + span_id=span_id, + parent=parent, + disabled=disabled, + ) diff --git a/src/agents/tracing/logger.py b/src/agents/tracing/logger.py new file mode 100644 index 0000000..661d09b --- /dev/null +++ b/src/agents/tracing/logger.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("openai.agents.tracing") diff --git a/src/agents/tracing/processor_interface.py b/src/agents/tracing/processor_interface.py new file mode 100644 index 0000000..4dcd897 --- /dev/null +++ b/src/agents/tracing/processor_interface.py @@ -0,0 +1,69 @@ +import abc +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from .spans import Span + from .traces import Trace + + +class TracingProcessor(abc.ABC): + """Interface for processing spans.""" + + @abc.abstractmethod + def on_trace_start(self, trace: "Trace") -> None: + """Called when a trace is started. + + Args: + trace: The trace that started. + """ + pass + + @abc.abstractmethod + def on_trace_end(self, trace: "Trace") -> None: + """Called when a trace is finished. + + Args: + trace: The trace that started. + """ + pass + + @abc.abstractmethod + def on_span_start(self, span: "Span[Any]") -> None: + """Called when a span is started. + + Args: + span: The span that started. + """ + pass + + @abc.abstractmethod + def on_span_end(self, span: "Span[Any]") -> None: + """Called when a span is finished. Should not block or raise exceptions. + + Args: + span: The span that finished. + """ + pass + + @abc.abstractmethod + def shutdown(self) -> None: + """Called when the application stops.""" + pass + + @abc.abstractmethod + def force_flush(self) -> None: + """Forces an immediate flush of all queued spans/traces.""" + pass + + +class TracingExporter(abc.ABC): + """Exports traces and spans. For example, could log them or send them to a backend.""" + + @abc.abstractmethod + def export(self, items: list["Trace | Span[Any]"]) -> None: + """Exports a list of traces and spans. + + Args: + items: The items to export. + """ + pass diff --git a/src/agents/tracing/processors.py b/src/agents/tracing/processors.py new file mode 100644 index 0000000..282bc23 --- /dev/null +++ b/src/agents/tracing/processors.py @@ -0,0 +1,261 @@ +from __future__ import annotations + +import os +import queue +import random +import threading +import time +from typing import Any + +import httpx + +from .logger import logger +from .processor_interface import TracingExporter, TracingProcessor +from .spans import Span +from .traces import Trace + + +class ConsoleSpanExporter(TracingExporter): + """Prints the traces and spans to the console.""" + + def export(self, items: list[Trace | Span[Any]]) -> None: + for item in items: + if isinstance(item, Trace): + print(f"[Exporter] Export trace_id={item.trace_id}, name={item.name}, ") + else: + print(f"[Exporter] Export span: {item.export()}") + + +class BackendSpanExporter(TracingExporter): + def __init__( + self, + api_key: str | None = None, + organization: str | None = None, + project: str | None = None, + endpoint: str = "https://api.openai.com/v1/traces/ingest", + max_retries: int = 3, + base_delay: float = 1.0, + max_delay: float = 30.0, + ): + """ + Args: + api_key: The API key for the "Authorization" header. Defaults to + `os.environ["OPENAI_TRACE_API_KEY"]` if not provided. + organization: The OpenAI organization to use. Defaults to + `os.environ["OPENAI_ORG_ID"]` if not provided. + project: The OpenAI project to use. Defaults to + `os.environ["OPENAI_PROJECT_ID"]` if not provided. + endpoint: The HTTP endpoint to which traces/spans are posted. + max_retries: Maximum number of retries upon failures. + base_delay: Base delay (in seconds) for the first backoff. + max_delay: Maximum delay (in seconds) for backoff growth. + """ + self.api_key = api_key or os.environ.get("OPENAI_API_KEY") + self.organization = organization or os.environ.get("OPENAI_ORG_ID") + self.project = project or os.environ.get("OPENAI_PROJECT_ID") + self.endpoint = endpoint + self.max_retries = max_retries + self.base_delay = base_delay + self.max_delay = max_delay + + # Keep a client open for connection pooling across multiple export calls + self._client = httpx.Client(timeout=httpx.Timeout(timeout=60, connect=5.0)) + + def set_api_key(self, api_key: str): + """Set the OpenAI API key for the exporter. + + Args: + api_key: The OpenAI API key to use. This is the same key used by the OpenAI Python + client. + """ + self.api_key = api_key + + def export(self, items: list[Trace | Span[Any]]) -> None: + if not items: + return + + if not self.api_key: + logger.warning("OPENAI_API_KEY is not set, skipping trace export") + return + + traces: list[dict[str, Any]] = [] + spans: list[dict[str, Any]] = [] + + data = [item.export() for item in items if item.export()] + payload = {"data": data} + + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + "OpenAI-Beta": "traces=v1", + } + + # Exponential backoff loop + attempt = 0 + delay = self.base_delay + while True: + attempt += 1 + try: + response = self._client.post(url=self.endpoint, headers=headers, json=payload) + + # If the response is successful, break out of the loop + if response.status_code < 300: + logger.debug(f"Exported {len(traces)} traces, {len(spans)} spans") + return + + # If the response is a client error (4xx), we wont retry + if 400 <= response.status_code < 500: + logger.error(f"Tracing client error {response.status_code}: {response.text}") + return + + # For 5xx or other unexpected codes, treat it as transient and retry + logger.warning(f"Server error {response.status_code}, retrying.") + except httpx.RequestError as exc: + # Network or other I/O error, we'll retry + logger.warning(f"Request failed: {exc}") + + # If we reach here, we need to retry or give up + if attempt >= self.max_retries: + logger.error("Max retries reached, giving up on this batch.") + return + + # Exponential backoff + jitter + sleep_time = delay + random.uniform(0, 0.1 * delay) # 10% jitter + time.sleep(sleep_time) + delay = min(delay * 2, self.max_delay) + + def close(self): + """Close the underlying HTTP client.""" + self._client.close() + + +class BatchTraceProcessor(TracingProcessor): + """Some implementation notes: + 1. Using Queue, which is thread-safe. + 2. Using a background thread to export spans, to minimize any performance issues. + 3. Spans are stored in memory until they are exported. + """ + + def __init__( + self, + exporter: TracingExporter, + max_queue_size: int = 8192, + max_batch_size: int = 128, + schedule_delay: float = 5.0, + export_trigger_ratio: float = 0.7, + ): + """ + Args: + exporter: The exporter to use. + max_queue_size: The maximum number of spans to store in the queue. After this, we will + start dropping spans. + max_batch_size: The maximum number of spans to export in a single batch. + schedule_delay: The delay between checks for new spans to export. + export_trigger_ratio: The ratio of the queue size at which we will trigger an export. + """ + self._exporter = exporter + self._queue: queue.Queue[Trace | Span[Any]] = queue.Queue(maxsize=max_queue_size) + self._max_queue_size = max_queue_size + self._max_batch_size = max_batch_size + self._schedule_delay = schedule_delay + self._shutdown_event = threading.Event() + + # The queue size threshold at which we export immediately. + self._export_trigger_size = int(max_queue_size * export_trigger_ratio) + + # Track when we next *must* perform a scheduled export + self._next_export_time = time.time() + self._schedule_delay + + self._shutdown_event = threading.Event() + self._worker_thread = threading.Thread(target=self._run, daemon=True) + self._worker_thread.start() + + def on_trace_start(self, trace: Trace) -> None: + try: + self._queue.put_nowait(trace) + except queue.Full: + logger.warning("Queue is full, dropping trace.") + + def on_trace_end(self, trace: Trace) -> None: + # We send traces via on_trace_start, so we don't need to do anything here. + pass + + def on_span_start(self, span: Span[Any]) -> None: + # We send spans via on_span_end, so we don't need to do anything here. + pass + + def on_span_end(self, span: Span[Any]) -> None: + try: + self._queue.put_nowait(span) + except queue.Full: + logger.warning("Queue is full, dropping span.") + + def shutdown(self, timeout: float | None = None): + """ + Called when the application stops. We signal our thread to stop, then join it. + """ + self._shutdown_event.set() + self._worker_thread.join(timeout=timeout) + + def force_flush(self): + """ + Forces an immediate flush of all queued spans. + """ + self._export_batches(force=True) + + def _run(self): + while not self._shutdown_event.is_set(): + current_time = time.time() + queue_size = self._queue.qsize() + + # If it's time for a scheduled flush or queue is above the trigger threshold + if current_time >= self._next_export_time or queue_size >= self._export_trigger_size: + self._export_batches(force=False) + # Reset the next scheduled flush time + self._next_export_time = time.time() + self._schedule_delay + else: + # Sleep a short interval so we don't busy-wait. + time.sleep(0.2) + + # Final drain after shutdown + self._export_batches(force=True) + + def _export_batches(self, force: bool = False): + """Drains the queue and exports in batches. If force=True, export everything. + Otherwise, export up to `max_batch_size` repeatedly until the queue is empty or below a + certain threshold. + """ + while True: + items_to_export: list[Span[Any] | Trace] = [] + + # Gather a batch of spans up to max_batch_size + while not self._queue.empty() and ( + force or len(items_to_export) < self._max_batch_size + ): + try: + items_to_export.append(self._queue.get_nowait()) + except queue.Empty: + # Another thread might have emptied the queue between checks + break + + # If we collected nothing, we're done + if not items_to_export: + break + + # Export the batch + self._exporter.export(items_to_export) + + +# Create a shared global instance: +_global_exporter = BackendSpanExporter() +_global_processor = BatchTraceProcessor(_global_exporter) + + +def default_exporter() -> BackendSpanExporter: + """The default exporter, which exports traces and spans to the backend in batches.""" + return _global_exporter + + +def default_processor() -> BatchTraceProcessor: + """The default processor, which exports traces and spans to the backend in batches.""" + return _global_processor diff --git a/src/agents/tracing/scope.py b/src/agents/tracing/scope.py new file mode 100644 index 0000000..9ccd9f8 --- /dev/null +++ b/src/agents/tracing/scope.py @@ -0,0 +1,45 @@ +# Holds the current active span +import contextvars +from typing import TYPE_CHECKING, Any + +from .logger import logger + +if TYPE_CHECKING: + from .spans import Span + from .traces import Trace + +_current_span: contextvars.ContextVar["Span[Any] | None"] = contextvars.ContextVar( + "current_span", default=None +) + +_current_trace: contextvars.ContextVar["Trace | None"] = contextvars.ContextVar( + "current_trace", default=None +) + + +class Scope: + @classmethod + def get_current_span(cls) -> "Span[Any] | None": + return _current_span.get() + + @classmethod + def set_current_span(cls, span: "Span[Any] | None") -> "contextvars.Token[Span[Any] | None]": + return _current_span.set(span) + + @classmethod + def reset_current_span(cls, token: "contextvars.Token[Span[Any] | None]") -> None: + _current_span.reset(token) + + @classmethod + def get_current_trace(cls) -> "Trace | None": + return _current_trace.get() + + @classmethod + def set_current_trace(cls, trace: "Trace | None") -> "contextvars.Token[Trace | None]": + logger.debug(f"Setting current trace: {trace.trace_id if trace else None}") + return _current_trace.set(trace) + + @classmethod + def reset_current_trace(cls, token: "contextvars.Token[Trace | None]") -> None: + logger.debug("Resetting current trace") + _current_trace.reset(token) diff --git a/src/agents/tracing/setup.py b/src/agents/tracing/setup.py new file mode 100644 index 0000000..bc340c9 --- /dev/null +++ b/src/agents/tracing/setup.py @@ -0,0 +1,211 @@ +from __future__ import annotations + +import os +import threading +from typing import Any + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope +from .spans import NoOpSpan, Span, SpanImpl, TSpanData +from .traces import NoOpTrace, Trace, TraceImpl + + +class SynchronousMultiTracingProcessor(TracingProcessor): + """ + Forwards all calls to a list of TracingProcessors, in order of registration. + """ + + def __init__(self): + # Using a tuple to avoid race conditions when iterating over processors + self._processors: tuple[TracingProcessor, ...] = () + self._lock = threading.Lock() + + def add_tracing_processor(self, tracing_processor: TracingProcessor): + """ + Add a processor to the list of processors. Each processor will receive all traces/spans. + """ + with self._lock: + self._processors += (tracing_processor,) + + def set_processors(self, processors: list[TracingProcessor]): + """ + Set the list of processors. This will replace the current list of processors. + """ + with self._lock: + self._processors = tuple(processors) + + def on_trace_start(self, trace: Trace) -> None: + """ + Called when a trace is started. + """ + for processor in self._processors: + processor.on_trace_start(trace) + + def on_trace_end(self, trace: Trace) -> None: + """ + Called when a trace is finished. + """ + for processor in self._processors: + processor.on_trace_end(trace) + + def on_span_start(self, span: Span[Any]) -> None: + """ + Called when a span is started. + """ + for processor in self._processors: + processor.on_span_start(span) + + def on_span_end(self, span: Span[Any]) -> None: + """ + Called when a span is finished. + """ + for processor in self._processors: + processor.on_span_end(span) + + def shutdown(self) -> None: + """ + Called when the application stops. + """ + for processor in self._processors: + logger.debug(f"Shutting down trace processor {processor}") + processor.shutdown() + + def force_flush(self): + """ + Force the processors to flush their buffers. + """ + for processor in self._processors: + processor.force_flush() + + +class TraceProvider: + def __init__(self): + self._multi_processor = SynchronousMultiTracingProcessor() + self._disabled = os.environ.get("OPENAI_AGENTS_DISABLE_TRACING", "false").lower() in ( + "true", + "1", + ) + + def register_processor(self, processor: TracingProcessor): + """ + Add a processor to the list of processors. Each processor will receive all traces/spans. + """ + self._multi_processor.add_tracing_processor(processor) + + def set_processors(self, processors: list[TracingProcessor]): + """ + Set the list of processors. This will replace the current list of processors. + """ + self._multi_processor.set_processors(processors) + + def get_current_trace(self) -> Trace | None: + """ + Returns the currently active trace, if any. + """ + return Scope.get_current_trace() + + def get_current_span(self) -> Span[Any] | None: + """ + Returns the currently active span, if any. + """ + return Scope.get_current_span() + + def set_disabled(self, disabled: bool) -> None: + """ + Set whether tracing is disabled. + """ + self._disabled = disabled + + def create_trace( + self, + name: str, + trace_id: str | None = None, + group_id: str | None = None, + metadata: dict[str, Any] | None = None, + disabled: bool = False, + ) -> Trace: + """ + Create a new trace. + """ + if self._disabled or disabled: + logger.debug(f"Tracing is disabled. Not creating trace {name}") + return NoOpTrace() + + trace_id = trace_id or util.gen_trace_id() + + logger.debug(f"Creating trace {name} with id {trace_id}") + + return TraceImpl( + name=name, + trace_id=trace_id, + group_id=group_id, + metadata=metadata, + processor=self._multi_processor, + ) + + def create_span( + self, + span_data: TSpanData, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, + ) -> Span[TSpanData]: + """ + Create a new span. + """ + if self._disabled or disabled: + logger.debug(f"Tracing is disabled. Not creating span {span_data}") + return NoOpSpan(span_data) + + if not parent: + current_span = Scope.get_current_span() + current_trace = Scope.get_current_trace() + if current_trace is None: + logger.error( + "No active trace. Make sure to start a trace with `trace()` first" + "Returning NoOpSpan." + ) + return NoOpSpan(span_data) + elif isinstance(current_trace, NoOpTrace) or isinstance(current_span, NoOpSpan): + logger.debug( + f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan" + ) + return NoOpSpan(span_data) + + parent_id = current_span.span_id if current_span else None + trace_id = current_trace.trace_id + + elif isinstance(parent, Trace): + if isinstance(parent, NoOpTrace): + logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + return NoOpSpan(span_data) + trace_id = parent.trace_id + parent_id = None + elif isinstance(parent, Span): + if isinstance(parent, NoOpSpan): + logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + return NoOpSpan(span_data) + parent_id = parent.span_id + trace_id = parent.trace_id + + logger.debug(f"Creating span {span_data} with id {span_id}") + + return SpanImpl( + trace_id=trace_id, + span_id=span_id, + parent_id=parent_id, + processor=self._multi_processor, + span_data=span_data, + ) + + def shutdown(self) -> None: + try: + logger.debug("Shutting down trace provider") + self._multi_processor.shutdown() + except Exception as e: + logger.error(f"Error shutting down trace provider: {e}") + + +GLOBAL_TRACE_PROVIDER = TraceProvider() diff --git a/src/agents/tracing/span_data.py b/src/agents/tracing/span_data.py new file mode 100644 index 0000000..5e5d38c --- /dev/null +++ b/src/agents/tracing/span_data.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +import abc +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from openai.types.responses import Response, ResponseInputItemParam + + +class SpanData(abc.ABC): + @abc.abstractmethod + def export(self) -> dict[str, Any]: + pass + + @property + @abc.abstractmethod + def type(self) -> str: + pass + + +class AgentSpanData(SpanData): + __slots__ = ("name", "handoffs", "tools", "output_type") + + def __init__( + self, + name: str, + handoffs: list[str] | None = None, + tools: list[str] | None = None, + output_type: str | None = None, + ): + self.name = name + self.handoffs: list[str] | None = handoffs + self.tools: list[str] | None = tools + self.output_type: str | None = output_type + + @property + def type(self) -> str: + return "agent" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "handoffs": self.handoffs, + "tools": self.tools, + "output_type": self.output_type, + } + + +class FunctionSpanData(SpanData): + __slots__ = ("name", "input", "output") + + def __init__(self, name: str, input: str | None, output: str | None): + self.name = name + self.input = input + self.output = output + + @property + def type(self) -> str: + return "function" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "input": self.input, + "output": self.output, + } + + +class GenerationSpanData(SpanData): + __slots__ = ( + "input", + "output", + "model", + "model_config", + "usage", + ) + + def __init__( + self, + input: Sequence[Mapping[str, Any]] | None = None, + output: Sequence[Mapping[str, Any]] | None = None, + model: str | None = None, + model_config: Mapping[str, Any] | None = None, + usage: dict[str, Any] | None = None, + ): + self.input = input + self.output = output + self.model = model + self.model_config = model_config + self.usage = usage + + @property + def type(self) -> str: + return "generation" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "input": self.input, + "output": self.output, + "model": self.model, + "model_config": self.model_config, + "usage": self.usage, + } + + +class ResponseSpanData(SpanData): + __slots__ = ("response", "input") + + def __init__( + self, + response: Response | None = None, + input: str | list[ResponseInputItemParam] | None = None, + ) -> None: + self.response = response + # This is not used by the OpenAI trace processors, but is useful for other tracing + # processor implementations + self.input = input + + @property + def type(self) -> str: + return "response" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "response_id": self.response.id if self.response else None, + } + + +class HandoffSpanData(SpanData): + __slots__ = ("from_agent", "to_agent") + + def __init__(self, from_agent: str | None, to_agent: str | None): + self.from_agent = from_agent + self.to_agent = to_agent + + @property + def type(self) -> str: + return "handoff" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "from_agent": self.from_agent, + "to_agent": self.to_agent, + } + + +class CustomSpanData(SpanData): + __slots__ = ("name", "data") + + def __init__(self, name: str, data: dict[str, Any]): + self.name = name + self.data = data + + @property + def type(self) -> str: + return "custom" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "data": self.data, + } + + +class GuardrailSpanData(SpanData): + __slots__ = ("name", "triggered") + + def __init__(self, name: str, triggered: bool = False): + self.name = name + self.triggered = triggered + + @property + def type(self) -> str: + return "guardrail" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "triggered": self.triggered, + } diff --git a/src/agents/tracing/spans.py b/src/agents/tracing/spans.py new file mode 100644 index 0000000..d682a9a --- /dev/null +++ b/src/agents/tracing/spans.py @@ -0,0 +1,264 @@ +from __future__ import annotations + +import abc +import contextvars +from typing import Any, Generic, TypeVar + +from typing_extensions import TypedDict + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope +from .span_data import SpanData + +TSpanData = TypeVar("TSpanData", bound=SpanData) + + +class SpanError(TypedDict): + message: str + data: dict[str, Any] | None + + +class Span(abc.ABC, Generic[TSpanData]): + @property + @abc.abstractmethod + def trace_id(self) -> str: + pass + + @property + @abc.abstractmethod + def span_id(self) -> str: + pass + + @property + @abc.abstractmethod + def span_data(self) -> TSpanData: + pass + + @abc.abstractmethod + def start(self, mark_as_current: bool = False): + """ + Start the span. + + Args: + mark_as_current: If true, the span will be marked as the current span. + """ + pass + + @abc.abstractmethod + def finish(self, reset_current: bool = False) -> None: + """ + Finish the span. + + Args: + reset_current: If true, the span will be reset as the current span. + """ + pass + + @abc.abstractmethod + def __enter__(self) -> Span[TSpanData]: + pass + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + @property + @abc.abstractmethod + def parent_id(self) -> str | None: + pass + + @abc.abstractmethod + def set_error(self, error: SpanError) -> None: + pass + + @property + @abc.abstractmethod + def error(self) -> SpanError | None: + pass + + @abc.abstractmethod + def export(self) -> dict[str, Any] | None: + pass + + @property + @abc.abstractmethod + def started_at(self) -> str | None: + pass + + @property + @abc.abstractmethod + def ended_at(self) -> str | None: + pass + + +class NoOpSpan(Span[TSpanData]): + __slots__ = ("_span_data", "_prev_span_token") + + def __init__(self, span_data: TSpanData): + self._span_data = span_data + self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None + + @property + def trace_id(self) -> str: + return "no-op" + + @property + def span_id(self) -> str: + return "no-op" + + @property + def span_data(self) -> TSpanData: + return self._span_data + + @property + def parent_id(self) -> str | None: + return None + + def start(self, mark_as_current: bool = False): + if mark_as_current: + self._prev_span_token = Scope.set_current_span(self) + + def finish(self, reset_current: bool = False) -> None: + if reset_current and self._prev_span_token is not None: + Scope.reset_current_span(self._prev_span_token) + self._prev_span_token = None + + def __enter__(self) -> Span[TSpanData]: + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + reset_current = True + if exc_type is GeneratorExit: + logger.debug("GeneratorExit, skipping span reset") + reset_current = False + + self.finish(reset_current=reset_current) + + def set_error(self, error: SpanError) -> None: + pass + + @property + def error(self) -> SpanError | None: + return None + + def export(self) -> dict[str, Any] | None: + return None + + @property + def started_at(self) -> str | None: + return None + + @property + def ended_at(self) -> str | None: + return None + + +class SpanImpl(Span[TSpanData]): + __slots__ = ( + "_trace_id", + "_span_id", + "_parent_id", + "_started_at", + "_ended_at", + "_error", + "_prev_span_token", + "_processor", + "_span_data", + ) + + def __init__( + self, + trace_id: str, + span_id: str | None, + parent_id: str | None, + processor: TracingProcessor, + span_data: TSpanData, + ): + self._trace_id = trace_id + self._span_id = span_id or util.gen_span_id() + self._parent_id = parent_id + self._started_at: str | None = None + self._ended_at: str | None = None + self._processor = processor + self._error: SpanError | None = None + self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None + self._span_data = span_data + + @property + def trace_id(self) -> str: + return self._trace_id + + @property + def span_id(self) -> str: + return self._span_id + + @property + def span_data(self) -> TSpanData: + return self._span_data + + @property + def parent_id(self) -> str | None: + return self._parent_id + + def start(self, mark_as_current: bool = False): + if self.started_at is not None: + logger.warning("Span already started") + return + + self._started_at = util.time_iso() + self._processor.on_span_start(self) + if mark_as_current: + self._prev_span_token = Scope.set_current_span(self) + + def finish(self, reset_current: bool = False) -> None: + if self.ended_at is not None: + logger.warning("Span already finished") + return + + self._ended_at = util.time_iso() + self._processor.on_span_end(self) + if reset_current and self._prev_span_token is not None: + Scope.reset_current_span(self._prev_span_token) + self._prev_span_token = None + + def __enter__(self) -> Span[TSpanData]: + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + reset_current = True + if exc_type is GeneratorExit: + logger.debug("GeneratorExit, skipping span reset") + reset_current = False + + self.finish(reset_current=reset_current) + + def set_error(self, error: SpanError) -> None: + self._error = error + + @property + def error(self) -> SpanError | None: + return self._error + + @property + def started_at(self) -> str | None: + return self._started_at + + @property + def ended_at(self) -> str | None: + return self._ended_at + + def export(self) -> dict[str, Any] | None: + return { + "object": "trace.span", + "id": self.span_id, + "trace_id": self.trace_id, + "parent_id": self._parent_id, + "started_at": self._started_at, + "ended_at": self._ended_at, + "span_data": self.span_data.export(), + "error": self._error, + } diff --git a/src/agents/tracing/traces.py b/src/agents/tracing/traces.py new file mode 100644 index 0000000..bf3b43d --- /dev/null +++ b/src/agents/tracing/traces.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import abc +import contextvars +from typing import Any + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope + + +class Trace: + """ + A trace is the root level object that tracing creates. It represents a logical "workflow". + """ + + @abc.abstractmethod + def __enter__(self) -> Trace: + pass + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + @abc.abstractmethod + def start(self, mark_as_current: bool = False): + """ + Start the trace. + + Args: + mark_as_current: If true, the trace will be marked as the current trace. + """ + pass + + @abc.abstractmethod + def finish(self, reset_current: bool = False): + """ + Finish the trace. + + Args: + reset_current: If true, the trace will be reset as the current trace. + """ + pass + + @property + @abc.abstractmethod + def trace_id(self) -> str: + """ + The trace ID. + """ + pass + + @property + @abc.abstractmethod + def name(self) -> str: + """ + The name of the workflow being traced. + """ + pass + + @abc.abstractmethod + def export(self) -> dict[str, Any] | None: + """ + Export the trace as a dictionary. + """ + pass + + +class NoOpTrace(Trace): + """ + A no-op trace that will not be recorded. + """ + + def __init__(self): + self._started = False + self._prev_context_token: contextvars.Token[Trace | None] | None = None + + def __enter__(self) -> Trace: + if self._started: + if not self._prev_context_token: + logger.error("Trace already started but no context token set") + return self + + self._started = True + self.start(mark_as_current=True) + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish(reset_current=True) + + def start(self, mark_as_current: bool = False): + if mark_as_current: + self._prev_context_token = Scope.set_current_trace(self) + + def finish(self, reset_current: bool = False): + if reset_current and self._prev_context_token is not None: + Scope.reset_current_trace(self._prev_context_token) + self._prev_context_token = None + + @property + def trace_id(self) -> str: + return "no-op" + + @property + def name(self) -> str: + return "no-op" + + def export(self) -> dict[str, Any] | None: + return None + + +NO_OP_TRACE = NoOpTrace() + + +class TraceImpl(Trace): + """ + A trace that will be recorded by the tracing library. + """ + + __slots__ = ( + "_name", + "_trace_id", + "group_id", + "metadata", + "_prev_context_token", + "_processor", + "_started", + ) + + def __init__( + self, + name: str, + trace_id: str | None, + group_id: str | None, + metadata: dict[str, Any] | None, + processor: TracingProcessor, + ): + self._name = name + self._trace_id = trace_id or util.gen_trace_id() + self.group_id = group_id + self.metadata = metadata + self._prev_context_token: contextvars.Token[Trace | None] | None = None + self._processor = processor + self._started = False + + @property + def trace_id(self) -> str: + return self._trace_id + + @property + def name(self) -> str: + return self._name + + def start(self, mark_as_current: bool = False): + if self._started: + return + + self._started = True + self._processor.on_trace_start(self) + + if mark_as_current: + self._prev_context_token = Scope.set_current_trace(self) + + def finish(self, reset_current: bool = False): + if not self._started: + return + + self._processor.on_trace_end(self) + + if reset_current and self._prev_context_token is not None: + Scope.reset_current_trace(self._prev_context_token) + self._prev_context_token = None + + def __enter__(self) -> Trace: + if self._started: + if not self._prev_context_token: + logger.error("Trace already started but no context token set") + return self + + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish(reset_current=exc_type is not GeneratorExit) + + def export(self) -> dict[str, Any] | None: + return { + "object": "trace", + "id": self.trace_id, + "workflow_name": self.name, + "group_id": self.group_id, + "metadata": self.metadata, + } diff --git a/src/agents/tracing/util.py b/src/agents/tracing/util.py new file mode 100644 index 0000000..3e5cad9 --- /dev/null +++ b/src/agents/tracing/util.py @@ -0,0 +1,17 @@ +import uuid +from datetime import datetime, timezone + + +def time_iso() -> str: + """Returns the current time in ISO 8601 format.""" + return datetime.now(timezone.utc).isoformat() + + +def gen_trace_id() -> str: + """Generates a new trace ID.""" + return f"trace_{uuid.uuid4().hex}" + + +def gen_span_id() -> str: + """Generates a new span ID.""" + return f"span_{uuid.uuid4().hex[:24]}" diff --git a/src/agents/usage.py b/src/agents/usage.py new file mode 100644 index 0000000..23d989b --- /dev/null +++ b/src/agents/usage.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass + + +@dataclass +class Usage: + requests: int = 0 + """Total requests made to the LLM API.""" + + input_tokens: int = 0 + """Total input tokens sent, across all requests.""" + + output_tokens: int = 0 + """Total output tokens received, across all requests.""" + + total_tokens: int = 0 + """Total tokens sent and received, across all requests.""" + + def add(self, other: "Usage") -> None: + self.requests += other.requests if other.requests else 0 + self.input_tokens += other.input_tokens if other.input_tokens else 0 + self.output_tokens += other.output_tokens if other.output_tokens else 0 + self.total_tokens += other.total_tokens if other.total_tokens else 0 diff --git a/src/agents/version.py b/src/agents/version.py new file mode 100644 index 0000000..a0b7e9b --- /dev/null +++ b/src/agents/version.py @@ -0,0 +1,7 @@ +import importlib.metadata + +try: + __version__ = importlib.metadata.version("agents") +except importlib.metadata.PackageNotFoundError: + # Fallback if running from source without being installed + __version__ = "0.0.0" diff --git a/src/openai_agents.egg-info/PKG-INFO b/src/openai_agents.egg-info/PKG-INFO new file mode 100644 index 0000000..ebf2d7c --- /dev/null +++ b/src/openai_agents.egg-info/PKG-INFO @@ -0,0 +1,217 @@ +Metadata-Version: 2.2 +Name: openai-agents +Version: 0.0.1 +Summary: OpenAI Agents SDK +Author-email: OpenAI +Project-URL: Homepage, https://github.com/openai/openai-agents-python +Project-URL: Repository, https://github.com/openai/openai-agents-python +Classifier: Typing :: Typed +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Requires-Dist: openai@ {root:parent:uri}/openai-1.30.1-py3-none-any.whl +Requires-Dist: pydantic<3,>=2.10 +Requires-Dist: griffe<2,>=1.5.6 +Requires-Dist: typing-extensions<5,>=4.12.2 +Requires-Dist: requests<3,>=2.0 +Requires-Dist: types-requests<3,>=2.0 + +# OpenAI Agents SDK + +The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. + +### Core concepts: +1. [**Agents,**](docs/agents.md) which are LLMs configured with instructions, tools, guardrails, and handoffs +2. [**Handoffs,**](docs/handoffs.md) which allow agents to transfer control to other agents for specific tasks +3. [**Guardrails,**](docs/guardrails.md) which makes it easy to watch an agent execution and validate inputs/outputs +4. [**Tracing,**](docs/tracing.md) which automatically captures the entire agentic run, allowing you to view, debug and optimize your workflows + +Explore examples of the SDK in action in the [examples](examples) directory. + +## Using the SDK + +1. Set up python env + +``` +python -m venv env +source env/bin/activate +``` + +2. Install Agents SDK + +``` +pip install git+ssh://git@github.com/openai/agentsdk_prototype.git#subdirectory=agents +``` + +## Development (only needed if you need to edit the SDK/examples) + +0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed. + +```bash +uv --version +``` + +1. Install dependencies/setup virtual environment + +```bash +uv sync +``` + +2. Install the dependencies + +```bash +uv sync --all-extras --all-packages +``` + +3. Activate the virtual environment + +```bash +source .venv/bin/activate +``` + +## Tests + +Make sure the virtual environment is activated first. + +```bash +pytest +``` + +## Hello world example + +```py +from agents.agent import Agent +from agents.run import Runner +import asyncio + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent." +) + +async def main(): + out = await Runner.run(agent, input="Hola, ¿cómo estás?") + print(out) + + +if __name__ == "__main__": + asyncio.run(main()) + +# The capital of the United States is Washington, D.C. +``` + +## Handoffs example + +```py +from agents.agent import Agent +from agents.run import Runner +import asyncio + +spanish_agent = Agent( + name="spanish_agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="english_agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="triage_agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], +) + + +async def main(): + out = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(out) + + +if __name__ == "__main__": + asyncio.run(main()) + +# ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás? +``` + +## Functions example + +```python +from agents.agent import Agent +from agents.run import Runner +import asyncio +from agents.tool import function_tool + + +@function_tool +def get_weather(city: str) -> str: + print(f"Getting weather for {city}") + return f"The weather in {city} is sunny." + + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent.", + tools=[get_weather], +) + + +async def main(): + out = await Runner.run(agent, input="What's the weather in Tokyo?") + print(out.final_output) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +For more complex systems, we recommend including detailed instructions about handoffs. We have a recommendation in `handoff.RECOMMENDED_PROMPT_PREFIX` that can be used to add these instructions to an agent. + +```py +agent = Agent( + ..., + instructions=f"{handoff.RECOMMENDED_PROMPT_PREFIX}\n\n{instructions}" +) +``` + +## The agent loop + +When you call `Runner.run()`, we run a loop until we get a final output. + +1. We call the LLM, using the model and settings on the agent, and the message history. +2. The LLM returns a response, which may include tool calls. +3. If the response has a final output (see below for the more on this), we return it and end the loop. +4. If the response has a handoff, we set the agent to the new agent and go back to step 1. +5. We process the tool calls (if any) and append the tool responses messsages. Then we go to step 1. + +There is a `max_turns` parameter that you can use to limit the number of times the loop executes. + +### Final output + +There are two ways to get a **final output**: + +1. If you set an `output_type` on the agent, the LLM is given a special tool called `final_output`. If it uses this tool, the output of the tool is the final output. +2. If there's no `output_type`, then we assume the final output is a string. As soon as the LLM produces a message without any tool calls, that is considered the final output. + +As a result, the mental model for the agent loop is: + +1. If the current agent has an `output_type`, the loop runs until the agent uses that tool to return the final output. +2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls. + +## Common agent patterns + +There are a number of useful patterns in agentic apps. There are a number of examples in [`examples/agent_patterns`](examples/agent_patterns), and we recommend reading them. diff --git a/src/openai_agents.egg-info/SOURCES.txt b/src/openai_agents.egg-info/SOURCES.txt new file mode 100644 index 0000000..695ad1f --- /dev/null +++ b/src/openai_agents.egg-info/SOURCES.txt @@ -0,0 +1,81 @@ +README.md +pyproject.toml +src/agents/__init__.py +src/agents/_config.py +src/agents/_debug.py +src/agents/_run_impl.py +src/agents/_utils.py +src/agents/agent.py +src/agents/agent_output.py +src/agents/call_agent_tool.py +src/agents/computer.py +src/agents/exceptions.py +src/agents/function_schema.py +src/agents/guardrail.py +src/agents/handoffs.py +src/agents/items.py +src/agents/lifecycle.py +src/agents/logger.py +src/agents/model_settings.py +src/agents/result.py +src/agents/run.py +src/agents/run_context.py +src/agents/strict_schema.py +src/agents/tool.py +src/agents/usage.py +src/agents/version.py +src/agents/extensions/__init__.py +src/agents/extensions/handoff_filters.py +src/agents/extensions/handoff_prompt.py +src/agents/models/__init__.py +src/agents/models/_openai_shared.py +src/agents/models/fake_id.py +src/agents/models/interface.py +src/agents/models/map.py +src/agents/models/openai_chatcompletions.py +src/agents/models/openai_responses.py +src/agents/tracing/__init__.py +src/agents/tracing/create.py +src/agents/tracing/logger.py +src/agents/tracing/processor_interface.py +src/agents/tracing/processors.py +src/agents/tracing/scope.py +src/agents/tracing/setup.py +src/agents/tracing/span_data.py +src/agents/tracing/spans.py +src/agents/tracing/traces.py +src/agents/tracing/util.py +src/openai_agents.egg-info/PKG-INFO +src/openai_agents.egg-info/SOURCES.txt +src/openai_agents.egg-info/dependency_links.txt +src/openai_agents.egg-info/requires.txt +src/openai_agents.egg-info/top_level.txt +tests/test_agent_config.py +tests/test_agent_hooks.py +tests/test_agent_runner.py +tests/test_agent_runner_streamed.py +tests/test_agent_tracing.py +tests/test_config.py +tests/test_doc_parsing.py +tests/test_function_schema.py +tests/test_function_tool.py +tests/test_function_tool_decorator.py +tests/test_global_hooks.py +tests/test_guardrails.py +tests/test_handoff_tool.py +tests/test_items_helpers.py +tests/test_max_turns.py +tests/test_model_mapper.py +tests/test_openai_chatcompletions_converter.py +tests/test_openai_responses_converter.py +tests/test_output_tool.py +tests/test_responses.py +tests/test_run_config.py +tests/test_run_step_execution.py +tests/test_run_step_processing.py +tests/test_tool_converter.py +tests/test_trace_processor.py +tests/test_tracing.py +tests/test_tracing_errors.py +tests/test_tracing_errors_streamed.py +tests/testing_processor.py \ No newline at end of file diff --git a/src/openai_agents.egg-info/dependency_links.txt b/src/openai_agents.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/openai_agents.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/openai_agents.egg-info/requires.txt b/src/openai_agents.egg-info/requires.txt new file mode 100644 index 0000000..3dbad2b --- /dev/null +++ b/src/openai_agents.egg-info/requires.txt @@ -0,0 +1,6 @@ +openai@ {root:parent:uri}/openai-1.30.1-py3-none-any.whl +pydantic<3,>=2.10 +griffe<2,>=1.5.6 +typing-extensions<5,>=4.12.2 +requests<3,>=2.0 +types-requests<3,>=2.0 diff --git a/src/openai_agents.egg-info/top_level.txt b/src/openai_agents.egg-info/top_level.txt new file mode 100644 index 0000000..4a33ff6 --- /dev/null +++ b/src/openai_agents.egg-info/top_level.txt @@ -0,0 +1 @@ +agents diff --git a/tests/LICENSE b/tests/LICENSE new file mode 100644 index 0000000..e5ad2c5 --- /dev/null +++ b/tests/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 OpenAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..7dd9bbd --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,37 @@ +.PHONY: sync +sync: + uv sync --all-extras --all-packages --group dev + +.PHONY: format +format: + uv run ruff format + +.PHONY: lint +lint: + uv run ruff check + +.PHONY: mypy +mypy: + uv run mypy . + +.PHONY: tests +tests: + uv run pytest + +.PHONY: old_version_tests +old_version_tests: + UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m pytest + UV_PROJECT_ENVIRONMENT=.venv_39 uv run --python 3.9 -m mypy . + +.PHONY: build-docs +build-docs: + uv run mkdocs build + +.PHONY: serve-docs +serve-docs: + uv run mkdocs serve + +.PHONY: deploy-docs +deploy-docs: + uv run mkdocs gh-deploy --force --verbose + diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..8acd13c --- /dev/null +++ b/tests/README.md @@ -0,0 +1,174 @@ +# OpenAI Agents SDK + +The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. + +Image of the Agents Tracing UI + +### Core concepts: + +1. [**Agents**](docs/agents.md): LLMs configured with instructions, tools, guardrails, and handoffs +2. [**Handoffs**](docs/handoffs.md): Allow agents to transfer control to other agents for specific tasks +3. [**Guardrails**](docs/guardrails.md): Configurable safety checks for input and output validation +4. [**Tracing**](docs/tracing.md): Built-in tracking of agent runs, allowing you to view, debug and optimize your workflows + +Explore the [examples](examples) directory to see the SDK in action. + +## Get started + +1. Set up your Python environment + +``` +python -m venv env +source env/bin/activate +``` + +2. Install Agents SDK + +``` +pip install openai-agents +``` + +## Hello world example + +```python +from agents import Agent, Runner + +agent = Agent(name="Assistant", instructions="You are a helpful assistant") + +result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") +print(result.final_output) + +# Code within the code, +# Functions calling themselves, +# Infinite loop's dance. +``` + +(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_) + +## Handoffs example + +```py +from agents import Agent, Runner +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], +) + + +async def main(): + result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(result.final_output) + # ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás? + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Functions example + +```python +import asyncio + +from agents import Agent, Runner, function_tool + + +@function_tool +def get_weather(city: str) -> str: + return f"The weather in {city} is sunny." + + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent.", + tools=[get_weather], +) + + +async def main(): + result = await Runner.run(agent, input="What's the weather in Tokyo?") + print(result.final_output) + # The weather in Tokyo is sunny. + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## The agent loop + +When you call `Runner.run()`, we run a loop until we get a final output. + +1. We call the LLM, using the model and settings on the agent, and the message history. +2. The LLM returns a response, which may include tool calls. +3. If the response has a final output (see below for the more on this), we return it and end the loop. +4. If the response has a handoff, we set the agent to the new agent and go back to step 1. +5. We process the tool calls (if any) and append the tool responses messsages. Then we go to step 1. + +There is a `max_turns` parameter that you can use to limit the number of times the loop executes. + +### Final output + +Final output is the last thing the agent produces in the loop. + +1. If you set an `output_type` on the agent, the final output is when the LLM returns something of that type. We use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) for this. +2. If there's no `output_type` (i.e. plain text responses), then the first LLM response without any tool calls or handoffs is considered as the final output. + +As a result, the mental model for the agent loop is: + +1. If the current agent has an `output_type`, the loop runs until the agent produces structured output matching that type. +2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls/handoffs. + +## Common agent patterns + +The Agents SDK is designed to be highly flexible, allowing you to model a wide range of LLM workflows including deterministic flows, iterative loops, and more. See examples in [`examples/agent_patterns`](examples/agent_patterns). + +## Tracing + +The Agents SDK includes built-in tracing, making it easy to track and debug the behavior of your agents. Tracing is extensible by design, supporting custom spans and a wide variety of external destinations, including [Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents), [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk), and [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk). See [Tracing](http://openai.github.io/openai-agents-python/tracing.md) for more details. + +## Development (only needed if you need to edit the SDK/examples) + +0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed. + +```bash +uv --version +``` + +1. Install dependencies + +```bash +make sync +``` + +2. (After making changes) lint/test + +``` +make tests # run tests +make mypy # run typechecker +make lint # run linter +``` + +## Acknowledgements + +We'd like to acknowledge the excellent work of the open-source community, especially: + +- [Pydantic](https://docs.pydantic.dev/latest/) (data validation) and [PydanticAI](https://ai.pydantic.dev/) (advanced agent framework) +- [MkDocs](https://github.com/squidfunk/mkdocs-material) +- [Griffe](https://github.com/mkdocstrings/griffe) +- [uv](https://github.com/astral-sh/uv) and [ruff](https://github.com/astral-sh/ruff) + +We're committed to continuing to build the Agents SDK as an open source framework so others in the community can expand on our approach. diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__pycache__/__init__.cpython-311.pyc b/tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ebfe479b465cc594cc29edb05cbec8c28419a04 GIT binary patch literal 167 zcmZ3^%ge<81b<8prGx0lAOZ#$p^VRLK*n^26oz01O-8?!3`I;p{%4TnFDw1f;?$yI z{i0m`eo1O^NwI!>d}dx|NqoFsLFF$Fo80`A f(wtPgB37VLAj^vRfy4)9Mn=XD3^1aI87Kw-jwUG3 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/__init__.cpython-313.pyc b/tests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63635d77f77d0fb322b685859b0fa9a7a7368800 GIT binary patch literal 150 zcmey&%ge<81Xk%6(n0iN5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iUerR!OQL%ne zu6}ZUN~(T-L26!Nrfy<-YF$TZ%!9kIamWj77{q766_|Bennl literal 0 HcmV?d00001 diff --git a/tests/__pycache__/__init__.cpython-39.pyc b/tests/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ca2739e357647db8313837cef9e5e0bf1dbe2ee GIT binary patch literal 144 zcmYe~<>g`k0;}{3=^*+sh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vMKeRZts93)! zS3fyFB~?GaAT=*BQ#Ua^HLs*tx1h2lBR@~SB(=DtSU)~KGcU6wK3=b&@)n0pZhlH> NPO2TqxX(b$002g-AxQuL literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-311-pytest-8.2.0.pyc b/tests/__pycache__/conftest.cpython-311-pytest-8.2.0.pyc new file mode 100644 index 0000000000000000000000000000000000000000..124055e013296eaadd4606dbbed5abd070350b4b GIT binary patch literal 3338 zcmb6bO-~y~bY>ZUV1vzv!&gP>21u})+DRZ)4-te=6rrRh0h(04tQPNr4ZB`6Kx0fheG1Gf+d!3WzIp&cY68LLRc8H_nDs6wX2?uq8h3K(hrOyF=P zmVdDX{Q|rof0UnfF)ov}8iD4ul zCAfOV-I5%V!_V7x1%&pXIYn;YM!SISxdfEyX_5**kL-dDd(fOBM~}(${-;dGF`2$* z89DU4vuSr-$E5n3rH<{c`@DAZ0$X9JP|yv@$m<1`hH*sHi%LPti!3Km zMFz^i91x~vr*o1qt!qV9v3bAHWkrSAm@_-Cup+FWFptg@Lo}$ARm39IvkGH6Wx#*t z=Iyx~Q@6#%`Kjp{@y`6*z1h!a=HGIA!femNovAdZPR}eX%*~67GYgBe>6=ynR%Wop z#e`t_SQfTu1*DRpmza`u{NTVA=%C?)OMvVk!*dfthUaF4R&b*edC)ANXE>b@D8w$J zJd~fI1Ayq=Sn5lrP?n-vDho=dq!p!LFnNvZW$4C6QArsJGZ@SkmN}LzZdfr;zf=@i zQ7Y8+r`@n#6dD@-ELSpQeZ64$vZ^9c+5w!l zGPDaYD`r!hjnW5G{8+N90Ju>GD&%~bj8@2~Nk-rB;ZT{3RLF=)M(km{OwLuvIn%B7 zFHSa88rI|q$AI1~qGzfmx@R02H}HGr;OZxY^de}_Q<~K+%F41-QVsXetSK8-Pct#A z<{@INei5Qor0zixmo{9+W;>m6?BpIq#W3=P6-Ec)m%On!fGxD2_~g4Zn5ug567Lh$ z)EMtCns#+!NU&pwwt>Jg`wa1O@)^wBLfE#^J+uP}`RJKBJ*et|AkZMJL3=o8@^`Nr z5A{UwATKMFehADX(AY45zu*IHNbA-N|K5;(@NJn4SIDsGR>#Ahe?ZCy4g7)+hQXUV zXcMmj!6{EX|GE_3%S~sV3S0+S*hb$7o9M~WwLLMz@1bxBZ3?^a$319H!RUXIyypsnhwwoXkkhhEvpJm< z3Wwv-=)5ASaeH|dXF0v3%5k`2;!8>#4uBlb7vgrl{0MTnzqrA1i!4id6~;uOE6nn* zt@EC7pTy%%?uc>@I0oVa$i;9IJQ|bpOj=U;iIQAxoRh|q*Hj&JWE06Iq06u!>j$uf z-UK5*gw5DQIXGDfPMU7D1I=n%fy>K1saBL3@DI%+BzzVrfc2~{&X!emxSR}8ljxd4 z`Ar44sX-|Z9m=CL(PD))y#T49C>hzD6`;!3C6J&O_-?qd)nG+&waL<=6C!xiVR-kVTU$9-T19XI^|;Cig4k zzUfvc|9oJDL~*%fl&B(#lXEqd99js3dFY66%?$B%+nswk=>(pWpNAW0$-{)2g_lgW{*tksWSu>c)r|pWyVHss zGVyl}#G?IHuwsqGqo{i}Kgv2@tFu0tJ+cJ>kJJ!=`W86l#4UvJS8$v-tgl?B JIWu<9{{vd=4ut>! literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-311-pytest-8.3.4.pyc b/tests/__pycache__/conftest.cpython-311-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4936b44d72bfd6681322ffe6d886b40bfb9dc44 GIT binary patch literal 1205 zcmaJ65fMd-cu7lBFS#shc9L%0WS5y)+T42R z2k=%uLAAE{A^ZdzSV0Daf_UgH(pyiy*=?d5(V2N~-oN?te($|6m5K;P@!{vTt|9bY zDZ@doh0Bj193dOo*hLK-?^-9*)G+FhMiwWYTq6e_&CPf8h7PujP257K*eGJ8A)4Er z7+z~uy&xndME- zr!sE^!V5TUCNC#E1Dm4D6<|k5j6Q~t7$t^K8>hdm;Hsk&T&rkI9cNi!C+HG5x_V^1 z;*@cNb&RHGQ^RW0uHg0?^?l)qo&ff2n)rhQy!^I}SUBOI;EtRFpB+halgmjj>qm0v~!znCwM_BwfO1%+} z@hF8BQ%zbSF;sG*p79+A44p)NLl+<4bjw_rlmy=KZo;lKH zO}`fx#^O*=0qs;}bdNcL%EHJb^Fvz^4y$m}VltPAaw;i)RrRmaZO6?bZ>cEI{;06( zADM4C`yya8t~XQpjUjQRKkj&T;L;~-0lcbo{1dQ224nnHq41AcI*8{vGQ#vaM{8kv z4Roa4JeWCLcjh%5U`&aq!}Yi@jCb&3GsX%&qsKEpk?sV00C2> L3n@!%j$!`=aV{BT literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cdf4fb826e78944018f79f7b0d36ebb348aa3d7 GIT binary patch literal 2007 zcma)6&2Jk;6rWj-?Tx>ZK%l7w>5`PFmXKXqaww{jR#Aj%3#WJ!QK?9)&3f#tve{kD ztO?;lRFIGm5?m@F{(|<#kz>S#YZ5ss8l~a@J@n?_D+k`Jzaye_q^BCz!$0fvYxGTj7S2R>4EUI-hF%|=jQi>Ny*(gng}!bD5X&mU0e@?5a=QyZ4qi&0yKv_8l77p36V>s?-7PR7YE(+y^Ng z$T%f|i6>hg-NuU3q;6@Beq`6Cer101Zc2U?T(3NVaj-KLjPGK^8EDq!;YD=8> zu&~Z4;{~=|u)v>!S*LEmt6TEPffwxhJy9W`Jm3IwYm&?RyJ89)Z1@KEO?TiSn*g&( z_;?1$9{QV1H?!9dF28?FZZIjzVJ53<#2fkM(3?lVfHy!ptJ3$TCqZbopMGr^359xg zor8>>#f{pmBbEY0j)Z{{$(qL?g*B(aH-*YK8$s3EaYfW}sL5CwkfB>_95^u*O3#Mm z4Ny(O$LE2_kYt~c`6DvlB-c(PKX*jtn&jP(*N%vGOlCuyRzIt7PNpu5(Qm*&v~AgxT;rQw!>MYn7ib$)gXXDfwyT9D{0?+h%-@x~AT`r=;GdYR&P8l% zNA6tKoThaWIxZ2mTQ@$3E`h!jH>)-`Hymm(xXuW#Gc3oJU6a4d^=U8DbZ+32Jo zZ*rJFa-QZH-C=e>#aLe?;yqac4$F)g&~oza;%aA5HAi6qE8wnf(6z?u>+Z&Hf^2FdEJ}Cc8Tf>e*qS-iFsT9Jv9ZB?Iea(@*+N2ao7nlHRjLR=D2=dFlX{`8no6t1UVkWy*PU61 zQbj6FBSq?^y&(12V{+>;NB#srMxarUDy31C8!4yW`exQ^8#iAg?acFgzkPmR^WGkI zb_NivkMo=Pf)Al*{D-@|4j~K){5g-A4N^i4cbe zc6g-2Q$Vbv+gzWecLqo8wp}+88HFAD) z=)w-ZNP2)3l}INXM;zhi3PNUtVSKc_Ak$FV8 z{ZTAp#DY1CKw5KVg%+5f7AkhuS}8Ga#x!(7x&bNNBE3Mdu;8|aWDrEh;Gtu%@sLDz z@z^#VtK#veoF3iAqg8xb(8@MecJPSM`RW*b3QBmdXhg}ZC;kYx9y?WP!a|&#l}AxI zwE%AN$gw^xYvlBbX}dS#jCo&JUT84l(3z9W6>}V6?7uJy#8hTEEcaHKq<4 z@-QtTs3b3*C%Wo>s>_(^$26~(#wT^s?$t=)xI3eB5c z$`+}I@?Mhw;O*YU16z2YI(YU`>4{avsXg3PXKlu&HcnRY=S{qef220%t9YTAwRw4V z$p~4)#|^7G$*Aoox;%L(>+Fv6;iYf z)?R1vXgIt8yv!>=gJwU<^>#XAG3L~d(>zZd;l*m*E(6~XnZ`GSh&mg7CDctS75QPI z8i;>UKuL*-&B8~SF)@8b$6`I)LSB3Ij}K>d`EteYcud4lCVUKu&MTUh$Xsii^IrwG zDyA_>rU1;x3Vj0Gfk%?0eKhhnLJ!bSozKzuK1%MRseKgRM;|^%69;}I`yUS9AO5xY zD7+=bUdobm>Cl7Z_yMO50yXh(cuP9*QuaurTsVBdse@3f@N!*vxk>o@oKP%Fr)!FV SYE>MRzLLbtFJ(@O;`k3x!ifn0 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/conftest.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96b6d263cb28799b0903f8c6330786719164fe8f GIT binary patch literal 787 zcmZ`%O>fgM7`ELsanrO2X+lEc#4VTRiV#9W(}cKm(^4+EL?L$DK)&SH(bn_0?mxhB ze}kXlE2sShPP}&6Iw7#+_v?A%yw777vsp-Rg|B{WI3whzO&%`~lVjYlMI(ryHOZ*+ ztQj#vN#kW>x<~xXM}*Zu!!wSxk?`tC6J{YL(*HaaW8sV7h6yevU&%GiB3JcActgch zM6NayG$rxu4*y1$F*QdZb*@!W%Dh)yDFHeKcN9?spQY#TmihU`*~w{|o?Yat({%M= z`Q8M8eO1@4VrIrrcD*z~G3agpS=xgiFC+ST4+fzI@dNy5m3ME|E18fb4?Vv?>*_v@# z%R(J!?IZgl%#nls8uAGu;G-doQ%;rDES?+wX{c&lwP2#-ewROF&+~rgNEd)&X}pnz zl9#H|(o7yoCb+MK__1xz3uXL$n9cXXF6d bQsZx$P7JktYo8HBXkN02MP5Y1aY%myN0Gw} literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/conftest.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..758ab7c53c863fcf737aaaacd088b406f2939976 GIT binary patch literal 2070 zcmZ`)NpBlB6edTb#oD~dYZs_N3sfi~qbPbQf*`JH1O`$$kklM_A;v>$i8(VWITP1` zdrGeP5A`wsrsp}fr~HMU`W{D{4SNKUkKcP7z4yIEUaeLp@ccCTdo(T*@-H4%9~TbK z2%+X55Cjoak$^HvQFfHWa&~uvyhA7n3amKOlvoKgIkgs)S=nN)ssvS5wQ^q7f;y{P zxuDjA4YpzBqS_3$*cQkov8J|z25V3vORslCSyV*zgClC9{+_&}Y}cX{vHpRI4Y6sl zy%lUrY+G!9hLuGFZtsX)iyo|4?umVi9SWyUS_hvn`W8(qUKmEP7f+(lbm3g*(Nu=s zgqyLar2ypc41nX)&e)4PQ7~1~+Wr~|sX*Jvv^{C2@CIoXxREiBwdYGd)sZiaiL?Rx zo#!vkem;J|2fgFY34hr;dv*HDN$(TQyxHx)JnkaeIqCP$dVFxwADnidr$u(FMTu8Y~})MjN~Q_v6uT zMrw1UgCieeJIZKy?2Tj?o5$0ucpQaCu{5y(eKeOm^Iz=Is+;oHIAAAoR8UqjXfy`g#oe_c2t_n!ReTtf}SIpJPAH#tgXr~Jyv zy`^)-OlR{^=(2rJspyjXu<-ypl?M_ESeRRMplj$7Uv^98OjdM|*tg&Iq^Fwp6{cy% zQKCc>=0)>dHX(VUISHG#C%%HlEI^Y$SETOHnw5a9p3Ea?!XtAsa8_MCs z?tX_+p*hJ_sq2(-b&4O|1f=y@TIPJ1#EF)iYfMI3I!`7F>e{5$XVWXbI34he+qP$5 zNZ|l(Yk8?Bu}s$%5PF$mWw?x~;4lxcaak=JW9=-FS+9P53v18%%7-!UFUP!PgME3g z>6~n1d1LjN5e|JwtR1tgaT{#gX=$5_ZK8)*0gL|!JKoabaPkLoVkf7aO17zOH(BCV p__vv`Nhm#m*b*h5;y;Ob1WVO<$Sc9VgO?^V0Ixu5KVA`DWt3wqk7mhl5s}E?$|6@&u;IG zQ6POltyJ++DQ$!rsZtfGeZX)136<3!5SLJ|u8e}zq%CiY>(9@^rKmqcM($r{kprO_Y)}SxV7VDNWOy9+~bcb<=K+ zE7O@$mS#&mw1y~R(FW<3j7e4B~xCqF#Ww*KP>Xlp8k_p7f_e{ImSXj_PEctGuYSn|! zS>}3xUbW#1SZs-9J2X!WC-!uRx=Sr{DOk^wr(3RDvlkuMq|6s!3I4iH%?i+A3!&?c zMjfW3degKoQ`(>`4Le2MKwZNDDsahV$6T~3-cfnbKR03X*D!uaZV)Qygo?UAr4ceh z^cX^T$92g+(~zFfLx@vZPwFzq!g@*%b1dTV?+^8~9^stI{yg3cSW@AfQ9Vjyi=x)e zqF2lhEw1|WUfAzU#os>HMFa?AVe?I{N?EJ`rATbCEFR?ygqSd27K_3)p-9T4?o$bQ zzpE|;gGI6;%)n75++}JT4Q^_OrCsl=4a+uf)~%{0v5;fc7w7eEe zRh6A9mK}>aMY>e1fMXOJP58Wa0*qk0&WYw-_g2F$x|ZWQ#RbsJSVHe9H19ISFi@mn zuuMngLO|;S>5c~AK555DVqh(Pur++(!OUOO!h;Xi5<|b6YD*-V{8Guc!$d7UQHC~w znc#7B9^4^6tnUEw5DsIuGqsJ(BG;a$ES&X^N+pYg_QSL$(mcFa*ldD+RTvKPp?`L` ztg6ysq@^9pHEio6*FPzi-&SwW)M4Ov(8~8oOG&IN`86fKuBdB@`g!jYVK~i zY;}i95nm-|r%rjZS~ZrI>uwElg|W+P4sUC>FVoomdiFh0G*8ctcLHlC{=kmTMFM&`I@ekKtS>u|XTAaHM462TyxZ@&WPs~pIXVkfcJDLW zwU6)5EF=Cm#j8m+OS_>An?23gVj9OJo&^ zt5_8CfQ2QDb|YKVFe-J^aUd5$R>TbeCOIypQIv;Awi5F)PS&~WK#7Ia3De6JUg?=! z!_v!AJhvJkoR?B2yHGJ5mh>XpaCk9i$}O*yJ1pWSd`R<9Jh^TTx*nu;|M4LID&N2BYHKG7ztz^$6Km;-R#(q2Q$J5_^bEJu@n@sQ)<@5*jhUj3;zG%U`qaR zeo7O5b@B96zqFc)0lwNFn;MI(9*O{7>9{^{qV|#qfp-HO=l_AH%iDpsBV5Dh#dD!M zfX_5+3*=#v z{QHqt`Uto&SKm9%xnJp%d2om@wXg}H*wJZS^dc@81B3Z{pi@W~@d&$xb}lLN^b#;p zEMoM#{CkfPw4SGzkKP#S@b028;rGkP5fxFuRfuzOwl z9@20@`U3bUiqZP^XOpd+}{C<5$Tc-f*?5xWL}dx$H$`e45uv^0nwyRSUE(o1Y1o|pS zmx3Km0?jatRxo{fD#(`d46)1+p-Zq*Hs1913}fMS#A z2Q7;SDHgh!ofiN)156xz2#8YzaGz|%v#ogVdi=m=M_Sq5R%)P?&bQLJR?k2y)zwO6 zT0;k4WMheNo5bX>cX<3N73TZAm){+jJF*JBjjQpf;jzSbID$^Sunb`jPEFyw;igq- zfI40eR4{K=Xb7pj-VL;XHNzW~qSU zUCF(QhRtPXGrR}ot)Ulz+(PpxQbmBF$&@#}EHFv$zz|v*;$ZS=OM)OgC5OHt+F!|~ zZ^)^qI3iOgRaOzrV9>M^nowQpBi`CAGrm`1y zW>{D(4HT7Mh5i9QIFI@r?GNbly!NH}1qA}c={d7pij)%|XqV90Gnbv2^PO|PGYT7x z8iD7X(LYE3ZW8h@Y^**uH0}{X#Xmq{gwceg)Tb16BQa9bH??jiR%-ipTJbBoZ6(#z z@g1$(iJR8^n${~xJ@tGqZTO9}={M7s-_rfnq@8Z~8(Mdg&9vip(o6m&-FB19Y1i*+ zy_Q@_xBRVi+uzphdU7?r=3k@a1HwGkct%(w?wpwZb#|GxSetFI&F3cTuuIQu{|0Lg zNw50_+SjA19%flCLpjbf0nNty;y8;wlraxw&U=P(ejAGkjK!*3YWMSWlEgA*5B?Nq z5kX8rUk`a0K|dV9dYI=447f=J=9{T(*pv1(djQ9ro9y6K6?B@onuqvQwGKaa(DXX#iGt0LS zeP}gV3tBdewV?&*L#u-LgFm|L3fp4a>?*tV++o+*4H&Cl7`v&*Zm}I0bC?1CdQG`| z;b*ZnaBrZj1F&rNW-=D?4@;2Fk=x&y?>u-C#S^p>^g*7*fI~|b&;)T79waeS&2cuC z<1h(w?YqbXQ_BZ+1ODzK2Hyu|ZZI+>+#8Xn#@yiDxiO-dF(PvkkU0ej2o1>@9l%bW zobqh3s^FL5>z=7B5hsVb!Mo6NM)*~DefiVfM7yv5;c-5FiLr45t3%SUEv}=MRD_u#C_dY&+C4yss^Dwz-6>Urw97{+;lGkf<#1 zK>N!Fb;LyD(FSEsj>ro-r=M#c7!r=h=ox$qUaAsKzzhb>ZD{fvDA3wMT*E0X5z8Qp zpU4GHx1m$$9qDZQ{PY)Q&EC*Pd?!oxF%1h-MD%~y0%u&YWoK!IHb~pD9WoZA(?pJe z8G=>kW8{=p-9ESKTjwMCJjN9w3ZIq_>Pz%7dJuh48$rYseqi7S$P;70acb!XbCOSb zwD7@2_($vT_uw;80YPYmT5B%2F!RfrkbIFOsLfrGNY6sWT@pjL*qaSDJHIS|#OTSz*T9J*pl#(w(~}l$k(})~SJsF?at;WesN6Pq0|pr{<@?DvVh0JAtA(BzK88 zdW%r~`#ne`kzQqRMwP?HqTnf8llPgStSn4pRZU}ooPB2Sx8an^VxbIsrp_{DPjN`K zd3+f2IE!LjA{eut$2VYd!9TC#XSK0FJVThH+$CKq7c(x#YQZthl^X<65(*In$T)Za z&`j`yb`IZ!IcK#Z#D0Jq{16336aNv4H&DEZ;tmKPL8v<4$Oj&V8DCV}KFk%bfgrUu zb&MKBq(Kd%LMtz=m)6&oYZ=hS-mhle^R!xBVs?A2mY>&&;dwFL|3?4cH^Bch*n7?Y zzx&_)&um%Okx@A{J^>$px)K3d8zLZiB?77gju`N_P~1iF6A-h_B|uSm3xRTD$>T|^ zoO0|HSM#6Y0zXIb3l!*A{tk+FQT!4G-tkm>6~G`rIO6Z&*hT69=fibm>8y`nj@SXA zokhRRS(TS-yuQ9h&)6IETzwu@3$VydA#4NX1;HPuVN!%0zYcrwTPQF#IEFF5hvL^L z&X1`zWM>w!O|?AM48i-oW-b(l@t2l<8G3L57qn z802DMsl)91$RIaCfe4}#hf!wW#c2)`*p^x(pC6J2&Vg{lQE z;|PdqMLFzQFm*pXh`0&8ez&;X^f6vVzl2ig!{`X~JH@@HuV0Is0mriBw-zHMk@G6{ zdW+sMxJwj_gf~}?T+Y!I{^iAt^)jIglYeD(O!UVYT>KA10I-3dV+DeL5cEZ?kKpkZ zF4!!4irZw7O~|dw!)OZD%%~$wZV~A3VbKL2hIXuEqLbnJ-LNsj(U>A@r zDRrwjnM6*~v1eM($kR@%PSY8w^-P_%KQighOf&A}M*xB}I9GY1q;4kepHh+kSkq4X z-rnur0yutvA|1)%&0+EOmgep&?Jc8a3?_YAub=vR%V{}_ zd-^JRD`_Qzy?s@^0UGG7rqwK7(pS@4OKTbI>#OTsN7n_F7rg@=ykqwNzz!SckY3uP z?6M-Xu?V~}39qRLJWaxDE&|Ul;jJ$MuUx`wDFUxT!rRcGv?_XKlcHC>=-cHshl_43 zqPBokTX0MbReyq&^oLZV^qJvA^tt$GbOIut!(-7{GUPH!P7cRL z6aeT*JeG`JOn#XTPfkYZh(!YIuLQzx!td^%5k+ZJfd7*oT_0KX*!uA+ohUg8v#ehDyNRo`*zs zSCkfR35lhBu# zM=#NRVb2bg#k@o1yk4oU3Z7>FL#tk17ypmK0&!4{xelt6NA%MCQP#_bU@W0OT#D>pD4gYv7k)#(! zlH>7M;--4fb?{(FyrtahoP~=c6xsM2rT>CSu=^zS0a-%o{T3iP4cAec!dcTMp&T;bmiRmSjXfg zBf#2_FV^VB;*rFqL^66lBIk4*n23)IPb3a?6dAe& zYsK#WDg5sGAuyi`V=9{sxaQJfe$jM2WYZQ1tP7?a05+=40&>5Mos0Ll5DTkM;B(*; z*6F6t5;*p6xQI&I00@;)j8hE)BQO>fBLbs)ells4Jw17eH9Jg=Km>+mB0{5+bObVo zY1Al_1beR#e+Ff#_hi=4+XonzC3PF$`=3q9=H*mBY3gi4F-Urge5&GC4 zc)R_(eY52?vvn_Dki0WzjLEZJ_UpLPw08aOu{Gc}7({D!f7e2+r zIj>S){Z`+beW}g6ulA+Fln1OUm{ zq2~8IH%4!2*oWrnL#I>E52g==&Dd9e4|~)s^{5AXw8ybWPrpBuA`qmtr?bR5r^wT+ zM;M)!;0zWNr33&)e%q+{yMF73;0^|#C3eHiz0q;Xr@pVAQpgXF5CDI&OFiW#KiSP9 zKi#LE@{*tKf5rd6%jQ4^@W#qJWKJsP9>SK{5f)rijOgC@VA#X>x^tYbJE4vO*EA&fxdBHmO)m+1 zfwS|SIc9Tqz$GDpvtz)K%i`k9ekd~v_mR|!;GL`r;ICEU6**VNdpX>cgI#T7amWe# zw5pc^=T=NFVXaOhnLi~4#N2065X$thf6H3 z*f&(i$4IIRpEr32P>_$;#s4j?m;hHC4ZHOUd_u+F9l)n(-T~C57E;&fw{k{iA=;{x zX-n#<^Jtdx9xhsPIU`xs;a;VYECm^Zk7i)O(R8nkrb%K8(4{<@j5Y_5S;{+xkOg9g zEfwq4yDaXpK>95^bDgb_|HAL?(SKhO4QsGI)||O6t&g=UzVeiDs^@(hUdqZNuwdn> z)zua6mDp=2s#gLFdZp@hmPGH@*Xi|PzqNLi7qNDg^ReOoxOT-fShd)FwgQwhP}WiW|W?Y{@I?0+-qsj=TE zF~z&in<}k@C4qVO|GH;yLZz zbh8HN(@+Cz;1gs%4Xllc=Fm~zyDn(We{P(DC~L~waOqEBVdz{}PQO5ir?r?Rhj}IKJ<<92ugr_(C9%(qgyaQ$}s#Wq@BGmJ^>r53Bzkj$wosY@lyQ41lBi6 zBV!Zsv%?b+yd)Yz%qK9|is)tb)f$0#3}o(C3!}5ujj2Ez2$if%nNqbbTiG^M*>?Rz zrgGbrlee^rs~5j_FhyFf9cDpVYsnJp%#fC=AY0lA0MC$??{|x|bWY4n{t&P($|ZOV zPgGi~u0{wpR1@;WziENJm!Nl-4!@aQ!PRNNXKgNM+{~=}2jtOn91fT-|Si z87Udy>t7KF0+=Bkv#a04?Z!>lH@(}OZQPx1+|8uwOizF6`GHKMo~_ZZJUgq^r?u8B zvCb*ddi8*anZQg=S~@3YmiUmgUh5H9P(q4{WT^UfQ|tB4ceiGndeTij(|b;R(4N_I zI`t(z(=?c^9lY|~tk#g$+Oottr$`%4lbrzYOio%lCuSD?khEPpA+Vr?6cfoA()Qa@ zrFKxg<5lXy>Su|^fSljxE7vR3pH+fHMt)Xx)DMxLuUBZI9SA|C zg>1VeVhiDCKnSF~2ByX-Vs=d7c{KTk1@8Rk9WgS5+68tD1T8@Ni~k+azF33f{~Xo|wjqHv zlW20Gk*_<`XA4sjjx%*GgO2reMXhMKwQ19gj&p#SQkr8R!FdVPnNZOHjSOl_352bR9>I&KfZ zW}HReF)wU!{yl6K@D;<6#&u8O)17T`+Sk1h-8MaP>n~q1TBW7H0&`Z3_QL!XT``*1 zR*bb30}O`BhgNUJaHMf3m0J-j#`;Hd#TdY?p}b=VH+|S4ZR5AhyNzFQ<~HLn{*OPO z*f##+>vSb-<8L@~+vYFyjVu1_6x*)6Z)_Xi?wj+oQ((cJyz#+&qU%9x8^225q_>8v ztZn>25qDjHKlAxN+s3ctPv#Y%jOuW8Sk0r2TRFVTe9v%38;C7YN85^1M>W6lqNqb) z0d=&&R{l!!dty_*s9p&y=#_i*sKeUI{~y@Ozr(ij+xyM!-rcZZ*n7T4d6Evt5@1#o zNyZ~xM)_zoF+#^D(SRt@1%_zV$#~q{VvfXLjZ!*38Z{bOnp~1+(Y;vvehhH`pB}(~ z?QHKu=nw{nF?a?8*v?hx5e$xEa0~+2b-%d49=_;|y@lTi8}z1f0U=Af(8JRJu#$Fx z3ScyPZur7P5-!!crn1+W5(3l+poE|(8Q=8U>Hyr=--SUB>#sq;C~wETN^^&s8RT`M zN&u6)Lh9wt;yMnx4!GKHFNP!y^9o#aPHob)L-+SAt0!<3^f=Dm2@FnRa0-Jy3~<$E z+x|D}OvMLoAyQn`>2nz5>O5#ark%#%c?@z@8uUw$W|WGlW3y!h3Z~Sk%BN1FA*YZu z1att~L36yDHS;P%sE&mOG3de|j6nwkvX*4G9NBAXZ%hSAXJ<~V+Z>}8u#zuhV5>$@ z&y}Yd!KkSoX<=q{IqDG}iJyI$P695fq7vOe#7otYeC)4z`$WdS{+7RHwyJ)%t!ws) zy?07TdBvQkq`K^mr;O=N8rF0tpO5Z@t7nd9wP0EcrpWOhoXEBxOt*uY>UgUC;E%>K z?Z+8-Jgpti66?%BrWQ1#)8shngfK@=7G)tH>g1SdJ2t~w5IH6QrGGRg(x5btvC?)A zL=gallBt_tMfdg7DFQ)S1O1tGPLXcbXN*phZtOFH87Udy>!$^R0A@(HP?Zg4wJm9F zONtD>*OT4am)_b3U}|gM2PZOH2N^h+)&{f0Ix~=|Z84+MWDpz0964E(g?y-!W2Wuc z3~NE;m;jXi0EX3S2TJo8D{c2c6ahdC)INR{`))jyA`qmteOY3iQ)D0OGe)P$KI}7s z87Udy8(?W_M(^gd@_hR*EoqDTQ9jzj_>W=y$a(k0HT0?HP z9@!3&k84y2b2M_O@nRaelVDP~avHg?ZQ^?W`nUN_Ty6QvnZI{7aV>_AUdleO55|xk z1zi48_JIqqfv-jr*J66Pl-*rnG+Y$7FR-9jWsC0aE=OxLpNZ?zcXgI(;_9q1n$N^_ zX|$DV;%ehK%lpgFTP!-}9!_=od?v2u!MtO{{E7S$7MtVTG$%^ltm&=-+CE{;f`5x2pO#t}$9Jjf)+p zW$T#NXxw@ssYn`sAom;>y+>^w^XEt7d>wbx7)k5+>S;wy8n0hl$BU%#hux0i^P_RT zjxU$SS85$!qj9B38edz-muf3yzH+uyU3J3>>#9q_sE=&S(N#AU#WycyCqQVO1;4x$ zJ1>C+wAb{2>Z+TCu6n)RvckG*w)!nuS3N&vELT@;(MF!Gdc%rShtN6~MI8bQsAIze zs;gc#>L}1vZ*l(jciol0j zow=rKByB~#{dacb%dP6KHmNVSkYBYP^+V*>9qP+L^6TwKwnOBfX8o|jMnpNc;@qm+qoOmneQAU3V)_!Q5!;X1e846vNPM(Bvx<}eca?~H@yoraL zwcUcy&RI>upZqoq?-#QIwXq!E_p{%j0p7_Ywc%V^NYRUy6mA&7xuWU(#65(MeRfFL z&blfwT3o+mI*ysFo_B^oipW2ToS_olmhM}dq2p2!!*t_5j*k{6o+1G|SWsm+m=YDX|27>cYf7k=^Z26gX$-4HDx$C6)bH0;>wp{1$ zpn7$DbJAaceI&ezp3#d5z%!F!uRcSCTw z;XFDo4w_Cgg0Xlqcs2@uvFPQ~Wrrj?Uhvmp+7JU^*$abZubBjZ?X#qVRTxQFj*iP- zHxs~k7_9`rmk1hR(q0Vkv2bs_SsqL@9XfGOU(rrPpp~-M@0G;6pFBSv8@~xIn_252 z%Rx&;Zr{0wmM@{*@BI$|caj1Y*EdPtFwkKf3)z$DUej^Q-cW^U7=FjJcL?4u zEq|8g9Gy>$Eo>ZYl!|et$R;r#Dj}cg^zsEv*p30SwVgmnhd_2wvM0w#cL#Ig$!&2> z1Kb)W)8`E#vSZ(cU*aAp2rZYE;LZ|Ulb|olIiKPWWVMZHZDWcY&T5;}+GYS|w9Q$F zlEbMj80MW6QlDIS}F(%2a0WDpLCQYJ7(PJ!=ExX@em?Kk$O>vzE}1iRoe>4LlX1$W(O zOc4muT341>=M?E;*I=hW)(q*wE?8-4I|do7Lzt71Faf^NC=dhy#XrBXfC2g}fh_79 z{l5OPT#nrb$&6#~pNC_&J#WCfAOyGz&tCU@PO){bRJw$fMh_ioSatHu0bGab;GX%% zz;$4+*fEb|%TngDyi9gku4@mVBP>n262~{pB}eM^H9MCS+`8_!czur!?pY*9>R{h# z9#X3g&wh9kF6c-dX3N0~2F#ZCzGka(ZMIfvwj4O~ch zSU7TOVt#H)!#?2itInK|`1~*W9VfVV>3(v)HwAC52P|JRUcFkc5qy4W5iSvh1!*3i z4<9*TPDsB4_owrW3wt+v=A=(S;Qom{%R##ghd`RE{Zmt*K>2%#^Sc z~C2pMgev))a=fysjFrf*;rIq?L(c*mJ+@uFKbn@{VT0^qwIz9&Qf1PcBI zVl=|urp;#0uI~^GD7?0Y9JgPosCjGX&7s-#Ph!^2bp4Jwk5bV%r?@M;SB}pCgIfL8 ze)O{!y!IFi;7zM6vCb(H#5Zj01b}BCZMR5E+cC_r3TLo3QAPleJgb2KVDkO@QlWkD zeh-8NFZcj6pY!?-yt{*KT4`xJ1{sWP#vFVHD3pU{Vn8};)qIBx+zCFG*5Fb*ZnU$Y zz?~p;+;%VsFzC^+aVsrt#~_2fw1qj$3`1=LvH%2tOf8iA2tYxAkw%<-*-SIe6R+Lhz3ABKz_c z1^$@2H!Cla7d^TQg-+c~y<;TgG2F-F@lXB%QVjRmVamQ4(H|-?d=chpGZHb%A`y@S zFHA%M_D3RraA9}?7XgS>Mk35V=)`y|8jD9FbQ@L!slzeYd|;ntNIrhv~3e84uX|Zj6($bp-#L^Ne%mWZYpFa55yU+OUeR^rwZS97VBA&g1H_LnWqVB>$dI6ddJkzpi zrVMCC)ba(Qnc}!Sd~d_1N5_3{dzAR>2-=77ldv7Bxey!f0c|yDW=P0N0}ed2to4JB zmnrdR*!!#+5AbUm{o9;ta$a@(;C}@5BriyGQ&H5tirUFV#yu?F6ytqc(pn?1V4N$Y zHXLWi-aF7MEnB=2y-E*Ael?fA4>8YseUt0L9>@2gR$}D`PcV)v|HC;OYb(DZwAP*h z2fOtZS@~<`S@|D!nt9|`epqLm-q#5XJSKQn0mNHv{*>_$@A6d^d_}`rDXpB&&j5G0 z6rKUKVds z)D_qtih;&{GL&P%A$p)Ug5`blUcxji4zf=S-W`bl(DK7;%RN6{67`|nv;Iu8L~{?h zw6iG9+3yLqSQ3X1Y}Yr2eb#LHi;y_{oVNJCZGSPj)miF**=oA4*$S@B)@sbw8ozHo z)E;zx|GrndO*!|^L(Z)|K&zc2r+we6UF+Ixt;TA%#^VYWo&m6*Q{0Zn8jlO(HsEYY zGLH__hf4oRZIF;TZxG+;MQA6z2_ZIpphojyAJC`f39WrYaKDT_Ub-2jHR7+) z;YpY%up{MD0P|*d3%#qNo+ngbQHca&yV5nvQIRdK%cvDmsmD-BC1Sk`fw9hc;*FNV zbh!%k^8FAK&d+zT3*W;4jY3z3grHuii22`LX!Z&;^~;`eY_QhUs^j#~zr=vuA*e}Q zidOxvupE*-bBAlxvyTCC8o=8SJ{x#$e2k7^k#Pu&id>Rm(-i!N4897j~e;}T1`vZcy1zBxsj-%h5q<}9(!Dbjp(r-+%rOio(bjzPxT$-Kf4g%Yz` zg30^$}X(GV{Q1=hbTFI#Uw_=!aNMx7*e1K4{0ZPkJQ5ehv zpc~u{;J|cyAsqxEQB0NzARR#45*=7EMh9RDkb{79AXNcmBS%GHMhDQ94QXvdiga?5 zgwFRy(pqO064;p`onT<_-iSyrff>??J+ca=?HFXFv<)(@C}jfE1X>VjfGNd9GSp(( zRM*zeZtR$S?7*E;53^~hU`EyEZM2w=SYb4RCL}n3?t8fM@5@iGc$=9 zp-pKLtr(j`t2EjHwdbIT$z`oRh{XOO>9ovs;ydD9YG{+;ov-~4v{eLPOexYppNZI}irS~JH(`Pmg}FK-*cxz;@NO0|#Y hBlu~%s+M03eX778yjzod9W81X>*pO8K+Hnw{{buFq!IuC literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_config.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_config.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a2792921044513859a948c53a84dea84b71711c GIT binary patch literal 27941 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7208114a061c9c0b486803fa0420ddac3c14e80a GIT binary patch literal 13954 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 WQJTQ-Ab!X2y9>V&{6>du*Z4oxRsm1| literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_config.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c8cce40e1a2f5c369231a6da791c9e05a824cad GIT binary patch literal 10716 zcmcgyU2GiJb)NtI-zAq6Mar@)|0LG75haqcEX9&*=g&^+u&HCW#n#Pa#Timd?_cj7 z+LAh}2P+MNpazH_Xn<5Gs6G^7P!xp`W9 zr?<4a&i{?JX=u9Ew(7R7dYrn09;20MXX{yRGh4ZKzMgLv>VgiwAYSj-g2W_>cN4d{5r2plv=eKHhWIw;*cY-MMVx!}A*Vm)zm$y2f z?REnHTJZa#(d+pla?sSl|IAe-vqmpU{&J{faJ`F5{!G`j0P0wPsyxFp-!}SM0EOMs z`+BJD7@>Yg|04Qd)cS^J^v%!+&DV{-6tJOc^bCL^lD@Uz6|P4J?DqvG7%azjdA z1l)JNCaLj!&5Z1Z+Ieegw?&TdgDufnGm@=^|5V4G0x0Y*`Ze-aHddpoyxv)Jfo)gY zH3WXUny6B7Bq{Dbd*)S)D$j`anKkV7Gu@uwX*5sspz?I@dT_PdIYYCQT)N!h0?c}I zW3hKVn&uJG6Q)rlqtkD!(37sbE(5>qriB(SwYqDKmV9<`?D?85$lQoo6ccCp4>Uk} zmbpeUO*OGOdVDZNaTK>vRmr3=Pjr>meq}V?NjNSr^P%?;usZsG&jGWYRH~R zdQ3AM-O!B>9V=@H5<=4}FKGQ-nAWEo6tHqL)Wa(`o2&~Y79o)h^LO-Z zLtJ3(ppLbf_4Jg~D=ek4)Yn)Fbx7&n5q5<|Z_Ya!7J0==@rsp_6$2L2gRUl^Er;b$ zkI|a)EZ$5jrqH5?(axuE{4?VyGmigY9H;}wyk`!iOlhzbocy*aiY$dXq|C#iW#_Z7 z8fBC%UfR_K(Tn7MIjO9`1<7&id|JL z-%q?1d!&}%5sUP+Mc(PGDABN}t%xbqMm*RB<-slp@=F+Pd7shX5Es=v$aRj4w`=kM z;vC+<1AIX5is)_ZO93=Nw~Wz>>rDYOtK1`|zCywPw>*LiURRW@y{{+SjaK(cqvg_+#0h%x z5h_m7u3Uh$3Y>wYpCyAgn?k^FB`@hK`u>EaV8($+jS;L7Nq6HgAhm3_Y` z9;c@+4VsnUQrhgVh)_(-H=Ds#<@+VgM?Qfnb;v5Jo2bR zn2j9*jlS0*y!XIIPv&0kTLBJseRc(K=d?lv<;r33-$~&(LOp!Kq1mUj;LRl~g(p_= zL@ubX(Ip+`sSa~GM|E-?a24^&LpmhsP`Qe4vPA4ExL$zt(c2DOZ4pL$Vc;^9dA@p|y=Tc^K_V9tJ+Otr8d3D%k<<>gp?=wY;v57653uNg;` zar_N8)tk z23>c#Uh;goCYn7u2f52QJ6D5lS2)AOSRZc9rhUE(yo{R|bvfW%oU*tfHH zU8dWos5nmrQ4R4l6&I+uhyuLh+5s$P0A@&HmOxPyy`oyoubxiYP>GwK0txc`^~P2! zz*OTo<@-#-K%_v!1f6$xogpD2X7dykPcz=Sg*d|5yA|c*Ifk%Aq==fAYx<3)j|LoU z53~K|y&g)b`94f~p(#YM4($SabCBIF@zU1&>jtPvA!{nxW zWs`I+>1Q*>5LKXnf(8F$eTaZTF=31EgE7(-v&KKdv-`8gpWKr*rh*6KTyZpb@Q<)r zV3%XTgM=#vWZ#%85^GGjA_Wf;i9aU^=8D7`6Rw!&dYZ9K3LYH96_en>B(6xDCqX?7 z9?7`l_=6MFvzghLD<1LYJ`z_<*jjpih|G_0VN=rSB+>I>=4Z+$ z2r+YEQ!EnVn5_-xH}u`6e3_)lUn3Cl8c8Ru1X-ds-Q(WF2l8F4#ssu!zU#;zji62V?uh|fM}D1g5Z`sgCgV_i_e6^C zI+D(eO6QK)W~qel9tth<-A5+!-Cvuair<0TWbPrpLbb0_@d_2MQgLr8Bz}+N{*a12 z(V!;}boeeZo+>&-PFqsQYRvWP`BZS|ef@p7`}Y%f|24>uxO?JabiIp9{v2Ff1&I(L zwca)<8M&DRg$$%j^-W|65MNIxO25xZKV$>2Ide&(6iypkmP0_VaXJ)BC)QER#y^}q z*HtQ&^8twXQ{3I=h<}nwMFs&swF#+IASnQeokoI4|0f}DQDy>I$`i2sC;BslK>Rn4 zuQt@LkEIAK&K@j3PGPBxV*%Q037TzPymkNZ@W!876g~@Q@b(GdhX*7K25_ejc$}v>`anq?QXV5z z6h0jWr0;Pw8EB=JQR%`f^fYsPJ~vP#Hysi_pT4=U*A7&9;Zz8l^wsQa{3%`#rvbt1 zncF*#r4oYnxAi~M#Reose%`p(OkB_5l9a9WHTqzrZoI1brjJzFC43@+47(YbU+8ut z^GZXk;vgDo1rhbqZcFjysoN%Jxrz znn0v);u{gKf>VSU=C+U$WsL9OZ8oCil&qbmo>Bc^76FaTtVS0%Y3$fID;)tRx8a=Q zRb%5kaTmd!Hh>lTn;%?hCt7O-Z;u!wU3tqQGL5vYz(&mL5U zq%^NWWr*2*QNby2A93e7&JEfTh(A^4pTL?s5@A)FGzP(J=n+CmKY!s-5!?7x>cQ~JgP;<7 zkbRuW$cs-=_B5p`xQ?2&cLFrdh_Wu$WF1kxi#J}1LwV3W<2yWxrgZVWNVDV8*J+3% z0!M91xewAJ$GD;@=Qv|^D_tDgrF8I?s5-99sE|@XR2soiD=Y8QBLtMmWEYh7HRps+ z&E$_%;OR-Kv5#7(+G|w2fMO(Obv9+&$*f05Qyyx)*+CXoP4X|$DQ8hIGo`OpkgB|< z@4&+Sl`h^ydu=|-CV5?Us?mb0zTH_=Yk|1)qVV4o%>du+44R@$+{XHg9Q8<)?Opd8 zouIiU;5oF&VV^10GFWKg(4LAnl9P$7c0;_5)IEL5)a>%7Gn_mX6blo?klBy&4}syP pCO&X#QTCGZi_cR0np3rp=|$tX>FD`6To2(oh3ipV(^HnC{~x|prk(%* literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..680275dfb3385be6e1ad8278334deb807a7d2dbc GIT binary patch literal 21342 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 O+Y^Ssbskw}>hpgyUiTOP literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_hooks.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8abba76e2a1d10e1ef83d769311994ee76de3c5d GIT binary patch literal 23133 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 sdvRNpBq25$lH;Tur!$(@q=xEc^!kl$ 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_hooks.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_hooks.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37d3bdff06825804f0dd7429d5eafe6c49a13b42 GIT binary patch literal 9404 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 UJH28?`W9OHvdt%Np$z-~1M`Cep8x;= literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_runner.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..355e69b6e92d067c034615c78f5df880ec21089c GIT binary patch literal 52554 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_runner.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a88334aa6623cca2daeb8e19795354a87736b4b5 GIT binary patch literal 21847 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 V&k1~v<8w#&Ie_m;e5M@Z{{XbHQr`do literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner_streamed.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_runner_streamed.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f894581a8108a39046845064a1f43ff1432e4b63 GIT binary patch literal 52824 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63c58a50d7e584a3aebaeeb64fa5c8032c33e019 GIT binary patch literal 20914 zcmeHPYj9lWS>AKmt9Dnb`^9#=j;~4`CAJeMO_Pv7o1_;UNYfS;+GVY?k|pnIcPzTfS=ep@RPatZvs|H;2uc;)^?;$NAG|JleqkVqKrr;S9y zNjOF$(KITC!L->hE0%oQ&7_$y63tX49n@qh8Pr&fY%^EM1!V)30hHN|e6vs~NLjK` zYz|fiC7o)NnnRVL=5S?L^6AD%bF?zr9IK2;KGPU)PE;nEla>AX{Dl$!@C2P9o=+}u1^IoLc@In+E{Iov!_Inq2@Iodo{ zIc6m414}bYw>X1N>8ZqJqw=~SKjaKc{&k{O^6nHL7#9M#CSHx&SjN_s6#s>84{W5AcfhFv4P@P+cVY)kUw~^h;4; zv+laJh58)FJNz3)B7@IM+)-DN@)DO5>j}?r49{$1M?Gse*0bii(N1hwp6%F|jRoTh zcAJ-6O106-Nut(ln;YpXiIa)7{3-0iM-uN!tXmiEKb>fsOPO`sG1il9yPZ65uBX~5 z_j6u$Bj+GBZ#Zd|&zo0lC*uul8J*OarfC2XnM~8(R@$w0b97 zbzz_FU@vt!P%-#?5}46C`zK}PmK>bK?UKxNKA7ozY^IBtWtnZf%UqgRw=tK7-b2sX?j6A!xr_F& zrh69wr!S+09w$qEMR4hvWoK`+zHK)pt+BVCvsFo2BaQXF*V!l74mmeD``bf;Z7IMO z>$S7bu(!>n$#pv#q1c`WzJHBA!&O=Cvnp-(X~BSd$5uZl81&#PWv^xDPsLW{fv%mO zbUz&6#Z`H8pH-Q3%fw4oWm4Tj9Qv%vr27zC$*N4MA7HCpt;&PDT$TDXKGc00d(+P0 z%Ld>Pl$$*#bFp(WKbO4lWjUL-KM_;z$3eqRCt}LIn-t5qq}2D4;y&Y&a^J&NGA=1~ zj;(ezF0buGJk)g}rd?teuDyOIV%p_i)mWxgaSN8;4%gZKM9jFu0bbjkh-u+VdW_TR z1>)6bjMMI?W7w0s?rw~Ce{KoUK6#<#t?>uo~LY*NzOG<|!y)~x$Q$yVJ!^7E7Pi%Pc>L2J{vYTkF> zrm=SV@UfLkCuiK;dxJ#zzA{s{&$zQK$fqLs$s@V-@_oAQ&KcKFsk*z`@HQ>qLSurH z1b1F7)?CQ4o^)~-8lvznRf4>Rmp#g!@w9#!5}lVFW7Vm!3W=cV1n^j{KOusln_Hjh zU~xA)Z`7AJlj;b%=Zj7doKnYv?A=!n)t{`*d5ufu)^fc}*|O}OYpph%^4WSBVtHNF zyt;FBD0hDh=@TH`x6#?{!1#_}RQe51Tqy3K1*-7eJ6;>5deShoE6^zFTj;$y8>dGa zm&3;WC{q;Zfz9j=gR6!54*sWszolmJpt>y>$J1Stig8!B>wZt}FglHz>s2AUFMFN7 z@4(0=r3|m&q^YC9EAHre1#nQWN6n0}c4~)LKm`eO5{y)AalUM7^xpZ}CSjTCh_|Yi zwNA2i7H$m|(Ye~idO6%AW!WTUXkML_B}>jvx%Ii$vg4-%ZN?w0L2Eo$SJj~4Ws9`# zQA7ArNrD1^KZwm+6=d`rsR>pUS(R6-%T?D?b!>IVFX&XYei5VX`o$0>jlZ9JVC51I ziE2&xL!ymUReeRxwVEq6Rrm9#uAW_8Y(PcCzHYRj+PVH@RTC*qs|$_RStx*VvSh1n zZ5~?dit>vO%3gI%^85cW3hbQtyf3Ikp}@UOHE)~4 zaetfaCD%D$T~RHs<-zO_6y3%NcLnNASjCFLc^563*_BKFn5+tSvANQyL&XPwV5_}& zk$V~-VWtcd|CwtkyJQxPQNuQ$F}?&n@k_=Bj2Fxgn_n_Zrfrm7OrdP(bJM=- znwxG4%uO)t->|s}hMbtWDJ3SuaF!4BF*l`R3R|j=!iHIPZjQ`NDfcrnsx&vH)aPYX zwYlkL=U`xNI^-O_uDNM6HvdPuPSbSP{P#Oe)3NzaZ#)0#*!-to!~7rJ-TViNsG0=d zS5pLg0Q{tPZV~Q|{VX^@aFBpvm^w@_LvRZLl|%J90?LQ#R)P}*CkajwJV5Xu!S@3A zsrlBb^3Fj$c>J}A_}vn6uUffwFfQP)sQSgl)~Xu__-9wW@C!odgt?MAcGv zF?Bb=Jp@#f)Efvy2EC7|?;)UaqaFfSn~lFJL~C1J!kwAw(Gt|d>?+a>)FaHkncyu1 zZzXse!S@rKA$XMF2MB(U;4y-?6UgvC&Qyiq9R%+rco)GB5&Q@NMNthJNcp2$j9fkI z)-S9=1ECP}j z56TIWc1q4r`VrjW7-|cq+PJN;r*j~{UcTuV8Vejlg^dXZJvtL`3{6IkAx+Qrj-kmI zO;cSonK-FCG<7&*hbBspAx)GZ`_p8?vv!sJc-cS9377c>KT-10OK#7neJs(BS6BRsyR-z3DfZ+tqx*l3ux0^ zY?O`cCAYQgC!N*iiYpec7+({%bfbJt*jIu|gyFN-AYr(`M#d=Bsu#G_8EhFkhWb z3bYB;`|u238vS*}KAg4tF+v^tuq7J8jF+^HHklw8rjrzwnR?b6>_t zI2D9>qDF8U4RJz?A4iK}Z$g;oGsJ;uVV)7@X!QbH5o0jVPq0-N_5&U4r4ByyfO!^? zpNuijqR=xK&;w6=M9%=|N#j!h1@_WZjHanBnoRd^(Pv20G^y>O$#fq_3r&-$-b0M~ z&}6!6Y$Y_A>M6F`6-^!Hd80HKjFw)iv9b(J0`iZhhXFr}QLy$QTD568bTnxqLB83%gFYSrV!OdNB zV7q6~S2za;`^*7jAHq3c>_h)Ku-t2GC39e_3|sAL4i149Ex_E+90;>N9AoxcwJW%- zCJe?w+?VKdGj@#UXM#8{SGhR3)GEP60*~Mcf+q<&x}os<9<`9{wTIPTJrL($o-oF4 zFPPim&o9H$td+*K^YO>-inty*p3Z>Vb)zx`&sRgLYR-T|UUr0roPE_=LIyu=a4A2I{{tk)VTn#IPfD>5z)! zH?y}NTUPJoi^zLbn_!(lw8}B2sDOQkSg@#;qDzt)&jdc2%SeZVb}~$y(&mEK$eEXj z^CCeHk1hz04%g^2Xd&8a#GT>p2;Cm?ZKRCcw^C`_8a0b%<|;V$facgAf+Iw8>rcqH z`XKu1;@8M0EPNiI;yR8*JyAi7U&VX}qJZm`W8RDSUik5C-+BmAv;{|=Z=J2FGbqVa z#otz~`nhTqt9%vWQq|8_s~1*l4SM2`8?07iA*j`Aj8q|-`ne*7e}s=~0^|(IcVSsV zc^;pah~;PdN);XT8nIGINDD?>op#l0bdr?8*&8Y z2M-FjNE4xQ9<}fW+6JG&d$Lf-1BHD-OB?wmm^D2pP3WQM6xf4Pgydnht&PDR$-}AzBRplu8`&783<6F*jTU2VydRwW z5YppJgWnHHn#%idto0*o?PA2TE|gGmF=3$Blu&Y+V2w~R8N=Zr*(L~85FOmys+6%53(zLgWCOSeo zH0|p{lj;63T4- zS!EWa$`C_KJ+lQvuCj78M;0R8*jh_29D(sbaw$6pL~<#jY>#s@o;rxRIMfcgyI&yPrgFA@=?Di0$4l_;8QdiaVfte7pCE?Kaq2_K2-6vi0@$2-@s%4&T5m z#CD6wE=S_B%ZG5p`8k}fh-qN#Qe0*cNyQ(~nrM}dbag?R!?>|sbM@nFVV$k5iD0v7 zD8509Hg94ol5b$BmROW+;;iD)51l|3#M$VmBP4=gD8$bk1OE5X;s6l=!BCn*@`Rt! zVNeoLqi_o)Ffvo`TpR2OZ_-BfJ_45d2|h@(?aKp9iP_+HFn(v_t-2BW=$C>2ODTZJ&H(9z>ENR))_IjUnc4lywp97Soq1Zt5!;ad59A9#Jii$w*}M+6Mpa*eW7HX{MxY6++fevF}3!9kJ*f zv*hU7YSA%Hh}A;O84hcg2>MAx(xDKEMn=vsAqr`+C}-HASY!$W?oCB88Py?P)JIC!cf0N47jpz&1E={z=E3}4sWg?s*o0;IfE|^ z{+NbLlK4JKClo}zL_4v>dmQGoyp3a>5vd0~7G!Z&&n>!MOI;G5#*8ViQy;-|Gq|0B zub7NP@yIW!db4%0j!3H@%J4r>>Sh2E2z~)M*VL~f&GC)?29aWmmx$AgkgK-4gVHS} zL9#(C{PisZ3>dcxh>z+ zn~TC%GQ8Q=upLb%>^&WtCi~FD zJ1!wjrn(2|el(fxJK0KTGS!c<)vjoo+8s^8Bd6m$at{_Y_RPKb^7DE-%_B0ft`M-f z7lmTf*No3N;TtD?n=yHP)pF2GV zN?>=xa{N?&ovu|lS8;0-H%Xs@ZirJcF$E1cyszh3p0!~=oxo1DHj?~aOtg)s6S%KO z{VoNaau7~kTBy^oKbLS)W}G3w?9;eqx?mwU96K*_8v`Enlb=0}v$L>Ngnb(KO#}OM zua=m+@Tt?d`^S5CcH**SB$i7j67E-S$4y-R|1dtXOU8P4$<^`4dhbvE2(Ev=Tm3YK z=W6ajMm|dcLgel0ocE-k(wt8H3L4B&7Z3lKCYSqpjxJf<(}?ZgG&En^G}UjRNWB06 z5fd{I_q`9b7^Qr%b-o^6t~`XEMAJ_gYj+D@ueq0&=K?OP7f5v;w@}^Ldv&bu{j*y- z5vIwz1mwyZVImm*G0o2xLxkHvSp7Cigt-FE-w)H8+X|x{1){aaqSJ-S(HOdKxIuLL z^|P7sv*LD<80C^Aow_@RtG2v0>ksL}jDDr6J9n<$tf}7xj($!X;P{k!o=E>1!AA*x zonV6?vQ}=EdJmBt>ir?7K3{{3t1q?+29sSyF--I^`d)S$oj86i@d?}LcNHmzk5pTd`hU_;Dqo*rrhl!?afKHSAlS*nM)gwJ4sG1iFEI3Bul-LIow z2Mb(9Ds+pEOPVSm%V?~g5Jff%qxV!~cHcHwz$vZ|Je?5L6|QKgoG+puV!BkiQ8^=>8x|E*m(P<4SdE9Y6Vk zef{KTSnP73B)O4a8)t$p@*aB&S(?xAi@^h%7J3Gu=8s-10CtP^xg zemW5Mt*m$(!OoGqkE6+ELA}J5a$tcOZIKn<&9d#(&iFO7JM{ej727uef;(fq^ZgO7 z2u^6iw9WCDNEuT)4f7S@qj?*rTgn)J#Y}cNQ%&`cc>cR5+5fYXO#L}#U44pE-zyh0 zFFG-|O#JXtf5B(Y6Z|ECEKkXDjmp~pTz-8VGwibaJW_wdG9EWE9oc?gz+1bvA59UF zmHR1VAVi25@w`M1!jc040v>++(vp*Y7Hr-_OyT83AVwev-?J$u1eTceQl=}&KHFo( zwk|x@N94S?MdZw%Q5MH13xH7LGQRvYE?Dzdr25-dNQY=LTt~oIgrUH9@HqSiq1$h9 zdA{Wjd%>@BRgus?&jrVUyv&rBsV|}zJv-5o^M@D0ANPvlqW+7M!r`MEfkwBrcgYlX zT5f$)B)L90R~NfQ=e}-atm!--y4i%c0?B_OVp9MZmc97$OPZrbPq^BA8?DrtoW5>O zEalF*=2d^h>>m@*?$^VO;5C)^@U)LPqGTvKNh!Xqv$?i z4yQPDIf<7ItPWpB`ONDX6EnX`?;V0(cux44@Cv)|M}MlSO)9h6^$8Qutg1hu@EZ__ zMGtSS`{|%e-;*H&_EV5Q@LOqmjd3LWAzgw??OuJ^MNh8cimK$rbmY#;mkeZOC@89j z^XKXzq&=rn3M!*fQ8MmaT9v_|E;ycCl|!j^U2)M?#)5i%H9q`R51F%yHetE5xOO9R zRT&N%qo5jY40^2m+&q5Rjj=@ahRn2Ilobizss6nQC63GyzMA8&#t6m z&Z0eW5f}Ygu|n`Zf)5f5S)(|Wv5nMOJ(+uTywm+y z2?_TMK;FFB$eS5_@Mj{pDw literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_agent_runner_streamed.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a83fc93df1eb07d6bb3f702770f18890337a8b24 GIT binary patch literal 25974 zcmeHw3y>Vwb=`DN&u3?6W@q-li{AhQK@1531W1A)`2SOq1t}!;@tD$Zusyrj<<9Kl zbuUP+du2+bC5k1=ELr(k5VNpVN?GDSxe2%1juER=So6YO=K~YLbfstz0b^lnvGf zQD!aXTZLLd%2JEP)=+JzRjQR*o2KjyV)snv{ z$nUogNdD#^e~o=m^0(OLgNf>)*T@xB!yjz4+e>c4onLA@$B<9n(S8Dd1|E81xoO`$ zKjT*Oe(H|7X4~~A?ptXnTQ%kv?_Fun@QL@VxXUZ9pSi!$wwGpS{Zf#4M;pa=H{6C_ zlw|avzoR?X1FEsS+*E!V zMQ!{Yc%Rc$_b9cb{LBfBB#6tP_8o4sMLPAfv-5MvObDItTecf+(|*uZ%|`2$CIi#$tU*x}n}`I!6fJ@=%Tv2w6y4tZz!tI zG!_^AF_s62NlRTk!eY`>pJz|j=YgzWiVA}d&2R|Ae`81t;P(&Nzc9?^+8f5B0W%TGKPv(`OQg607-ZAq?(Md?2xwJbC1B z!ZQ~#YnE-Sr98_^J!-C{y|nYPn_VBUQ(k)3u+uD`HP2Xf#?7q{o=KcaIM1Ly@0n)| z^%Mv)vS*B^jE@<1!b+@J_@(iE zbD^|0;Fi6Cr+|$;xR&!$UJe)ydV|j!&R@fG#Ay&X|Q<)5KNh~*CjleepnN6*{GrgJX9i+w!Or;knN zY2qb)Y^qNauReWjI={+R(#NLy8e3gbACF#gA48x|CDw{wabXP91#@6+$Q$s6z&|N3 z^?cHK5_quBQ=n~}HRK<`tr(1XIQAUl3E|ulmjbUOBb^UMIv*S9B1Tz88{aY)#@8&2 zrJ?80^Omze_#$V~G;6wN5peoFw9x&ORNoO?dPdpa67jd?B&0Rw_Vbp?No%CR-&^dh zf^FH}W^eb(f^8|lwiLtG!rV3&Ce{Wp!@!}KX(`OKL3_vAus7%p1Md-UKkmeDHjX|qcDu#-+4NX8CSqF!}c!Z zFT;%6?G=Qw(SR~=NhCp{8He#00X13@UfwMU-AQ}TQwHD_C^vhgVzDC?pHH1UCUWuo z(kSih25y^4qm=VyQZId!QeP+aeflWne4DMLk5cN7*lN>0dV8eNnz|)o6WF{mj3}|BLDkPmeuqN_hdEwn{vPs_CpOx@VJq5^V?$5nO++*l?isx$^7{Xo$ia)F_lAeB(WQA)eOX zK%(=Fy{x)6tU@BFI*8A=n~#YW=H%9@9W0LU<%`Ys*_7Ig*YiUM39eOF2H7L$b~hhy z%(#nBRF>M!3KhAEb7E;_(XJeCR-jfl6_w3%<=m|?qz{5(-@?nT0mjz_ozbsX;`(O? zEl`E$9{lkrRgK5$=-6f01I-Q8p4%U%Mn(IyBZs*#P))lVhqKBKbzK=MLi(Qr1lZp>jBPP9Wc;| z#;Imy36>L$kcv!@3T&JXR|&iIvKHo-u+||z?KEeW+P0qw4VR$?Ou-XP6V@?xVc)~!Df&0)2l=#;}odg8U5IX@qtPky0mD)?zl zvY+cPmS5-^PBo49R>#y1{86_O?BR!ux<5$T^mh)%GuOp4*XoQ${ct>UB;M>$Jaa=l z)Ah`axx^y}yiR9=8jbdi@n?>7z5Cj;3AGGD&)s)`=C*S{wGPZc z&>vV@ZnhiqSIc~JuEyY>SZW_22RH)too3OF5Sg;j4 z)42{HVWtg}|Gkm6N@md*H7xU4M zGC#4H{~xlLdo7e<*mi?n5f;jjHw63n@j!nX5<3+&Jr)Z5pJ)-a8Se6vBpupgq1HUr$A(Wj<5Axv6@4cCwC1F9Gh0dDBo%!o-8%n;wB~78nwiff zJ`a2L68$CTx4JCn1n>xH>*oVXIzJ%RqBkei>%_XxxLeL7TM2EJ+Qn9zj(e{~aOpmv zQTTu+yR0tDA$FvN-hnNBd_b1-%@~&7+JNO-vAXCb>F;MtIj;nG(Fe4(5C5c`_Y*Ja ziavwxe!?i*yCWY^I`A0L2b2yxM%o92?FGSh)W*hwH!9eU1fG(Sn5RVhfJQd> zfcADt*o@-`J#{__n{gf{-ogbLHA}qva6!iT7+VP!WYnkFYEv!Mnx23GN}dm*74CKRvs&qTCZzWA$A>o|P7u!!gqVR?+HZam!#?HBZeitvG>YaD2tB z%q_VUb(mP(KyZYB7Mi+|;3&b(1hlTyEd-*)-p14&1hk3Ny8u@A$3JyJBgCmrcB}~X zQ+!KgIjDCtdyIf)f_e`D_AV3ZuMj*y@F2lM1n(nwKfwnGr2jw2)Q1RunxIDTGXxI< zY|zKmhgnr8c!Z!qaGZeZp|S~@1hWKl1Sbeu1Z{#Pf_Z`kf=3A!38)2Ya1tqhRI9No z$DQWM71%*kVx4!Q#v$70(_*Q;4nwC+o8Rf^w1!&26J7di83oaf&x>Za{_2{xiJcyE zj=MWa+51VlmJNTdo3c$ey`HhtHg;Sv^UXOEHCbvJb6AbBFO*#vz`hXnWCA@Sw_#uC z!=hyjV%5sVS1ov@^6LeAP}Zz`v}R!sWPM2Xg}#jX5@$F(p8551FxLx`=6c04?h6$~ zR%PrVyAUasq z0zdp@mkc(Y7w{!gMj1Rs{a3Wy4rSC|L&~VX_NUCmj#o&Ti5)Leu^DBQ!E8erJRNAc zq;L!JTcPD{i?m!#VSI<7N6Qr%yj{y+KRLU!R3;_Nqy>9Xu}PG=wuM9Dkq@ zIwyRi;Ty+&%Z6Xg&#%Dn3~OQ%Tx4B82`=JUgrGnESIL5xXx1X8a*qWrDC-e zM`Ly#J{}SF0TSJ!cAva9@Zac644s}&M(DB2D#1gYMX5_fPXH%>s$tvp7Di&8cDTEu z+JW20(b9X8jjRP{sqLrimDaK&o}idK7jb;C@?6Arw>+Qna z3Syd)-JusZ64OTRN9nYcoHnzsn{QeJq1)jLhWY0H)K>x3$I!Yy_o9z`<5w{3|4Pd#U?C1DU!fG6G5dO9Y$4L3v4?Pfh}cHt>(rHdd>iXb0Y z$azUSXAeSg3Z9)Q)-(k3evNI2A;cqb3k|0ZwSqMvJmLE~5N#;=r_s z@(6RZ`Yc-!V~Fw>*s2SA3b+7!`HKzRK$I7epNNU_qR=xG&;zqCqGu5FWbiA10&8KC zAqv=1&498DC@a9&D|rR41j=BLz+v$+-b~8MBFa(ILz(H^j~1FTQ$0Yu`cP(S`h_x6 z3H_T=hQ)_(@*tuEQ9gwHL`;+q35CM}g)n+HQV1%`pvGF5VrYnLZazf)S%v`MOmq_s9}c7%o)$|mfcpk)`9q00i3Y&5_< zz8#!i%gWNTJFfm0oTV1LjX~Uw=>1hIwgP2?I2}hlj%lq{34WH~2?CE`ji6&c$m-K$ zI#7Am+IO_@oZA_f`0feSgmmUcS!V%?tri)+;4T6h7ORiOpS>YkD!5GO3|58C0u@_; z_Ju3JzDht?l+Y`bUe5Zj6=EFt8S(7a4=Nf6-Bj?EO}m1iFnqoev^!Vo+T{v2S*p`9 z+kgv?`Z)rwF6v2wj}nNzFvV1npkp+=)kUVuO85lc9ny6uOkAstz_-V45JN#shMrZZ zAgfTwWS;^e4vn#B{eiPPbb8BQBW>hfPiL&;s97|#=ddOX>NV+8FdMiY9kLYP(zPP- zg;<9Yw=+y^9EoP4h9Ik&`98#gHj}n_Gon1Pb!+*_d!ddcv5n&=k2lmYlw|9&@lvn* zxq2N8x&o!3?&s_ElPiryZpk1wRIkSZ3hVWlNhIXbE>h0{yojFzqa~3uB;SSQ2+9ZX zdx==Sf|%8KFk8p8ffL8V+Gyx?6Xhv8%@xywDPw0)V`9~`VDO~G;4$rNVDMnQM`XZjMjj{aF}6G?K8k&gnIEZ~^WL7iu@2FW7n3*afVwNiVrx>{(x}lV8I6 znp_w{xLS5C?Iyi6--mTB6UggnZ)Wi3&UX3F? z#xz#7lB8)cm1C`svbC#Q-^~_GqjgzYb8Y4uxHb=qfieI?XVeSVX1;%-Yi(wzS{A=F z@J@0qb75>?70cUWD8ULHtk0(NQ+Sq?aD5(UjZiWX!|heH7^zdC0!F=I3BoWNlI}YT# zUD2o|ov%u3y*~fCwASnMF8eYaeulDXdpDlC9JucZ0@0wypnSrM>lP{KpqtMc3qeU8 z?EB3c%efORG#6QloY~Dq-LuAWmf2cnjipxE`a-h?ZKmxjK+T1CEjE8d>)0D;9lrnx z=jR|?1AF&uOxqA0!ygPHi(Ld`t5eb(M)LWZtEbrl(t1@?lCy^57qn7BuTw`eK}5#; zH;J5x?}B$?C*WYz;SWdz;SUI>*bDq`;k&S!!yBnrN(%TOCPo4*)b0HIO$2l;o*UA8 z$`YW%kO%ED?V}p0pXW35s=k(E>MjE6J?B0X*L%c3K3X{>(GYKUd!Nx(ihw=Ir4R@jvp*w)mRB#ykg ztydjlcV0jz;!++{jHa1AM)3A%ML_R)}(j6)Hr@VPp6WlS)z1GEBq^ zq~AcaKxPz?0|KnJG)8jLZc-($w$M}vP%(`N%_G@KF= zZo*3}Fp%DWO;7D9c}(=4YSJ$VYt~O7*k?&S;g{i*kK+9q4CaU%yJ`v!>I-;T)lk2T zKQYBge90x%Y%QH?BKRf@#`$x1a6Qkse=MnkESKdc46Szn?_Bea?mR0n2>aE@9zMdxO9z@8zLr{T7}hPI=%| zW{u!99O8r)$IxQPg)s?L?1RLCX(%wmc7!=vxojoNyrDkAR$bV`@gZeY#aIW0#vTy` zrhxpI&4|)5ERo}ZC$$*R12rHrzjL66PLn(+NMc7b2Z~Sv$}m!4CGE>tpl$o0TO(~;_MhdwjJ}C-J5&5x5Mcd+O z7fLyx5WB1!DFl_EMwZU4F@qhG=2oz`pVw1t7J*|;g|NQJXROyl1XK?zecF$GiEOSo{+t|y;KfWysoi+|@6p7BfqXR2rrreT5>vGYd9 zZ5nRbAxO}ST>{KLgJX%1qi}D=G=4H$Mq{((-1q~%r{sPHNBuseK8@Zum%D>;vQmI> zYO6luKJKUWI;egH4Q6PmhyR%78u*LsObdx;5bS-{&}-gVQ+)+R>hA+U0~L*-Guq{) zHr{$_>CtEv+>JMhA0T0@9+6eQ;XKiv3D#iE5A|7`Idt~ltV2R?-M`@_vU)PgoNKm5 zmILxepbtiFm8S!IFdKMK=&+0~p0L>r6qb{8sF#L&IzBGK*lp0jCxKsFpC zl(i&DyXnl}JR}cR`eiNDxVKVwW==F)4fPL!qo32RG(M&NHj(~21ZN3;mEhM1A~)pO zp_uf@pvo`X&DjPNUwzn6K0eu16hlHwQt`z_qQ+0_7+xqwjd=u!t08`{NbqigV*m(X z-oZ2xR{xlwM`aX#_!=vuqql9^k2oQmaE_vJ!iu6kQ{%}}QgV<7S^P(-V43Fs7P(OB z$c4N?$)J84Pya+7RO9kM{ayAyC%^hK!QW#SU2v9nCXMMa+ISU=>xq9d6%ofluE@G=<+gQO@Ld&HZtHH_UUw??W5-{bhW&i{ELkMC;$Tk-6CQ z?_$%9V(|rT)w-ZpaU?Wr23e+qpTu`$#JJOGzLEupqY(PZ|X_%#8 zC>W*#&n3ZNIZ>z#@#)k5o)R|0p;b4to<*wyR4(`&?ID=4G*qE(MTV;03oqNlfl{?` zFZ?snUU&%xYJ4wz7Y#rhc;;R>46MMfJr?dw%D%MT3;#Y^hP%mQl8*0%kBg}~0B`qX z+maZaTyQEoAqbswj+8+5Kf2#_67hr^KMSdI1%Z>Di zH5s9t&LhE#2BtXv)D3gPe2 zb_iSaOf)>xiSY!|Vl(E@IC##_=_xBd)^2Ue&jqrY@AxH+pVd%vPLGpcTY&c%#u>g z8CM&UJ$tG+h$hFy&W^`4zYnKsDHuth~oDNrg}tE zon?;@Gzg9pP+ExH7(aq^onE_NVV$Ul`Y=%MDE|`qK<|%Q=6Fms6a!J4iExR@0-3iU z$I{05kIYopKB=j`gRlHVX8nI4v(&f9Pydv|-K*p>|BL;^l=@vhBQofpF(oV+i6r6o zS7OrWY7rLNdhO-b&191Lmwb7*yWJ zN`ik&;1dWboq4up6a2sRw#>Fa#8W%bJu(-A$kDZ)zkw5mq~hongg6P)E#h4D%<0RPV&lz4K5R$GVXDi6oPe5Y@#GE zQgj`iwfqc@%JN1Y9TC5aHNOHd!#3eRrb&_*CwU}*x{&l7u3S2sJoXSa8Nw~S`S$FR zKkNqgAJvi27a+kIAzw_Ye~%Z$z9yp;ai2dt7hc8`g)IFq4idXFkf0~f*e8jLWC_*zFl|29;Rf^si!aQc*|t|kX<*5_b1HJEISWdNr?6g+y}3re1?_b zKblh!-+mgI{(K7^R>JDZxrkCR?HnRN5-^j%$KcjH%_Y}bM|4WS#d8!~OV7n2QKo7221ZZHnM z8YJkjE+>00>!vj8ibh1GGqz{i-AVqPZ@oM6?Bj8Kg$!I0^#?nn9TxrpYPwjsOZ0vm zwf$L`q89>}yLb-hx)MY;~QAF z*q3$H9})i~K=_a8^UB%|gR1YLT|cJWvw zyYR=);fPQjOARli%V|+LMhZHt)*~gk!>gs~F{b`AK@WS~^R^JSn;o_tuMb6}^+r0C z*E`P?m~TR0Vt-xzPqgUbv@T)!S=9FDw2i`&3p$tcXrU+HaiKS{*P2D}UIN?(QA(p9 zCO`LDlIy)^m8_G;`|#bzH}GAlKi~Z+srhmE?$7XG&p?PgxMKs~9gl{mR}8&v0#W@} zV)K21tpwW$WK9lO@V~L(e*jbmdnH|m^Lm7waGuCP;k^IOSM+e6_K%UJ-x6uJvCk@a ze@D0>n4=%_{Sa-sn2+QV$Cs#KeiLhU-0}H;HsbbJGk&W-D%bK)rr56n%Yc8eE3FvqA= z#yJiE?=<2Yi|#fLPG$rPOrc~*DAC~$r|~Q)Vfe!|Ys7c9CBn^g_MpY2i(}=_o9Y_k z!89n@0$M_P8Q~hzYpVCNRTsSo(2z2OJIG&BvQ<-p{C1m%*0;msHW^(GxsfJb88{>% z52R@aA~%Z&>=*)N*g{?_;WECmSMo5LIJ!QJQxPA*n@QO)D66ohhq5G&n1_@laV=nf z%95IX8A)8e#8#V9hDcLsgMf|bGCoqc3;FnEd?T)=Fn+{5q7YOLgPQb0g=dZ=GL=_( zTFz0tJLJ2|StB%c#vzGo@xpOP;#$03Y{_?fqEWLPQ(7}J)lzwBts_%$6ka^C zUOZLB=v)~bj~@$g$I;CwC?(5)p-pEEuSe`)-*47f&SwN4&KgURGrRk`d)8RaZ?JU` znfkkIeW6){Hrws1K+T1CEjEAbee&NBwqdS5omv}rcY5RNyUrj~VQm7z2qj#dFzHQt z6W}{=BrbvE2yl#w;Jf{-;TRD+ZxUZV5cPfv7i)5?CO~aUQ36acjhS`8zDB&!Qz+YR zAM_?Zm#`1%Zy|N9Hx786m&7F@?2+p+GKak>3GkW@MrJxTGB_NIIbs1{;3s(UDwwqM zHN2g8GIIL{*7VGeG>&G5^COKzvh3e*<{ycuNaK(!set;6ixu9>R!oD6Ba&u>c8BIP z0>DC=(+B{I(|qG;3m$mRitYAMq4}oIBgoCZ8T8%aO$&Wn0{U>IHsZXZ$N*l*S+w`z zdWNDmfUzKr8Asu5A&s}PriaFi!;#U_Eu)IWC!+DTh{g=Aoh8=rsXU)i*Rd7Tpz$_I zhtdsl+P+<=pxoYW-+}y{!2T|8Ot8-f*dzF>gFU4huuj=U%}EQF$vBw-4Os`_W)Tf{ zg9bCsPvQ6Ndt%uB0ZJ!%R2kU*2Wg>?E#G6`E3;bn@qHK3x0ApQ{R`}dy0GI_t?c0} z_FlTN6Nx0>1h>myazIDsWSvteo%ASkvWhccifQ!ao-XVtb1pRU0d}$vbAK#$Tv3@; zf0f`A!Cxo%8w7uo;0Xe8A&RE=PG)x!z}JzG>k$1hs%8vB4g5WIxvtHh*1mxieGzt7 zopwEbj&ImQAiD~unfg4z^#t!CxPf3l!2yDwC%BH_1%kT>?k4zK1osl$M{o;)>>u35 z)H4L1B)ExSo#0M@YAOhy;XX-ChtI?}j^4qBC4gEna>eT4YC~_v;7-TbW{f}O;tuph zUKfXrl_o-d*e5efD{Z$5ZGbQ17R_q*9%*1#Rtpt<`Helt3+Ztp_%A`~g9Su}wITxR=;OD2Y#ZH6PzPTFq53 ztaA6Cv)X;)4i^{7meeI@xo^AsfphKAfJ;2=Aeka7zdAU>H!nd8XO=P<3}~Z)}3y+1VSLWcEpdj}nN( zXNoEMR$?BWX=Z8n_(@#Lj%!YFLc>q<@_AgSXmPwQZ4a%+_sMkOZkoQ#Kt3l6Z|`Z;{-Iudac6Xc0ai)a=tyMg^u$ujl7Sb zc@%(f;n@Bm3;qWScm2g9{3r2Tk-x4;|5sDu6KAB@U!=2n(>8{2L=sQFX}!4zCjoMA z?n(Zp@q(dJ5RcVcdBkycg~u{*h7p)0{A_qNrCdwnPt|oG+l-;I|XMDkH6O*Osz?EZ>LUmH1tO-_^ly41Xu^o5pWz G+W0>uUdw9$ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_runner_streamed_warnings.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_runner_streamed_warnings.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..322e4dd9e9e82a56573d3a25179f14a76565c967 GIT binary patch literal 3385 zcma)8&2JmW6`$df%Uz13NJ*4r(Uw+JD2b{q#)4H@iDf%+B(!9sB3!Y7%h-!8xuR)P zOJa6qTR{+{b%6@Kq-YSJ0RptgF2m3dnPkL1~PFHSI}E>?y?(ryUo4a#x7EW)36@) ze2dQ&M|<+Eh6d1dQ{c8_sEkwLXH2@7!c=(0T!Y5H*J@R*s8_2r!oiq4H(_``g8%Vl$dlXR2vQ(N z3N4GArv+Oe(b_`~Sf8-Z5>^8P=&p3vYhtyd&~w78g)e#4 zAlP_KPkSU4v3X!M--xZy=Z;b6by2S0MeR0$mP9vWPgoALEnIZc@)9R#V*X*^rD@wD7E&kS=K?pB(nq?CMVlQbA zoO)wT*4JpXWSYxfMdw9(6K08O{n%%Za%ZMjml4WcdUm8vy)rCNi!XI-8Ah9}E( zir$j3AYR?Zo!ePmaiw`I&2XE!YpMc*j`L3EK!7a5kTN1sxk_AK{#1U_e+|qEhohu_(Ss@f7t|b<3u~ zRXA(+K4IC%{Zsu^a?g&2AyC1%#*_7-s2z-bj!WK(dZczd9g7{gN z-3K&UGM4q#DqLyohU3xqpAwx|J6{JqqnAqB3b<72RYPNmufeqkJg^8mSi&w1D%RK7 z`2aQaie*@Ym2JYV8ybZ4Y~>~mIa#W~J!#rj%1>DN=Nn2!n01pEQ;cI4%t^6E&Vj;W zD^U1z-N}-3|1}@THL#B|$GebOJYk=UDA3iAm2Fx1Oit`ek%lz1Ee-7jdp>@5bLQsX z1dkkeE}`ha2OaCa{jR>fXy1N({FBwK`1Fxa=nky=4+B_^?@rA9zPL5<>LG&8pKkDG zcj@Ntk8f=)-Ts;h4@E5Z?_QW=s{Y+qbI(}|vv>FZ#{28bud9o-$j{J48(d~WyJ(q{gxoolx?Be(ZKH@Xu|ZAK;zeL^VwT=2n4 z!zkSI{;gl$`gr!UP;x(}G-4OFV;4RtKfb*c`@#CcUVOL_PjAK3yYY*#s!;4#`kx~` zM*^TtkoZt|v=K^v9!mc6>o+h8j5OrL=W^m-)=k*+N4*OJ_@md(EXcwir$*<~zKwqG zd{W#vi|0oI8|MUu6@ke|FvCgA@Td&-8|lFOoV+pX2mA!dFwPS>u+Zm!((MPFQmFKk zRj5`K)1txa^);>4JEJ)nIH_B}EymnQVmu)59Y*Dri27Yw0Gyo!BIHE~nTk+i;UkT6 zhOlp88gxqVByTviXtXIZ9u^H=9h^j`;ikMvcFr`;IT~wfPTwb-qsa9QM5kdMJ5Z!vGa20&2RR$57wXeJGQ}3)=QX}k<6Pjy_87#- zp^tP0hFh%~S4j?Jtom9yybcA7@l%9;jvjR$px!S~=qogMfX1GpO9v=@fD#92>MJz- z6x}!!kw^MP{HO5-j&I@k=GmnkoZrKKN7S>0dyYKkaqL(?9{&0fKJr4JyaBWP8%_Ex A@c;k- literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_tracing.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_tracing.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39ac0c2ba0d1e7c2a73c0e7c238cc17dfb59f3a4 GIT binary patch literal 29840 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_agent_tracing.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_agent_tracing.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11647b7f181485f9b04ffbd8235312f739ddcaa2 GIT binary patch literal 11423 zcmcIqU2GiJb)K2so&8_#k`yUfvNfV*N9(Hk7bClpVmG#{rl^|8PLnKLY*xHOYNh?t zouMT$%NI!~f}l>^APtbj2`C^i%AzRzP&8?uiX=dT!bpog6vZG90n!9D(1)TwK_Ikg zzjN-L;p}pEMO6-oy>su(nLGE){l0U~J$D+#V$Q(dw{HB+%IU`q{{asy5Xw*UH?sTb1^7Z5s8o$h2nKv$a{SXIs14bG5nld~Mz|-0aoeS2H3f@^_6p zW^Ipd7etZUkLh+vOmTa!ZkI)c+xv8TTFh|!aowI3ySV*?ZqJE%ZtvIa-C_^79o>FR z?B(_W-QFi2=k|hbKOy#W`$^q)!~t#})a?cFB)6Xu)+k2`&*Wp`o+upkMrAdwuNk89b<%c+Itc?^uD(Q-S1tzfsA3=)u~;y4v2saU)}_tMRtyX3pVIqtCFVP~c5JNL`E^9|2)rO%Hz zL!I?*oe5G5==;gvAQ|Vdb9vQw+^C1=9RG^zu)+=rZdN3jHkqU%ONEUhNXXTW%u{nW zHFMARZmBmVgGzm=(Q4JD+mlP(cCR7bpn%Ku%d5>6DCq@us|yJ)*j@LobXQw~Me8fA z?&U_S&M!7w_ZrI}UypiQ?#gxpbHJ*!y_KN!0#`x&m!#~v*O0rdQW3p2;#EGMQR zZJgkULOOeIlLQ;5cSu4Tr##o=R4&AfQ*ppJm0+Bv9@;n!=mDehE}l3+6G&X5k&*^5 zJFn6BB5lf{HjuhWQrbA(CMj*4%3L3(Q6(}?(`=k}s1qBfnTtU|QI3pL{kSXw4|0kM z0*stN5#&90soN23lHRlE^e!sqsUYGqG2~;Y-Jg%F$O&adJY`0tLvjbGApFZGQLG&t zG8-qJi_j~}qwJjnS#K6a4E<(= z|MNJ%4gX|qze|pIAG9S$OfL8fpN6xABlayn0jHdVQ-&LcQ%+t@!71A!9dpX+930lR zbJ_U0Pr!*M@qZ^_8lCcSIOJn+>Gc08{Ct~Tds@5pT%0{X%NakjkrjF7Pa)z@(a&w< z*|~oY*9$(}oBRQ4MXJHk6-6nM_$f&+fBuamG=EB5C$!Nya(7djKbeh^02X7;y#oGB zNBo(>9p#bEy~55tt)2VKfF_n#z(sb(xD+Pog8+-A)hvPe{IPtRE=2xVet?>XsHjr$ z3>CjZ#bGLbm5L)&9HoL>O5%A7S%z(swJ z+|*0BHlC)*h^7=Lf8h)hHK~c30#WT4QL)z9BIPy$eq;gxvjea-y=lM-u`&}d9}%z+ z0@C{i!I260#{@tPS*)BQiN7WZCg9(ageG8->v6c2Vgy8_8Y5sC1gtzX0f#mDPdssg zCZJY}MoJohfUnc|B5kHaZ9zR62@nT z8frSq4*@B!Zd&pM9D|H#dX391SI7dIBUy+DXGi|**d%|pw&nLaU5;j^it=-`rljz>E*gFVYf9_LvB11J z><4paTTRcWDEa&)^z|S38EYSA)=}IHw;sL)&AzC~;ifuyB7!ryP22pkx$l4FMM!KTc(fq?d%V3#3t~SdQXshr-sLvFsryZm(9|VQHVE`$jJjC-#;7|5>Xsjx zx&wNEa%4iXh@UBn9-#>&UZ#<1>VA~Qr>UD4l~9|I;F`Kja82FHM2)5+>dvt5+@VfP z*4c}rSotbEXjw0ABH)706u|{KTHPQPMHWU*B(A(b&~>OdK*g4$oF8(OAz!Q0<6UBk zx_H0+;$9WLHae?VuqsMo1y`rw!TVi2aA?K0k5+78i-ge_WJ(DqqKNgMv@0B!I#Nub zgx?zpksZsHfmqX{=x{=O=Ji8_$={FRD;{aGD`d8?E7WZHB}AHiQ_So!HY)d!OcF>g z_0xT8)m+c?Gr;&~S&!L%23*-iS2!Iu7h}N%(nPc166vB@FvoQ`Ffsp@g+x{s?ULAy z_8zeKv3{1*TRC4vz`0m@D??)td-oXYd68N#^z;1!l5YGFM4=n9R8pOn|p zcL7g$oS#?}NOAUy{G612PAc}CJf+gme_A;FEPLQtmiHR^r8#_mpLKwrl^%N5_uv0# zvBg*zX))H)FL%4n<%SFr$4{KBB~G1QtR|HI&v~xrA;nW~iXfvyPa!`>bSB2^G}vZa zQ#J3~P0vBPxN5G=>3o}`&TwuF8M(kl_NXiUswMYQ|H$zhYx`fNBr#>jaK%yCD(c3$ z>^c{_9d~Uo#dcby*iN$sc+|8`>4o{cQ^@XFavyr8Ox&rpl1fJL5RmJWPtb!uPM58w z_+?qOPon>7NmAghR{tlcc9M!yRGg+_kqVNGr^b%Qvta{yvc8EHz*_O;>n(aVr$`_Q!PiJmYFxxV0Mu=urnq_~jTPKY!z^ND^EMj+Ksc|RxPgCtmrlVCW{CMo9^ zHj2E6zl-Z7KZ`~D=TSo@Tjk%81PdkJCJBnIQO_J(oc%^Y0^(m@DqcYdWX;e|;!XiGN1bd)PeRE^>o^irhE1l&1luy94ziD3o z(IQ&N-Or3PIGhz*`RZ5b?=L^}aF!$rMC3tWth#Gd&=C|U%<+8bVt+scd8BhGx^KC@ z8>AcFt1!dIV;Dn2S~BB8(e` z-;lI-ruCrO5){I9wcz*+I`GJfZLzbIw87753Uu)ML1NaFS8;uSnY4KjG1J~5GdEH^ z$AxH)t?SP$s`Od{0c-+zPGaXQ)lc?Q;HBNSy`QlQOZRQ?5-Do%Q|+9E;Xf@4)Y|?J zPzyt9+Bpj)XjxDo2FgDp3913bIj)0`e1rqnJ7;OMOITj;Xa;u9(#*?@cKP5!#LFaj ziBU6Br^X}op#VC1r$*8o$Q3I7e}LP}Dqrzi)J2mD+DwsSy~a?gSHVibl`(~4^j^mw zq1&Ozxr9Vgl9EVmTpJIjBaa5A1aHmq?gy>oM?mhQ+aQ;YLk_0z34kCAxDn{EqSK(; zg4Cbjx&qyw6MXbKj&?*s(7j6%47z_L2@SeD*W;j5&Om_<{SXBPoWagOSH3toyD87o zB#+xHkmu;CLj|wVu2HQ;#n9}lY(tdR4-F%2b&bsUe-nM^Y(tn#R2Ybm(1FA@Bt&bd zyBZ0c_5bpNv;I8a_BIokiivr2`RgR|Ffsk4Jouwp zyOAh9%&zf<&Vz5+GQBJr+_L=^)X}l)Rz^GyVyZ8YQM(Sw@lfu?p$uF+)tBf8v)!f`u!JNSMl+`qZY=;1Y0cu*wTg< zLlv;yO;QnTsZOw^V;r^=BLkW-vp5J_6(f&t)LEajmAjJ2CL z$L+ZM9pOtwU&HvCwIXcEiP$<7VT*TNKJwsQmoXSQGXf*84Pa!@xMhco+d&wS9Fm`8 z+^`+0-DV*)T;4ml&c;pt6>6brqkWjr{QUz-5Gv#kNkZcTtE3^bruJc!SwpHPh7Yw% zu_LpFFM`xAg?t(BcT^6P7Z?wh;OtRXqag7tLt z(gnKeP;mgon8Xg77EV_`La;s(M^7=1+A3YWg_ohk^?zY#L*e&a7=HUS?5BfVy}rEa zui`_Ey0maA$b{c?@iud?r>?#NJEHPh$le{1?hV<*;x`+e3ex(V+N7pbPhhM2Y6bNb z)e%1M`ntADpBntsjz34M<@7cB)`Z_2eVSG)>9gdW!3#F#_%sh6c-FZS{)#QA&`TlO z;Crnlq#C(@*00&CFv^@-oxg7E>7Wd(uvo%ye^RxlCm?GWOXIcOVS&Q z1;ZQ`^QSMFvlzr`%RNt!@o0CgU@ce~?4B3QvRS~foUqN@6netUGC K&d=*}ko`X~;doqaEtONtcruvUugXj_>Q^|6&+i)HL0FMb|W2 zv-O77)R%Rg`bNW8j`6qIj2oJ+HLc}@I+I*Z@|o0f3g59tx|vzdG_%XuW^Osx%rEC5 zXWH?`M6SwzJ;CFBYFw}HwOtL;_2db{N=mCYdjT+L~D zLFxC;(UbnK-Z;Su3mLsYuio(gF?;mDxUMUS7|z~Tdg@| zBbZusyp^>|TiA|p?26l|w%i~q5AvPd6`Df$mqaIl&v)r%2Y85wm+k4EVH-E~nr_E# z>9%>x*wnn(71PJdx8t{rUxyle4lmw|-_$mB&+_$~`lex9n=#+;V^@t$(>L9J_Yzx4 zJK>wFx}BustHvGEPI;-V^d0Rj&Gm3T<5_of@q6fHsn;^>l%0+w-X#fNhr||1P!AGm z?i+V>{*K+z?aX`TjJ6raIA`NNG@EZ~--3jlo7L>RAFt~;Z+Ur0O!#=tP5lpaTQjvy z10QoU<`uAx1g=cnyN=ts`vY7zW+jk#9umBk+q!ra{qAZe!qq@(jimg<9UW4Fq^OS- zO>lqIiVAXG-_-0$o0Zp3ya+&Gh34*8Xlqcu>7^j;Ij&cMra;%yt95|^9og1{e7#lo z>eWW2z3z3^y=@~fTh*o$#G9mS5Qx`XI4($8w)Gw3{P}Hto#2`z}0yU?oS=^a<>FtiQ;yHF{p~Qm6 zO0~9E+9{+jR9)8*9zWt~x>@JO(?Oh7e8>DOWHvs1taN$ZD>=~}&X&A2r^E{`k>GsE zu6ot8D+H(T&p?L=?{&gJ5uKXmYrmV`)P4Pm_Q%FG^L1?-YYtL$g9=>_oEh^c0+SAi z$8iNWJI@~fEzHk7E}F+z@T(nfcbrzW{uDpmeX4WATWhzD)9>waE8jzf)>v=V<~uin z^tnde^;X)=&V@s`>wn{;ZJgqF=0c?5RK$9#QgW}u>DGe$bKC{ty(C0i1XH01URI;p zVk#75m9~>XoPRPgN0ZFZEaYuY_72YU4o=H~eCTI-2hRxDH~D_#n<~>y58dZC%|pn2 z42@6j z8c2PEr2Hf^?|URgeP-TqD=Nr&5%YeF?2NP~_gxcnZE;EDpbA08bynIfn^`ut6nsELR}JVnEKYK~KLf|>~Q#naS0L(N%gevO)Q)C_~`ll;R)GsP0z3K*(q?&Kr6 z^b5EpG#W5!8c99*v2oY57{MP~@oAm#`yoE!MabNPTO!Nvk`1_s7vgE7a0?vA2*)+( z1mhPD?JfQ5zm2|WTflOHu^hhwPXt&__WDbF%ouVk)_d$t_*W+WZ^m@3Rans6+b7^{ z(f=38>q;>C;ZreYQUmD!9r8HrG^0O5yuj$sdYP>(qyMKkpYx#q;y=;LQx8ah8y!jj zFXV$U>Vc|hl2Qzi<32F}9U}(Fv+reLJOMw3=cE{*0N-oU57AP9#N<#0C^7>i6$2Fe zR%5s)aNU^IV6OiJ30@E8`ab&I6;1W5=qDtl7~p3lr5Ip}`=eGgjTM2JzyN!g0d~12 zW`G&V0OA6)3VzpGT__gFu`*w8i>s@R_I3Cbxv*a$Gb*0N1zmDrhifD`2yzp;MDP(K zw#x=b2iV}$pgR;X!jyCso~PTfzhF_1Z}*81fOvuK7r9D;t?&`~ONeTxgHVN?nL+1< zc)@)VO$2E@`3rzLk!7=NNLcG90qXA)uwQ}X3WG0_M38 zrABtk1%2xPhKZBLtOOFTKqBOVOEj-&HIw0LLdH;Bz>J}|V3JodYDL9}3#QmS?{ZBl z88Qs!mtc+yDj<(=0fLx@k_2fAzTwRyJ1536fp`t#U8~&Vp3e>#~j_6$?9tnmDc_Qu=JkXs%(;xgE0BSw}YQ!a+=|c>$WMpH1qHOHA zB%(x&oq%BOX9B=n{G7OYPQ2$i z8A__+{t3Ip&$9ZSWw>9$eN#64pHK8zhxu8FfoJ{jlYbU7$C0IB%(0Ppx!o>Zu8JVG zaPri0?DUz%a!ffEHPK%0RO)t+R6edPz5vKPkZaG3Q`UE^x?4i!psa7qs7z}~9^tGg zaL~#`IIE$?iz@7g>uaHLHJBZ{S}LsrC^o zrA{JOWM>zy2xSgAZygu&*DiY2R>FpZA37!1p7fHOJ4mVAL83R>cK?qk2(=o@8>XVn zLE6i1IY(dn+p+TjFZ?a%*={{H#d2V<0Oo}e_~mMjeB3Ceqh`6oe^JR;W<4)PXukU?RB zY6lI+bAm+Gz0q2!w^5&II4B7`f&&xeBmi^8j=I%2vwzeNwsC&LNmO2_xHH>5NA8D zPa}g3XS;#(qiw>=tW;2oho~@-`?qBcC|c~=^H8*&n$rlWHAp4!TsVrp33WsO3^k1V z7Ss`mBXH*;GOKA+p%Sc+IHMFY%tL=b8ClLl|B!O9Di2Kx8A@=bn(T$lR8x-?lCrl@ zM9$p8x+#TBVmyHmndj4|kV#g^lqYl0uv(Eq#-WgjO)FGLs@QP8b}$Efkxq;%(1_>g zY>Aq~XjE}UbW^D@I>lMkN7c&hbWa)OAyrdmQKDK($lg?09$Hj+AG233%&?-0RMbda zTadm-U8C6Wug0qDh{DPrjV`RPawhIk;Wwb1vi!oOmJw2ZB*n#-5mMh?)ZQE{$9%6Z z?@KO~36n2jkQ#u&yTq@^`w|Q?1Q3Qnw#UhM3=*L$a+Dkj<%&@5SGit*(~BfU5P-w7 zL{bV46#q^z9O#?iAoIUT94{dM%k%2X|Augo&eaGwY^wsMgu_du!IUf&pQYvzDiquI zD>OVRL`0KjI6%k8*xFxLg@!IIt8lD;KSh8WxbtX6mmGSPp87|KsjuOR(W?Ka52O12 zL#lof35bzZ#u18sdx)Zcr%%yStmr6VFy9X<`a8tiLlix?m8a+dDoiyCr0r>R6TRZcm{do2ZsO|G>)l`5`Lh z^{Lc&?mPz$8^fLM_i ztQ(Xr2z7&MEdBOW)=;E9#T|+M6wSzb!>;UUd9r6aKvy9yiO|csxi~Dy!?* zd*oCq0;L1gVZ$wlytycttH@1?^D@VX{e$zuxi0F+F{GkHK|&pqTa&2J8CdeVz+%;R zWdehL7xu-ec8NW(*iK#HEv|A)RGycJ%t8knvmLn)Dww2$1fq^;W1ErN7QCs6L(9Ny zR>f7`2*|sNcn1O(6}e8rIX%t{a3m+lKW0xs5H||~n4lk`$75mNh4)7!&tYck4d()> vhIDltZx{OkVDU);AOS7_lH!uUf&dpF!S4@HB0-8W@w6grz+SI_6$lC3 zg=QC$NXHiA`)}>bxD<&q{^z}l*}Cb?Hp9nj-WK?V(uV?I8T)gdYFg19jCm^3!HMwH|S^n z!E#nUSivd=D_P}WfCUDtSk+)Ps~)UjHG{RRcCe1s4c4=I9@lkh-Qaq*ez1Wx3^uaH z!6w!;xPfgL+{iW#HnZlz7S_V)+^04T23c^hm9=tz*{Qa{cGe!08l*N!^)yMUceJV3 zDY#&pIo1cPf3(rWZW;9soi-S@-a}_Bx8b@AtyBYQm0I1es5KGC zXgP4TYMolIuIqQI>w&2NreU-auu*MNH}pHWd!yP+?k?<#ZsqAd0!%e9+tg6MtZs*|2EH9U2THl4-=%f}TMKMh?NWCly>9sH z;P2sac10Xr-d=~`M~#en)ZWq1=yv>h)V_$rg#ZE7-KrzvR`)bX&nmq-`^KB?xJ!LK zn3`=;H>&--X7{4(qGtE0`*{r<0LH4>gX$r(W)G={Z8duYbfRXDs>jTlwzkxZXYp@eak z2xj8XC6W{ze38Ycrx72JrKZ_rJd>EZ*ujI$QY2nVjDeTnGc!q%y~s4}JD*BDhXj!f zOJrtPGR+H}cxf7CMw(BiQd7s`Q&UgOWKb6;Gl>h~o75%Fgk;TmBzZC9*ZiV>j!jLD zJ*QO*&p>MC>8XT3`E(SEPiU2v`*ZQkdBcAyadDa@(rHbneLVF-LaP$As>!!jZemhX zQwHsms3pUFE`3qN2uXIPOlThx>j){eK9#^&}tsjRc^Q_ zzUCd8NgkuNozSXy@&p$BsJ|McU{>Xh>FS2R z_z(uy@@B~A>(Z#iWDHgqq?l86Fh$*Rw29sJlA2y0cPEoHm zv2_#Wp=K>`IyEsdnVc}%;O17Ob}4!!_+EIXy9rB+F zPNI()-JP_-3kf#)%;cD$NB`$~BCJ(geKIC`&zL^E@b)7&4b#gl0Iy43QdFAa0R)Dt zrqHV5nUJo^XRbOl7cYQT6I+ok0+n=P>KWFI7->p47}Sn*MbinE?qV0Z#!};nuGDlQ z8K3MFx~X&eBGgr~ix-u{*qE+?VsWkl!qXQuPb@YG#TScd4Mi0fHbO$kY&igMSz6k& z_loij-%{JI1<$5sprkRA2YATM`Ng~n{7%SM9cMJ>h4|Q2@W(a-nI@7T+XATh6Ui4Q zSt>~lp=~J6-4@sh>Sb#3%hLOSI!5Ku+WZGt66z5G z;IpKHvvSqRT0yG0N&6?qSsOgX?6Lhsv4SrvB~lh?N(Qa#DSy@Xif_@=nD;d1nmTTH zI+p^qi-E2Az}B2+tEhPSB~8Alxu2fNWKzj9WA@^@kqWPZqj0RP3WjT<&WJPOu&M%G z0>a}Oqe11WgEPtwBHgPB7bJEVZgzwK<(8&$1o3duV#ndr>Wdqbr`1o?)QO7to!y;1 zJJ**zlWT%(M@0MDTZ`n6Gj|w{wK7N3*<#kp$N3N$W=4<}ln^T`@ZpkBf1s#_P)tWOwJ9 zb}V>0gjnk80x5~|a~}4>vo^6TDm{%bFJk&6v?R&n3SIIgAXgTiB2kA+*8vo_M8|^X zkx!vBDHkwdMiUzPnRq<#W+ETl|EYm=oBQ+Y(q;jCm2 z8HW*|k6T=wYp%=;CS%a>n~^DdmWprZ_5u(qMUGM-jgj62P%LtN-#I?F`P-+m2W<)u z>9>KD4jI3#sv$DRP&`V$aXSr}U6OEC#bKotc_U zCXy)xm&Ib^sWCX5=?r6Ak(A~k^DDtJ7g-l&mLi;UGMQn0y}%0HO(H+wrA7XZQp!iS(05%CP~vRx5M?w z?6EsY;*nb(+12}6`W7g9XT@vo824QpxI@TWZrK%?9V1jED_(Qs6cVA3qw=hif{w~r z{XNGMIV!)&X^+a+Iq^}Ms5@tCiMTUcM+tP^0csoJJ6_|&9U_~KERWJbA>Ff2X4}8{ zd5YCN7ok|)gpSPC%_g$P=4!JizM0B5hI#3(MLtBh802}yjiKVq=b_cLcB{)6(F#oI zn2lo zbheEEb-)4>fW~EB2n7x@f7lM9D`j7gihVwlL>b3J-%Cg~{4j?%(V|ffNUKVTC zOYS}+Ru1swB|g~1n7QvDmG|1M^r&~n}c!V zrM0y5+3cY6NP^GkSMp`EVRWv7ZL(!ItT10bR-3IfVK|5MO+;|0QbDO2sm=7cJ*hi$ zA)~O-guU_{MQR0D$z7qv@)7$6s2J&*0yRgC`7zpUrPac@I!AduN`UlDfkNcwN@K}p zBby~j^rUiEGGwz_K4Pzeq_i?QijcJnpruSEB+kMpk`XKvs|{|ml;(g8m*){Ny&hmC z2V}bFI-+^gr_p~%dEKr5@abV`Hpr045F2-E2-B1i4Z+4i{3(dlGbTH{SJMz}^b~Hx zlpbddL*~W1b7V7RbdJbuDB#Eq1n($cfMf0QbuZ=&mpfX{Wz-AUG(bVu;S>1MEk@?c zC8BQwSSff#28=F)p&c?~pvQNEsyKbIuq_g^Gy)h2L9uOcnQ#P1IhnS>=!3Eivh9{_ zFsst!x;HCc*V?)7&CQl=kUhp{-@#uI@}z@r?M-x{D~4BH@0 z>2zi@MYh2#C+-m0bj3>IrF*l^ zW*bn&U8)nS3(f|Q0Pl!4N+!Q4w zha7Nz8&38ef&NW${|f@VGso}8dnH|} zNA+|$Qi-U0?+Hmty=cT4|2;_>sWeE3stlL)#>NM4v@9a4zTu~^SgCWN`bTOEnju=U zD|*_@?}yZ;<5*X)<+84b7~VFN+!&9Ivc*AXi`DS6V?80GOcp1lsa-}Mt5UiRO0_)f z>_m$t@fB08jCyv;+~*wuPvWtqwb8&?Z*b-{(lXu&SuHytCtU~R=@*_@D&1zOvkzMg zb@v%;F;z}|C+g$eqXRXAQl>W29xgMemsY!bF)zddzX=7io?Zs=M@+NAU^0 zcw-Cpr=;=qn)b-pH_Fh?jd6Uxx3QSlAB8NFL`jCE@j=C^Pf%{4E1rxMuka@DOlCdb7X_6AXx5kYKnT&t&#tSDJ5 z9e*Z~xfmNepBTgT5jU;S&ZN&UQkPfdzjYiqa8MvPE7qZVTSiokcN?A{D^ z+KBntAClis1rYVuZTCd%o?XHt((ki-_SngK>>-gfjn!oO8xLQK*ADR7C{ckZH{XAi z*7sh-Aj2^d*R-vw8Kwc!*YKDA4NOqTFt`(usy1EQkPjTVeB!;TZA<>ZQpLulK>box z-BL?%DbRGgqO96|`Q)7{X;bH7%aONRj$Ge1-}2OA!&5n>{)4jW*S6kh+P+w}BVV@T z{lNNHzxK-4a-Ng9wn(n;M6UHDxm!=>11A?f`tNPe$tzzoeZsK-+)E}dZ}})dC!Xgi znM9`JhUcW2??A5mSgv&-=NSOZ2L={B`tNPe0HtFg;9dX@nd-Q_<)Z+dc%G+Z5}6L* zmx4QT%8sl*@87Yg=)bp>9XbC-(<>Ycg~+_ki=eE(h#X!d^W%WjcuxK(O@qbPy9am6 zzatMul;0hc;nZAga^idj^VZ`C#jO52c!_!LpTl*N1m|W21)1~v_lV*z2#*=6bdx5T zH_HG(VVk}GH(=O*7ifC1_W?9zEW@TWdF-YKzMC}LL@a%({v-I<2Lh$fyvfHn{(W*$ zI-2Xn6q_1HG@62AqWzb66YXfE2Tdq^4cxvP~6ReaHN*SV_|%bb^5QSGrX z=DKa1l%{qX2p^%=KtQPTOXavMyic+*wQlR^F|n-m6sX08gGowKFnR6 zn7clS)+#agM{D^%R%?|%Y^_hCwMJ-U<`1`IumvC1DYoFlI`xd0^Sq%ZgUWitI^AYi zo3KuO1=gu|gyuv1fpywqxD9%97K&jB@)9({tvBSQTCIk4`f+-A%|n^_BxHtLrzP4f z)%~!=pusW%NC6om!*O-9}0$M#|dZU$u>tbq{=`G}l6mvT^IwDqHEX z&y0nUvd%VUm8Q0=(|Xf7U8k-$tyA|J#y@i%sBrw7-aFy5S*Hz`o-bvc265gazGCA- zhjpOCW~VaPTG$m76lP8EX`I=hPfLk$Zm1U6sN6seYvp1AUl^(E8Wbw)An@-1Lh_|u zy3JV1c(m;fC@>+p0mS|TrSb;^z6WrI(R2t2_WXA_0wVgH6w`W*tlOMoU- z?B5gkPXzvuz<(z2eFE13Le>0ZgV_J{M}+-j0zV+|Cj|bKz@HJw5y%thBS7*X4AA;w z1C&=5`>#Y`nw~3cKQ>R{HwY{cc!$6ufwu|pvM!P9U4Xk-ZwE_RZ=$L0$$a}OBu%T- z=_{BC8|K^QYsq}z_~jEz{@O+Vro4YsPT7|2B#w8Os{Y(C>@lgPLj8L2r#MV zDLs?ObO3!1M(WYGTaI4eGv9JZHZzpfh9U_Z}qU)c=+b!f{Z3T^hk#JWt9|>5E{+up)!v%#LZ-@)JB1pFa~4 z-%EB?(Ol^n?E4g(K+AZc662Z+Wx;x)*x8D;E$sBiQWSP{QgXq|V(;cwxXsF}+j#NKyvEs#4+-%T0v`bsZf(*kjHcrC zq#3&aOx_EE-$(rA4FIG~Z@DFJ#jEA7lrMTVHt{;VC?OaEdh&fX%=KIG;QShxFXDH;G$*8ZQJwlq_U=PA!?iE_WI|Ba_i7%5$ zW^rzw&6(|;bBnFqI`f-aany?O^6bkNxXsh5N)Q{(j$EamR%)qVBd+ciX7Dmjre9QgoPNbtBe? zh}HdL2|(O0C-1M`fNpz4<>-*h_mK#}T{>@u?u4VPKQvINF=SQADBY zcyE4$4kf0hf^=5*(^hZnf1!+Q8DOR2GR~D*r)f>q_16^Ze-j81DC`=af?i_(hqC>@ z0EPO8%el1=)@+Kf(J{GNB6itN2}sl#s3(6aT7@|&I|ZOfaqv!QDoa~^8^wmo;2O7# z67Uf45+F^$eB>$zuy@@`LJ}Yuw7TxV&!g*7F$%h_x#GNSv!S(D?Aeg4JsY%N1G_k6 zYu}ct(C!VjO!f3D*xf;^>HJZ>sxRtLy;wiPoP&0EVC}^@MPnm>M*N0rxLd90cVGtx zt=01fdkEw%d<6#9H9|U*KgL`k=^jo@nXA(ywJXBQMo8oGN0qR4Cu8kyoq-u@FkIG~ zzM)5zXj#p_+g`y|vAEB|DKT*Fijwj1iSL(fJ1z(&0 z>(t+wc6|`*eeRO!RN2z0GSjJ6UCEkgb+m?3O4deelYwZ!NbNDl`14rpN1EapYBOBc z8#`5MqLq9N<4+3KFwpwq8KNPp=qXz52Zy#?jO=Z3+U#qbkT`k_vUpQL?Cm1d4ok(R zH4U|XWG$t{WM;KeM$MIbvviOXTdb0$vtprC^{Y~wQm`szxzWz6QdSt0>bkIt?=7fP z*Q*WDI{oWKebhJ9X|Ge^j@BFTa00s0;Q28*j{aiH^Dc68t`>6Bc&_>!uA(vbIMuDG zO@3aI)3l~i-izdzYhFez@-@1kQRhRYCC7{f$!W6HcuDGWX1`yJGFol4>#CGB2Bo?| zmz;IDE225NPM4hZcafa+M!cY1Qo;tm)*HIir?wbhHg-x}IvdnYYB1WM=g~OQZP44w z+hgFYH_~b}@-^Q0wg&4p8NeTXWAo#$9DkMgtH56s{;Kg;o2=nFI%1_}xb90wmrCc8 z*3qrQ4|TL;Y1=jRG*h$x!_v`F($Q@+LwKm8qo%f9ljNAO?nOsOAL{6jrK6)|rDnfs z3t#2rF!j@@>$H??iZ&Uo$iLJD4Q+mxS#Eo@2|Z!+@ZVVdz&6&D9B-omV~kO^Jn;E~ zxkuOF;-2OUW-Q1euIJOg)NN27QMW}m=%eYzHH@Yk4GxXBF`A-&%Z5sfaSvO9Mg_VTAKSrOpeP4Yd^uYUsd4a{6`h*z^EfM-T_KAlr@mXmJH;op_ zfGQ`Oqk(8MUmLNH7t{A*-SY8bJH@}B-m&V~(roAtb%(CSThtDnS}LD`siX??6&*8sGmzybF|seQT#Mm(4f7` z?4t%!B&$1zf7_BJwflkVPswP1qipvyr!Zqde|E1bx8^>fRVmGNF11JBQM*yyrADF~ zbqNoyLBfLuXU1EXaI*NDlm4i8XphkX);slEe5Pn`t6TTb^V@k_^$!20t*!bVcw3pf zv)0sBW-PQ-->NNOw$*2J{<>S2iwCdye97jKeTF1Ny(9Y#x3La|o^JNw$K>%H*iErV z?O#(X-^<$NUNhhOS@m8K8?D}tc7M>T-lkODzbvYI*Ho%|sXMF9?x*fn#75oiUA4zp zqw>D6!dEI=)}W>|W`9kkx)+^dwb}jXlohd2 z%0sKl?I)vCTGhk4m2(6d=Ao6-9BoyP@>N2kUXDGi7qb4;0qhCT>qgWFO^gcGPw&MZ zKF;moNB>^f!-k{`ki4XRIy7Kta_gOJ!s>xP+Ju!pbKFV&z|dvm*vqFrwrbBe*N<1F zY%;Xvs+4AFR;4s|52z>fxy*yGxad1;6V5SycS6Ehj_NZn^P2`v;AUUj)IS2jQ51Sl?jm`)bHKvC)l-1vKYWBB^;9{v`>|A4U3)C=L5B1g;bK9)PAy zU+ms_$wL`*BPZ_f!6A_+Ii}ypvl}iw)9#2~pl*~=l!#nLC`wndOg)skpKo9^3dA#u zaM;V?ZVvki_VkEC^&mIyk1z+ydnsVM0HKF&Kp@)PDDdg~t3JLhdqw3Ty`hg&0V`7_ z_HkL4V-iqY*U*hRn@sb2AkdCnULZ`ko1t5@k$H?p+y_D8Wsj=g7lG=5kal9~Kv5%? z>=#7zp?ZMq1Hp0B9ZsJYi9TsvafFBWj>7Xo$n0m`TftirBB!%J&Eyx?OvG^7=>jtQ zeM+6nTo;OWsn^zWpcaw?hb}k}SuWtpdrB-R&&ha!v7XN|BI@`tfq_6H-UK~z5!sr zxqvHslWskz0e+{TaVg2&-+cSNwwsS?db{Hv!O*{vF-B78%q5hq_t)4Vm@#dDU?^g_6u_-=Jw{x z`z{~W(2$AhF&T=4sIm$MnM^DK0%72gHwYMl+y4~8M=;& z5GuotW2XE1^Jauq9Vpnl%D}5Hzw&ZU8TiJoD=*K}3CMwLHwStD=0#;-Ze+3ZV7~Jp zP;b=cIuFi=4#9byNOGY=*Sqtf0Z@tGJHXL7Nect_0wrzY@&!IK8da_y3lxSAQLnr# z0_JT=!=3Fm>FtgidZFw%xRK5qwI&W3*n9$wl7mrO5ey%qf@u*#We6QI-PcQ(!A!;O zK*8Sk*Dd-t=KULI&*GqIR?hp|=oXwr&UOpVJl$~9Ca$%)VRM@o-2j2b14V&8}05W^r^yLeDCK^P@ zY9(Lc?D?GS;N(0ViUePDaPo%DZ61jPGJD?i;S8nCXQDw=g0GG&`nPp(7Ux_!Uxc|~ zbDIZYfy_cRSchnBJ`)Whv{$JrQPF?%$`*cj*1{lYLD_O; zpGlIpdwd8bt5;>tB2BMtkqPzb~isE&BTw0d2p$()W$MCZfX4_}af3p*gEBj_U^ZrK`75z6aP{MzMJi-b5Y^OxrM!7y%O;VxoY7)lERtBTOo1ZoIyYmA#z+|*3M1+w^oo3ut!j}$liMOU)h_mOkGJYq;WJ>ennnkQc;a>w?O7MR-Bo6Ru zME_R=xQWLtKAN!?T@J2!_KSaZbdZfA6hA4wj3t*_PFa?3I~2L^!*WTMe_2|P*8NDT z_|Q=QaGSd}dI<-xOyI*hNj@h3g|y?2J0wS5JAVh4k3{Ya%Yd^7xaZceEIB;? zc*8GlSd{DXa@~?#COi#!xnW6Z`k}XO_L1z+T+g*<^4z3Iq% z_vH?p%Xx->SiWxd(W~J%yRXan?MMIMOfLFV?h7ONfze#(i}~^|EtQ9Extw0#ZI9$@ znEk?B`r5I%XY=0vHwWKpe=Ge~(_0m}eNW{)=ic?!&aTVunrqK{!?~TfnDF?!-p1Kq z$a_0-;lnx4ksp>f%$}cXoqHY`J)bW>xm3Q{Ot%g(Cv5SMQr;WPcH}%=w>DL|+{><7 zhvU|^dbwk%JaEfN@8Fi~*j(?m+G}Ii8m=D7ZQYv>?&BfvyS&RTP%MWK2mgJ^QGaVY zytj7VLsgxfYrXbZ-n&0{a5(4r{Ku}U7p|q}E+Dx#FTM3tE;{_yx$M{@_wlN}O)h>&`Tqm26C?@% literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_computer_action.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_computer_action.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a99b7e8fb3d7a826acd02538b1a28ad699a66815 GIT binary patch literal 16515 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_doc_parsing.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_doc_parsing.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3cc666fd1776ee23b0de8be4344e586705e55a09 GIT binary patch literal 12154 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e75946118c63aa2b3adc08a35e85a2a6ed8f2bd1 GIT binary patch literal 5670 zcmd5=O>8Sy6`p%%Y)|}`#Cd%bTEL68JVTqb@2Bq(nv&P5Dq;~mmBPZC(Zu)Kao*Tt z?j5&Ha5m%#A%xHqi>|Urx@uUlMnbAsAyzD^E=C9;Ayw**bry(o&Uhx*iJi9K1(VG2 znRBn_eE0m^bFTeLrD(zR=X-zZd;r`(@xiuKeD;H!@*hjW-MDCH(EE$r#D1fAFNL;Z1lFFNwD2mb}OOy_IvntA>^2ll~ z3MQ&YOe~5en6or~S{5~US{^^Gh->gv6YNJ;kZO3^fqoL}zIDTtsiAe<4%-k+9A%)dOGoJsAN|n)O0<(Ic3y+fUY-n0vYz z!G!G~^d~1?X;4!c@j6oUiE1WntO}_`)k4ORl`&J|YG$GSus7IS0a;?8B~=Fg3mNEL z(mcaJ>);N*cU2ILXUvKtT+w~A#|V%PKfu!(qHT=cwND24&@WzYNR&=1BV zHtZvDj&_OIhGbVu@W!~*3Vf|$|JezJs)6Ku|4cY^jH&{832vPS;<8$WGm>3O0Nfs5 z$BC~1iJ(*ne#n^>v5eyxXCGR}4nCdW4Eq>TB;kbxiC}MihVkBk8?Vhqp7vYXQz-ce z04-uB?)VAZ*s0)QnbV%Hq!&Kig7;}$m&Jzh~<3UL3UMcsz{j$ko| z?7$jXyZiu_b-+h##2@kldt~chz%m@cM|PVD8|7_&;s__o9p_K1cP#w_7%z;h6QfaYN2bQ$ouXaPoxqudE-|2Z@qj&;D`wn4wOTLL7m ziR3ZUHJG1bS(t+5V;E1c{GIg93riWWK;t6HNBjb!4-G_>%ZLE;Zi?wKQ_=iTfzK+2 z&#Du+xZX5yP4U`eu6c%wtMdGCP4yhte>Lz-@f_g!t%2ugu6Sm7&(>`N)im$f`bLJz zRyXH|3cOb}ytg>vJzM|Vz%|W#w*JV#^_ThKn(94A|Hi;G&3lf1kl}GubFO%1c`v7* zF;GqOo})iCQ2lFusAw}^+M><-l%4U7jZ?P&{IfR(dp8@p__Hxn->4(^VneTDxhfvi zHxkL6hK_CM$>E4%!#I+gNba0fp>9bP(f97+tij&h{pE=+2+t#+?ba3Agw!q79bkEw zPe8HT;>VK<1neI4h65N7E3Nl)*D!$8nfuM>pZen34!yx?iSE%6O; z^*)&p*8YZ4p^8_$cBp!OGlXpIFxi}gz4-+XTnDgm2@oaNlDKi7pSB^ zfN?6`kjl<6B>cFfBM+sVWOi24b5zoERMPWN5k^a+yiw9QlynF5v)wWvaZMyp9=`_j zQ!McIOIZE};|Z2eN$Qh1P4-$HT&2_0)2z%{B#S4k;|{jvHslJ6jS9?1(xR*|eB zc@YUV#&lv)w~)i5A0FdCQ3B^>l-@;h56O3ttRu;OsYp&g*C@;(i6g(?l3vo2cVObU zSItAOijl51KsFKC6HV$LD6%6)F^z4ieW^|T<=AT%+tf6se@H&VHuY`tSlSwnnNHIZ7zcW8nX-v~G z?90XU{*^IJ;f!avW{opsi1CVnjb|&0)>naVti*0wq9}*ruX;&Q zTTSW&?C7gXzN@;Cj2ALdTo}tJ1Dn^AAMohM0IUI?ZtcjPpA^jNphkTK308%vXGJ{Y rQCXl2QV7h*!Si3V+*gt#^s9IP)wm5jXV(^ME45Pf>#kc}uDbsL)B8V( literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_doc_parsing.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9826a1ea06f053d832dd6b777a4bd6dbe626fed9 GIT binary patch literal 5665 zcmd5=O>8UG6`ngYwkQ5e;yhj{fAs?BbKxcMes~ZF}HPPi0S8h zg=T>n!Vz<#$gKm5+dOyAZkDv3JTE{`S?h859Q0IvXYe8~-7|P;-)vTSnOE)^&3Vpv z^?>ntz5sI;My*9&gVy4xwZtz$tH#+IM&p6+0Hv|fVDZBp5eVr;!fg+NmdnG|peKUJ zi@ITOY{krr*=C%VA{xk`W#Ut#3wZ|jYj7)EJ-yi6y;rQm_2yPr)#0CB+783zB6Z)E z9kogvPV!b^o~XCGQbl!7c1ZGaHoTb(E71xAu8q@#*9npKvfFhj)WDBuG&aM#$HuWK z5tz&&aZ+-!w(^z|Qmx3|N-N}ICG3m9>t6Lh6Op>w--))uU?mbNQbbWFZr_ut8+4ZY zJMnzFCbtuY9bcdbMVcwg67WArOJ5SqDO$SYNv|g&z(}*MW*0Rhqc%d$JVZ^IQ(l3e`Q?D$f%9L{%M+(LuiL050fM_#dR8cS-UT z1+9WPyxv9rGnz5Yg>XfyiBqrssHL46U-h@T!Fx|;Yjb1;*9Q04g}%2Ax4HpD(4NpV zg(Yl};|wC-PczA^DQLlh5V!dJj`r1roBy1j)IWP zITnc6^F6_cQ>JM%6L>Bt@u@M-!H!e!X;`tXgx{VNrjlIAAy)$I*I>1Y6P0$$ppWHr z;HT+i_vfe+tL3ZjSIO=xnM%rID%ss=%*BrD2C#{^Zd`QTUdRVN@@3b3d*Jz_5exQ_ zI7hogY(cQg1$bjzYWbd0u>Wj@T-KoHbn8qwG>ohQc@}P!2jZ|=#bnfXE&y84=4kGvlm%S@99cDP?dQ!OI-3@r3!gU#}NPkr(ABI+gNnGF9qOb$^KU&lci0=p%v(NU7 zp|Nf5!Ls(uAsd=^%{^;qsrO+SHaCY>n{f;K+vbtY?I?GcKQi7i)bC-uFf@*sd>uFk zxxj>RzSw*v1 zbqtrO)-_xctajg2W`@g@<=NqyXgO1Tqv4rgIl%LghUZ|Wc&1s;Qf&>@Bw4SX#)bLEQo~`a=cx>66DV}N8%c;jT zRFkY{t8X<_|Ct>s+RPU=X!Aa1C+6DPG24Cmi7Wk`YYkQW(}=0B)scIqp_VaS75D0E zNzcuOiY=(g;dElbIFf5fZk|*jZwVREbGL9-f9KZj;#d`gM-h;A{Q_xRsFv*ZF+Ge4 zJz1J?SUI_booCBxs)|9(>IGt`gmpDxYE0T`R%^^l6VeT-E4EP=ad90^DVWDR885mm z&-Yy^`mz=F;H@Gq!?3$K==xDNP;uT5VTOtybk$Zk@HtI&JASz7`BWpsbFT8*0^bl< zZ<7jP?Z1^Wl<|_=4rR}aT=8Dt_X0W=#w87PTsyyzQ|!li`76L4mk?26ooDsN4ZU$a zX(R%l(;LrgB?y&;OUdd7Q3x80L<^1eM-ZeQ1Y$6U{e}INeQY}HKK!M>@@PZ|p%j$5aj*3>&g-NT-m}ts$gvNa^+v!`X()#2ng`(lNCsDLsE!pp zVu81x!}1RpPq2JTa%Y950$9?NuIUC4eXb#@oJRyRWQqw=`fzrrXj`dnB=z4TU|IPX z67)Y=NAft5CxD#U6Yyjt@gOIEjpPavI@UITYgCh}l0p#cWBD5-Pa%04$yFrFNLG+M zg9Hm>I$l=isk8vOp-a_&`k{6JyBFR2dB&VNCkr%Xto7ChTm^i9c z^H8gzr^_|y8}sb3BJ~UM?1+(1W0`85D^ouhS?z3@n)>t~$YxlkzDX8KOT&?WQ{S{Q zHSy`mWh$npvt{Zf4U3*GQ!Ibau>5PLSSnM?)Xy|Tda4{D0-yF%OyJY+%nnuR({v2` zaX!6!p-+=L;~6eFDgA#wEpJZn*#+`h?F<>BzoKK~*^0dNW#Ai2v6H4KilO+IZj#iN zlRN=C`mz-7%5Eg$xvVQLjQS`7o0pT1cJwg-bAYQ_TcYPB3G*_Dk-tEKSz+Q?5zly( p7AS%w0@Gsf^oy4BQgVcT84sWew}B_E+I(%PR;oVgIMv0f^FMMIJl6mK literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_extension_filters.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_extension_filters.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0bc4e2f4c3e0fdd007c95f0fc45357672a54c36 GIT binary patch literal 18744 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 RfCH`K;G4^XU literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_extension_filters.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_extension_filters.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b06885b0787723877b212d4671a722a6f4ab1636 GIT binary patch literal 8023 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 z z$!m92%5h-8tJP9}z2?&IAo)VcB)PPh>iJbGwgEVyinJ7L3EpLARDA@`tC1S9WtuL`&WrvyyIXU&jS!&yoH z56?>Kf&-15uW2st9xrjinMNuSTq?4YouGs+4ap8n!b6udB#VxRBdJLC9B`;87FUl7 z^ljroId~}=crh9Y2cknSgf0c0R}CjH!C_6UB$tp2IC#kr(g{h^piUr459@+w7>;S` zf}@sv?o}MqBo9M7l|u+FA{asN0suU}sjFLl5u8^F!7_13j?uE)<%^QAU2+giJAqsa_(kzfwX_UR5+t zk<|Kq3024=)}9}aLf$xL^h*d(`0lHdQEo|%-S?tPMz9M(4}#qY_8{1cU>}102%-pL z2qqA`jNlanuOhetz<6v^mwHAME#|Im@^3H#dWh534*4oZe5ev%v<^s?>Fya3RF7{R zvC6;VrzfKx-}CSv-3026>XA&n3D52)R#oCTZPD^MRMC1)SJ8Sd-t!zHdY)6n=aPc@ zTR;-fsnRjvUKE}~B`p=F=spM_N?I6_Ac<2daHEnIL#qiQNl#kJ#zeuec0ol_#I@EX zPO#Z&#))%wgxuz2a#P=-N2S`Uhad3f94r-2Y>v` zGYRoL@!jQw=XpCNASm$N;e-PF1I)_%2!7?b&E3xjM_eNyVGxssJ-{$VxrDx{UPAI~ zjl_s#@dy%%k&u!c;FDDB78C63hzW8I1_XFwX8ayg>|PV>{HrF&w>>7vxcHPQ_Ou!H z%rjvfuVn|l z@9HJ@A32(5cPAXXRnpx_$9~1JpZX5;(fg5OD(U{4B?-rImGpSh@wDQ2n)*(iRY{(H z`(nc3C!Y7jnk5^_fd7_QbH~c(*FL}uz0U#ol^t479t(Ydgm<-o2N=M}mMB>XiTrDg zCJoDCs@Nt%yOcrOY=&*wVTRq+Yld~~HpT8Y!yf1}!yY+ihIJe_#XfC@J$2R$>+qXk zp#n&`KEQGy;Eq-zq=a=magm~hv$Iz0b;iioG=GrXu-6$&i2H~qDIQWpI3NJp=1qEI zz0RSVhd!IV&KUXR$UCzRYW|=JGA`~j#U3)j-ZVo#5xtI^t6Dd6Nbzh2*1*No@%GHs zD6T!AVYmE59aP6_niW6+nCbr3#)aCc?bn}C#M-2w{uU^T`Ke^dKMZK=#1{h(#PxNWNK5zlr#`}H5Lj- z;W<&+f%x*^NGKeOwrRUM)X53qu~Qy3P@V_}h9cn@M5g;A%YnMe~w@d!90S$MeuhB{vN>(5X2EIAb1}E>If0t z9Kx+;9E6f`?7kEko)`=5mCK-j;KxPJ0$j54JpU7p`wF+nmHmWU|5MKO6R!Qo+@_y% zT|eeJ?%J(<(cOZ4zIv&W=ZoT-cFvb5oA$ujBYe?)hl8)aQ=ZQkUxy=1;5Hq7A8&Un zH}Hq~rDHs|`v89zC>y8o%o4tFW`f-2`pE6=LvXSR-+0&20Mz%N;w`*5<-5VwbQRGAXZ1WO__nRg3{x%%$g8sX;^?ci% z6Y!?R@%0^ZCCd73m`3q@1G(L7z?>A{b%=b$gW58_><$OuZpTrcueg8K0wpjdU2nY4 nfn%wF=WA!mF=X-d%- literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_function_schema.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_function_schema.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e044c3ef183faf428265d1dc660804a927db5ff GIT binary patch literal 65519 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

D}lMlD&(~S8tRrOj9{;Z0Ew=$@R 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 HohtkvN#jHg literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1078b4227d51510fbfd96dd4c9e6e643cc0f3096 GIT binary patch literal 24588 zcmeG^ZEPIJb-TBFAH2O!9=~al$D>I3B#YFE6lKa5Wqm}BNr$$$x<(C&98TnkqD|g0 zvqwjyp_Gl&SPI%&P9lU!-NI=b)GE+c1^UAc@}obDq=}K>mM8Yut&JFs`$Is0lA=;K zMho=4+0Q#2B~q0fJK*5%?YuW{W@qQ~&6|0%?)ADEaR2W8;l!)e4D$;_bm!DO>tP$i zTwz2;WCt0-(x+{JlWbvTfG75_8sZ=h2(b-12VBIZ(JDv<(6~YOfItLF;|DzhUgD*G z`=D>YPyE#H7z_*qNs#)TgOvjz5*nx?Re-xjt9xu>MFaC3PijUhhCB7_na|<9reF7o z?gnPRhLCzu7!8hA+X1KP@uG*O*RX~3yq5GD#b(hr`jF@!eW-^Q1DMuxENR+8YC%hC z&2>zK5i1)QF*GXd=hRG-7D}xGYW1kspoYa3v1YVItQ`%Dbx<-6v0i(7>lWVLV0n8) zY;0gioA?mG9a~7F$&yC<1RD+Cz>E(?S%o{2o`Xll(WI36OlCYag~+M7nbi34$+0XT zm6UokbuRhBWQKf-kPHE8aCkO-ER)Wr&SyVMk~1?Y616Fw6SL{D>|`c=DwCN~yk|8m zkww%&QrTIO9#cz9g)BU<2-kT4SC|R*s%=QIWuCvv5Fe7^&nU;c;!=u8U1Yjz3^Lf2 zoJgg!()ja<8Is9nvXE7c)Rj$1S&70#(Yrfm=9EB^OiZOD$p|4qNZgBW!IrkbtO(G& z!B><)>{Txz&(|QEZA=(4If9v{ZUlOo>h^22G!tir@moq71gyliLI7rg_mJI^+CamSXmi zke(k)T2(sDd_o@WdRqE31!LkNHq9PlXZAsD)tcP8Z=_!LiAK4IuKikVKLsP`IcN

0*XkV@Gfl@uNaq^q350Rw`42=;&I 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_function_tool.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d20e59acbe44d9eef37c5f5f9de085bf2bf7281e GIT binary patch literal 36417 zcmeHw32+?OnO^ry&w)7s3;+TI2@Hk@I6MG?01r?iC?2LnK!zkV$BQ)(PYr<~1sRyZ zb^|gAuQpt1v!*s%Hnp~5*vs1GUS)EVO}L3u*$f9fJ@Av-qzoXyP|Np=Ly znq%>njpJVDL{8-UIEiO>Td!TVg}Giqa)i|or{qM4tO|$II(IYCsq%8_uJJ%OU(=$0=8z@Z(ze>i{EY=N&#SN$#r&zD$ z-m-?=8!Wj;#Ely{X^YqhxOEL_Y_g=WZ4L1@TjFhBL%b$Syw)|uYqrGOah{Jx?okEz zM|s75EOimLisK16`RVj%a-2M8E>0##PmYaDMFquoYC1JCHI`1DNvFq^YDRNB`F!HV zv9$ClNlHsf)xdP>L^?H$CDHk-@}9|yQ_rVU?Ndp4N@mc=k(Hut@}g3ikj{@MW!VUks*$dbGN5sCiJSKZ zrAoL8%0~0SD@5}3~~znRkp6Tg=|Yi}u0lF57-BWlbYwUJx&i-DM1YcXLBEhgw`>36loCO4et++6>^J7_=0M}z%r_}<_@ zupK#agP+;GZO7!reNox-Ma>aD5+>K)sN9xHOe8%og^#G1{ZUyFXsWtlSL`4lu#dq0 z4+04pbIcTrco1Yj9#lAqMhvp3*3#1x>I4O*GZ|Ce4V$zPL=>U3AQfaLZ9+)YFIC{i z%B%=fAjR_pCN0cY@dT#T_()=WJT4_CrIGZ+WI{?RJ_N_lPLGXGjir#!cp4P4QXiL} zPfw4JGSc|@@$}imc$_6y5tkF^l9YlHIL00W^SC-`hvMhb(nMk^p1d$Qo=7EVhF1dV zRD3M;V*2w*HbY9(L2pz@_4F8C%3H0A4y&ij>giNHYSIU+oMdgwbfxz?UHY`M^hy#K_d zQ}eYA8KL3InOA%ALWBCdBQ#t-VE9zWTmdd?`VesCOc61>NaMbp3!LPCA=8Za@=3>& zcK()&f07q&xsM-)N3oAgT^KQ5NgyIOrYG}tD*54KuhJ+l9fDgXlCWCwHTrAu3u9By z$4At6yL1=?Gzm(S>oRY}9=tl&--@Us<63_!uD=!4&VZiqp}ZB>z{3RBc^GP&fsdGn zd6Msc4V=vP7$;NN&t_Yx76p@t8D;NaiOs&7$qN+)Cp8lwMnnn|pckwZA+VJIu^1n? zzZgkG4u?Bl(wPmK#HDQnwi9S2KrEmXC9spgE&{s=>>)t92F3|U?QoTPPd602rwiU0 zM-YVyEYINeV!W&O84 z!ihR~-fH4|8-=%;j~|BT2aP)aqu{bDN@<@*@}0Y|@V({UL*K1%TDwk=ks`b{=6&KEIIwGg zUjIc`gc~Bhfn9oOY+RVsldDyDRhMgHwqOAzm>wSHNHGJZCeNT9tWBte+s*0YVHEtp zIa`=(M*c*5#NN!E;~Dc4>c7D&b>|Xe=y8xJ{DJaJQKiNd?G8uI zf;rmvlEQZ~W-3~#YGha@55^=ZPE~^gEl^@iPRbG~i_3COHH>pAeogiv3UOWXcTq_i z({#1@jhd|Yl!a@GX6mCEfne4f%?sM^ju0hA$V9+B2OPL24VN{26rd63SV{(w;Q)Rq z*JNQ|>N7(974g+0d7)nY-4W_Z!=d?9$6Ns}Yx)pyMJytQ7Zgh}%{(!X3yFEZNKDhC z%Jn@b=c)Y4=HJ7&&fAp&xobSY-wyKxxA277=IQHn-EMaQGlDdYi10uDfUz8^gYJ85x56dOXcAhQ`aALEABW` zBgW&3Cmx?jk4}S^iz~i({MV)vy}od>`g$9U03jT+$Kva2p&Z2_*+3GQ9!SNO2>SrAA=1dajR zuuK1l+@B*52au@@(8_8GSyDugCoe!fecrNWHUgTbh`0j)Y<-~WE6G=rS4O^`d@cFv z#O3(xvDuMpTfXu9>}Rjrub;T#zBY5S>SpK7Z5i*OjBscv`|n0Fui(GF$ign$#k3rwqu--TRzvvtFVndr< zf0Bnbb8KAi9eSNU(wT!4-s~D6MQ724cZxnW8p<|N#CL2NAdb|@qE+WR*{?6vh* zGvU_>;kRy;!tbVj=_JZ85l^+eoO=(#A#|`z_o_IaPmYhL?~#HwYBQNSxD9@8CU_vs zs5uKR6TYER3T#zL1v2N2Rl%um&>hOw|`7;AkOI>#GhgMQc@wnmx{1 zBx*%?L54+wV|03AQfA_S;(-W~9FG%*ao3iWiPKczUIHX;M?LCt$zDPbPb%#r&`F>R zK=Hho7#|yrQx58?2{9DX0RWk55d5xosci2?CZ(6=eN&)+nj+psr5OtxZEs9vy-!<& z_4Z7CdqyCb^|t2)?RQ6LC;8h%z&!^Xgmn#@3AlT~WqqpH-E zv=^Dj`g4J2^6qCc?q>}3MLSbpR5MsZkdzqD@}?GphVIKJ4?nk`zx6o(T!V1y$Vopu zf9>I)+a&z8_hbN`A2#sMH48u7c%mAf%l4Ub=>a~wi6_|1V1y_9 zR?l<$T-i1k@RW^l(I~d#zgP`dY3oW6((ST~KM!hKxxV-V-2-eZAIaiRDLYvv=c&th zhHPl`1P)fR6~ELno_Bp2?@=t{m2640mFVlXFS8`OJ}N(kIvMCm)<+&`nu{)2Ykdq` z>pU!n`xWoVv^bUCu<$c|eELOftr#0kE-yLERI72rqe*#08k;2BUnEgu@y7L( zVB_4mj2X2hN`W;s1dm@sWl9o2)c1g^YKqsAeAKx-Q!kUoTxHu;uDywI?fNDYvSK|q zcHVd{S(>Ku;V*vxLFy9swzvLj^LPA(Yi!$Q{TTru>)nL0`58Bz_e?) ztm&fwjW|aM88n6iXxJ}bZO$AOv)(gIWXO9Pv);yx(3xrORAJWJnHRL*9pJr<_<8kKIf&A3m(o_sTxy%IMo2#?GO9O z?+Tqummdt~^J`dsD9wE8N{wE6AXrsj&(f-ht#D?NS*MzJ}No>S| zlGwhYlGuC-K~oaj*090DP}}QuOB*~=d^<6Kj+DL3D@qvbqU}uSSUPDu8I~1FQ;QB- z1WUPME}eE4mvYD4Q1>}?%#-{m791?kT5^#>bjBR8B61COnW}wDoYJonL%sM3ZC_&x zj_B4b#43n&*B1fOe>7k$-JqJaE9TML6LuyJy_OTOF{LG&mFnzQjAaUSp&|X3CG+r_ zd00{`X#s`6xp7?@Y!S-1}WI8 z9X@*z{5;!aJ#)y@W7)M>W(TAijVZz7^2qbaiG=4R#$v;so6HC$n$4A(#OUZ42?&Yt zXC>^fmZrv%GVJqgV-Oi;4wGnrJ&)nBl)gby-E4`SlG~?I!=EcN>T$okVE^VP) zXqIN%8D?CRShu>xvU|n{^C6aI2g9#{n(ufYo$Qsb2bQY`X*Y zRBF2()ifsL^UH63P^y;}o$Z-WoV3wBHL3*k6whM!Xc}8JQN(D#6jiL796KdiXHYR} z0S>U`2`gr=y2(=w?&v7+b*ifuY!|5d932|D=Ic_)HQ)XM-$6Nm%(Zn5?PguxAVEp% z2k|i(hFa9Aq~ArHm+_Z(LmjILNjYy+6H_8tZzLleoAXBU!ZG|ZTL~8ZNG{QES>c%O zn*+5T%^H5+W_LK}-AxO2x<|I>Bi-3Zx9W2@W!>HHQ@}fwHTlZcY-MX^M^Co$=z@dc zj^6cYPT$e@3HA3J!2fjKP%dvV4B-WCbNZBWGzau&Ae2gnti0TEpFjww$+py-lc%KL%~GKy9^D ztaM`-y@LPZzw~q5=YpTBR4v;?eh5hQ5^bg%XR6rgRFjEHb_R-oGf*6svn4Pb>6!?N z&LKf{Tg4UeXa z&a^OdDWTD{GM9SUm>_J%Dk<)nT0jWGfuWVC4N-PQN|TifY9f_GtQ4za4ow(wn$~cZ zv`Py$#^w=SsL+HF8-$M&Xe~qN&}$T;uub&*Qms|J-dc~MwU!@&rED7YaS-C3`aZ`^ zgHf8ifHseqZLPavE}9$C-_R@?|6Csj>}(RVXO|nJ9*oh@;H)`DE&a0e*s0L_qgeCM zN3&s-tanYzGTE^^_Oi8tTrc_YVEe)P6S9SgTo6x@(BKF1t+2U2&tX&RJr) z>lYi-e-ZPrcad4we}?zu%KhAie!gi?G5fi=`Jwk-Lw%(8gH`ojBNlq^X3LY59({)T zO>BB({e0&d`G;mP-0esOVu9sH#V#&wau75^TExghZ-ao|@7B}?Ml7^JWW_cx_yJ>t zc-JsO4CSU?QuVz)xmDbf{)198e7ZYke++9{X>)G>?kf8H))>w0+XlaC_W2c`%$(al znJr`TU)vuU|5XA0>rc?vS394zuFq#Q#SeUh^BLQ^YVVI2=W*=lSA@~TR6-)bsNi5F zQb~pIU!-kbv`>q6UUk~AzY6CSI^v`0RFZ8EyTMCe28B#UyV`Cl-)K56r?HC;#T!u9Qj%??V}x?s^~@{nBelBWhDZs*N(UFHTG)RTE=s_^5-OvL(&J zjXlB%NluQc<^4TiWMXWfl)H3ERmu6J>6Fn49;6AZ@xMtKYy7WU8oFBHMTzh=-HtW;R3ZJAxxJlX4gbd`gLt zspM#Q8b{)VX+T5;^)Q(!M3OEMXd_TdfcBKDiqN8SYh(mKdCbnv>mWt3dI%q#o##N! zO(#*2I5n?BRS<8bWJu(lIbhNJu0^T+1&R#YbSf~6(<96ZKJt>{VGebShAEd80wgn? zLUU1#Nkz}~8^FyR){3DksMk;wB}j)1#t~CjuwFolQT_ubCB{_hJ){MvT2V1f1r8m$ zs#b?XQ>&x0%Bj__QNgzmV71m<{NS|hJt0v=VAE;jrD%;h`l#L2bAD)wkjR`ysZzT$ z8Om$A;kBI|MAfG_#-@@Jvf4YS0$CS{R!LXMO|!hTmp~~)ef#H=7o|U>Q1E{oQ=c<4 zed!hQvgZ0@aTu#_17+vyL!QCbF|$JcYJ$GHhf@#gXeoA>~?G9eeyB)dv4BGKkpCC*L2P| zw&okVvyI)k#>eJ2Y`I_IU=}w4!{VlC)|n9ivfie=p#AO$O=L@CBH*3_4*GQsmoB%(rWCVg)Z%~5cg8<0u(}piw;4{#3?k=33xZ&tKWNoA_U_{sg2G%glUsj0FGinHnZ%&A? zbqkXeR0uC|UV)ekoyq&p(0K(d8KLFMaMs(B7qs6Up@lY5mD6eu-_eE=wZow*Wxw^fV|#p__C&Nj-_RU7>=CK z_f9C14|QZi9dElEX1C_t&6$?ti*{R$6GJTEymSf&ZPj5p>Jd~l8tdIHTp-5Z--Mjh z)3ko8f9Fsw|D!g3s8aY*`{`DA=Ii*OkTAdD_+fbdHpBytU?wpx+FL{u*Wy3J3V^2l zZ&*qH{{j#FJk7}LGS6gA@ujpODbkz}UsQC(Y@$$BuCT+vN-$-3)4(8oCgvc`ditC$ z9m6~dWnalW4dzi!$dazXPg!J1dU|#Z5Sv9B^eQt8ZCC{@LZ)P}B7 z^t1`g{K-XzSTUET_jC^t+r}JL{eY8Fg7#ecoF4pIP^?5)aUn`F8kLf(X zbo54bbc!jV!@c4&{nACCpmE)+>lQV0U0BbmdPO))z2Xj{>Wa!y z*aHtEBGa=$(R+>DTL4Ho+eP5OjP>g%hg8$uLm$|__>%NE5mQw#T`=f(cnNdP2BuSR4cKu(#6?K{B%c>gmCWZec zfqxCaY>D?4nh_&k>DP(iNdPR>JcC;_sGjggwsERnl&Q8^jcg}eD*b@gGumTkejJH50Q&0FG;Iuwf^02u@F|Rcn?|)h`X({T8OHQ?q)jF0)t=8c33k8 z?ksEmyM1+CQ2Gxj>wm;wzKWqhI??;kiMD)==|o$ucK$u|pe;Co!q+gnA?Izmbnir4CXe+Z7*p{#dDUeJCyVaH|Y7s+?m zbQ;0HUB$7X8gaJ3r=ou>?6_8ErO15Lny=}@K_m1;{LhIv3 zmxVr(wy9ZW+^o0Fhcc&Wc4Av0Kic|#l}V9bhRxW2Vv=OtFC|Gze~xKY`sW0W0a&F+ zCP^p<7~3vi#I8NNpxPl|!pG6wjX37L15@oHAr)<=ohdo>2 zLn)`%_|)Z-`Ws}sNV-rmiP|vh_`~2gg8BNr+4{Yi4OsHtmvQe?tLP+X*w-PHdbKd0 zrei1zK7p*-JB5jmiFFZjZ6!c2N$KAZcpN~fR8z3121wwP{w=@_P242*M}maKDHWf( zFp^}4^QdAt6GWK|YKou46_ovS7e3v|gwDSrq9Vz2KjHQgI6$Bipwy?mwymnZr3|9q zr6l&C&yx3i=6S1|?{^7r9cG@}F23I*+{TwqLG~koCkPavhv2%jf(k9Vu-3RPtrXP> z`@(sdmE(13MXfBWRn8|sl3bToShJPU34ACVaak#UU0V6b+vdTgm9L`gi(vp&;b)#? zWnKFsp=U*@MP2VotXXXNJ7mtj1x3uIWyMR~uJE!ipnk+$B4OeY2r|-trgZ-cfw!n6 zBpGQRMUsfSR(ctqyoTp0FPPk?Tz)`+Noa=nA>Ae%+gTAN*Rqm^^k0d@5H6%2lJ~y> zlw$JL6`&OhFXa*c=afbPmwz=xTzt!)hS&M#8d0%MLig>3lLZC9|C_>Sr36>`h{};#I@i zXhB|GUuNsRWR0u{6AA3sbFuLI8K0;VbrhL4U&rl6DdVtn!xW8 z_!a?LSY;A48(_tI-`tO54-US(*CYKDbUgm@KLuQ{^F06GIqtW(SG+&r>VC@kf5J8W zglqjDTr9`Me!_KPAo7*7TOe8Ul?!gp=KfOSZ#L%nnk-*)-{#;qF4{R;?E<+LDnXKI zYQJgEHg#bv^OfHTm5x?nM1$*oJ)o)QcP@mB9x=*C nG)lM@2Kl1kD!x}wiJS{Nio$F79a?m_7TU^$-nSu?&C~x6&6VSN literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2bd74c73a87761fb3b42ccc6d86d9a6c0c728ccd GIT binary patch literal 10520 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_function_tool.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95d02f2892019fb54c7b5c109c6966c91a1be718 GIT binary patch literal 15033 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 XUEa^{f|kqW#00hH_Sk(c8*~2#7-{J+ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5130ea0fc517971d150303ba2daca3a470e913bb GIT binary patch literal 21501 zcmeHPYj6`+mcA{u+O4N$%MWbXApF3{0XrBR0x=0-9zh0&sO=;ZF;OYDY#e0Cxh-cP zv$I291KFvmWOs_%>_6}RS^O_Gm8wZirD|NEwu<~3EAlMfF36-RGquY<0p6vO+O67i zZoeeaj)Q^SJbb~Y&%O8b?R)y(b0441?OR^2i$M6F|NfWp)7uI80wa2Gs0?@OGmel8 zL?RM5Kq4IFLde?&Y}C%Ke8eH!LL?&43N1&ZBIMWyoDmmwu{3_b9q~|)mhYur$af6* zB9*j~r3nN6NENMOe8oU@BtQd<7YAx0wX~M;&Vjl}J*{WFYhYcZfi^@MX(Nlf2ZE6% z+625u@(wgd*3K(AfZl(wJn zMvv<1o7d=|$?Kd{)ku1^2;F=a_f;3&7iP5xKrL!cR2sGDyo=Imi%NUwF7B%W7}(h@+B zAIfA>*P+W{b)1J1r;@CwutRAaPiEs|6X`@Mo|(>0O=sg-5RH!{QYod1U4Zv7K57sv@hIf+o4#DNjm z8VThkJ5a}ZV*Zod65mK(;;CS!sT_1t2g|9rBgZQVEJwT}$0t>=9A|kB#-&P0WVtTM zN!{bFuwU`(t#K$rhmvwuz8-|CkKl{d1NM9P<8EyM<^qWsc`;30LVC5dVUjY^#>p%< z7`9U$?o#X%>8v6oUx6+l&qC|T$<&yPH-(h`?qNAe26(M&dz zg)XEeb!V}DjK;@PnHQmfPhvmnnmVJ1@%Th~A{&n@4dn`T=~+$iIN1l}EV;UA+Xt=r zO^=-Cf91XfDWwXd3QXZgfw@42NfbrOLKI(1S

m8J_@xlb@t#qi4X{N=m>3H8jiMq(cu6;fsGWvsSdzH zKwTLI3aT5~Gc_p0AF1!T+ENEqL&QmBx%iuXnLgOut zdp}%Qzl95=J4HvGj=*<|x*TZLiMo9U zO(2?!Lm=fkkih1b5l~N{oqsbVSh1K`D^V1@9t&zReu~qT$IxYCwXhmA8KyC>H{y*! z@0lPDa|FIrmVi-Fd7YA<$SP%&8QTu@(7R-dzBFQE6G||%;MlW&F`hsL+fTtVffT$w z5Ur_FU)?i60!AR@BbAtTSmBl@Uece5oY(+3~t?}541ml7;T{UOspoaQ^oCl_}OP|tM ztnn?Q(io#?qlUw%E-^R+XLwc}zObnu7pa(r&V!Nb%msdZh57TdICvFE>&E}kQSSi~ z&1Zb0*e(MYTS*Eo12JdNr^*@{DX}F4gw(2_ZG@_BM+U6$5sY=?Zi2QhzoTZab1LkX z@cyyfms@?I)tg(xLThtaI1N^G^L@if6wwABB2m^*#RW|w&?z4yqa1VnaQOO&9Tqi3 zWB;J(b3#h5Ip~EfdO;6e1IfMSci$#|AVM*V?_h*}35?3(naDz6$RL=Xlqz1#_VdqeWA`$b%u)LFyla^i<{7gM?w3B;Lt4mTBnC&QX3Ay!K_kn zuw3A2u2j*2hKNiYx!#Of^g}p@aP=sqZZ`2UkG2cCQ9ax&C>N+(U|X&+Jl> zF5+JvuT-I5=bo2jJ+B_IW`O*N=RI%wL3! zZMz7Loyqc}g_8H^@xwd!%H@ZTy`_8QrLRi&wUYbCc~_z2=qit^>-}YU5jJl7QDF5D zp91+$dWZ#Fri?O7=!h1o^Y7~(X@HBgrgT|@>wRC;-otD4@_-uMXvLE|h6A%}46TH^ z1iV2BeOoW}C~+6~VgX}fwF&<|Kuid str: + return f"The weather in {city} is sunny" + +agent = Agent( + name="Haiku agent", + instructions="Always respond in haiku form", + model="o3-mini", + tools=[function_tool(get_weather)], +) +``` + +## Context + +Agents are generic on their `context` type. Context is a dependency-injection tool: it's an object you create and pass to `Runner.run()`, that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context. + +```python +@dataclass +class UserContext: + uid: str + is_pro_user: bool + + async def fetch_purchases() -> list[Purchase]: + return ... + +agent = Agent[UserContext]( + ..., +) +``` + +## Output types + +By default, agents produce plain text (i.e. `str`) outputs. If you want the agent to produce a particular type of output, you can use the `output_type` parameter. A common choice is to use [Pydantic](https://docs.pydantic.dev/) objects, but we support any type that can be wrapped in a Pydantic [TypeAdapter](https://docs.pydantic.dev/latest/api/type_adapter/) - dataclasses, lists, TypedDict, etc. + +```python +from pydantic import BaseModel +from agents import Agent + + +class CalendarEvent(BaseModel): + name: str + date: str + participants: list[str] + +agent = Agent( + name="Calendar extractor", + instructions="Extract calendar events from text", + output_type=CalendarEvent, +) +``` + +!!! note + + When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses. + +## Handoffs + +Handoffs are sub-agents that the agent can delegate to. You provide a list of handoffs, and the agent can choose to delegate to them if relevant. This is a powerful pattern that allows orchestrating modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation. + +```python +from agents import Agent + +booking_agent = Agent(...) +refund_agent = Agent(...) + +triage_agent = Agent( + name="Triage agent", + instructions=( + "Help the user with their questions." + "If they ask about booking, handoff to the booking agent." + "If they ask about refunds, handoff to the refund agent." + ), + handoffs=[booking_agent, refund_agent], +) +``` + +## Dynamic instructions + +In most cases, you can provide instructions when you create the agent. However, you can also provide dynamic instructions via a function. The function will receive the agent and context, and must return the prompt. Both regular and `async` functions are accepted. + +```python +def dynamic_instructions( + context: RunContextWrapper[UserContext], agent: Agent[UserContext] +) -> str: + return f"The user's name is {context.context.name}. Help them with their questions." + + +agent = Agent[UserContext]( + name="Triage agent", + instructions=dynamic_instructions, +) +``` + +## Lifecycle events (hooks) + +Sometimes, you want to observe the lifecycle of an agent. For example, you may want to log events, or pre-fetch data when certain events occur. You can hook into the agent lifecycle with the `hooks` property. Subclass the [`AgentHooks`][agents.lifecycle.AgentHooks] class, and override the methods you're interested in. + +## Guardrails + +Guardrails allow you to run checks/validations on user input, in parallel to the agent running. For example, you could screen the user's input for relevance. Read more in the [guardrails](guardrails.md) documentation. + +## Cloning/copying agents + +By using the `clone()` method on an agent, you can duplicate an Agent, and optionally change any properties you like. + +```python +pirate_agent = Agent( + name="Pirate", + instructions="Write like a pirate", + model="o3-mini", +) + +robot_agent = pirate_agent.clone( + name="Robot", + instructions="Write like a robot", +) +``` diff --git a/tests/docs/assets/images/favicon-platform.svg b/tests/docs/assets/images/favicon-platform.svg new file mode 100644 index 0000000..91ef0ae --- /dev/null +++ b/tests/docs/assets/images/favicon-platform.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/docs/assets/images/orchestration.png b/tests/docs/assets/images/orchestration.png new file mode 100644 index 0000000000000000000000000000000000000000..621a833b52bfa9c5e863bca18f5866ee9fbe2586 GIT binary patch literal 432783 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 z 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%0AVW6d*4@Y` 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 z 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 Ui-AeWwrxL~AN{G^>}2wP0j+0BPXGV_ literal 0 HcmV?d00001 diff --git a/tests/docs/assets/logo.svg b/tests/docs/assets/logo.svg new file mode 100644 index 0000000..ba36fc2 --- /dev/null +++ b/tests/docs/assets/logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tests/docs/config.md b/tests/docs/config.md new file mode 100644 index 0000000..198d7b7 --- /dev/null +++ b/tests/docs/config.md @@ -0,0 +1,94 @@ +# Configuring the SDK + +## API keys and clients + +By default, the SDK looks for the `OPENAI_API_KEY` environment variable for LLM requests and tracing, as soon as it is imported. If you are unable to set that environment variable before your app starts, you can use the [set_default_openai_key()][agents.set_default_openai_key] function to set the key. + +```python +from agents import set_default_openai_key + +set_default_openai_key("sk-...") +``` + +Alternatively, you can also configure an OpenAI client to be used. By default, the SDK creates an `AsyncOpenAI` instance, using the API key from the environment variable or the default key set above. You can chnage this by using the [set_default_openai_client()][agents.set_default_openai_client] function. + +```python +from openai import AsyncOpenAI +from agents import set_default_openai_client + +custom_client = AsyncOpenAI(base_url="...", api_key="...") +set_default_openai_client(client) +``` + +Finally, you can also customize the OpenAI API that is used. By default, we use the OpenAI Responses API. You can override this to use the Chat Completions API by using the [set_default_openai_api()][agents.set_default_openai_api] function. + +```python +from agents import set_default_openai_api + +set_default_openai_api("chat_completions") +``` + +## Tracing + +Tracing is enabled by default. It uses the OpenAI API keys from the section above by default (i.e. the environment variable or the default key you set). You can specifically set the API key used for tracing by using the [`set_tracing_export_api_key`][agents.set_tracing_export_api_key] function. + +```python +from agents import set_tracing_export_api_key + +set_tracing_export_api_key("sk-...") +``` + +You can also disable tracing entirely by using the [`set_tracing_disabled()`][agents.set_tracing_disabled] function. + +```python +from agents import set_tracing_disabled + +set_tracing_disabled(True) +``` + +## Debug logging + +The SDK has two Python loggers without any handlers set. By default, this means that warnings and errors are sent to `stdout`, but other logs are suppressed. + +To enable verbose logging, use the [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] function. + +```python +from agents import enable_verbose_stdout_logging + +enable_verbose_stdout_logging() +``` + +Alternatively, you can customize the logs by adding handlers, filters, formatters, etc. You can read more in the [Python logging guide](https://docs.python.org/3/howto/logging.html). + +```python +import logging + +logger = logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger + +# To make all logs show up +logger.setLevel(logging.DEBUG) +# To make info and above show up +logger.setLevel(logging.INFO) +# To make warning and above show up +logger.setLevel(logging.WARNING) +# etc + +# You can customize this as needed, but this will output to `stderr` by default +logger.addHandler(logging.StreamHandler()) +``` + +### Sensitive data in logs + +Certain logs may contain sensitive data (for example, user data). If you want to disable this data from being logged, set the following environment variables. + +To disable logging LLM inputs and outputs: + +```bash +export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1 +``` + +To disable logging tool inputs and outputs: + +```bash +export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1 +``` diff --git a/tests/docs/context.md b/tests/docs/context.md new file mode 100644 index 0000000..5dcaceb --- /dev/null +++ b/tests/docs/context.md @@ -0,0 +1,76 @@ +# Context management + +Context is an overloaded term. There are two main classes of context you might care about: + +1. Context available locally to your code: this is data and dependencies you might need when tool functions run, during callbacks like `on_handoff`, in lifecycle hooks, etc. +2. Context available to LLMs: this is data the LLM sees when generating a response. + +## Local context + +This is represented via the [`RunContextWrapper`][agents.run_context.RunContextWrapper] class and the [`context`][agents.run_context.RunContextWrapper.context] property within it. The way this works is: + +1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object. +2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`. +3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`. + +The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context. + +You can use the context for things like: + +- Contextual data for your run (e.g. things like a username/uid or other information about the user) +- Dependencies (e.g. logger objects, data fetchers, etc) +- Helper functions + +!!! danger "Note" + + The context object is **not** sent to the LLM. It is purely a local object that you can read from, write to and call methods on it. + +```python +import asyncio +from dataclasses import dataclass + +from agents import Agent, RunContextWrapper, Runner, function_tool + +@dataclass +class UserInfo: # (1)! + name: str + uid: int + +async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)! + return f"User {wrapper.context.name} is 47 years old" + +async def main(): + user_info = UserInfo(name="John", uid=123) # (3)! + + agent = Agent[UserInfo]( # (4)! + name="Assistant", + tools=[function_tool(fetch_user_age)], + ) + + result = await Runner.run( + starting_agent=agent, + input="What is the age of the user?", + context=user_info, + ) + + print(result.final_output) # (5)! + # The user John is 47 years old. + +if __name__ == "__main__": + asyncio.run(main()) +``` + +1. This is the context object. We've used a dataclass here, but you can use any type. +2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context. +3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type). +4. The context is passed to the `run` function. +5. The agent correctly calls the tool and gets the age. + +## Agent/LLM context + +When an LLM is called, the **only** data it can see is from the conversation history. This means that if you want to make some new data available to the LLM, you must do it in a way that makes it available in that history. There are a few ways to do this: + +1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date). +2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command). +3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data. +4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data. diff --git a/tests/docs/guardrails.md b/tests/docs/guardrails.md new file mode 100644 index 0000000..2b7369c --- /dev/null +++ b/tests/docs/guardrails.md @@ -0,0 +1,154 @@ +# Guardrails + +Guardrails run _in parallel_ to your agents, enabling you to do checks and validations of user input. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, which stops the expensive model from running and saves you time/money. + +There are two kinds of guardrails: + +1. Input guardrails run on the initial user input +2. Output guardrails run on the final agent output + +## Input guardrails + +Input guardrails run in 3 steps: + +1. First, the guardrail receives the same input passed to the agent. +2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult] +3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. + +!!! Note + + Input guardrails are intended to run on user input, so an agent's guardrails only run if the agent is the *first* agent. You might wonder, why is the `guardrails` property on the agent instead of passed to `Runner.run`? It's because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. + +## Output guardrails + +Output guardrailas run in 3 steps: + +1. First, the guardrail receives the same input passed to the agent. +2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] +3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception. + +!!! Note + + Output guardrails are intended to run on the final agent input, so an agent's guardrails only run if the agent is the *last* agent. Similar to the input guardrails, we do this because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability. + +## Tripwires + +If the input or output fails the guardrail, the Guardrail can signal this with a tripwire. As soon as we see a guardail that has triggered the tripwires, we immediately raise a `{Input,Output}GuardrailTripwireTriggered` exception and halt the Agent execution. + +## Implementing a guardrail + +You need to provide a function that receives input, and returns a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput]. In this example, we'll do this by running an Agent under the hood. + +```python +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + TResponseInputItem, + input_guardrail, +) + +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + reasoning: str + +guardrail_agent = Agent( # (1)! + name="Guardrail check", + instructions="Check if the user is asking you to do their math homework.", + output_type=MathHomeworkOutput, +) + + +@input_guardrail +async def math_guardrail( # (2)! + ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, input, context=ctx.context) + + return GuardrailFunctionOutput( + output_info=result.final_output, # (3)! + tripwire_triggered=result.final_output.is_math_homework, + ) + + +agent = Agent( # (4)! + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + input_guardrails=[math_guardrail], +) + +async def main(): + # This should trip the guardrail + try: + await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") + print("Guardrail didn't trip - this is unexpected") + + except InputGuardrailTripwireTriggered: + print("Math homework guardrail tripped") +``` + +1. We'll use this agent in our guardrail function. +2. This is the guardrail function that receives the agent's input/context, and returns the result. +3. We can include extra information in the guardrail result. +4. This is the actual agent that defines the workflow. + +Output guardrails are similar. + +```python +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + output_guardrail, +) +class MessageOutput(BaseModel): # (1)! + response: str + +class MathOutput(BaseModel): # (2)! + is_math: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the output includes any math.", + output_type=MathOutput, +) + +@output_guardrail +async def math_guardrail( # (3)! + ctx: RunContextWrapper, agent: Agent, output: MessageOutput +) -> GuardrailFunctionOutput: + result = await Runner.run(guardrail_agent, output.response, context=ctx.context) + + return GuardrailFunctionOutput( + output_info=result.final_output, + tripwire_triggered=result.final_output.is_math, + ) + +agent = Agent( # (4)! + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + output_guardrails=[math_guardrail], + output_type=MessageOutput, +) + +async def main(): + # This should trip the guardrail + try: + await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?") + print("Guardrail didn't trip - this is unexpected") + + except OutputGuardrailTripwireTriggered: + print("Math output guardrail tripped") +``` + +1. This is the actual agent's output type. +2. This is the guardrail's output type. +3. This is the guardrail function that receives the agent's output, and returns the result. +4. This is the actual agent that defines the workflow. diff --git a/tests/docs/handoffs.md b/tests/docs/handoffs.md new file mode 100644 index 0000000..0b868c4 --- /dev/null +++ b/tests/docs/handoffs.md @@ -0,0 +1,113 @@ +# Handoffs + +Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc. + +Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`. + +## Creating a handoff + +All agents have a [`handoffs`][agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff. + +You can create a handoff using the [`handoff()`][agents.handoffs.handoff] function provided by the Agents SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters. + +### Basic Usage + +Here's how you can create a simple handoff: + +```python +from agents import Agent, handoff + +billing_agent = Agent(name="Billing agent") +refund_agent = Agent(name="Refund agent") + +# (1)! +triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)]) +``` + +1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function. + +### Customizing handoffs via the `handoff()` function + +The [`handoff()`][agents.handoffs.handoff] function lets you customize things. + +- `agent`: This is the agent to which things will be handed off. +- `tool_name_override`: By default, the `Handoff.default_tool_name()` function is used, which resolves to `transfer_to_`. You can override this. +- `tool_description_override`: Override the default tool description from `Handoff.default_tool_description()` +- `on_handoff`: A callback function executed when the handoff is invoked. This is useful for things like kicking off some data fetching as soon as you know a handoff is being invoked. This function receives the agent context, and can optionally also receive LLM generated input. The input data is controlled by the `input_type` param. +- `input_type`: The type of input expected by the handoff (optional). +- `input_filter`: This lets you filter the input received by the next agent. See below for more. + +```python +from agents import Agent, handoff, RunContextWrapper + +def on_handoff(ctx: RunContextWrapper[None]): + print("Handoff called") + +agent = Agent(name="My agent") + +handoff_obj = handoff( + agent=agent, + on_handoff=on_handoff, + tool_name_override="custom_handoff_tool", + tool_description_override="Custom description", +) +``` + +## Handoff inputs + +In certain situations, you want the LLM to provide some data when it calls a handoff. For example, imagine a handoff to an "Escalation agent". You might want a reason to be provided, so you can log it. + +```python +from pydantic import BaseModel + +from agents import Agent, handoff, RunContextWrapper + +class EscalationData(BaseModel): + reason: str + +async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData): + print(f"Escalation agent called with reason: {input_data.reason}") + +agent = Agent(name="Escalation agent") + +handoff_obj = handoff( + agent=agent, + on_handoff=on_handoff, + input_type=EscalationData, +) +``` + +## Input filters + +When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`. + +There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][] + +```python +from agents import Agent, handoff +from agents.extensions import handoff_filters + +agent = Agent(name="FAQ agent") + +handoff_obj = handoff( + agent=agent, + input_filter=handoff_filters.remove_all_tools, # (1)! +) +``` + +1. This will automatically remove all tools from the history when `FAQ agent` is called. + +## Recommended prompts + +To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts. + +```python +from agents import Agent +from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX + +billing_agent = Agent( + name="Billing agent", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + .""", +) +``` diff --git a/tests/docs/index.md b/tests/docs/index.md new file mode 100644 index 0000000..28c6870 --- /dev/null +++ b/tests/docs/index.md @@ -0,0 +1,52 @@ +# OpenAI Agents SDK + +The OpenAI Agents SDK enables you to build agentic AI apps in a lightweight, easy to use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, [Swarm](https://github.com/openai/swarm/tree/main). The Agents SDK has a very small set of primitives: + +- **Agents**, which are LLMs equipped with instructions and tools +- **Handoffs**, which allow agents to delegate to other agents for specific tasks +- **Guardrails**, which enable the inputs to agents to be validated + +In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application. + +## Why use the Agents SDK + +The SDK has two driving design principles: + +1. Enough features to be worth using, but few enough primitives to make it quick to learn. +2. Works great out of the box, but you can customize exactly what happens. + +Here are the main features of the SDK: + +- Agent loop: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done. +- Python-first: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions. +- Handoffs: A powerful feature to coordinate and delegate between multiple agents. +- Guardrails: Run input validations and checks in parallel to your agents, breaking early if the checks fail. +- Function tools: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation. +- Tracing: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the OpenAI suite of evaluation, fine-tuning and distillation tools. + +## Installation + +```bash +pip install openai-agents +``` + +## Hello world example + +```python +from agents import Agent, Runner + +agent = Agent(name="Assistant", instructions="You are a helpful assistant") + +result = Runner.run_sync(agent, "Write a haiku about recursion in programming.") +print(result.final_output) + +# Code within the code, +# Functions calling themselves, +# Infinite loop's dance. +``` + +(_If running this, ensure you set the `OPENAI_API_KEY` environment variable_) + +```bash +export OPENAI_API_KEY=sk-... +``` diff --git a/tests/docs/models.md b/tests/docs/models.md new file mode 100644 index 0000000..7d2ff1f --- /dev/null +++ b/tests/docs/models.md @@ -0,0 +1,73 @@ +# Models + +The Agents SDK comes with out of the box support for OpenAI models in two flavors: + +- **Recommended**: the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel], which calls OpenAI APIs using the new [Responses API](https://platform.openai.com/docs/api-reference/responses). +- The [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel], which calls OpenAI APIs using the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). + +## Mixing and matching models + +Within a single workflow, you may want to use different models for each agent. For example, you could use a smaller, faster model for triage, while using a larger, more capable model for complex tasks. When configuring an [`Agent`][agents.Agent], you can select a specific model by either: + +1. Passing the name of an OpenAI model. +2. Passing any model name + a [`ModelProvider`][agents.models.interface.ModelProvider] that can map that name to a Model instance. +3. Directly providing a [`Model`][agents.models.interface.Model] implementation. + +!!!note + + While our SDK supports both the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] and the[`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] shapes, we recommend using a single model shape for each workflow because the two shapes support a different set of features and tools. If your workflow requires mixing and matching model shapes, make sure that all the features you're using are available on both. + +```python +from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", + model="o3-mini", # (1)! +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", + model=OpenAIChatCompletionsModel( # (2)! + model="gpt-4o", + openai_client=AsyncOpenAI() + ), +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], + model="gpt-3.5-turbo", +) + +async def main(): + result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(result.final_output) +``` + +1. Sets the the name of an OpenAI model directly. +2. Provides a [`Model`][agents.models.interface.Model] implementation. + +## Using other LLM providers + +Many providers also support the OpenAI API format, which means you can pass a `base_url` to the existing OpenAI model implementations and use them easily. `ModelSettings` is used to configure tuning parameters (e.g., temperature, top_p) for the model you select. + +```python +external_client = AsyncOpenAI( + api_key="EXTERNAL_API_KEY", + base_url="https://api.external.com/v1/", +) + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", + model=OpenAIChatCompletionsModel( + model="EXTERNAL_MODEL_NAME", + openai_client=external_client, + ), + model_settings=ModelSettings(temperature=0.5), +) +``` diff --git a/tests/docs/multi_agent.md b/tests/docs/multi_agent.md new file mode 100644 index 0000000..c118249 --- /dev/null +++ b/tests/docs/multi_agent.md @@ -0,0 +1,37 @@ +# Orchestrating multiple agents + +Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents: + +1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that. +2. Orchestrating via code: determining the flow of agents via your code. + +You can mix and match these patterns. Each has their own tradeoffs, described below. + +## Orchestrating via LLM + +An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like: + +- Web search to find information online +- File search and retrieval to search through proprietary data and connections +- Computer use to take actions on a computer +- Code execution to do data analysis +- Handoffs to specialized agents that are great at planning, report writing and more. + +This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are: + +1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within. +2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts. +3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve. +4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything. +5. Invest in [evals](https://platform.openai.com/docs/guides/evals). This lets you train your agents to improve and get better at tasks. + +## Orchestrating via code + +While orchestrating via LLM is powerful, orchestrating via LLM makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are: + +- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category. +- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it. +- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria. +- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other. + +We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/openai-agents-python/examples/agent_patterns). diff --git a/tests/docs/quickstart.md b/tests/docs/quickstart.md new file mode 100644 index 0000000..19051f4 --- /dev/null +++ b/tests/docs/quickstart.md @@ -0,0 +1,186 @@ +# Quickstart + +## Create a project and virtual environment + +You'll only need to do this once. + +```bash +mkdir my_project +cd my_project +python -m venv .venv +``` + +### Activate the virtual environment + +Do this every time you start a new terminal session. + +```bash +source .venv/bin/activate +``` + +### Install the Agents SDK + +```bash +pip install openai-agents # or `uv add openai-agents`, etc +``` + +### Set an OpenAI API key + +If you don't have one, follow [these instructions](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to create an OpenAI API key. + +```bash +export OPENAI_API_KEY=sk-... +``` + +## Create your first agent + +Agents are defined with instructions, a name, and optional config (such as `model_config`) + +```python +from agents import Agent + +agent = Agent( + name="Math Tutor", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) +``` + +## Add a few more agents + +Additional agents can be defined in the same way. `handoff_descriptions` provide additional context for determining handoff routing + +```python +from agents import Agent + +history_tutor_agent = Agent( + name="History Tutor", + handoff_description="Specialist agent for historical questions", + instructions="You provide assistance with historical queries. Explain important events and context clearly.", +) + +math_tutor_agent = Agent( + name="Math Tutor", + handoff_description="Specialist agent for math questions", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) +``` + +## Define your handoffs + +On each agent, you can define an inventory of outgoing handoff options that the agent can choose from to decide how to make progress on their task. + +```python +triage_agent = Agent( + name="Triage Agent", + instructions="You determine which agent to use based on the user's homework question", + handoffs=[history_tutor_agent, math_tutor_agent] +) +``` + +## Run the agent orchestration + +Let's check that the workflow runs and the triage agent correctly routes between the two specialist agents. + +```python +from agents import Runner + +async def main(): + result = await Runner.run(triage_agent, "What is the capital of France?") + print(result.final_output) +``` + +## Add a guardrail + +You can define custom guardrails to run on the input or output. + +```python +from agents import GuardrailFunctionOutput, Agent, Runner +from pydantic import BaseModel + +class HomeworkOutput(BaseModel): + is_homework: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking about homework.", + output_type=HomeworkOutput, +) + +async def homework_guardrail(ctx, agent, input_data): + result = await Runner.run(guardrail_agent, input_data, context=ctx.context) + final_output = result.final_output_as(HomeworkOutput) + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_homework, + ) +``` + +## Put it all together + +Let's put it all together and run the entire workflow, using handoffs and the input guardrail. + +```python +from agents import Agent, InputGuardrail,GuardrailFunctionOutput, Runner +from pydantic import BaseModel +import asyncio + +class HomeworkOutput(BaseModel): + is_homework: bool + reasoning: str + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking about homework.", + output_type=HomeworkOutput, +) + +math_tutor_agent = Agent( + name="Math Tutor", + handoff_description="Specialist agent for math questions", + instructions="You provide help with math problems. Explain your reasoning at each step and include examples", +) + +history_tutor_agent = Agent( + name="History Tutor", + handoff_description="Specialist agent for historical questions", + instructions="You provide assistance with historical queries. Explain important events and context clearly.", +) + + +async def homework_guardrail(ctx, agent, input_data): + result = await Runner.run(guardrail_agent, input_data, context=ctx.context) + final_output = result.final_output_as(HomeworkOutput) + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_homework, + ) + +triage_agent = Agent( + name="Triage Agent", + instructions="You determine which agent to use based on the user's homework question", + handoffs=[history_tutor_agent, math_tutor_agent], + input_guardrails=[ + InputGuardrail(guardrail_function=homework_guardrail), + ], +) + +async def main(): + result = await Runner.run(triage_agent, "what is life") + print(result.final_output) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## View your traces + +To review what happened during your agent run, navigate to the [Trace viewer in the OpenAI Dashboard](https://platform.openai.com/traces) to view traces of your agent runs. + +## Next steps + +Learn how to build more complex agentic flows: + +- Learn about how to configure [Agents](agents.md). +- Learn about [running agents](running_agents.md). +- Learn about [tools](tools.md), [guardrails](guardrails.md) and [models](models.md). diff --git a/tests/docs/ref/agent.md b/tests/docs/ref/agent.md new file mode 100644 index 0000000..9f8b10d --- /dev/null +++ b/tests/docs/ref/agent.md @@ -0,0 +1,3 @@ +# `Agents` + +::: agents.agent diff --git a/tests/docs/ref/agent_output.md b/tests/docs/ref/agent_output.md new file mode 100644 index 0000000..e453de0 --- /dev/null +++ b/tests/docs/ref/agent_output.md @@ -0,0 +1,3 @@ +# `Agent output` + +::: agents.agent_output diff --git a/tests/docs/ref/exceptions.md b/tests/docs/ref/exceptions.md new file mode 100644 index 0000000..7c1a254 --- /dev/null +++ b/tests/docs/ref/exceptions.md @@ -0,0 +1,3 @@ +# `Exceptions` + +::: agents.exceptions diff --git a/tests/docs/ref/extensions/handoff_filters.md b/tests/docs/ref/extensions/handoff_filters.md new file mode 100644 index 0000000..0ffcb13 --- /dev/null +++ b/tests/docs/ref/extensions/handoff_filters.md @@ -0,0 +1,3 @@ +# `Handoff filters` + +::: agents.extensions.handoff_filters diff --git a/tests/docs/ref/extensions/handoff_prompt.md b/tests/docs/ref/extensions/handoff_prompt.md new file mode 100644 index 0000000..ca80076 --- /dev/null +++ b/tests/docs/ref/extensions/handoff_prompt.md @@ -0,0 +1,8 @@ +# `Handoff prompt` + +::: agents.extensions.handoff_prompt + + options: + members: + - RECOMMENDED_PROMPT_PREFIX + - prompt_with_handoff_instructions diff --git a/tests/docs/ref/function_schema.md b/tests/docs/ref/function_schema.md new file mode 100644 index 0000000..06aac2a --- /dev/null +++ b/tests/docs/ref/function_schema.md @@ -0,0 +1,3 @@ +# `Function schema` + +::: agents.function_schema diff --git a/tests/docs/ref/guardrail.md b/tests/docs/ref/guardrail.md new file mode 100644 index 0000000..17ec929 --- /dev/null +++ b/tests/docs/ref/guardrail.md @@ -0,0 +1,3 @@ +# `Guardrails` + +::: agents.guardrail diff --git a/tests/docs/ref/handoffs.md b/tests/docs/ref/handoffs.md new file mode 100644 index 0000000..717a918 --- /dev/null +++ b/tests/docs/ref/handoffs.md @@ -0,0 +1,3 @@ +# `Handoffs` + +::: agents.handoffs diff --git a/tests/docs/ref/index.md b/tests/docs/ref/index.md new file mode 100644 index 0000000..1b8439f --- /dev/null +++ b/tests/docs/ref/index.md @@ -0,0 +1,13 @@ +# Agents module + +::: agents + + options: + members: + - set_default_openai_key + - set_default_openai_client + - set_default_openai_api + - set_tracing_export_api_key + - set_tracing_disabled + - set_trace_processors + - enable_verbose_stdout_logging diff --git a/tests/docs/ref/items.md b/tests/docs/ref/items.md new file mode 100644 index 0000000..29279e1 --- /dev/null +++ b/tests/docs/ref/items.md @@ -0,0 +1,3 @@ +# `Items` + +::: agents.items diff --git a/tests/docs/ref/lifecycle.md b/tests/docs/ref/lifecycle.md new file mode 100644 index 0000000..432af14 --- /dev/null +++ b/tests/docs/ref/lifecycle.md @@ -0,0 +1,6 @@ +# `Lifecycle` + +::: agents.lifecycle + + options: + show_source: false diff --git a/tests/docs/ref/model_settings.md b/tests/docs/ref/model_settings.md new file mode 100644 index 0000000..f7f411f --- /dev/null +++ b/tests/docs/ref/model_settings.md @@ -0,0 +1,3 @@ +# `Model settings` + +::: agents.model_settings diff --git a/tests/docs/ref/models/interface.md b/tests/docs/ref/models/interface.md new file mode 100644 index 0000000..e7bd89a --- /dev/null +++ b/tests/docs/ref/models/interface.md @@ -0,0 +1,3 @@ +# `Model interface` + +::: agents.models.interface diff --git a/tests/docs/ref/models/openai_chatcompletions.md b/tests/docs/ref/models/openai_chatcompletions.md new file mode 100644 index 0000000..76cf563 --- /dev/null +++ b/tests/docs/ref/models/openai_chatcompletions.md @@ -0,0 +1,3 @@ +# `OpenAI Chat Completions model` + +::: agents.models.openai_chatcompletions diff --git a/tests/docs/ref/models/openai_responses.md b/tests/docs/ref/models/openai_responses.md new file mode 100644 index 0000000..e1794ba --- /dev/null +++ b/tests/docs/ref/models/openai_responses.md @@ -0,0 +1,3 @@ +# `OpenAI Responses model` + +::: agents.models.openai_responses diff --git a/tests/docs/ref/result.md b/tests/docs/ref/result.md new file mode 100644 index 0000000..3a9e4a9 --- /dev/null +++ b/tests/docs/ref/result.md @@ -0,0 +1,3 @@ +# `Results` + +::: agents.result diff --git a/tests/docs/ref/run.md b/tests/docs/ref/run.md new file mode 100644 index 0000000..ddf4475 --- /dev/null +++ b/tests/docs/ref/run.md @@ -0,0 +1,8 @@ +# `Runner` + +::: agents.run + + options: + members: + - Runner + - RunConfig diff --git a/tests/docs/ref/run_context.md b/tests/docs/ref/run_context.md new file mode 100644 index 0000000..49e8730 --- /dev/null +++ b/tests/docs/ref/run_context.md @@ -0,0 +1,3 @@ +# `Run context` + +::: agents.run_context diff --git a/tests/docs/ref/stream_events.md b/tests/docs/ref/stream_events.md new file mode 100644 index 0000000..ea48431 --- /dev/null +++ b/tests/docs/ref/stream_events.md @@ -0,0 +1,3 @@ +# `Streaming events` + +::: agents.stream_events diff --git a/tests/docs/ref/tool.md b/tests/docs/ref/tool.md new file mode 100644 index 0000000..887bef7 --- /dev/null +++ b/tests/docs/ref/tool.md @@ -0,0 +1,3 @@ +# `Tools` + +::: agents.tool diff --git a/tests/docs/ref/tracing/create.md b/tests/docs/ref/tracing/create.md new file mode 100644 index 0000000..c983e33 --- /dev/null +++ b/tests/docs/ref/tracing/create.md @@ -0,0 +1,3 @@ +# `Creating traces/spans` + +::: agents.tracing.create diff --git a/tests/docs/ref/tracing/index.md b/tests/docs/ref/tracing/index.md new file mode 100644 index 0000000..88a0fe6 --- /dev/null +++ b/tests/docs/ref/tracing/index.md @@ -0,0 +1,3 @@ +# Tracing module + +::: agents.tracing diff --git a/tests/docs/ref/tracing/processor_interface.md b/tests/docs/ref/tracing/processor_interface.md new file mode 100644 index 0000000..9fb04e8 --- /dev/null +++ b/tests/docs/ref/tracing/processor_interface.md @@ -0,0 +1,3 @@ +# `Processor interface` + +::: agents.tracing.processor_interface diff --git a/tests/docs/ref/tracing/processors.md b/tests/docs/ref/tracing/processors.md new file mode 100644 index 0000000..d7ac4af --- /dev/null +++ b/tests/docs/ref/tracing/processors.md @@ -0,0 +1,3 @@ +# `Processors` + +::: agents.tracing.processors diff --git a/tests/docs/ref/tracing/scope.md b/tests/docs/ref/tracing/scope.md new file mode 100644 index 0000000..7b5b9fd --- /dev/null +++ b/tests/docs/ref/tracing/scope.md @@ -0,0 +1,3 @@ +# `Scope` + +::: agents.tracing.scope diff --git a/tests/docs/ref/tracing/setup.md b/tests/docs/ref/tracing/setup.md new file mode 100644 index 0000000..1dc6a0f --- /dev/null +++ b/tests/docs/ref/tracing/setup.md @@ -0,0 +1,3 @@ +# `Setup` + +::: agents.tracing.setup diff --git a/tests/docs/ref/tracing/span_data.md b/tests/docs/ref/tracing/span_data.md new file mode 100644 index 0000000..6ace7a8 --- /dev/null +++ b/tests/docs/ref/tracing/span_data.md @@ -0,0 +1,3 @@ +# `Span data` + +::: agents.tracing.span_data diff --git a/tests/docs/ref/tracing/spans.md b/tests/docs/ref/tracing/spans.md new file mode 100644 index 0000000..9071707 --- /dev/null +++ b/tests/docs/ref/tracing/spans.md @@ -0,0 +1,9 @@ +# `Spans` + +::: agents.tracing.spans + + options: + members: + - Span + - NoOpSpan + - SpanImpl diff --git a/tests/docs/ref/tracing/traces.md b/tests/docs/ref/tracing/traces.md new file mode 100644 index 0000000..0b7377f --- /dev/null +++ b/tests/docs/ref/tracing/traces.md @@ -0,0 +1,3 @@ +# `Traces` + +::: agents.tracing.traces diff --git a/tests/docs/ref/tracing/util.md b/tests/docs/ref/tracing/util.md new file mode 100644 index 0000000..2be3d58 --- /dev/null +++ b/tests/docs/ref/tracing/util.md @@ -0,0 +1,3 @@ +# `Util` + +::: agents.tracing.util diff --git a/tests/docs/ref/usage.md b/tests/docs/ref/usage.md new file mode 100644 index 0000000..b8b29db --- /dev/null +++ b/tests/docs/ref/usage.md @@ -0,0 +1,3 @@ +# `Usage` + +::: agents.usage diff --git a/tests/docs/results.md b/tests/docs/results.md new file mode 100644 index 0000000..d1864fa --- /dev/null +++ b/tests/docs/results.md @@ -0,0 +1,52 @@ +# Results + +When you call the `Runner.run` methods, you either get a: + +- [`RunResult`][agents.result.RunResult] if you call `run` or `run_sync` +- [`RunResultStreaming`][agents.result.RunResultStreaming] if you call `run_streamed` + +Both of these inherit from [`RunResultBase`][agents.result.RunResultBase], which is where most useful information is present. + +## Final output + +The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either: + +- a `str`, if the last agent didn't have an `output_type` defined +- an object of type `last_agent.output_type`, if the agent had an output type defined. + +!!! note + + `final_output` is of type `Any`. We can't statically type this, because of handoffs. If handoffs occur, that means any Agent might be the last agent, so we don't statically know the set of possible output types. + +## Inputs for the next turn + +You can use [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] to turn the result into an input list that concatenates the original input you provided, to the items generated during the agent run. This makes it convenient to take the outputs of one agent run and pass them into another run, or to run it in a loop and append new user inputs each time. + +## Last agent + +The [`last_agent`][agents.result.RunResultBase.last_agent] property contains the last agent that ran. Depending on your application, this is often useful for the next time the user inputs something. For example, if you have a frontline triage agent that hands off to a language-specific agent, you can store the last agent, and re-use it the next time the user messages the agent. + +## New items + +The [`new_items`][agents.result.RunResultBase.new_items] property contains the new items generated during the run. The items are [`RunItem`][agents.items.RunItem]s. A run item wraps the raw item generated by the LLM. + +- [`MessageOutputItem`][agents.items.MessageOutputItem] indicates a message from the LLM. The raw item is the message generated. +- [`HandoffCallItem`][agents.items.HandoffCallItem] indicates that the LLM called the handoff tool. The raw item is the tool call item from the LLM. +- [`HandoffOutputItem`][agents.items.HandoffOutputItem] indicates that a handoff occured. The raw item is the tool response to the handoff tool call. You can also access the source/target agents from the item. +- [`ToolCallItem`][agents.items.ToolCallItem] indicates that the LLM invoked a tool. +- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] indicates that a tool was called. The raw item is the tool response. You can also access the tool output from the item. +- [`ReasoningItem`][agents.items.ReasoningItem] indicates a reasoning item from the LLM. The raw item is the reasoning generated. + +## Other information + +### Guardrail results + +The [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] properties contain the results of the guardrails, if any. Guardrail results can sometimes contain useful information you want to log or store, so we make these available to you. + +### Raw responses + +The [`raw_responses`][agents.result.RunResultBase.raw_responses] property contains the [`ModelResponse`][agents.items.ModelResponse]s generated by the LLM. + +### Original input + +The [`input`][agents.result.RunResultBase.input] property contains the original input you provided to the `run` method. In most cases you won't need this, but it's available in case you do. diff --git a/tests/docs/running_agents.md b/tests/docs/running_agents.md new file mode 100644 index 0000000..a2f137c --- /dev/null +++ b/tests/docs/running_agents.md @@ -0,0 +1,95 @@ +# Running agents + +You can run agents via the [`Runner`][agents.run.Runner] class. You have 3 options: + +1. [`Runner.run()`][agents.run.Runner.run], which runs async and returns a [`RunResult`][agents.result.RunResult]. +2. [`Runner.run_sync()`][agents.run.Runner.run_sync], which is a sync method and just runs `.run()` under the hood. +3. [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which runs async and returns a [`RunResultStreaming`][agents.result.RunResultStreaming]. It calls the LLM in streaming mode, and streams those events to you as they are received. + +```python +from agents import Agent, Runner + +async def main(): + agent = Agent(name="Assistant", instructions="You are a helpful assistant") + + result = await Runner.run(agent, "Write a haiku about recursion in programming.") + print(result.final_output) + # Code within the code, + # Functions calling themselves, + # Infinite loop's dance. +``` + +Read more in the [results guide](results.md). + +## The agent loop + +When you use the run method in `Runner`, you pass in a starting agent and input. The input can either be a string (which is considered a user message), or a list of input items, which are the items in the OpenAI Responses API. + +The runner then runs a loop: + +1. We call the LLM for the current agent, with the current input. +2. The LLM produces its output. + 1. If the LLM returns a `final_output`, the loop ends and we return the result. + 2. If the LLM does a handoff, we update the current agent and input, and re-run the loop. + 3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop. +3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception. + +!!! note + + The rule for whether the LLM output is considered as a "final output" is that it produces text output with the desired type, and there are no tool calls. + +## Streaming + +Streaming allows you to additionally receive streaming events as the LLM runs. Once the stream is done, the [`RunResultStreaming`][agents.result.RunResultStreaming] will contain the complete information about the run, including all the new outputs produces. You can call `.stream_events()` for the streaming events. Read more in the [streaming guide](streaming.md). + +## Run config + +The `run_config` parameter lets you configure some global settings for the agent run: + +- [`model`][agents.run.RunConfig.model]: Allows setting a global LLM model to use, irrespective of what `model` each Agent has. +- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to OpenAI. +- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`. +- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs. +- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details. +- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run. +- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs. +- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The session ID is an optional field that lets you link traces across multiple runs. +- [`trace_metadata`][agents.run.RunConfig.trace_metadata]: Metadata to include on all traces. + +## Conversations/chat threads + +Calling any of the run methods can result in one or more agents running (and hence one or more LLM calls), but it represents a single logical turn in a chat conversation. For example: + +1. User turn: user enter text +2. Runner run: first agent calls LLM, runs tools, does a handoff to a second agent, second agent runs more tools, and then produces an output. + +At the end of the agent run, you can choose what to show to the user. For example, you might show the user every new item generated by the agents, or just the final output. Either way, the user might then ask a followup question, in which case you can call the run method again. + +You can use the base [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] method to get the inputs for the next turn. + +```python +async def main(): + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + with trace(workflow_name="Conversation", group_id=thread_id): + # First turn + result = await Runner.run(agent, "What city is the Golden Gate Bridge in?") + print(result.final_output) + # San Francisco + + # Second turn + new_input = output.to_input_list() + [{"role": "user", "content": "What state is it in?"}] + result = await Runner.run(agent, new_input) + print(result.final_output) + # California +``` + +## Exceptions + +The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview: + +- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK. +- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods. +- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools. +- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK. +- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped. diff --git a/tests/docs/streaming.md b/tests/docs/streaming.md new file mode 100644 index 0000000..b2c7c09 --- /dev/null +++ b/tests/docs/streaming.md @@ -0,0 +1,87 @@ +# Streaming + +Streaming lets you subscribe to updates of the agent run as it proceeds. This can be useful for showing the end-user progress updates and partial responses. + +To stream, you can call [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which will give you a [`RunResultStreaming`][agents.result.RunResultStreaming]. Calling `result.stream_events()` gives you an async stream of [`StreamEvent`][agents.stream_events.StreamEvent] objects, which are described below. + +## Raw response events + +[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in OpenAI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated. + +For example, this will output the text generated by the LLM token-by-token. + +```python +import asyncio +from openai.types.responses import ResponseTextDeltaEvent +from agents import Agent, Runner + +async def main(): + agent = Agent( + name="Joker", + instructions="You are a helpful assistant.", + ) + + result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") + async for event in result.stream_events(): + if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): + print(event.data.delta, end="", flush=True) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Run item events and agent events + +[`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent]s are higher level events. They inform you when an item has been fully generated. This allows you to push progress updates at the level of "message generated", "tool ran", etc, instead of each token. Similarly, [`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] gives you updates when the current agent changes (e.g. as the result of a handoff). + +For example, this will ignore raw events and stream updates to the user. + +```python +import asyncio +import random +from agents import Agent, ItemHelpers, Runner, function_tool + +@function_tool +def how_many_jokes() -> int: + return random.randint(1, 10) + + +async def main(): + agent = Agent( + name="Joker", + instructions="First call the `how_many_jokes` tool, then tell that many jokes.", + tools=[how_many_jokes], + ) + + result = Runner.run_streamed( + agent, + input="Hello", + ) + print("=== Run starting ===") + + async for event in result.stream_events(): + # We'll ignore the raw responses event deltas + if event.type == "raw_response_event": + continue + # When the agent updates, print that + elif event.type == "agent_updated_stream_event": + print(f"Agent updated: {event.new_agent.name}") + continue + # When items are generated, print them + elif event.type == "run_item_stream_event": + if event.item.type == "tool_call_item": + print("-- Tool was called") + elif event.item.type == "tool_call_output_item": + print(f"-- Tool output: {event.item.output}") + elif event.item.type == "message_output_item": + print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") + else: + pass # Ignore other event types + + print("=== Run complete ===") + + +if __name__ == "__main__": + asyncio.run(main()) +``` diff --git a/tests/docs/stylesheets/extra.css b/tests/docs/stylesheets/extra.css new file mode 100644 index 0000000..89cf164 --- /dev/null +++ b/tests/docs/stylesheets/extra.css @@ -0,0 +1,194 @@ +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 400; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Regular.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 400; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-RegularItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 500; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Medium.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 500; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-MediumItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 600; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Semibold.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 600; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-SemiboldItalic.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: normal; + font-weight: 700; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-Bold.woff2") + format("woff2"); +} + +@font-face { + font-display: swap; + font-family: "OpenAI Sans"; + font-style: italic; + font-weight: 700; + src: url("https://cdn.openai.com/common/fonts/openai-sans/OpenAISans-BoldItalic.woff2") + format("woff2"); +} + +/* + Root variables that apply to all color schemes. + Material for MkDocs automatically switches data-md-color-scheme + between "default" (light) and "slate" (dark) when you use the toggles. +*/ +:root { + /* Font families */ + --md-text-font: "OpenAI Sans", -apple-system, system-ui, Helvetica, Arial, + sans-serif; + --md-typeface-heading: "OpenAI Sans", -apple-system, system-ui, Helvetica, + Arial, sans-serif; + + /* Global color variables */ + --md-default-fg-color: #212121; + --md-default-bg-color: #ffffff; + --md-primary-fg-color: #000; + --md-accent-fg-color: #000; + + /* Code block theming */ + --md-code-fg-color: red; + --md-code-bg-color: #f5f5f5; + + /* Tables, blockquotes, etc. */ + --md-table-row-border-color: #e0e0e0; + --md-admonition-bg-color: #f8f8f8; + --md-admonition-title-fg-color: #373737; + --md-default-fg-color--light: #000; + + --md-typeset-a-color: #000; + --md-accent-fg-color: #000; + + --md-code-fg-color: #000; +} + +/* Header styling */ +.md-header { + background-color: #000; +} + +.md-header--shadow { + box-shadow: none; +} + +.md-content .md-typeset h1 { + color: #000; +} + +.md-typeset p, +.md-typeset li { + font-size: 16px; +} + +.md-typeset__table p { + line-height: 1em; +} + +.md-nav { + font-size: 14px; +} +.md-nav__title { + color: #000; + font-weight: 600; +} + +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4 { + font-weight: 600; +} + +.md-typeset h1 code { + color: #000; + padding: 0; + background-color: transparent; +} +.md-footer { + display: none; +} + +.md-header__title { + margin-left: 0 !important; +} + +.md-typeset .admonition, +.md-typeset details { + border: none; + outline: none; + border-radius: 8px; + overflow: hidden; +} + +.md-typeset pre > code { + font-size: 14px; +} + +.md-typeset__table code { + font-size: 14px; +} + +/* Custom link styling */ +.md-content a { + text-decoration: none; +} + +.md-content a:hover { + text-decoration: underline; +} + +/* Code block styling */ +.md-content .md-code__content { + border-radius: 8px; +} + +.md-clipboard.md-icon { + color: #9e9e9e; +} + +/* Reset scrollbar styling to browser default with high priority */ +.md-sidebar__scrollwrap { + scrollbar-color: auto !important; +} diff --git a/tests/docs/tools.md b/tests/docs/tools.md new file mode 100644 index 0000000..f7a8869 --- /dev/null +++ b/tests/docs/tools.md @@ -0,0 +1,270 @@ +# Tools + +Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK: + +- Hosted tools: these run on LLM servers alongside the AI models. OpenAI offers retrieval, web search and computer use as hosted tools. +- Function calling: these allow you to use any Python function as a tool. +- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them. + +## Hosted tools + +OpenAI offers a few built-in tools when using the [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel]: + +- The [`WebSearchTool`][agents.tool.WebSearchTool] lets an agent search the web. +- The [`FileSearchTool`][agents.tool.FileSearchTool] allows retrieving information from your OpenAI Vector Stores. +- The [`ComputerTool`][agents.tool.ComputerTool] allows automating computer use tasks. + +```python +from agents import Agent, FileSearchTool, Runner, WebSearchTool + +agent = Agent( + name="Assistant", + tools=[ + WebSearchTool(), + FileSearchTool( + max_num_results=3, + vector_store_ids=["VECTOR_STORE_ID"], + ), + ], +) + +async def main(): + result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?") + print(result.final_output) +``` + +## Function tools + +You can use any Python function as a tool. The Agents SDK will setup the tool automatically: + +- The name of the tool will be the name of the Python function (or you can provide a name) +- Tool description will be taken from the docstring of the function (or you can provide a description) +- The schema for the function inputs is automatically created from the function's arguments +- Descriptions for each input are taken from the docstring of the function, unless disabled + +We use Python's `inspect` module to extract the function signature, along with [`griffe`](https://mkdocstrings.github.io/griffe/) to parse docstrings and `pydantic` for schema creation. + +```python +import json + +from typing_extensions import TypedDict, Any + +from agents import Agent, FunctionTool, RunContextWrapper, function_tool + + +class Location(TypedDict): + lat: float + long: float + +@function_tool # (1)! +async def fetch_weather(location: Location) -> str: + # (2)! + """Fetch the weather for a given location. + + Args: + location: The location to fetch the weather for. + """ + # In real life, we'd fetch the weather from a weather API + return "sunny" + + +@function_tool(name_override="fetch_data") # (3)! +def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str: + """Read the contents of a file. + + Args: + path: The path to the file to read. + directory: The directory to read the file from. + """ + # In real life, we'd read the file from the file system + return "" + + +agent = Agent( + name="Assistant", + tools=[fetch_weather, read_file], # (4)! +) + +for tool in agent.tools: + if isinstance(tool, FunctionTool): + print(tool.name) + print(tool.description) + print(json.dumps(tool.params_json_schema, indent=2)) + print() + +``` + +1. You can use any Python types as arguments to your functions, and the function can be sync or async. +2. Docstrings, if present, are used to capture descriptions and argument descriptions +3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc. +4. You can pass the decorated functions to the list of tools. + +??? note "Expand to see output" + + ``` + fetch_weather + Fetch the weather for a given location. + { + "$defs": { + "Location": { + "properties": { + "lat": { + "title": "Lat", + "type": "number" + }, + "long": { + "title": "Long", + "type": "number" + } + }, + "required": [ + "lat", + "long" + ], + "title": "Location", + "type": "object" + } + }, + "properties": { + "location": { + "$ref": "#/$defs/Location", + "description": "The location to fetch the weather for." + } + }, + "required": [ + "location" + ], + "title": "fetch_weather_args", + "type": "object" + } + + fetch_data + Read the contents of a file. + { + "properties": { + "path": { + "description": "The path to the file to read.", + "title": "Path", + "type": "string" + }, + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The directory to read the file from.", + "title": "Directory" + } + }, + "required": [ + "path" + ], + "title": "fetch_data_args", + "type": "object" + } + ``` + +### Custom function tools + +Sometimes, you don't want to use a Python function as a tool. You can directly create a [`FunctionTool`][agents.tool.FunctionTool] if you prefer. You'll need to provide: + +- `name` +- `description` +- `params_json_schema`, which is the JSON schema for the arguments +- `on_invoke_tool`, which is an async function that receives the context and the arguments as a JSON string, and must return the tool output as a string. + +```python +from typing import Any + +from pydantic import BaseModel + +from agents import RunContextWrapper, FunctionTool + + + +def do_some_work(data: str) -> str: + return "done" + + +class FunctionArgs(BaseModel): + username: str + age: int + + +async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: + parsed = FunctionArgs.model_validate_json(args) + return do_some_work(data=f"{parsed.username} is {parsed.age} years old") + + +tool = FunctionTool( + name="process_user", + description="Processes extracted user data", + params_json_schema=FunctionArgs.model_json_schema(), + on_invoke_tool=run_function, +) +``` + +### Automatic argument and docstring parsing + +As mentioned before, we automatically parse the function signature to extract the schema for the tool, and we parse the docstring to extract descriptions for the tool and for individual arguments. Some notes on that: + +1. The signature parsing is done via the `inspect` module. We use type annotations to understand the types for the arguments, and dynamically build a Pydantic model to represent the overall schema. It supports most types, including Python primitives, Pydantic models, TypedDicts, and more. +2. We use `griffe` to parse docstrings. Supported docstring formats are `google`, `sphinx` and `numpy`. We attempt to automatically detect the docstring format, but this is best-effort and you can explicitly set it when calling `function_tool`. You can also disable docstring parsing by setting `use_docstring_info` to `False`. + +The code for the schema extraction lives in [`agents.function_schema`][]. + +## Agents as tools + +In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools. + +```python +from agents import Agent, Runner +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You translate the user's message to Spanish", +) + +french_agent = Agent( + name="French agent", + instructions="You translate the user's message to French", +) + +orchestrator_agent = Agent( + name="orchestrator_agent", + instructions=( + "You are a translation agent. You use the tools given to you to translate." + "If asked for multiple translations, you call the relevant tools." + ), + tools=[ + spanish_agent.as_tool( + tool_name="translate_to_spanish", + tool_description="Translate the user's message to Spanish", + ), + french_agent.as_tool( + tool_name="translate_to_french", + tool_description="Translate the user's message to French", + ), + ], +) + +async def main(): + result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.") + print(result.final_output) +``` + +## Handling errors in function tools + +When you create a function tool via `@function_tool`, you can pass a `failure_error_function`. This is a function that provides an error response to the LLM in case the tool call crashes. + +- By default (i.e. if you don't pass anything), it runs a `default_tool_error_function` which tells the LLM an error occurred. +- If you pass your own error function, it runs that instead, and sends the response to the LLM. +- If you explicitly pass `None`, then any tool call errors will be re-raised for you to handle. This could be a `ModelBehaviorError` if the model produced invalid JSON, or a `UserError` if your code crashed, etc. + +If you are manually creating a `FunctionTool` object, then you must handle errors inside the `on_invoke_tool` function. diff --git a/tests/docs/tracing.md b/tests/docs/tracing.md new file mode 100644 index 0000000..fbf2ae4 --- /dev/null +++ b/tests/docs/tracing.md @@ -0,0 +1,95 @@ +# Tracing + +The Agents SDK includes built-in tracing, collecting a comprehensive record of events during an agent run: LLM generations, tool calls, handoffs, guardrails, and even custom events that occur. Using the [Traces dashboard](https://platform.openai.com/traces), you can debug, visualize, and monitor your workflows during development and in production. + +!!!note + + Tracing is enabled by default. There are two ways to disable tracing: + + 1. You can globally disable tracing by setting the env var `OPENAI_AGENTS_DISABLE_TRACING=1` + 2. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True` + +## Traces and spans + +- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties: + - `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service". + - `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`. + - `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID. + - `disabled`: If True, the trace will not be recorded. + - `metadata`: Optiona metadata for the trace. +- **Spans** represent operations that have a start and end time. Spans have: + - `started_at` and `ended_at` timestamps. + - `trace_id`, to represent the trace they belong to + - `parent_id`, which points to the parent Span of this Span (if any) + - `span_data`, which is information about the Span. For example, `AgentSpanData` contains information about the Agent, `GenerationSpanData` contains information about the LLM generation, etc. + +## Default tracing + +By default, the SDK traces the following: + +- The entire `Runner.{run, run_sync, run_streamed}()` is wrapped in a `trace()`. +- Each time an agent runs, it is wrapped in `agent_span()` +- LLM generations are wrapped in `generation_span()` +- Function tool calls are each wrapped in `function_span()` +- Guardrails are wrapped in `guardrail_span()` +- Handoffs are wrapped in `handoff_span()` + +By default, the trace is named "Agent trace". You can set this name if you use `trace`, or you can can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig]. + +In addition, you can set up [custom trace processors](#custom-tracing-processors) to push traces to other destinations (as a replacement, or secondary destination). + +## Higher level traces + +Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`. + +```python +from agents import Agent, Runner, trace + +async def main(): + agent = Agent(name="Joke generator", instructions="Tell funny jokes.") + + with trace("Joke workflow"): # (1)! + first_result = await Runner.run(agent, "Tell me a joke") + second_result = await Runner.run(agent, f"Rate this joke: {first_output.final_output}") + print(f"Joke: {first_result.final_output}") + print(f"Rating: {second_result.final_output}") +``` + +1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces. + +## Creating traces + +You can use the [`trace()`][agents.tracing.trace] function to create a trace. Traces need to be started and finished. You have two options to do so: + +1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time. +2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish]. + +The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace. + +## Creating spans + +You can use the various [`*_span()`][agents.tracing.create] methods to create a span. In general, you don't need to manually create spans. A [`custom_span()`][agents.tracing.custom_span] function is available for tracking custom span information. + +Spans are automatically part of the current trace, and are nested under the nearest current span, which is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). + +## Sensitive data + +Some spans track potentially sensitive data. For example, the `generation_span()` stores the inputs/outputs of the LLM generation, and `function_span()` stores the inputs/outputs of function calls. These may contain sensitive data, so you can disable capturing that data via [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]. + +## Custom tracing processors + +The high level architecture for tracing is: + +- At initialization, we create a global [`TraceProvider`][agents.tracing.setup.TraceProvider], which is responsible for creating traces. +- We configure the `TraceProvider` with a [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor] that sends traces/spans in batches to a [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter], which exports the spans and traces to the OpenAI backend in batches. + +To customize this default setup, to send traces to alternative or additional backends or modifying exporter behavior, you have two options: + +1. [`add_trace_processor()`][agents.tracing.add_trace_processor] lets you add an **additional** trace processor that will receive traces and spans as they are ready. This lets you do your own processing in addition to sending traces to OpenAI's backend. +2. [`set_trace_processors()`][agents.tracing.set_trace_processors] lets you **replace** the default processors with your own trace processors. This means traces will not be sent to the OpenAI backend unless you include a `TracingProcessor` that does so. + +External trace processors include: + +- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk) +- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents) +- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk) diff --git a/tests/examples/__init__.py b/tests/examples/__init__.py new file mode 100644 index 0000000..e333a2e --- /dev/null +++ b/tests/examples/__init__.py @@ -0,0 +1,3 @@ +# Make the examples directory into a package to avoid top-level module name collisions. +# This is needed so that mypy treats files like examples/customer_service/main.py and +# examples/researcher_app/main.py as distinct modules rather than both named "main". diff --git a/tests/examples/__pycache__/__init__.cpython-313.pyc b/tests/examples/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b690047891112aa3055c72f6c7c91365406e7d6f GIT binary patch literal 151 zcmey&%ge<81ip^v(wP|<7#@Q-FaYF(!DkjAHI*TqL6gyMB|{MtkoOrRam!3Uv^ce> zSidM&KRG`oRX;I3HLs*NB|E;LD8D4Xq_QAYKeZw;w;(6ASU)~KGcU6wK3=b&@)n0p dZhlH>PO4oIE6^B_HN_ytM`lJw#v*1Q3jp1mBz^z@ literal 0 HcmV?d00001 diff --git a/tests/examples/agent_patterns/README.md b/tests/examples/agent_patterns/README.md new file mode 100644 index 0000000..4599b00 --- /dev/null +++ b/tests/examples/agent_patterns/README.md @@ -0,0 +1,54 @@ +# Common agentic patterns + +This folder contains examples of different common patterns for agents. + +## Deterministic flows + +A common tactic is to break down a task into a series of smaller steps. Each task can be performed by an agent, and the output of one agent is used as input to the next. For example, if your task was to generate a story, you could break it down into the following steps: + +1. Generate an outline +2. Generate the story +3. Generate the ending + +Each of these steps can be performed by an agent. The output of one agent is used as input to the next. + +See the [`deterministic.py`](./deterministic.py) file for an example of this. + +## Handoffs and routing + +In many situations, you have specialized sub-agents that handle specific tasks. You can use handoffs to route the task to the right agent. + +For example, you might have a frontline agent that receives a request, and then hands off to a specialized agent based on the language of the request. +See the [`routing.py`](./routing.py) file for an example of this. + +## Agents as tools + +The mental model for handoffs is that the new agent "takes over". It sees the previous conversation history, and owns the conversation from that point onwards. However, this is not the only way to use agents. You can also use agents as a tool - the tool agent goes off and runs on its own, and then returns the result to the original agent. + +For example, you could model the translation task above as tool calls instead: rather than handing over to the language-specific agent, you could call the agent as a tool, and then use the result in the next step. This enables things like translating multiple languages at once. + +See the [`agents_as_tools.py`](./agents_as_tools.py) file for an example of this. + +## LLM-as-a-judge + +LLMs can often improve the quality of their output if given feedback. A common pattern is to generate a response using a model, and then use a second model to provide feedback. You can even use a small model for the initial generation and a larger model for the feedback, to optimize cost. + +For example, you could use an LLM to generate an outline for a story, and then use a second LLM to evaluate the outline and provide feedback. You can then use the feedback to improve the outline, and repeat until the LLM is satisfied with the outline. + +See the [`llm_as_a_judge.py`](./llm_as_a_judge.py) file for an example of this. + +## Parallelization + +Running multiple agents in parallel is a common pattern. This can be useful for both latency (e.g. if you have multiple steps that don't depend on each other) and also for other reasons e.g. generating multiple responses and picking the best one. + +See the [`parallelization.py`](./parallelization.py) file for an example of this. It runs a translation agent multiple times in parallel, and then picks the best translation. + +## Guardrails + +Related to parallelization, you often want to run input guardrails to make sure the inputs to your agents are valid. For example, if you have a customer support agent, you might want to make sure that the user isn't trying to ask for help with a math problem. + +You can definitely do this without any special Agents SDK features by using parallelization, but we support a special guardrail primitive. Guardrails can have a "tripwire" - if the tripwire is triggered, the agent execution will immediately stop and a `GuardrailTripwireTriggered` exception will be raised. + +This is really useful for latency: for example, you might have a very fast model that runs the guardrail and a slow model that runs the actual agent. You wouldn't want to wait for the slow model to finish, so guardrails let you quickly reject invalid inputs. + +See the [`guardrails.py`](./guardrails.py) file for an example of this. diff --git a/tests/examples/agent_patterns/agents_as_tools.py b/tests/examples/agent_patterns/agents_as_tools.py new file mode 100644 index 0000000..9fd118e --- /dev/null +++ b/tests/examples/agent_patterns/agents_as_tools.py @@ -0,0 +1,79 @@ +import asyncio + +from agents import Agent, ItemHelpers, MessageOutputItem, Runner, trace + +""" +This example shows the agents-as-tools pattern. The frontline agent receives a user message and +then picks which agents to call, as tools. In this case, it picks from a set of translation +agents. +""" + +spanish_agent = Agent( + name="spanish_agent", + instructions="You translate the user's message to Spanish", + handoff_description="An english to spanish translator", +) + +french_agent = Agent( + name="french_agent", + instructions="You translate the user's message to French", + handoff_description="An english to french translator", +) + +italian_agent = Agent( + name="italian_agent", + instructions="You translate the user's message to Italian", + handoff_description="An english to italian translator", +) + +orchestrator_agent = Agent( + name="orchestrator_agent", + instructions=( + "You are a translation agent. You use the tools given to you to translate." + "If asked for multiple translations, you call the relevant tools in order." + "You never translate on your own, you always use the provided tools." + ), + tools=[ + spanish_agent.as_tool( + tool_name="translate_to_spanish", + tool_description="Translate the user's message to Spanish", + ), + french_agent.as_tool( + tool_name="translate_to_french", + tool_description="Translate the user's message to French", + ), + italian_agent.as_tool( + tool_name="translate_to_italian", + tool_description="Translate the user's message to Italian", + ), + ], +) + +synthesizer_agent = Agent( + name="synthesizer_agent", + instructions="You inspect translations, correct them if needed, and produce a final concatenated response.", +) + + +async def main(): + msg = input("Hi! What would you like translated, and to which languages? ") + + # Run the entire orchestration in a single trace + with trace("Orchestrator evaluator"): + orchestrator_result = await Runner.run(orchestrator_agent, msg) + + for item in orchestrator_result.new_items: + if isinstance(item, MessageOutputItem): + text = ItemHelpers.text_message_output(item) + if text: + print(f" - Translation step: {text}") + + synthesizer_result = await Runner.run( + synthesizer_agent, orchestrator_result.to_input_list() + ) + + print(f"\n\nFinal response:\n{synthesizer_result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/deterministic.py b/tests/examples/agent_patterns/deterministic.py new file mode 100644 index 0000000..0c163af --- /dev/null +++ b/tests/examples/agent_patterns/deterministic.py @@ -0,0 +1,80 @@ +import asyncio + +from pydantic import BaseModel + +from agents import Agent, Runner, trace + +""" +This example demonstrates a deterministic flow, where each step is performed by an agent. +1. The first agent generates a story outline +2. We feed the outline into the second agent +3. The second agent checks if the outline is good quality and if it is a scifi story +4. If the outline is not good quality or not a scifi story, we stop here +5. If the outline is good quality and a scifi story, we feed the outline into the third agent +6. The third agent writes the story +""" + +story_outline_agent = Agent( + name="story_outline_agent", + instructions="Generate a very short story outline based on the user's input.", +) + + +class OutlineCheckerOutput(BaseModel): + good_quality: bool + is_scifi: bool + + +outline_checker_agent = Agent( + name="outline_checker_agent", + instructions="Read the given story outline, and judge the quality. Also, determine if it is a scifi story.", + output_type=OutlineCheckerOutput, +) + +story_agent = Agent( + name="story_agent", + instructions="Write a short story based on the given outline.", + output_type=str, +) + + +async def main(): + input_prompt = input("What kind of story do you want? ") + + # Ensure the entire workflow is a single trace + with trace("Deterministic story flow"): + # 1. Generate an outline + outline_result = await Runner.run( + story_outline_agent, + input_prompt, + ) + print("Outline generated") + + # 2. Check the outline + outline_checker_result = await Runner.run( + outline_checker_agent, + outline_result.final_output, + ) + + # 3. Add a gate to stop if the outline is not good quality or not a scifi story + assert isinstance(outline_checker_result.final_output, OutlineCheckerOutput) + if not outline_checker_result.final_output.good_quality: + print("Outline is not good quality, so we stop here.") + exit(0) + + if not outline_checker_result.final_output.is_scifi: + print("Outline is not a scifi story, so we stop here.") + exit(0) + + print("Outline is good quality and a scifi story, so we continue to write the story.") + + # 4. Write the story + story_result = await Runner.run( + story_agent, + outline_result.final_output, + ) + print(f"Story: {story_result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/input_guardrails.py b/tests/examples/agent_patterns/input_guardrails.py new file mode 100644 index 0000000..6259188 --- /dev/null +++ b/tests/examples/agent_patterns/input_guardrails.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import asyncio + +from pydantic import BaseModel + +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + TResponseInputItem, + input_guardrail, +) + +""" +This example shows how to use guardrails. + +Guardrails are checks that run in parallel to the agent's execution. +They can be used to do things like: +- Check if input messages are off-topic +- Check that output messages don't violate any policies +- Take over control of the agent's execution if an unexpected input is detected + +In this example, we'll setup an input guardrail that trips if the user is asking to do math homework. +If the guardrail trips, we'll respond with a refusal message. +""" + + +### 1. An agent-based guardrail that is triggered if the user is asking to do math homework +class MathHomeworkOutput(BaseModel): + is_math_homework: bool + reasoning: str + + +guardrail_agent = Agent( + name="Guardrail check", + instructions="Check if the user is asking you to do their math homework.", + output_type=MathHomeworkOutput, +) + + +@input_guardrail +async def math_guardrail( + context: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + """This is an input guardrail function, which happens to call an agent to check if the input + is a math homework question. + """ + result = await Runner.run(guardrail_agent, input, context=context.context) + final_output = result.final_output_as(MathHomeworkOutput) + + return GuardrailFunctionOutput( + output_info=final_output, + tripwire_triggered=not final_output.is_math_homework, + ) + + +### 2. The run loop + + +async def main(): + agent = Agent( + name="Customer support agent", + instructions="You are a customer support agent. You help customers with their questions.", + input_guardrails=[math_guardrail], + ) + + input_data: list[TResponseInputItem] = [] + + while True: + user_input = input("Enter a message: ") + input_data.append( + { + "role": "user", + "content": user_input, + } + ) + + try: + result = await Runner.run(agent, input_data) + print(result.final_output) + # If the guardrail didn't trigger, we use the result as the input for the next run + input_data = result.to_input_list() + except InputGuardrailTripwireTriggered: + # If the guardrail triggered, we instead add a refusal message to the input + message = "Sorry, I can't help you with your math homework." + print(message) + input_data.append( + { + "role": "assistant", + "content": message, + } + ) + + # Sample run: + # Enter a message: What's the capital of California? + # The capital of California is Sacramento. + # Enter a message: Can you help me solve for x: 2x + 5 = 11 + # Sorry, I can't help you with your math homework. + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/llm_as_a_judge.py b/tests/examples/agent_patterns/llm_as_a_judge.py new file mode 100644 index 0000000..d13a67c --- /dev/null +++ b/tests/examples/agent_patterns/llm_as_a_judge.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import Literal + +from agents import Agent, ItemHelpers, Runner, TResponseInputItem, trace + +""" +This example shows the LLM as a judge pattern. The first agent generates an outline for a story. +The second agent judges the outline and provides feedback. We loop until the judge is satisfied +with the outline. +""" + +story_outline_generator = Agent( + name="story_outline_generator", + instructions=( + "You generate a very short story outline based on the user's input." + "If there is any feedback provided, use it to improve the outline." + ), +) + + +@dataclass +class EvaluationFeedback: + score: Literal["pass", "needs_improvement", "fail"] + feedback: str + + +evaluator = Agent[None]( + name="evaluator", + instructions=( + "You evaluate a story outline and decide if it's good enough." + "If it's not good enough, you provide feedback on what needs to be improved." + "Never give it a pass on the first try." + ), + output_type=EvaluationFeedback, +) + + +async def main() -> None: + msg = input("What kind of story would you like to hear? ") + input_items: list[TResponseInputItem] = [{"content": msg, "role": "user"}] + + latest_outline: str | None = None + + # We'll run the entire workflow in a single trace + with trace("LLM as a judge"): + while True: + story_outline_result = await Runner.run( + story_outline_generator, + input_items, + ) + + input_items = story_outline_result.to_input_list() + latest_outline = ItemHelpers.text_message_outputs(story_outline_result.new_items) + print("Story outline generated") + + evaluator_result = await Runner.run(evaluator, input_items) + result: EvaluationFeedback = evaluator_result.final_output + + print(f"Evaluator score: {result.score}") + + if result.score == "pass": + print("Story outline is good enough, exiting.") + break + + print("Re-running with feedback") + + input_items.append({"content": f"Feedback: {result.feedback}", "role": "user"}) + + print(f"Final story outline: {latest_outline}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/output_guardrails.py b/tests/examples/agent_patterns/output_guardrails.py new file mode 100644 index 0000000..526a085 --- /dev/null +++ b/tests/examples/agent_patterns/output_guardrails.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import asyncio +import json + +from pydantic import BaseModel, Field + +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + output_guardrail, +) + +""" +This example shows how to use output guardrails. + +Output guardrails are checks that run on the final output of an agent. +They can be used to do things like: +- Check if the output contains sensitive data +- Check if the output is a valid response to the user's message + +In this example, we'll use a (contrived) example where we check if the agent's response contains +a phone number. +""" + + +# The agent's output type +class MessageOutput(BaseModel): + reasoning: str = Field(description="Thoughts on how to respond to the user's message") + response: str = Field(description="The response to the user's message") + user_name: str | None = Field(description="The name of the user who sent the message, if known") + + +@output_guardrail +async def sensitive_data_check( + context: RunContextWrapper, agent: Agent, output: MessageOutput +) -> GuardrailFunctionOutput: + phone_number_in_response = "650" in output.response + phone_number_in_reasoning = "650" in output.reasoning + + return GuardrailFunctionOutput( + output_info={ + "phone_number_in_response": phone_number_in_response, + "phone_number_in_reasoning": phone_number_in_reasoning, + }, + tripwire_triggered=phone_number_in_response or phone_number_in_reasoning, + ) + + +agent = Agent( + name="Assistant", + instructions="You are a helpful assistant.", + output_type=MessageOutput, + output_guardrails=[sensitive_data_check], +) + + +async def main(): + # This should be ok + await Runner.run(agent, "What's the capital of California?") + print("First message passed") + + # This should trip the guardrail + try: + result = await Runner.run( + agent, "My phone number is 650-123-4567. Where do you think I live?" + ) + print( + f"Guardrail didn't trip - this is unexpected. Output: {json.dumps(result.final_output.model_dump(), indent=2)}" + ) + + except OutputGuardrailTripwireTriggered as e: + print(f"Guardrail tripped. Info: {e.guardrail_result.output.output_info}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/parallelization.py b/tests/examples/agent_patterns/parallelization.py new file mode 100644 index 0000000..fe2a8ec --- /dev/null +++ b/tests/examples/agent_patterns/parallelization.py @@ -0,0 +1,61 @@ +import asyncio + +from agents import Agent, ItemHelpers, Runner, trace + +""" +This example shows the parallelization pattern. We run the agent three times in parallel, and pick +the best result. +""" + +spanish_agent = Agent( + name="spanish_agent", + instructions="You translate the user's message to Spanish", +) + +translation_picker = Agent( + name="translation_picker", + instructions="You pick the best Spanish translation from the given options.", +) + + +async def main(): + msg = input("Hi! Enter a message, and we'll translate it to Spanish.\n\n") + + # Ensure the entire workflow is a single trace + with trace("Parallel translation"): + res_1, res_2, res_3 = await asyncio.gather( + Runner.run( + spanish_agent, + msg, + ), + Runner.run( + spanish_agent, + msg, + ), + Runner.run( + spanish_agent, + msg, + ), + ) + + outputs = [ + ItemHelpers.text_message_outputs(res_1.new_items), + ItemHelpers.text_message_outputs(res_2.new_items), + ItemHelpers.text_message_outputs(res_3.new_items), + ] + + translations = "\n\n".join(outputs) + print(f"\n\nTranslations:\n\n{translations}") + + best_translation = await Runner.run( + translation_picker, + f"Input: {msg}\n\nTranslations:\n{translations}", + ) + + print("\n\n-----") + + print(f"Best translation: {best_translation.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/agent_patterns/routing.py b/tests/examples/agent_patterns/routing.py new file mode 100644 index 0000000..3dcaefa --- /dev/null +++ b/tests/examples/agent_patterns/routing.py @@ -0,0 +1,70 @@ +import asyncio +import uuid + +from openai.types.responses import ResponseContentPartDoneEvent, ResponseTextDeltaEvent + +from agents import Agent, RawResponsesStreamEvent, Runner, TResponseInputItem, trace + +""" +This example shows the handoffs/routing pattern. The triage agent receives the first message, and +then hands off to the appropriate agent based on the language of the request. Responses are +streamed to the user. +""" + +french_agent = Agent( + name="french_agent", + instructions="You only speak French", +) + +spanish_agent = Agent( + name="spanish_agent", + instructions="You only speak Spanish", +) + +english_agent = Agent( + name="english_agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="triage_agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[french_agent, spanish_agent, english_agent], +) + + +async def main(): + # We'll create an ID for this conversation, so we can link each trace + conversation_id = str(uuid.uuid4().hex[:16]) + + msg = input("Hi! We speak French, Spanish and English. How can I help? ") + agent = triage_agent + inputs: list[TResponseInputItem] = [{"content": msg, "role": "user"}] + + while True: + # Each conversation turn is a single trace. Normally, each input from the user would be an + # API request to your app, and you can wrap the request in a trace() + with trace("Routing example", group_id=conversation_id): + result = Runner.run_streamed( + agent, + input=inputs, + ) + async for event in result.stream_events(): + if not isinstance(event, RawResponsesStreamEvent): + continue + data = event.data + if isinstance(data, ResponseTextDeltaEvent): + print(data.delta, end="", flush=True) + elif isinstance(data, ResponseContentPartDoneEvent): + print("\n") + + inputs = result.to_input_list() + print("\n") + + user_msg = input("Enter a message: ") + inputs.append({"content": user_msg, "role": "user"}) + agent = result.current_agent + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/basic/agent_lifecycle_example.py b/tests/examples/basic/agent_lifecycle_example.py new file mode 100644 index 0000000..bc0bbe4 --- /dev/null +++ b/tests/examples/basic/agent_lifecycle_example.py @@ -0,0 +1,112 @@ +import asyncio +import random +from typing import Any + +from pydantic import BaseModel + +from agents import Agent, AgentHooks, RunContextWrapper, Runner, Tool, function_tool + + +class CustomAgentHooks(AgentHooks): + def __init__(self, display_name: str): + self.event_counter = 0 + self.display_name = display_name + + async def on_start(self, context: RunContextWrapper, agent: Agent) -> None: + self.event_counter += 1 + print(f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started") + + async def on_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended with output {output}" + ) + + async def on_handoff(self, context: RunContextWrapper, agent: Agent, source: Agent) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {source.name} handed off to {agent.name}" + ) + + async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} started tool {tool.name}" + ) + + async def on_tool_end( + self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str + ) -> None: + self.event_counter += 1 + print( + f"### ({self.display_name}) {self.event_counter}: Agent {agent.name} ended tool {tool.name} with result {result}" + ) + + +### + + +@function_tool +def random_number(max: int) -> int: + """ + Generate a random number up to the provided maximum. + """ + return random.randint(0, max) + + +@function_tool +def multiply_by_two(x: int) -> int: + """Simple multiplication by two.""" + return x * 2 + + +class FinalResult(BaseModel): + number: int + + +multiply_agent = Agent( + name="Multiply Agent", + instructions="Multiply the number by 2 and then return the final result.", + tools=[multiply_by_two], + output_type=FinalResult, + hooks=CustomAgentHooks(display_name="Multiply Agent"), +) + +start_agent = Agent( + name="Start Agent", + instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", + tools=[random_number], + output_type=FinalResult, + handoffs=[multiply_agent], + hooks=CustomAgentHooks(display_name="Start Agent"), +) + + +async def main() -> None: + user_input = input("Enter a max number: ") + await Runner.run( + start_agent, + input=f"Generate a random number between 0 and {user_input}.", + ) + + print("Done!") + + +if __name__ == "__main__": + asyncio.run(main()) +""" +$ python examples/basic/agent_lifecycle_example.py + +Enter a max number: 250 +### (Start Agent) 1: Agent Start Agent started +### (Start Agent) 2: Agent Start Agent started tool random_number +### (Start Agent) 3: Agent Start Agent ended tool random_number with result 37 +### (Start Agent) 4: Agent Start Agent started +### (Start Agent) 5: Agent Start Agent handed off to Multiply Agent +### (Multiply Agent) 1: Agent Multiply Agent started +### (Multiply Agent) 2: Agent Multiply Agent started tool multiply_by_two +### (Multiply Agent) 3: Agent Multiply Agent ended tool multiply_by_two with result 74 +### (Multiply Agent) 4: Agent Multiply Agent started +### (Multiply Agent) 5: Agent Multiply Agent ended with output number=74 +Done! +""" diff --git a/tests/examples/basic/dynamic_system_prompt.py b/tests/examples/basic/dynamic_system_prompt.py new file mode 100644 index 0000000..7bcf90c --- /dev/null +++ b/tests/examples/basic/dynamic_system_prompt.py @@ -0,0 +1,69 @@ +import asyncio +import random +from typing import Literal + +from agents import Agent, RunContextWrapper, Runner + + +class CustomContext: + def __init__(self, style: Literal["haiku", "pirate", "robot"]): + self.style = style + + +def custom_instructions( + run_context: RunContextWrapper[CustomContext], agent: Agent[CustomContext] +) -> str: + context = run_context.context + if context.style == "haiku": + return "Only respond in haikus." + elif context.style == "pirate": + return "Respond as a pirate." + else: + return "Respond as a robot and say 'beep boop' a lot." + + +agent = Agent( + name="Chat agent", + instructions=custom_instructions, +) + + +async def main(): + choice: Literal["haiku", "pirate", "robot"] = random.choice(["haiku", "pirate", "robot"]) + context = CustomContext(style=choice) + print(f"Using style: {choice}\n") + + user_message = "Tell me a joke." + print(f"User: {user_message}") + result = await Runner.run(agent, user_message, context=context) + + print(f"Assistant: {result.final_output}") + + +if __name__ == "__main__": + asyncio.run(main()) + +""" +$ python examples/basic/dynamic_system_prompt.py + +Using style: haiku + +User: Tell me a joke. +Assistant: Why don't eggs tell jokes? +They might crack each other's shells, +leaving yolk on face. + +$ python examples/basic/dynamic_system_prompt.py +Using style: robot + +User: Tell me a joke. +Assistant: Beep boop! Why was the robot so bad at soccer? Beep boop... because it kept kicking up a debug! Beep boop! + +$ python examples/basic/dynamic_system_prompt.py +Using style: pirate + +User: Tell me a joke. +Assistant: Why did the pirate go to school? + +To improve his arrr-ticulation! Har har har! 🏴‍☠️ +""" diff --git a/tests/examples/basic/hello_world.py b/tests/examples/basic/hello_world.py new file mode 100644 index 0000000..169290d --- /dev/null +++ b/tests/examples/basic/hello_world.py @@ -0,0 +1,20 @@ +import asyncio + +from agents import Agent, Runner + + +async def main(): + agent = Agent( + name="Assistant", + instructions="You only respond in haikus.", + ) + + result = await Runner.run(agent, "Tell me about recursion in programming.") + print(result.final_output) + # Function calls itself, + # Looping in smaller pieces, + # Endless by design. + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/basic/lifecycle_example.py b/tests/examples/basic/lifecycle_example.py new file mode 100644 index 0000000..9b36510 --- /dev/null +++ b/tests/examples/basic/lifecycle_example.py @@ -0,0 +1,118 @@ +import asyncio +import random +from typing import Any + +from pydantic import BaseModel + +from agents import Agent, RunContextWrapper, RunHooks, Runner, Tool, Usage, function_tool + + +class ExampleHooks(RunHooks): + def __init__(self): + self.event_counter = 0 + + def _usage_to_str(self, usage: Usage) -> str: + return f"{usage.requests} requests, {usage.input_tokens} input tokens, {usage.output_tokens} output tokens, {usage.total_tokens} total tokens" + + async def on_agent_start(self, context: RunContextWrapper, agent: Agent) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Agent {agent.name} started. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_agent_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Agent {agent.name} ended with output {output}. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_tool_start(self, context: RunContextWrapper, agent: Agent, tool: Tool) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Tool {tool.name} started. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_tool_end( + self, context: RunContextWrapper, agent: Agent, tool: Tool, result: str + ) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Tool {tool.name} ended with result {result}. Usage: {self._usage_to_str(context.usage)}" + ) + + async def on_handoff( + self, context: RunContextWrapper, from_agent: Agent, to_agent: Agent + ) -> None: + self.event_counter += 1 + print( + f"### {self.event_counter}: Handoff from {from_agent.name} to {to_agent.name}. Usage: {self._usage_to_str(context.usage)}" + ) + + +hooks = ExampleHooks() + +### + + +@function_tool +def random_number(max: int) -> int: + """Generate a random number up to the provided max.""" + return random.randint(0, max) + + +@function_tool +def multiply_by_two(x: int) -> int: + """Return x times two.""" + return x * 2 + + +class FinalResult(BaseModel): + number: int + + +multiply_agent = Agent( + name="Multiply Agent", + instructions="Multiply the number by 2 and then return the final result.", + tools=[multiply_by_two], + output_type=FinalResult, +) + +start_agent = Agent( + name="Start Agent", + instructions="Generate a random number. If it's even, stop. If it's odd, hand off to the multipler agent.", + tools=[random_number], + output_type=FinalResult, + handoffs=[multiply_agent], +) + + +async def main() -> None: + user_input = input("Enter a max number: ") + await Runner.run( + start_agent, + hooks=hooks, + input=f"Generate a random number between 0 and {user_input}.", + ) + + print("Done!") + + +if __name__ == "__main__": + asyncio.run(main()) +""" +$ python examples/basic/lifecycle_example.py + +Enter a max number: 250 +### 1: Agent Start Agent started. Usage: 0 requests, 0 input tokens, 0 output tokens, 0 total tokens +### 2: Tool random_number started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 3: Tool random_number ended with result 101. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 4: Agent Start Agent started. Usage: 1 requests, 148 input tokens, 15 output tokens, 163 total tokens +### 5: Handoff from Start Agent to Multiply Agent. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens +### 6: Agent Multiply Agent started. Usage: 2 requests, 323 input tokens, 30 output tokens, 353 total tokens +### 7: Tool multiply_by_two started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 8: Tool multiply_by_two ended with result 202. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 9: Agent Multiply Agent started. Usage: 3 requests, 504 input tokens, 46 output tokens, 550 total tokens +### 10: Agent Multiply Agent ended with output number=202. Usage: 4 requests, 714 input tokens, 63 output tokens, 777 total tokens +Done! + +""" diff --git a/tests/examples/basic/stream_items.py b/tests/examples/basic/stream_items.py new file mode 100644 index 0000000..c1f2257 --- /dev/null +++ b/tests/examples/basic/stream_items.py @@ -0,0 +1,65 @@ +import asyncio +import random + +from agents import Agent, ItemHelpers, Runner, function_tool + + +@function_tool +def how_many_jokes() -> int: + return random.randint(1, 10) + + +async def main(): + agent = Agent( + name="Joker", + instructions="First call the `how_many_jokes` tool, then tell that many jokes.", + tools=[how_many_jokes], + ) + + result = Runner.run_streamed( + agent, + input="Hello", + ) + print("=== Run starting ===") + async for event in result.stream_events(): + # We'll ignore the raw responses event deltas + if event.type == "raw_response_event": + continue + elif event.type == "agent_updated_stream_event": + print(f"Agent updated: {event.new_agent.name}") + continue + elif event.type == "run_item_stream_event": + if event.item.type == "tool_call_item": + print("-- Tool was called") + elif event.item.type == "tool_call_output_item": + print(f"-- Tool output: {event.item.output}") + elif event.item.type == "message_output_item": + print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}") + else: + pass # Ignore other event types + + print("=== Run complete ===") + + +if __name__ == "__main__": + asyncio.run(main()) + + # === Run starting === + # Agent updated: Joker + # -- Tool was called + # -- Tool output: 4 + # -- Message output: + # Sure, here are four jokes for you: + + # 1. **Why don't skeletons fight each other?** + # They don't have the guts! + + # 2. **What do you call fake spaghetti?** + # An impasta! + + # 3. **Why did the scarecrow win an award?** + # Because he was outstanding in his field! + + # 4. **Why did the bicycle fall over?** + # Because it was two-tired! + # === Run complete === diff --git a/tests/examples/basic/stream_text.py b/tests/examples/basic/stream_text.py new file mode 100644 index 0000000..a73c1fe --- /dev/null +++ b/tests/examples/basic/stream_text.py @@ -0,0 +1,21 @@ +import asyncio + +from openai.types.responses import ResponseTextDeltaEvent + +from agents import Agent, Runner + + +async def main(): + agent = Agent( + name="Joker", + instructions="You are a helpful assistant.", + ) + + result = Runner.run_streamed(agent, input="Please tell me 5 jokes.") + async for event in result.stream_events(): + if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent): + print(event.data.delta, end="", flush=True) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/customer_service/main.py b/tests/examples/customer_service/main.py new file mode 100644 index 0000000..bd802e2 --- /dev/null +++ b/tests/examples/customer_service/main.py @@ -0,0 +1,169 @@ +from __future__ import annotations as _annotations + +import asyncio +import random +import uuid + +from pydantic import BaseModel + +from agents import ( + Agent, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + RunContextWrapper, + Runner, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, + function_tool, + handoff, + trace, +) +from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX + +### CONTEXT + + +class AirlineAgentContext(BaseModel): + passenger_name: str | None = None + confirmation_number: str | None = None + seat_number: str | None = None + flight_number: str | None = None + + +### TOOLS + + +@function_tool( + name_override="faq_lookup_tool", description_override="Lookup frequently asked questions." +) +async def faq_lookup_tool(question: str) -> str: + if "bag" in question or "baggage" in question: + return ( + "You are allowed to bring one bag on the plane. " + "It must be under 50 pounds and 22 inches x 14 inches x 9 inches." + ) + elif "seats" in question or "plane" in question: + return ( + "There are 120 seats on the plane. " + "There are 22 business class seats and 98 economy seats. " + "Exit rows are rows 4 and 16. " + "Rows 5-8 are Economy Plus, with extra legroom. " + ) + elif "wifi" in question: + return "We have free wifi on the plane, join Airline-Wifi" + return "I'm sorry, I don't know the answer to that question." + + +@function_tool +async def update_seat( + context: RunContextWrapper[AirlineAgentContext], confirmation_number: str, new_seat: str +) -> str: + """ + Update the seat for a given confirmation number. + + Args: + confirmation_number: The confirmation number for the flight. + new_seat: The new seat to update to. + """ + # Update the context based on the customer's input + context.context.confirmation_number = confirmation_number + context.context.seat_number = new_seat + # Ensure that the flight number has been set by the incoming handoff + assert context.context.flight_number is not None, "Flight number is required" + return f"Updated seat to {new_seat} for confirmation number {confirmation_number}" + + +### HOOKS + + +async def on_seat_booking_handoff(context: RunContextWrapper[AirlineAgentContext]) -> None: + flight_number = f"FLT-{random.randint(100, 999)}" + context.context.flight_number = flight_number + + +### AGENTS + +faq_agent = Agent[AirlineAgentContext]( + name="FAQ Agent", + handoff_description="A helpful agent that can answer questions about the airline.", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + You are an FAQ agent. If you are speaking to a customer, you probably were transferred to from the triage agent. + Use the following routine to support the customer. + # Routine + 1. Identify the last question asked by the customer. + 2. Use the faq lookup tool to answer the question. Do not rely on your own knowledge. + 3. If you cannot answer the question, transfer back to the triage agent.""", + tools=[faq_lookup_tool], +) + +seat_booking_agent = Agent[AirlineAgentContext]( + name="Seat Booking Agent", + handoff_description="A helpful agent that can update a seat on a flight.", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + You are a seat booking agent. If you are speaking to a customer, you probably were transferred to from the triage agent. + Use the following routine to support the customer. + # Routine + 1. Ask for their confirmation number. + 2. Ask the customer what their desired seat number is. + 3. Use the update seat tool to update the seat on the flight. + If the customer asks a question that is not related to the routine, transfer back to the triage agent. """, + tools=[update_seat], +) + +triage_agent = Agent[AirlineAgentContext]( + name="Triage Agent", + handoff_description="A triage agent that can delegate a customer's request to the appropriate agent.", + instructions=( + f"{RECOMMENDED_PROMPT_PREFIX} " + "You are a helpful triaging agent. You can use your tools to delegate questions to other appropriate agents." + ), + handoffs=[ + faq_agent, + handoff(agent=seat_booking_agent, on_handoff=on_seat_booking_handoff), + ], +) + +faq_agent.handoffs.append(triage_agent) +seat_booking_agent.handoffs.append(triage_agent) + + +### RUN + + +async def main(): + current_agent: Agent[AirlineAgentContext] = triage_agent + input_items: list[TResponseInputItem] = [] + context = AirlineAgentContext() + + # Normally, each input from the user would be an API request to your app, and you can wrap the request in a trace() + # Here, we'll just use a random UUID for the conversation ID + conversation_id = uuid.uuid4().hex[:16] + + while True: + user_input = input("Enter your message: ") + with trace("Customer service", group_id=conversation_id): + input_items.append({"content": user_input, "role": "user"}) + result = await Runner.run(current_agent, input_items, context=context) + + for new_item in result.new_items: + agent_name = new_item.agent.name + if isinstance(new_item, MessageOutputItem): + print(f"{agent_name}: {ItemHelpers.text_message_output(new_item)}") + elif isinstance(new_item, HandoffOutputItem): + print( + f"Handed off from {new_item.source_agent.name} to {new_item.target_agent.name}" + ) + elif isinstance(new_item, ToolCallItem): + print(f"{agent_name}: Calling a tool") + elif isinstance(new_item, ToolCallOutputItem): + print(f"{agent_name}: Tool call output: {new_item.output}") + else: + print(f"{agent_name}: Skipping item: {new_item.__class__.__name__}") + input_items = result.to_input_list() + current_agent = result.last_agent + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/handoffs/message_filter.py b/tests/examples/handoffs/message_filter.py new file mode 100644 index 0000000..9dd56ef --- /dev/null +++ b/tests/examples/handoffs/message_filter.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import json +import random + +from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace +from agents.extensions import handoff_filters + + +@function_tool +def random_number_tool(max: int) -> int: + """Return a random integer between 0 and the given maximum.""" + return random.randint(0, max) + + +def spanish_handoff_message_filter(handoff_message_data: HandoffInputData) -> HandoffInputData: + # First, we'll remove any tool-related messages from the message history + handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) + + # Second, we'll also remove the first two items from the history, just for demonstration + history = ( + tuple(handoff_message_data.input_history[2:]) + if isinstance(handoff_message_data.input_history, tuple) + else handoff_message_data.input_history + ) + + return HandoffInputData( + input_history=history, + pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), + new_items=tuple(handoff_message_data.new_items), + ) + + +first_agent = Agent( + name="Assistant", + instructions="Be extremely concise.", + tools=[random_number_tool], +) + +spanish_agent = Agent( + name="Spanish Assistant", + instructions="You only speak Spanish and are extremely concise.", + handoff_description="A Spanish-speaking assistant.", +) + +second_agent = Agent( + name="Assistant", + instructions=( + "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." + ), + handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], +) + + +async def main(): + # Trace the entire run as a single workflow + with trace(workflow_name="Message filtering"): + # 1. Send a regular message to the first agent + result = await Runner.run(first_agent, input="Hi, my name is Sora.") + + print("Step 1 done") + + # 2. Ask it to square a number + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [{"content": "Can you generate a random number between 0 and 100?", "role": "user"}], + ) + + print("Step 2 done") + + # 3. Call the second agent + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user", + } + ], + ) + + print("Step 3 done") + + # 4. Cause a handoff to occur + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "Por favor habla en español. ¿Cuál es mi nombre y dónde vivo?", + "role": "user", + } + ], + ) + + print("Step 4 done") + + print("\n===Final messages===\n") + + # 5. That should have caused spanish_handoff_message_filter to be called, which means the + # output should be missing the first two messages, and have no tool calls. + # Let's print the messages to see what happened + for message in result.to_input_list(): + print(json.dumps(message, indent=2)) + # tool_calls = message.tool_calls if isinstance(message, AssistantMessage) else None + + # print(f"{message.role}: {message.content}\n - Tool calls: {tool_calls or 'None'}") + """ + $python examples/handoffs/message_filter.py + Step 1 done + Step 2 done + Step 3 done + Step 4 done + + ===Final messages=== + + { + "content": "Can you generate a random number between 0 and 100?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "Sure! Here's a random number between 0 and 100: **42**.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "As of the most recent estimates, the population of New York City is approximately 8.6 million people. However, this number is constantly changing due to various factors such as migration and birth rates. For the latest and most accurate information, it's always a good idea to check the official data from sources like the U.S. Census Bureau.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "No tengo acceso a esa informaci\u00f3n personal, solo s\u00e9 lo que me has contado: vives en Nueva York.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + """ + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/tests/examples/handoffs/message_filter_streaming.py b/tests/examples/handoffs/message_filter_streaming.py new file mode 100644 index 0000000..8d1b420 --- /dev/null +++ b/tests/examples/handoffs/message_filter_streaming.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +import json +import random + +from agents import Agent, HandoffInputData, Runner, function_tool, handoff, trace +from agents.extensions import handoff_filters + + +@function_tool +def random_number_tool(max: int) -> int: + """Return a random integer between 0 and the given maximum.""" + return random.randint(0, max) + + +def spanish_handoff_message_filter(handoff_message_data: HandoffInputData) -> HandoffInputData: + # First, we'll remove any tool-related messages from the message history + handoff_message_data = handoff_filters.remove_all_tools(handoff_message_data) + + # Second, we'll also remove the first two items from the history, just for demonstration + history = ( + tuple(handoff_message_data.input_history[2:]) + if isinstance(handoff_message_data.input_history, tuple) + else handoff_message_data.input_history + ) + + return HandoffInputData( + input_history=history, + pre_handoff_items=tuple(handoff_message_data.pre_handoff_items), + new_items=tuple(handoff_message_data.new_items), + ) + + +first_agent = Agent( + name="Assistant", + instructions="Be extremely concise.", + tools=[random_number_tool], +) + +spanish_agent = Agent( + name="Spanish Assistant", + instructions="You only speak Spanish and are extremely concise.", + handoff_description="A Spanish-speaking assistant.", +) + +second_agent = Agent( + name="Assistant", + instructions=( + "Be a helpful assistant. If the user speaks Spanish, handoff to the Spanish assistant." + ), + handoffs=[handoff(spanish_agent, input_filter=spanish_handoff_message_filter)], +) + + +async def main(): + # Trace the entire run as a single workflow + with trace(workflow_name="Streaming message filter"): + # 1. Send a regular message to the first agent + result = await Runner.run(first_agent, input="Hi, my name is Sora.") + + print("Step 1 done") + + # 2. Ask it to square a number + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [{"content": "Can you generate a random number between 0 and 100?", "role": "user"}], + ) + + print("Step 2 done") + + # 3. Call the second agent + result = await Runner.run( + second_agent, + input=result.to_input_list() + + [ + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user", + } + ], + ) + + print("Step 3 done") + + # 4. Cause a handoff to occur + stream_result = Runner.run_streamed( + second_agent, + input=result.to_input_list() + + [ + { + "content": "Por favor habla en español. ¿Cuál es mi nombre y dónde vivo?", + "role": "user", + } + ], + ) + async for _ in stream_result.stream_events(): + pass + + print("Step 4 done") + + print("\n===Final messages===\n") + + # 5. That should have caused spanish_handoff_message_filter to be called, which means the + # output should be missing the first two messages, and have no tool calls. + # Let's print the messages to see what happened + for item in stream_result.to_input_list(): + print(json.dumps(item, indent=2)) + """ + $python examples/handoffs/message_filter_streaming.py + Step 1 done + Step 2 done + Step 3 done + Tu nombre y lugar de residencia no los tengo disponibles. Solo sé que mencionaste vivir en la ciudad de Nueva York. + Step 4 done + + ===Final messages=== + + { + "content": "Can you generate a random number between 0 and 100?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "Sure! Here's a random number between 0 and 100: **37**.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "I live in New York City. Whats the population of the city?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "As of the latest estimates, New York City's population is approximately 8.5 million people. Would you like more information about the city?", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + { + "content": "Por favor habla en espa\u00f1ol. \u00bfCu\u00e1l es mi nombre y d\u00f3nde vivo?", + "role": "user" + } + { + "id": "...", + "content": [ + { + "annotations": [], + "text": "No s\u00e9 tu nombre, pero me dijiste que vives en Nueva York.", + "type": "output_text" + } + ], + "role": "assistant", + "status": "completed", + "type": "message" + } + """ + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/tests/examples/research_bot/README.md b/tests/examples/research_bot/README.md new file mode 100644 index 0000000..4060983 --- /dev/null +++ b/tests/examples/research_bot/README.md @@ -0,0 +1,25 @@ +# Research bot + +This is a simple example of a multi-agent research bot. To run it: + +```bash +python -m examples.research_bot.main +``` + +## Architecture + +The flow is: + +1. User enters their research topic +2. `planner_agent` comes up with a plan to search the web for information. The plan is a list of search queries, with a search term and a reason for each query. +3. For each search item, we run a `search_agent`, which uses the Web Search tool to search for that term and summarize the results. These all run in parallel. +4. Finally, the `writer_agent` receives the search summaries, and creates a written report. + +## Suggested improvements + +If you're building your own research bot, some ideas to add to this are: + +1. Retrieval: Add support for fetching relevant information from a vector store. You could use the File Search tool for this. +2. Image and file upload: Allow users to attach PDFs or other files, as baseline context for the research. +3. More planning and thinking: Models often produce better results given more time to think. Improve the planning process to come up with a better plan, and add an evaluation step so that the model can choose to improve it's results, search for more stuff, etc. +4. Code execution: Allow running code, which is useful for data analysis. diff --git a/tests/examples/research_bot/__init__.py b/tests/examples/research_bot/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/examples/research_bot/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/examples/research_bot/__pycache__/__init__.cpython-313.pyc b/tests/examples/research_bot/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d1acc93f4bc9c71f89e2d10e448709f37eac452 GIT binary patch literal 165 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 literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/__pycache__/printer.cpython-313.pyc b/tests/examples/research_bot/__pycache__/printer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e9ac76c97d24a53666bbf942c29c78cac09adea GIT binary patch literal 2823 zcmb7G&2JM&6rcUF*I$WCNJtztF>$H01Y#NzQa)8m1XV&)nyn=&qOOd+Nes@~on5!U zp~|IIBDI1`q#_|TQhP{F<=8)=r;1exj#hwBd#JdXCKcDdH|w=cvX{JN|P;!z>nbN4h|ZV-LGYvRuFxN|O*Hg#&I=wd2c%;~Ajylz-#?z&c@MXPAtEa@rz zMrN^8&_PEx^$g7}XjZXUNR?dJhD$dcS<~`H-qJKDS_5;~lLTNJ^DvMl@_29e+CF=) zvMhcZ*curH7JQsM_+xold??4ZQ)4 z(mi!Ye&iME8#vn+*b(?*x`Y-@rU6d3@A|fF3j+5 z9YS;P&HoCaG>6AjUCO@(Z}PhAP=slUY4!<(9K#E*iN=Av5^HT3Tnq>|Cb~Q{UX`;L zJp{%OVf~38Zr#1Lp0xW%?!ROAkKMN(^qp#gXBQ65d5_hZrPA9VT_q`EK;3Pb`^{Eh zgu{!34)VbzLdxKr!6Pk=HbozV;pKR%ihL5jm;r5-`-oF{LrQZqE#mRAie;{mwjYi| zG`G}jbOlvN3#xdWZ?XcHLL`oPfFAD}t$M>1EMsh>xnoWHL#L3Y%Obq_rcA z-!QQsv21bEg<|A55ic-Uvh4?vc?$@{!~Vo_Xlp3-^NEcUm7$aN(8)(br|ocZ`GVb< zyfgo_y=yCRure}Xk4*fYn7G^Z>%bk~RlY*SQvY&~s>0KFqRA5!^b!A{;SY(%N*gvc zBeSSOQwwU^Vlh`P;5@8ppO-U*+M9Mwo6A$vD&!5_C~6u-xanRbP|KiJn--<$up?fD zo=LIN(IF%Zm`+WD&TQqgTE?f@xr^`lh+?EdHuvZ_ozN`S5Bc96Z4Wog(w4xVIM2|7ObDaaf16qC^j(|mSexc1NFdXFLF1ehHEOWa=+ zSI=~R2HiMgo}C8ehPRG<$_4rieIH&x+7T0RtO^{*Jt19xk>nE+dqUzbefzoLzX=d# F{4b?4CgA`8 literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__init__.py b/tests/examples/research_bot/agents/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a094b5a5310588de48141f37146944bc4283a4ae GIT binary patch literal 172 zcmey&%ge<81b+h0rGx0lAOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=2{m|mnqGJ7` zT>a$ylvMr1^whkP;*{+8f};GA{F2IoRQ=S7#N2|MRG^yF;?%^VKEC5pNEtmiR literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/base_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f33d418871c61aa28bea1c82722cfc0b8c064195 GIT binary patch literal 929 zcmYjP&2G~`5MKW#ah%eW6p13WTPT8{QVY=w;zug#0i_}w++4s?WSni{HnFYU4OKY7 z4RN6yDwRj*Gw=>b*v z$>6ItHGvDB>Xp2L z0dXX|MX)VT|7Vvx(^I6iZn@S1+SPXzTJGv~`v>??T@~6!G!n%p2@m7Q@7EPkdgwFq zIPQ=>oEjTl5^Y}__@!*0Q(Xu-Vb>X^B%$7@CAvmM7)a6B3`xpsDtCYVB8NG$MFt?gfehU@tH!_ ztnFUN@ILhuHjPOlLLpVwmH!an6(C~aAnAuYBOF6|n!_2yonfD_HLH27(;}2cOH#2hxp#vv?NjO<)th3inXv|~TV}}w(d>ZuH5_o>Wfku#a zc6lus2|J*~=edKOt-8-EpNlo?jl%oPW1>K*zPE(~@DEDKZRm z0vF+nG@Er>n2SykN}QsYj5>bA!+^@~CCn+tX*F`#LWB7TUG`V#5^{#%7}2T> zaV`sSZ`z&*^dguFF!MpAnxZJbi%41ih8Dl0#neVuu8nWrIxWpA=GdyF8th-@=2HXq w)I@geVDqTES^xk5 literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/planner_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b836aaccefaaea442c2c69164006214838ee5e6f GIT binary patch literal 1133 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 literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/search_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3281242f38bbe1eca339d39aef19fa8b6c80d19 GIT binary patch literal 964 zcmY*XL2DC16rN3X)22;IYbgq%JbIBp&6XC?f+$i!nqmuXO0fjOWOtGb?#`?;v#r~k zXV3lz{WC>x?SgplS4dEfiqH*db{W^<0Y8F zG~25Gw%Jyt@Lr6VFh%o_4FX10IPOXrx16H+T1G4mm@!<8G(B?&c`GxZiU9@1bcqG3 zHP?m;(}q|4mT4RS2GxfnAzVFMUS1!hbQSOicJrAkIwXlX+!LarI;EJ>)! zK@JhgY_}{nx(!_nZgNSc4QxEzgp?{eQZyZFO9nC1+VTt7TwgztD#E&LnwkuSTe5_5 z0jcD|5*{jUEM*vC9Or8wBnZYLFcQY}n4zL1jhV&ZkeCRW29!UNGQyTtq!qoJErijUsNi6i9`V6BJv{5BrwG7Uk2Yqru=hO3Vb9XP6;mgtYRp_Z`r4@$f{rvLx| literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/summarization_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b809d7c5a494bde94badff40aa5cd0ecc2255b19 GIT binary patch literal 625 zcmZuvO^XyU5KVe!zlPbOiwVKG?lb#dl3JF z_#^x?f;Z`-p1kckc(-~!ZWbX_sK>#P*UuT_J2zTjbuAiq%rCl7TU#jkkw_0WIkxBQ%1b@H6Up~ zSZZ4>Y6}>G2@zd{xWouyhYw)AE1=>!Qw8t~i28k;z!URMJ3u}Zjj|d%!KI#8j9Mec zP?67T)rlzYlnENn@7!f3!h*0`ZZ#S}MLq=hcs`x}{Pg+WbPAWA@~~+{VHIm);keXL zqH$1p<6;;C&zIxeSQDEu_mP7sD^KeYavjmwFoDM&oR46fvUSd#I9AJZ6ODIhxvjEf zcaR$JOi!JnTMKQ*Z@|Q$?P4-Y+P=e5w?pGXq*`?Z;l-%e4(1rPCR2}{Z%=m4VTak+ z|5dEGB--cctDq^QRHn-3I$iH3HSOVpk`1={ t5e=EW+j{`Mu6k0+zlTEJzTOw7w|*SmxaucIFXi>Y!D;ecoKW}E{sH>R%#8p5 literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc b/tests/examples/research_bot/agents/__pycache__/writer_agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be550b1efb6bd192deb292e8968ec6c3d8a0aa0f GIT binary patch literal 1293 zcmY*ZPm9||6dzfV?e*@G4cm2h7n)83r5F->Lr6-X1X6k^3Hd{mQVND)RvO#GN;B%4 zQC#=dQ?I=Q+K8 zj*`wePPz{wXiv8Wz4zWc2;A1!+=!<-6FF|;M^m9JzX}NXL06Q6pqv_^qy`GY2oC8? z0JYqliHy#rozXc+E7cVJQauw!z82_NYOr*ILHxfk`eP+vF{D24T;@4F6|@BXT4w7^ zb|$C>IhBg%jrT3eRYr|21eFSpLH!y9&9oBqbg_8?H%1!El^yo{DQ2dtJfjl{#v(XO zQk4y6uBfhTE-`}#=?`9Ewli*NCQJ(QRG8XgEL5qjJQWCLlIuCulZ}`meKiy67DeU+ za4R-Ubp!|uR;b0y+|Y%t05SO*5kpxaSd9OlW{n&fl8P1_&N4k$9%{jD!-=#TPPtH1 zJ3}fEUnVS`uePR0?=QtJz}+-T%*CF7jMs6}h!HAxJeX?xIdy_fY+e8Vk& ze;o~O7uy4tnX1477w+1+OxAS5?N4-$T(hcVI61~jt<0a~*`VvXj456S#$1oFLT6R( zNaSw2nKkCIKErl&%SDD4MW*1}?AlF5fd3xcym5Er2KDV<8y{4oAa^O9?Up4eUgimpcKMi6Y;BpZLLGX^;ct;K|+U@xMMM#3qRlggI jemlA%`1#N$dk4#{{_^_A%ijKS@A$)3yx00=r%wGZ=IxVm literal 0 HcmV?d00001 diff --git a/tests/examples/research_bot/agents/planner_agent.py b/tests/examples/research_bot/agents/planner_agent.py new file mode 100644 index 0000000..e80a8e6 --- /dev/null +++ b/tests/examples/research_bot/agents/planner_agent.py @@ -0,0 +1,29 @@ +from pydantic import BaseModel + +from agents import Agent + +PROMPT = ( + "You are a helpful research assistant. Given a query, come up with a set of web searches " + "to perform to best answer the query. Output between 5 and 20 terms to query for." +) + + +class WebSearchItem(BaseModel): + reason: str + "Your reasoning for why this search is important to the query." + + query: str + "The search term to use for the web search." + + +class WebSearchPlan(BaseModel): + searches: list[WebSearchItem] + """A list of web searches to perform to best answer the query.""" + + +planner_agent = Agent( + name="PlannerAgent", + instructions=PROMPT, + model="gpt-4o", + output_type=WebSearchPlan, +) diff --git a/tests/examples/research_bot/agents/search_agent.py b/tests/examples/research_bot/agents/search_agent.py new file mode 100644 index 0000000..72cbc8e --- /dev/null +++ b/tests/examples/research_bot/agents/search_agent.py @@ -0,0 +1,18 @@ +from agents import Agent, WebSearchTool +from agents.model_settings import ModelSettings + +INSTRUCTIONS = ( + "You are a research assistant. Given a search term, you search the web for that term and" + "produce a concise summary of the results. The summary must 2-3 paragraphs and less than 300" + "words. Capture the main points. Write succintly, no need to have complete sentences or good" + "grammar. This will be consumed by someone synthesizing a report, so its vital you capture the" + "essence and ignore any fluff. Do not include any additional commentary other than the summary" + "itself." +) + +search_agent = Agent( + name="Search agent", + instructions=INSTRUCTIONS, + tools=[WebSearchTool()], + model_settings=ModelSettings(tool_choice="required"), +) diff --git a/tests/examples/research_bot/agents/writer_agent.py b/tests/examples/research_bot/agents/writer_agent.py new file mode 100644 index 0000000..7b7d01a --- /dev/null +++ b/tests/examples/research_bot/agents/writer_agent.py @@ -0,0 +1,33 @@ +# Agent used to synthesize a final report from the individual summaries. +from pydantic import BaseModel + +from agents import Agent + +PROMPT = ( + "You are a senior researcher tasked with writing a cohesive report for a research query. " + "You will be provided with the original query, and some initial research done by a research " + "assistant.\n" + "You should first come up with an outline for the report that describes the structure and " + "flow of the report. Then, generate the report and return that as your final output.\n" + "The final output should be in markdown format, and it should be lengthy and detailed. Aim " + "for 5-10 pages of content, at least 1000 words." +) + + +class ReportData(BaseModel): + short_summary: str + """A short 2-3 sentence summary of the findings.""" + + markdown_report: str + """The final report""" + + follow_up_questions: list[str] + """Suggested topics to research further""" + + +writer_agent = Agent( + name="WriterAgent", + instructions=PROMPT, + model="o3-mini", + output_type=ReportData, +) diff --git a/tests/examples/research_bot/main.py b/tests/examples/research_bot/main.py new file mode 100644 index 0000000..a0fd43d --- /dev/null +++ b/tests/examples/research_bot/main.py @@ -0,0 +1,12 @@ +import asyncio + +from .manager import ResearchManager + + +async def main() -> None: + query = input("What would you like to research? ") + await ResearchManager().run(query) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/research_bot/manager.py b/tests/examples/research_bot/manager.py new file mode 100644 index 0000000..47306f1 --- /dev/null +++ b/tests/examples/research_bot/manager.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +import asyncio +import time + +from rich.console import Console + +from agents import Runner, custom_span, gen_trace_id, trace + +from .agents.planner_agent import WebSearchItem, WebSearchPlan, planner_agent +from .agents.search_agent import search_agent +from .agents.writer_agent import ReportData, writer_agent +from .printer import Printer + + +class ResearchManager: + def __init__(self): + self.console = Console() + self.printer = Printer(self.console) + + async def run(self, query: str) -> None: + trace_id = gen_trace_id() + with trace("Research trace", trace_id=trace_id): + self.printer.update_item( + "trace_id", + f"View trace: https://platform.openai.com/traces/{trace_id}", + is_done=True, + hide_checkmark=True, + ) + + self.printer.update_item( + "starting", + "Starting research...", + is_done=True, + hide_checkmark=True, + ) + search_plan = await self._plan_searches(query) + search_results = await self._perform_searches(search_plan) + report = await self._write_report(query, search_results) + + final_report = f"Report summary\n\n{report.short_summary}" + self.printer.update_item("final_report", final_report, is_done=True) + + self.printer.end() + + print("\n\n=====REPORT=====\n\n") + print(f"Report: {report.markdown_report}") + print("\n\n=====FOLLOW UP QUESTIONS=====\n\n") + follow_up_questions = "\n".join(report.follow_up_questions) + print(f"Follow up questions: {follow_up_questions}") + + async def _plan_searches(self, query: str) -> WebSearchPlan: + self.printer.update_item("planning", "Planning searches...") + result = await Runner.run( + planner_agent, + f"Query: {query}", + ) + self.printer.update_item( + "planning", + f"Will perform {len(result.final_output.searches)} searches", + is_done=True, + ) + return result.final_output_as(WebSearchPlan) + + async def _perform_searches(self, search_plan: WebSearchPlan) -> list[str]: + with custom_span("Search the web"): + self.printer.update_item("searching", "Searching...") + num_completed = 0 + tasks = [asyncio.create_task(self._search(item)) for item in search_plan.searches] + results = [] + for task in asyncio.as_completed(tasks): + result = await task + if result is not None: + results.append(result) + num_completed += 1 + self.printer.update_item( + "searching", f"Searching... {num_completed}/{len(tasks)} completed" + ) + self.printer.mark_item_done("searching") + return results + + async def _search(self, item: WebSearchItem) -> str | None: + input = f"Search term: {item.query}\nReason for searching: {item.reason}" + try: + result = await Runner.run( + search_agent, + input, + ) + return str(result.final_output) + except Exception: + return None + + async def _write_report(self, query: str, search_results: list[str]) -> ReportData: + self.printer.update_item("writing", "Thinking about report...") + input = f"Original query: {query}\nSummarized search results: {search_results}" + result = Runner.run_streamed( + writer_agent, + input, + ) + update_messages = [ + "Thinking about report...", + "Planning report structure...", + "Writing outline...", + "Creating sections...", + "Cleaning up formatting...", + "Finalizing report...", + "Finishing report...", + ] + + last_update = time.time() + next_message = 0 + async for _ in result.stream_events(): + if time.time() - last_update > 5 and next_message < len(update_messages): + self.printer.update_item("writing", update_messages[next_message]) + next_message += 1 + last_update = time.time() + + self.printer.mark_item_done("writing") + return result.final_output_as(ReportData) diff --git a/tests/examples/research_bot/printer.py b/tests/examples/research_bot/printer.py new file mode 100644 index 0000000..e820c75 --- /dev/null +++ b/tests/examples/research_bot/printer.py @@ -0,0 +1,41 @@ +from typing import Any + +from rich.console import Console, Group +from rich.live import Live +from rich.spinner import Spinner + + +class Printer: + def __init__(self, console: Console): + self.live = Live(console=console) + self.items: dict[str, tuple[str, bool]] = {} + self.hide_done_ids: set[str] = set() + self.live.start() + + def end(self) -> None: + self.live.stop() + + def hide_done_checkmark(self, item_id: str) -> None: + self.hide_done_ids.add(item_id) + + def update_item( + self, item_id: str, content: str, is_done: bool = False, hide_checkmark: bool = False + ) -> None: + self.items[item_id] = (content, is_done) + if hide_checkmark: + self.hide_done_ids.add(item_id) + self.flush() + + def mark_item_done(self, item_id: str) -> None: + self.items[item_id] = (self.items[item_id][0], True) + self.flush() + + def flush(self) -> None: + renderables: list[Any] = [] + for item_id, (content, is_done) in self.items.items(): + if is_done: + prefix = "✅ " if item_id not in self.hide_done_ids else "" + renderables.append(prefix + content) + else: + renderables.append(Spinner("dots", text=content)) + self.live.update(Group(*renderables)) diff --git a/tests/examples/research_bot/sample_outputs/product_recs.md b/tests/examples/research_bot/sample_outputs/product_recs.md new file mode 100644 index 0000000..70789eb --- /dev/null +++ b/tests/examples/research_bot/sample_outputs/product_recs.md @@ -0,0 +1,180 @@ +# Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options + +Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Board Types and Design Considerations](#board-types-and-design-considerations) +3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) +4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) +5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) +6. [Budget and Pricing Options](#budget-and-pricing-options) +7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) +8. [Conclusion](#conclusion) +9. [Follow-up Questions](#follow-up-questions) + +--- + +## Introduction + +Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. + +The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. + +--- + +## Board Types and Design Considerations + +Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: + +### Longboards and Mini-Mals + +Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. + +### Funboards and Hybrids + +Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6’6" to 8’0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. + +### Shortboards + +Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. + +--- + +## Key Board Dimensions and Features + +When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: + +### Length and Width + +- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. +- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. + +### Thickness and Volume + +- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. +- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surfer’s weight and experience level. + +### Nose and Tail Shape + +- **Nose Shape**: A wide, rounded nose expands the board’s planing surface, which can help in catching waves sooner and maintaining stability as you ride. +- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. + +### Rocker + +- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surfer’s skills improve. + +--- + +## Materials: Soft-Top vs. Hard-Top Boards + +The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: + +### Soft-Top (Foam) Boards + +Soft-top boards are constructed almost entirely from foam. Their attributes include: + +- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. +- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. +- **Maintenance**: They often require less maintenance—there is typically no need for waxing and they are more resistant to dings and scratches. + +However, as a surfer’s skills progress, a soft-top might limit maneuverability and overall performance. + +### Hard-Top Boards + +Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: + +- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. +- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. + +Deciding between soft-top and hard-top boards often depends on a beginner’s progression goals, overall comfort, and budget constraints. + +--- + +## Tips for Transitioning from Longboards to Shorter Boards + +For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: + +### Gradual Downsizing + +Experts recommend reducing the board length gradually—by about a foot at a time—to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. + +### Strengthening Core Skills + +Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: + +- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. +- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. + +### Experimenting with Rentals or Borrowed Boards + +If possible, try out a friend’s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. + +--- + +## Budget and Pricing Options + +Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, it’s important to understand what features you can expect at different price points. + +### Budget-Friendly Options + +For those on a tight budget, several entry-level models offer excellent value. Examples include: + +- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. +- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. + +### Moderate Price Range + +As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. + +### Higher-End Models and Transitional Packages + +For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. + +--- + +## Recommended Models and Buying Options + +Based on extensive research and community recommendations, here are some standout models and tips on where to buy: + +### Recommended Models + +- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. +- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. +- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. +- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. + +### Buying Options + +- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and condition—especially if you are considering a used board. +- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. +- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. + +--- + +## Conclusion + +Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. + +For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stability—such as funboards and hybrids—before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. + +Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrum—from budget-friendly models to premium options—allows you to make an informed purchase that suits both your financial and performance needs. + +With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. + +--- + +## Follow-up Questions + +1. What is your current budget range for a new surfboard, or are you considering buying used? +2. How frequently do you plan to surf, and in what type of wave conditions? +3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? +4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? +5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? + +--- + +With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! diff --git a/tests/examples/research_bot/sample_outputs/product_recs.txt b/tests/examples/research_bot/sample_outputs/product_recs.txt new file mode 100644 index 0000000..78865f2 --- /dev/null +++ b/tests/examples/research_bot/sample_outputs/product_recs.txt @@ -0,0 +1,212 @@ +# Terminal output for a product recommendation related query. See product_recs.md for final report. + +$ uv run python -m examples.research_bot.main + +What would you like to research? Best surfboards for beginners. I can catch my own waves, but previously used an 11ft board. What should I look for, what are my options? Various budget ranges. +View trace: https://platform.openai.com/traces/trace_... +Starting research... +✅ Will perform 15 searches +✅ Searching... 15/15 completed +✅ Finishing report... +✅ Report summary + +This report provides a detailed guide on selecting the best surfboards for beginners, especially for those transitioning from an 11-foot longboard to a +shorter board. It covers design considerations such as board dimensions, shape, materials, and volume, while comparing soft-top and hard-top boards. In +addition, the report discusses various budget ranges, recommended board models, buying options (both new and used), and techniques to ease the transition to +more maneuverable boards. By understanding these factors, beginner surfers can select a board that not only enhances their skills but also suits their +individual needs. + + +=====REPORT===== + + +Report: # Comprehensive Guide on Best Surfboards for Beginners: Transitioning, Features, and Budget Options + +Surfing is not only a sport but a lifestyle that hooks its enthusiasts with the allure of riding waves and connecting with nature. For beginners, selecting the right surfboard is critical to safety, learning, and performance. This comprehensive guide has been crafted to walk through the essential aspects of choosing the ideal surfboard for beginners, especially those looking to transition from an 11-foot longboard to a shorter, more dynamic board. We discuss various board types, materials, design elements, and budget ranges, providing a detailed road map for both new surfers and those in the process of progression. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Board Types and Design Considerations](#board-types-and-design-considerations) +3. [Key Board Dimensions and Features](#key-board-dimensions-and-features) +4. [Materials: Soft-Top vs. Hard-Top Boards](#materials-soft-top-vs-hard-top-boards) +5. [Tips for Transitioning from Longboards to Shorter Boards](#tips-for-transitioning-from-longboards-to-shorter-boards) +6. [Budget and Pricing Options](#budget-and-pricing-options) +7. [Recommended Models and Buying Options](#recommended-models-and-buying-options) +8. [Conclusion](#conclusion) +9. [Follow-up Questions](#follow-up-questions) + +--- + +## Introduction + +Surfing is a dynamic sport that requires not only skill and technique but also the proper equipment. For beginners, the right surfboard can make the difference between a frustrating experience and one that builds confidence and enthusiasm. Many newcomers start with longboards due to their stability and ease of paddling; however, as skills develop, transitioning to a shorter board might be desirable for enhancing maneuverability and performance. This guide is designed for surfers who can already catch waves on an 11-foot board and are now considering stepping down to a more versatile option. + +The overarching goal of this document is to help beginners identify which surfboard characteristics are most important, including board length, width, thickness, volume, and materials, while also considering factors like weight distribution, buoyancy, and control. We will also take a look at board types that are particularly welcoming for beginners and discuss gradual transitioning strategies. + +--- + +## Board Types and Design Considerations + +Choosing a board involves understanding the variety of designs available. Below are the main types of surfboards that cater to beginners and transitional surfers: + +### Longboards and Mini-Mals + +Longboards, typically 8 to 11 feet in length, provide ample stability, smoother paddling, and are well-suited for wave-catching. Their generous volume and width allow beginners to build confidence when standing up and riding waves. Mini-mal or mini-malibus (often around 8 to 9 feet) are a popular bridge between the longboard and the more agile shortboard, offering both stability and moderate maneuverability, which makes them excellent for gradual progress. + +### Funboards and Hybrids + +Funboards and hybrid boards blend the benefits of longboards and shortboards. They typically range from 6’6" to 8’0" in length, with extra volume and width that help preserve stability while introducing elements of sharper turning and improved agility. Hybrids are particularly helpful for surfers transitioning from longboards, as they maintain some of the buoyancy and ease of catching waves, yet offer a taste of the performance found in smaller boards. + +### Shortboards + +Shortboards emphasize performance, maneuverability, and a more responsive ride. However, they have less volume and require stronger paddling, quicker pop-up techniques, and more refined balance. For beginners, moving to a traditional shortboard immediately can be challenging. It is generally advised to make a gradual transition, potentially starting with a funboard or hybrid before making a direct leap to a performance shortboard. + +--- + +## Key Board Dimensions and Features + +When selecting a beginner surfboard, several key dimensions and features drastically affect performance, ease of learning, and safety: + +### Length and Width + +- **Length**: Starting with an 8 to 9-foot board is ideal. Longer boards offer enhanced stability and improved paddling capabilities. Gradual downsizing is recommended if you plan to move from an 11-foot board. +- **Width**: A board with a width over 20 inches provides greater stability and facilitates balance, especially vital for beginners. + +### Thickness and Volume + +- **Thickness**: Typically around 2.5 to 3 inches. Thicker decks increase buoyancy, allowing the surfer to paddle easier while catching waves. +- **Volume**: Measured in liters, volume is critical in understanding a board's flotation capacity. Higher volumes (e.g., 60-100 liters) are essential for beginners as they make the board more forgiving and stable. Suitable volumes might vary according to the surfer’s weight and experience level. + +### Nose and Tail Shape + +- **Nose Shape**: A wide, rounded nose expands the board’s planing surface, which can help in catching waves sooner and maintaining stability as you ride. +- **Tail Design**: Square or rounded tails are generally recommended as they enhance stability and allow for controlled turns, essential during the learning phase. + +### Rocker + +- **Rocker**: This is the curvature of the board from nose to tail. For beginners, a minimal or relaxed rocker provides better stability and ease during paddling. A steeper rocker might be introduced progressively as the surfer’s skills improve. + +--- + +## Materials: Soft-Top vs. Hard-Top Boards + +The material composition of a surfboard is a crucial factor in determining its performance, durability, and safety. Beginners have two primary choices: + +### Soft-Top (Foam) Boards + +Soft-top boards are constructed almost entirely from foam. Their attributes include: + +- **Safety and Forgiveness**: The foam construction minimizes injury upon impact which is advantageous for beginners who might fall frequently. +- **Stability and Buoyancy**: These boards typically offer greater buoyancy due to their softer material and thicker construction, easing the initial learning process. +- **Maintenance**: They often require less maintenance—there is typically no need for waxing and they are more resistant to dings and scratches. + +However, as a surfer’s skills progress, a soft-top might limit maneuverability and overall performance. + +### Hard-Top Boards + +Hard-tops, in contrast, offer a more traditional surfboard feel. They generally rely on a foam core encased in resin, with two prevalent combinations: + +- **PU (Polyurethane) Core with Polyester Resin**: This combination gives a classic feel and is relatively economical; however, these boards can be heavier and, as they age, more prone to damage. +- **EPS (Expanded Polystyrene) Core with Epoxy Resin**: Lightweight and durable, EPS boards are often more buoyant and resistant to damage, although they usually carry a higher price tag and may be less forgiving. + +Deciding between soft-top and hard-top boards often depends on a beginner’s progression goals, overall comfort, and budget constraints. + +--- + +## Tips for Transitioning from Longboards to Shorter Boards + +For surfers who have mastered the basics on an 11-foot board, the transition to a shorter board requires careful consideration, patience, and incremental changes. Here are some key tips: + +### Gradual Downsizing + +Experts recommend reducing the board length gradually—by about a foot at a time—to allow the body to adjust slowly to a board with less buoyancy and more responsiveness. This process helps maintain wave-catching ability and reduces the shock of transitioning to a very different board feel. + +### Strengthening Core Skills + +Before transitioning, make sure your surfing fundamentals are solid. Focus on practicing: + +- **Steep Take-offs**: Ensure that your pop-up is swift and robust to keep pace with shorter boards that demand a rapid transition from paddling to standing. +- **Angling and Paddling Techniques**: Learn to angle your takeoffs properly to compensate for the lower buoyancy and increased maneuverability of shorter boards. + +### Experimenting with Rentals or Borrowed Boards + +If possible, try out a friend’s shorter board or rent one for a day to experience firsthand the differences in performance. This practical trial can provide valuable insights and inform your decision before making a purchase. + +--- + +## Budget and Pricing Options + +Surfboards are available across a range of prices to match different budgets. Whether you are looking for an affordable beginner board or a more expensive model that grows with your skills, it’s important to understand what features you can expect at different price points. + +### Budget-Friendly Options + +For those on a tight budget, several entry-level models offer excellent value. Examples include: + +- **Wavestorm 8' Classic Pinline Surfboard**: Priced affordably, this board is popular for its ease of use, ample volume, and forgiving nature. Despite its low cost, it delivers the stability needed to get started. +- **Liquid Shredder EZ Slider Foamie**: A smaller board catering to younger or lighter surfers, this budget option provides easy paddling and a minimal risk of injury due to its soft construction. + +### Moderate Price Range + +As you move into the intermediate range, boards typically become slightly more specialized in their design, offering features such as improved stringer systems or versatile fin setups. These are excellent for surfers who wish to continue progressing their skills without compromising stability. Many surfboard packages from retailers also bundle a board with essential accessories like board bags, leashes, and wax for additional savings. + +### Higher-End Models and Transitional Packages + +For surfers looking for durability, performance, and advanced design features, investing in an EPS/epoxy board might be ideal. Although they come at a premium, these boards are lightweight, strong, and customizable with various fin configurations. Some options include boards from brands like South Bay Board Co. and ISLE, which combine high-quality construction with beginner-friendly features that help mediate the transition from longboard to shortboard performance. + +--- + +## Recommended Models and Buying Options + +Based on extensive research and community recommendations, here are some standout models and tips on where to buy: + +### Recommended Models + +- **South Bay Board Co. 8'8" Heritage**: Combining foam and resin construction, this board is ideal for beginners who need stability and a forgiving surface. Its 86-liter volume suits both lightweight and somewhat heavier surfers. +- **Rock-It 8' Big Softy**: With a high volume and an easy paddling profile, this board is designed for beginners, offering ample buoyancy to smooth out the learning curve. +- **Wave Bandit EZ Rider Series**: Available in multiple lengths (7', 8', 9'), these boards offer versatility, with construction features that balance the stability of longboards and the agility required for shorter boards. +- **Hybrid/Funboards Like the Poacher Funboard**: Perfect for transitioning surfers, these boards blend the ease of catching waves with the capability for more dynamic maneuvers. + +### Buying Options + +- **Surf Shops and Local Retailers**: Traditional surf shops allow you to test different boards, which is ideal for assessing the board feel and condition—especially if you are considering a used board. +- **Online Retailers and Marketplaces**: Websites like Evo, Surfboards Direct, and even local online marketplaces like Craigslist and Facebook Marketplace provide options that range from new to gently used boards. Always inspect reviews and verify seller policies before purchase. +- **Package Deals and Bundles**: Many retailers offer bundled packages that include not just the board, but also essentials like a leash, wax, fins, and board bags. These packages can be more cost-effective and are great for beginners who need a complete surf kit. + +--- + +## Conclusion + +Selecting the right surfboard as a beginner is about balancing various factors: stability, buoyancy, maneuverability, and budget. + +For those who have honed the basics using an 11-foot longboard, the transition to a shorter board should be gradual. Start by focusing on boards that preserve stability—such as funboards and hybrids—before moving to the more performance-oriented shortboards. Key characteristics like board length, width, thickness, volume, and material profoundly influence your surfing experience. Soft-top boards provide a forgiving entry point, while hard-top boards, especially those with EPS cores and epoxy resin, offer benefits for more advanced progression despite the increased learning curve. + +Emphasizing fundamentals like proper pop-up technique and effective paddle work will ease the transition and ensure that the new board complements your evolving skills. Additionally, understanding the pricing spectrum—from budget-friendly models to premium options—allows you to make an informed purchase that suits both your financial and performance needs. + +With a thoughtful approach to board selection, you can enhance your learning curve, enjoy safer sessions in the water, and ultimately develop the skills necessary to master the diverse challenges surfing presents. Whether your goal is to ride gentle waves or eventually experiment with sharper turns and dynamic maneuvers, choosing the right board is your first step towards a rewarding and sustainable surfing journey. + +--- + +## Follow-up Questions + +1. What is your current budget range for a new surfboard, or are you considering buying used? +2. How frequently do you plan to surf, and in what type of wave conditions? +3. Are you interested in a board that you can grow into as your skills progress, or do you prefer one that is more specialized for certain conditions? +4. Would you be interested in additional equipment bundles (like fins, leashes, boards bags) offered by local retailers or online shops? +5. Have you had the opportunity to test ride any boards before, and what feedback did you gather from that experience? + +--- + +With this detailed guide, beginners should now have a comprehensive understanding of the surfboard market and the key factors influencing board performance, safety, and ease of progression. Happy surfing, and may you find the perfect board that rides the waves as beautifully as your passion for the sport! + + +=====FOLLOW UP QUESTIONS===== + + +Follow up questions: What is your current budget range for a new surfboard, or are you considering a used board? +What types of waves do you typically surf, and how might that affect your board choice? +Would you be interested in a transitional board that grows with your skills, or are you looking for a more specialized design? +Have you had experience with renting or borrowing boards to try different sizes before making a purchase? +Do you require additional equipment bundles (like fins, leash, or wax), or do you already have those? diff --git a/tests/examples/research_bot/sample_outputs/vacation.md b/tests/examples/research_bot/sample_outputs/vacation.md new file mode 100644 index 0000000..82c137a --- /dev/null +++ b/tests/examples/research_bot/sample_outputs/vacation.md @@ -0,0 +1,177 @@ +Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration + +The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) +3. [Surfing in the Caribbean](#surfing-in-the-caribbean) + - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) + - 3.2 [Puerto Rico: Rincón and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) + - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) +4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) + - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) + - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) +5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) + - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) + - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) +6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) +7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) +8. [Conclusion](#conclusion) + +--- + +## Introduction + +Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. + +Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. + +--- + +## Why April is the Perfect Time in the Caribbean + +April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: + +- **Consistent Warm Temperatures:** Average daytime highs around 29°C (84°F) foster comfortable conditions for both land and water activities. +- **Pleasant Sea Temperatures:** With sea temperatures near 26°C (79°F), swimmers, surfers, and divers are treated to inviting waters. +- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. +- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. + +These factors create an ideal backdrop for balancing your outdoor pursuits, whether you’re catching epic waves, trekking rugged trails, or partaking in water sports. + +--- + +## Surfing in the Caribbean + +Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. + +### Barbados: The Tale of Two Coasts + +Barbados is a prime destination: + +- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. +- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. + +Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. + +### Puerto Rico: Rincón and Beyond + +Rincón in Puerto Rico is hailed as the Caribbean’s surfing capital: + +- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. +- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. + +In addition, Puerto Rico’s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. + +### Dominican Republic and Other Hotspots + +Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: + +- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. +- **Jamaica and The Bahamas:** Jamaica’s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamas’ Surfer’s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. + +These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. + +--- + +## Hiking Adventures Across the Caribbean + +The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. + +### Trekking Through Tropical Rainforests + +For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: + +- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquí frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. +- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. + +### Volcanic Peaks and Rugged Landscapes + +For those seeking more rugged challenges, several destinations offer unforgettable adventures: + +- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. +- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. +- **La Soufrière, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. + +Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelée, which combine historical context with natural beauty. + +--- + +## Diverse Water Sports Experiences + +While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. + +### Snorkeling, Diving, and Jet Skiing + +Caribbean waters teem with life and color, making them ideal for underwater exploration: + +- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. +- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. +- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. + +### Kiteboarding and Windsurfing + +Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: + +- **Aruba:** Known as "One Happy Island," Aruba’s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. +- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. + +Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. + +--- + +## Combining Adventures: Multi-Activity Destinations + +For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. + +- **Puerto Rico:** With its robust surf scene in Rincón, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. +- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. +- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaica’s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. + +Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. + +--- + +## Practical Advice and Travel Tips + +### Weather and Timing + +- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. +- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincón in Puerto Rico), many destinations maintain consistent conditions during this month. + +### Booking and Costs + +- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. +- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. +- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. + +### Cultural Considerations + +- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. +- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisine—with its fusion of flavors—can be as adventurous as the outdoor activities. + +### Health and Safety + +- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. +- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. +- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. + +--- + +## Conclusion + +The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. + +This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offerings—from epic surf breaks and challenging hiking trails to vibrant water sports—the Caribbean stands out as a multi-adventure destination where every day brings a new experience. + +Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. + +Happy travels! + +--- + +_References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning._ diff --git a/tests/examples/research_bot/sample_outputs/vacation.txt b/tests/examples/research_bot/sample_outputs/vacation.txt new file mode 100644 index 0000000..b264998 --- /dev/null +++ b/tests/examples/research_bot/sample_outputs/vacation.txt @@ -0,0 +1,206 @@ +# Terminal output for a vacation related query. See vacation.md for final report. + +$ uv run python -m examples.research_bot.main +What would you like to research? Caribbean vacation spots in April, optimizing for surfing, hiking and water sports +View trace: https://platform.openai.com/traces/trace_.... +Starting research... +✅ Will perform 15 searches +✅ Searching... 15/15 completed +✅ Finishing report... +✅ Report summary + +This report provides an in-depth exploration of selected Caribbean vacation spots in April that are ideal for surfing, hiking, and water sports. Covering +destinations from Barbados and Puerto Rico to the Bahamas and Jamaica, it examines favorable weather conditions, recommended surf breaks, scenic hiking +trails, and various water sports activities. Detailed destination profiles, activity highlights, and travel tips are integrated to help travelers design a +multi-adventure itinerary in the Caribbean during April. + + +=====REPORT===== + + +Report: # Caribbean Adventure in April: Surfing, Hiking, and Water Sports Exploration + +The Caribbean is renowned for its crystal-clear waters, vibrant culture, and diverse outdoor activities. April is an especially attractive month for visitors: warm temperatures, clear skies, and the promise of abundant activities. This report explores the best Caribbean destinations in April, with a focus on optimizing your vacation for surfing, hiking, and water sports. + +--- + +## Table of Contents + +1. [Introduction](#introduction) +2. [Why April is the Perfect Time in the Caribbean](#why-april-is-the-perfect-time-in-the-caribbean) +3. [Surfing in the Caribbean](#surfing-in-the-caribbean) + - 3.1 [Barbados: The Tale of Two Coasts](#barbados-the-tale-of-two-coasts) + - 3.2 [Puerto Rico: Rincón and Beyond](#puerto-rico-rinc%C3%B3n-and-beyond) + - 3.3 [Dominican Republic and Other Hotspots](#dominican-republic-and-other-hotspots) +4. [Hiking Adventures Across the Caribbean](#hiking-adventures-across-the-caribbean) + - 4.1 [Trekking Through Tropical Rainforests](#trekking-through-tropical-rainforests) + - 4.2 [Volcanic Peaks and Rugged Landscapes](#volcanic-peaks-and-rugged-landscapes) +5. [Diverse Water Sports Experiences](#diverse-water-sports-experiences) + - 5.1 [Snorkeling, Diving, and Jet Skiing](#snorkeling-diving-and-jet-skiing) + - 5.2 [Kiteboarding and Windsurfing](#kiteboarding-and-windsurfing) +6. [Combining Adventures: Multi-Activity Destinations](#combining-adventures-multi-activity-destinations) +7. [Practical Advice and Travel Tips](#practical-advice-and-travel-tips) +8. [Conclusion](#conclusion) + +--- + +## Introduction + +Caribbean vacations are much more than just beach relaxation; they offer adventure, exploration, and a lively cultural tapestry waiting to be discovered. For travelers seeking an adrenaline-filled getaway, April provides optimal conditions. This report synthesizes diverse research findings and travel insights to help you create an itinerary that combines the thrill of surfing, the challenge of hiking, and the excitement of water sports. + +Whether you're standing on the edge of a powerful reef break or trekking through lush tropical landscapes, the Caribbean in April invites you to dive into nature, adventure, and culture. The following sections break down the best destinations and activities, ensuring that every aspect of your trip is meticulously planned for an unforgettable experience. + +--- + +## Why April is the Perfect Time in the Caribbean + +April stands at the crossroads of seasons in many Caribbean destinations. It marks the tail end of the dry season, ensuring: + +- **Consistent Warm Temperatures:** Average daytime highs around 29°C (84°F) foster comfortable conditions for both land and water activities. +- **Pleasant Sea Temperatures:** With sea temperatures near 26°C (79°F), swimmers, surfers, and divers are treated to inviting waters. +- **Clear Skies and Minimal Rainfall:** Crisp, blue skies make for excellent visibility during snorkeling and diving, as well as clear panoramic views while hiking. +- **Festivals and Cultural Events:** Many islands host seasonal festivals such as Barbados' Fish Festival and Antigua's Sailing Week, adding a cultural layer to your vacation. + +These factors create an ideal backdrop for balancing your outdoor pursuits, whether you’re catching epic waves, trekking rugged trails, or partaking in water sports. + +--- + +## Surfing in the Caribbean + +Surfing in the Caribbean offers diverse wave experiences, ranging from gentle, beginner-friendly rollers to powerful reef breaks that challenge even seasoned surfers. April, in particular, provides excellent conditions for those looking to ride its picturesque waves. + +### Barbados: The Tale of Two Coasts + +Barbados is a prime destination: + +- **Soup Bowl in Bathsheba:** On the east coast, the Soup Bowl is famous for its consistent, powerful waves. This spot attracts experienced surfers who appreciate its challenging right-hand reef break with steep drops, providing the kind of performance wave rarely found elsewhere. +- **Freights Bay:** On the south coast, visitors find more forgiving, gentle wave conditions. Ideal for beginners and longboarders, this spot offers the perfect balance for those still mastering their craft. + +Barbados not only excels in its surfing credentials but also complements the experience with a rich local culture and events in April, making it a well-rounded destination. + +### Puerto Rico: Rincón and Beyond + +Rincón in Puerto Rico is hailed as the Caribbean’s surfing capital: + +- **Diverse Breaks:** With spots ranging from challenging reef breaks such as Tres Palmas and Dogman's to more inviting waves at Domes and Maria's, Puerto Rico offers a spectrum for all surfing skill levels. +- **Local Culture:** Aside from its surf culture, the island boasts vibrant local food scenes, historic sites, and exciting nightlife, enriching your overall travel experience. + +In addition, Puerto Rico’s coasts often feature opportunities for hiking and other outdoor adventures, making it an attractive option for multi-activity travelers. + +### Dominican Republic and Other Hotspots + +Other islands such as the Dominican Republic, with Playa Encuentro on its north coast, provide consistent surf year-round. Highlights include: + +- **Playa Encuentro:** A hotspot known for its dependable breaks, ideal for both intermediate and advanced surfers during the cooler months of October to April. +- **Jamaica and The Bahamas:** Jamaica’s Boston Bay offers a mix of beginner and intermediate waves, and The Bahamas’ Surfer’s Beach on Eleuthera draws parallels to the legendary surf spots of Hawaii, especially during the winter months. + +These destinations not only spotlight surfing but also serve as gateways to additional outdoor activities, ensuring there's never a dull moment whether you're balancing waves with hikes or cultural exploration. + +--- + +## Hiking Adventures Across the Caribbean + +The Caribbean's topography is as varied as it is beautiful. Its network of hiking trails traverses volcanic peaks, ancient rainforests, and dramatic coastal cliffs, offering breathtaking vistas to intrepid explorers. + +### Trekking Through Tropical Rainforests + +For nature enthusiasts, the lush forests of the Caribbean present an immersive encounter with biodiversity: + +- **El Yunque National Forest, Puerto Rico:** The only tropical rainforest within the U.S. National Forest System, El Yunque is rich in endemic species such as the Puerto Rican parrot and the famous coquí frog. Trails like the El Yunque Peak Trail and La Mina Falls Trail provide both challenging hikes and scenic rewards. +- **Virgin Islands National Park, St. John:** With over 20 well-defined trails, this park offers hikes that reveal historical petroglyphs, colonial ruins, and stunning coastal views along the Reef Bay Trail. + +### Volcanic Peaks and Rugged Landscapes + +For those seeking more rugged challenges, several destinations offer unforgettable adventures: + +- **Morne Trois Pitons National Park, Dominica:** A UNESCO World Heritage Site showcasing volcanic landscapes, hot springs, the famed Boiling Lake, and lush trails that lead to hidden waterfalls. +- **Gros Piton, Saint Lucia:** The iconic hike up Gros Piton provides a moderately challenging trek that ends with panoramic views of the Caribbean Sea, a truly rewarding experience for hikers. +- **La Soufrière, St. Vincent:** This active volcano not only offers a dynamic hiking environment but also the opportunity to observe the ongoing geological transformations up close. + +Other noteworthy hiking spots include the Blue Mountains in Jamaica for coffee plantation tours and expansive views, as well as trails in Martinique around Montagne Pelée, which combine historical context with natural beauty. + +--- + +## Diverse Water Sports Experiences + +While surfing and hiking attract a broad range of adventurers, the Caribbean also scores high on other water sports. Whether you're drawn to snorkeling, jet skiing, or wind- and kiteboarding, the islands offer a plethora of aquatic activities. + +### Snorkeling, Diving, and Jet Skiing + +Caribbean waters teem with life and color, making them ideal for underwater exploration: + +- **Bonaire:** Its protected marine parks serve as a magnet for divers and snorkelers. With vibrant coral reefs and diverse marine species, Bonaire is a top destination for those who appreciate the underwater world. +- **Cayman Islands:** Unique attractions such as Stingray City provide opportunities to interact with friendly stingrays in clear, calm waters. Additionally, the Underwater Sculpture Park is an innovative blend of art and nature. +- **The Bahamas:** In places like Eleuthera, excursions often cater to families and thrill-seekers alike. Options include jet ski rentals, where groups can explore hidden beaches and pristine coves while enjoying the vibrant marine life. + +### Kiteboarding and Windsurfing + +Harnessing the steady trade winds and warm Caribbean waters, several islands have become hubs for kiteboarding and windsurfing: + +- **Aruba:** Known as "One Happy Island," Aruba’s Fisherman's Huts area provides consistent winds, perfect for enthusiasts of windsurfing and kiteboarding alike. +- **Cabarete, Dominican Republic and Silver Rock, Barbados:** Both destinations benefit from reliable trade winds, making them popular among kitesurfers. These spots often combine water sports with a lively beach culture, ensuring that the fun continues on land as well. + +Local operators provide equipment rental and lessons, ensuring that even first-time adventurers can safely and confidently enjoy these exciting sports. + +--- + +## Combining Adventures: Multi-Activity Destinations + +For travelers seeking a comprehensive vacation where surfing, hiking, and water sports converge, several Caribbean destinations offer the best of all worlds. + +- **Puerto Rico:** With its robust surf scene in Rincón, world-class hiking in El Yunque, and opportunities for snorkeling and jet skiing in San Juan Bay, Puerto Rico is a true multi-adventure destination. +- **Barbados:** In addition to the surf breaks along its coasts, Barbados offers a mix of cultural events, local cuisine, and even hiking excursions to scenic rural areas, making for a well-rounded experience. +- **Dominican Republic and Jamaica:** Both are renowned not only for their consistent surf conditions but also for expansive hiking trails and water sports. From the rugged landscapes of the Dominican Republic to Jamaica’s blend of cultural history and natural exploration, these islands allow travelers to mix and match activities seamlessly. + +Group tours and local guides further enhance these experiences, providing insider tips, safe excursions, and personalized itineraries that cater to multiple interests within one trip. + +--- + +## Practical Advice and Travel Tips + +### Weather and Timing + +- **Optimal Climate:** April offers ideal weather conditions across the Caribbean. With minimal rainfall and warm temperatures, it is a great time to schedule outdoor activities. +- **Surfing Seasons:** While April marks the end of the prime surf season in some areas (like Rincón in Puerto Rico), many destinations maintain consistent conditions during this month. + +### Booking and Costs + +- **Surfing Lessons:** Expect to pay between $40 and $110 per session depending on the location. For instance, Puerto Rico typically charges around $75 for beginner lessons, while group lessons in the Dominican Republic average approximately $95. +- **Equipment Rentals:** Pricing for jet ski, surfboard, and snorkeling equipment may vary. In the Bahamas, an hour-long jet ski tour might cost about $120 per group, whereas a similar experience might be available at a lower cost in other regions. +- **Accommodations:** Prices also vary by island. Many travelers find that even affordable stays do not skimp on amenities, allowing you to invest more in guided excursions and local experiences. + +### Cultural Considerations + +- **Festivals and Events:** Check local event calendars. Destinations like Barbados and Antigua host festivals in April that combine cultural heritage with festive outdoor activities. +- **Local Cuisine:** Incorporate food tours into your itinerary. Caribbean cuisine—with its fusion of flavors—can be as adventurous as the outdoor activities. + +### Health and Safety + +- **Staying Hydrated:** The warm temperatures demand that you stay properly hydrated. Always carry water, especially during long hikes. +- **Sun Protection:** Use sunscreen, hats, and sunglasses to protect yourself during extended periods outdoors on both land and water. +- **Local Guides:** Utilize local tour operators for both hiking and water sports. Their expertise not only enriches your experience but also ensures safety in unfamiliar terrain or water bodies. + +--- + +## Conclusion + +The Caribbean in April is a haven for adventure seekers. With its pristine beaches, diverse ecosystems, and rich cultural tapestry, it offers something for every type of traveler. Whether you're chasing the perfect wave along the shores of Barbados and Puerto Rico, trekking through the lush landscapes of El Yunque or Morne Trois Pitons, or engaging in an array of water sports from snorkeling to kiteboarding, your ideal vacation is only a booking away. + +This report has outlined the best destinations and provided practical advice to optimize your vacation for surfing, hiking, and water sports. By considering the diverse offerings—from epic surf breaks and challenging hiking trails to vibrant water sports—the Caribbean stands out as a multi-adventure destination where every day brings a new experience. + +Plan carefully, pack wisely, and get ready to explore the vibrant mosaic of landscapes and activities that make the Caribbean in April a truly unforgettable adventure. + +Happy travels! + +--- + +*References available upon request. Many insights were drawn from trusted sources including Lonely Planet, TravelPug, and various Caribbean-centric exploration sites, ensuring a well-rounded and practical guide for your vacation planning.* + + + +=====FOLLOW UP QUESTIONS===== + + +Follow up questions: Would you like detailed profiles for any of the highlighted destinations (e.g., Puerto Rico or Barbados)? +Are you interested in more information about booking details and local tour operators in specific islands? +Do you need guidance on combining cultural events with outdoor adventures during your Caribbean vacation? \ No newline at end of file diff --git a/tests/examples/tools/computer_use.py b/tests/examples/tools/computer_use.py new file mode 100644 index 0000000..35fc865 --- /dev/null +++ b/tests/examples/tools/computer_use.py @@ -0,0 +1,165 @@ +import asyncio +import base64 +import logging +from typing import Literal, Union + +from playwright.async_api import Browser, Page, Playwright, async_playwright + +from agents import ( + Agent, + AsyncComputer, + Button, + ComputerTool, + Environment, + ModelSettings, + Runner, + trace, +) + +logging.getLogger("openai.agents").setLevel(logging.DEBUG) +logging.getLogger("openai.agents").addHandler(logging.StreamHandler()) + + +async def main(): + async with LocalPlaywrightComputer() as computer: + with trace("Computer use example"): + agent = Agent( + name="Browser user", + instructions="You are a helpful agent.", + tools=[ComputerTool(computer)], + # Use the computer using model, and set truncation to auto because its required + model="computer-use-preview-2025-02-04", + model_settings=ModelSettings(truncation="auto"), + ) + result = await Runner.run(agent, "Search for SF sports news and summarize.") + print(result.final_output) + + +CUA_KEY_TO_PLAYWRIGHT_KEY = { + "/": "Divide", + "\\": "Backslash", + "alt": "Alt", + "arrowdown": "ArrowDown", + "arrowleft": "ArrowLeft", + "arrowright": "ArrowRight", + "arrowup": "ArrowUp", + "backspace": "Backspace", + "capslock": "CapsLock", + "cmd": "Meta", + "ctrl": "Control", + "delete": "Delete", + "end": "End", + "enter": "Enter", + "esc": "Escape", + "home": "Home", + "insert": "Insert", + "option": "Alt", + "pagedown": "PageDown", + "pageup": "PageUp", + "shift": "Shift", + "space": " ", + "super": "Meta", + "tab": "Tab", + "win": "Meta", +} + + +class LocalPlaywrightComputer(AsyncComputer): + """A computer, implemented using a local Playwright browser.""" + + def __init__(self): + self._playwright: Union[Playwright, None] = None + self._browser: Union[Browser, None] = None + self._page: Union[Page, None] = None + + async def _get_browser_and_page(self) -> tuple[Browser, Page]: + width, height = self.dimensions + launch_args = [f"--window-size={width},{height}"] + browser = await self.playwright.chromium.launch(headless=False, args=launch_args) + page = await browser.new_page() + await page.set_viewport_size({"width": width, "height": height}) + await page.goto("https://www.bing.com") + return browser, page + + async def __aenter__(self): + # Start Playwright and call the subclass hook for getting browser/page + self._playwright = await async_playwright().start() + self._browser, self._page = await self._get_browser_and_page() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self._browser: + await self._browser.close() + if self._playwright: + await self._playwright.stop() + + @property + def playwright(self) -> Playwright: + assert self._playwright is not None + return self._playwright + + @property + def browser(self) -> Browser: + assert self._browser is not None + return self._browser + + @property + def page(self) -> Page: + assert self._page is not None + return self._page + + @property + def environment(self) -> Environment: + return "browser" + + @property + def dimensions(self) -> tuple[int, int]: + return (1024, 768) + + async def screenshot(self) -> str: + """Capture only the viewport (not full_page).""" + png_bytes = await self.page.screenshot(full_page=False) + return base64.b64encode(png_bytes).decode("utf-8") + + async def click(self, x: int, y: int, button: Button = "left") -> None: + playwright_button: Literal["left", "middle", "right"] = "left" + + # Playwright only supports left, middle, right buttons + if button in ("left", "right", "middle"): + playwright_button = button # type: ignore + + await self.page.mouse.click(x, y, button=playwright_button) + + async def double_click(self, x: int, y: int) -> None: + await self.page.mouse.dblclick(x, y) + + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + await self.page.mouse.move(x, y) + await self.page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})") + + async def type(self, text: str) -> None: + await self.page.keyboard.type(text) + + async def wait(self) -> None: + await asyncio.sleep(1) + + async def move(self, x: int, y: int) -> None: + await self.page.mouse.move(x, y) + + async def keypress(self, keys: list[str]) -> None: + for key in keys: + mapped_key = CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) + await self.page.keyboard.press(mapped_key) + + async def drag(self, path: list[tuple[int, int]]) -> None: + if not path: + return + await self.page.mouse.move(path[0][0], path[0][1]) + await self.page.mouse.down() + for px, py in path[1:]: + await self.page.mouse.move(px, py) + await self.page.mouse.up() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/tools/file_search.py b/tests/examples/tools/file_search.py new file mode 100644 index 0000000..2a3d4cf --- /dev/null +++ b/tests/examples/tools/file_search.py @@ -0,0 +1,36 @@ +import asyncio + +from agents import Agent, FileSearchTool, Runner, trace + + +async def main(): + agent = Agent( + name="File searcher", + instructions="You are a helpful agent.", + tools=[ + FileSearchTool( + max_num_results=3, + vector_store_ids=["vs_67bf88953f748191be42b462090e53e7"], + include_search_results=True, + ) + ], + ) + + with trace("File search example"): + result = await Runner.run( + agent, "Be concise, and tell me 1 sentence about Arrakis I might not know." + ) + print(result.final_output) + """ + Arrakis, the desert planet in Frank Herbert's "Dune," was inspired by the scarcity of water + as a metaphor for oil and other finite resources. + """ + + print("\n".join([str(out) for out in result.new_items])) + """ + {"id":"...", "queries":["Arrakis"], "results":[...]} + """ + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/examples/tools/web_search.py b/tests/examples/tools/web_search.py new file mode 100644 index 0000000..35eeb68 --- /dev/null +++ b/tests/examples/tools/web_search.py @@ -0,0 +1,23 @@ +import asyncio + +from agents import Agent, Runner, WebSearchTool, trace + + +async def main(): + agent = Agent( + name="Web searcher", + instructions="You are a helpful agent.", + tools=[WebSearchTool(user_location={"type": "approximate", "city": "New York"})], + ) + + with trace("Web search example"): + result = await Runner.run( + agent, + "search the web for 'local sports news' and give me 1 interesting update in a sentence.", + ) + print(result.final_output) + # The New York Giants are reportedly pursuing quarterback Aaron Rodgers after his ... + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/tests/fake_model.py b/tests/fake_model.py new file mode 100644 index 0000000..f2ba622 --- /dev/null +++ b/tests/fake_model.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator + +from openai.types.responses import Response, ResponseCompletedEvent + +from agents.agent_output import AgentOutputSchema +from agents.handoffs import Handoff +from agents.items import ( + ModelResponse, + TResponseInputItem, + TResponseOutputItem, + TResponseStreamEvent, +) +from agents.model_settings import ModelSettings +from agents.models.interface import Model, ModelTracing +from agents.tool import Tool +from agents.tracing import SpanError, generation_span +from agents.usage import Usage + + +class FakeModel(Model): + def __init__( + self, + tracing_enabled: bool = False, + initial_output: list[TResponseOutputItem] | Exception | None = None, + ): + if initial_output is None: + initial_output = [] + self.turn_outputs: list[list[TResponseOutputItem] | Exception] = ( + [initial_output] if initial_output else [] + ) + self.tracing_enabled = tracing_enabled + + def set_next_output(self, output: list[TResponseOutputItem] | Exception): + self.turn_outputs.append(output) + + def add_multiple_turn_outputs(self, outputs: list[list[TResponseOutputItem] | Exception]): + self.turn_outputs.extend(outputs) + + def get_next_output(self) -> list[TResponseOutputItem] | Exception: + if not self.turn_outputs: + return [] + return self.turn_outputs.pop(0) + + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + with generation_span(disabled=not self.tracing_enabled) as span: + output = self.get_next_output() + + if isinstance(output, Exception): + span.set_error( + SpanError( + message="Error", + data={ + "name": output.__class__.__name__, + "message": str(output), + }, + ) + ) + raise output + + return ModelResponse( + output=output, + usage=Usage(), + referenceable_id=None, + ) + + async def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[TResponseStreamEvent]: + with generation_span(disabled=not self.tracing_enabled) as span: + output = self.get_next_output() + if isinstance(output, Exception): + span.set_error( + SpanError( + message="Error", + data={ + "name": output.__class__.__name__, + "message": str(output), + }, + ) + ) + raise output + + yield ResponseCompletedEvent( + type="response.completed", + response=get_response_obj(output), + ) + + +def get_response_obj(output: list[TResponseOutputItem], response_id: str | None = None) -> Response: + return Response( + id=response_id or "123", + created_at=123, + model="test_model", + object="response", + output=output, + tool_choice="none", + tools=[], + top_p=None, + parallel_tool_calls=False, + ) diff --git a/tests/mkdocs.yml b/tests/mkdocs.yml new file mode 100644 index 0000000..398fb74 --- /dev/null +++ b/tests/mkdocs.yml @@ -0,0 +1,121 @@ +site_name: OpenAI Agents SDK +theme: + name: material + features: + # Allows copying code blocks + - content.code.copy + # Allows selecting code blocks + - content.code.select + # Shows the current path in the sidebar + - navigation.path + # Shows sections in the sidebar + - navigation.sections + # Shows sections expanded by default + - navigation.expand + # Enables annotations in code blocks + - content.code.annotate + palette: + primary: black + logo: assets/logo.svg + favicon: images/favicon-platform.svg +nav: + - Intro: index.md + - Quickstart: quickstart.md + - Documentation: + - agents.md + - running_agents.md + - results.md + - streaming.md + - tools.md + - handoffs.md + - tracing.md + - context.md + - guardrails.md + - multi_agent.md + - models.md + - config.md + - API Reference: + - Agents: + - ref/index.md + - ref/agent.md + - ref/run.md + - ref/tool.md + - ref/result.md + - ref/stream_events.md + - ref/handoffs.md + - ref/lifecycle.md + - ref/items.md + - ref/run_context.md + - ref/usage.md + - ref/exceptions.md + - ref/guardrail.md + - ref/model_settings.md + - ref/agent_output.md + - ref/function_schema.md + - ref/models/interface.md + - ref/models/openai_chatcompletions.md + - ref/models/openai_responses.md + - Tracing: + - ref/tracing/index.md + - ref/tracing/create.md + - ref/tracing/traces.md + - ref/tracing/spans.md + - ref/tracing/processor_interface.md + - ref/tracing/processors.md + - ref/tracing/scope.md + - ref/tracing/setup.md + - ref/tracing/span_data.md + - ref/tracing/util.md + - Extensions: + - ref/extensions/handoff_filters.md + - ref/extensions/handoff_prompt.md + +plugins: + - search + - mkdocstrings: + handlers: + python: + paths: ["src/agents"] + selection: + docstring_style: google + options: + # Shows links to other members in signatures + signature_crossrefs: true + # Orders members by source order, rather than alphabetical + members_order: source + # Puts the signature on a separate line from the member name + separate_signature: true + # Shows type annotations in signatures + show_signature_annotations: true + # Makes the font sizes nicer + heading_level: 3 + +extra: + # Remove material generation message in footer + generator: false + +markdown_extensions: + - admonition + - pymdownx.details + - pymdownx.superfences + - attr_list + - md_in_html + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + +validation: + omitted_files: warn + absolute_links: warn + unrecognized_links: warn + anchors: warn + +extra_css: + - stylesheets/extra.css + +watch: + - "src/agents" diff --git a/tests/pyproject.toml b/tests/pyproject.toml new file mode 100644 index 0000000..24e08eb --- /dev/null +++ b/tests/pyproject.toml @@ -0,0 +1,119 @@ +[project] +name = "openai-agents" +version = "0.0.1" +description = "OpenAI Agents SDK" +readme = "README.md" +requires-python = ">=3.9" +license = "MIT" +authors = [ + { name = "OpenAI", email = "support@openai.com" }, +] +dependencies = [ + "openai>=1.66.0", + "pydantic>=2.10, <3", + "griffe>=1.5.6, <2", + "typing-extensions>=4.12.2, <5", + "requests>=2.0, <3", + "types-requests>=2.0, <3", +] +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License" +] + +[project.urls] +Homepage = "https://github.com/openai/openai-agents-python" +Repository = "https://github.com/openai/openai-agents-python" + +[dependency-groups] +dev = [ + "mypy", + "ruff==0.9.2", + "pytest", + "pytest-asyncio", + "pytest-mock>=3.14.0", + "rich", + "mkdocs>=1.6.0", + "mkdocs-material>=9.6.0", + "mkdocstrings[python]>=0.28.0", + "coverage>=7.6.12", + "playwright==1.50.0", +] +[tool.uv.workspace] +members = ["agents"] + +[tool.uv.sources] +agents = { workspace = true } + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/agents"] + + +[tool.ruff] +line-length = 100 +target-version = "py39" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] +isort = { combine-as-imports = true, known-first-party = ["agents"] } + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"examples/**/*.py" = ["E501"] + +[tool.mypy] +strict = true +disallow_incomplete_defs = false +disallow_untyped_defs = false +disallow_untyped_calls = false + +[tool.coverage.run] +source = [ + "tests", + "src/agents", +] + +[tool.coverage.report] +show_missing = true +sort = "-Cover" +exclude_also = [ + # This is only executed while typechecking + "if TYPE_CHECKING:", + "@abc.abstractmethod", + "raise NotImplementedError", + "logger.debug", +] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + # This is a warning that is expected to happen: we have an async filter that raises an exception + "ignore:coroutine 'test_async_input_filter_fails..invalid_input_filter' was never awaited:RuntimeWarning", +] +markers = [ + "allow_call_model_methods: mark test as allowing calls to real model implementations", +] \ No newline at end of file diff --git a/tests/src/agents/__init__.py b/tests/src/agents/__init__.py new file mode 100644 index 0000000..69c500a --- /dev/null +++ b/tests/src/agents/__init__.py @@ -0,0 +1,223 @@ +import logging +import sys +from typing import Literal + +from openai import AsyncOpenAI + +from . import _config +from .agent import Agent +from .agent_output import AgentOutputSchema +from .computer import AsyncComputer, Button, Computer, Environment +from .exceptions import ( + AgentsException, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + OutputGuardrailTripwireTriggered, + UserError, +) +from .guardrail import ( + GuardrailFunctionOutput, + InputGuardrail, + InputGuardrailResult, + OutputGuardrail, + OutputGuardrailResult, + input_guardrail, + output_guardrail, +) +from .handoffs import Handoff, HandoffInputData, HandoffInputFilter, handoff +from .items import ( + HandoffCallItem, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + ModelResponse, + ReasoningItem, + RunItem, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, +) +from .lifecycle import AgentHooks, RunHooks +from .model_settings import ModelSettings +from .models.interface import Model, ModelProvider, ModelTracing +from .models.openai_chatcompletions import OpenAIChatCompletionsModel +from .models.openai_provider import OpenAIProvider +from .models.openai_responses import OpenAIResponsesModel +from .result import RunResult, RunResultStreaming +from .run import RunConfig, Runner +from .run_context import RunContextWrapper, TContext +from .stream_events import ( + AgentUpdatedStreamEvent, + RawResponsesStreamEvent, + RunItemStreamEvent, + StreamEvent, +) +from .tool import ( + ComputerTool, + FileSearchTool, + FunctionTool, + Tool, + WebSearchTool, + default_tool_error_function, + function_tool, +) +from .tracing import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + Span, + SpanData, + SpanError, + Trace, + add_trace_processor, + agent_span, + custom_span, + function_span, + gen_span_id, + gen_trace_id, + generation_span, + get_current_span, + get_current_trace, + guardrail_span, + handoff_span, + set_trace_processors, + set_tracing_disabled, + set_tracing_export_api_key, + trace, +) +from .usage import Usage + + +def set_default_openai_key(key: str) -> None: + """Set the default OpenAI API key to use for LLM requests and tracing. This is only necessary if + the OPENAI_API_KEY environment variable is not already set. + + If provided, this key will be used instead of the OPENAI_API_KEY environment variable. + """ + _config.set_default_openai_key(key) + + +def set_default_openai_client(client: AsyncOpenAI, use_for_tracing: bool = True) -> None: + """Set the default OpenAI client to use for LLM requests and/or tracing. If provided, this + client will be used instead of the default OpenAI client. + + Args: + client: The OpenAI client to use. + use_for_tracing: Whether to use the API key from this client for uploading traces. If False, + you'll either need to set the OPENAI_API_KEY environment variable or call + set_tracing_export_api_key() with the API key you want to use for tracing. + """ + _config.set_default_openai_client(client, use_for_tracing) + + +def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: + """Set the default API to use for OpenAI LLM requests. By default, we will use the responses API + but you can set this to use the chat completions API instead. + """ + _config.set_default_openai_api(api) + + +def enable_verbose_stdout_logging(): + """Enables verbose logging to stdout. This is useful for debugging.""" + for name in ["openai.agents", "openai.agents.tracing"]: + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + logger.addHandler(logging.StreamHandler(sys.stdout)) + + +__all__ = [ + "Agent", + "Runner", + "Model", + "ModelProvider", + "ModelTracing", + "ModelSettings", + "OpenAIChatCompletionsModel", + "OpenAIProvider", + "OpenAIResponsesModel", + "AgentOutputSchema", + "Computer", + "AsyncComputer", + "Environment", + "Button", + "AgentsException", + "InputGuardrailTripwireTriggered", + "OutputGuardrailTripwireTriggered", + "MaxTurnsExceeded", + "ModelBehaviorError", + "UserError", + "InputGuardrail", + "InputGuardrailResult", + "OutputGuardrail", + "OutputGuardrailResult", + "GuardrailFunctionOutput", + "input_guardrail", + "output_guardrail", + "handoff", + "Handoff", + "HandoffInputData", + "HandoffInputFilter", + "TResponseInputItem", + "MessageOutputItem", + "ModelResponse", + "RunItem", + "HandoffCallItem", + "HandoffOutputItem", + "ToolCallItem", + "ToolCallOutputItem", + "ReasoningItem", + "ModelResponse", + "ItemHelpers", + "RunHooks", + "AgentHooks", + "RunContextWrapper", + "TContext", + "RunResult", + "RunResultStreaming", + "RunConfig", + "RawResponsesStreamEvent", + "RunItemStreamEvent", + "AgentUpdatedStreamEvent", + "StreamEvent", + "FunctionTool", + "ComputerTool", + "FileSearchTool", + "Tool", + "WebSearchTool", + "function_tool", + "Usage", + "add_trace_processor", + "agent_span", + "custom_span", + "function_span", + "generation_span", + "get_current_span", + "get_current_trace", + "guardrail_span", + "handoff_span", + "set_trace_processors", + "set_tracing_disabled", + "trace", + "Trace", + "SpanError", + "Span", + "SpanData", + "AgentSpanData", + "CustomSpanData", + "FunctionSpanData", + "GenerationSpanData", + "GuardrailSpanData", + "HandoffSpanData", + "set_default_openai_key", + "set_default_openai_client", + "set_default_openai_api", + "set_tracing_export_api_key", + "enable_verbose_stdout_logging", + "gen_trace_id", + "gen_span_id", + "default_tool_error_function", +] diff --git a/tests/src/agents/__pycache__/__init__.cpython-311.pyc b/tests/src/agents/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daf05e7ea70ebab1d91fa405ff92bd37f7227058 GIT binary patch literal 6280 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/__init__.cpython-313.pyc b/tests/src/agents/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1c330f1c056069724e5658b84a52848f59eaeae GIT binary patch literal 5759 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/__init__.cpython-39.pyc b/tests/src/agents/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99cabb1791512b6116c1ed068eda6c6efd68ec58 GIT binary patch literal 5230 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 S`Fu?tuNRuNnZ`_|TKzW{qWdcV literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_config.cpython-311.pyc b/tests/src/agents/__pycache__/_config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fd0d6a77c98e6b40fcfe342f6d72e62d5bc0802 GIT binary patch literal 1508 zcmZ`&&1(}u6ragXcDHHM)~`ZEO0_N1}i+_Sg(aT2mP!PI2xQ6&AR)v!Rqr* zU}-E?CF5o95GUbb$A?h4>WG(R&L|)*3nm@O%Wk#4>af=hpG&9Qs5sq!eR zls7z?)(%x!n41t5Dof$-g8f`DE^J=2U7s?$yvpiQ&~>N5ed)_)BX!x52`TJi(H7k8 zz--6$>y_$ivC-s1Dz8%U&u@USg$~HXJJBMOA(;%w+0L}pc*WUO^b?N1~h1e`@;DkGzF zqY0SVFg}^8rm}8-N=kvpsITM;QK`Or!_k)%>9h%cYDlwN< zKGKud%vCQ$=b+J(M=V)gC{j65e!T{7!V}3+jLuU}`a%4M*o=&PEf@gM&;O0VDc-%&?P&k;*RPkB=0ajCC;Q#;t literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_config.cpython-313.pyc b/tests/src/agents/__pycache__/_config.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25b6ae85b7b7457731f99b493f41213471fd2654 GIT binary patch literal 1340 zcmah|O=}ZD7@o~ecDMOz{i?P|T~ov^lm=`oSR_!WRTHVuZB=Ms*>0w3+B6$xHehbO z_!9~qzR%R0LbpaTaI7H0;^otdxgGpo(MyaPIbQ*j};Vc;FOP`32t3MtP%23LMk zbO<#|r-GkO7&fHLqQTZpO7c)K%!p$+)U=A_fif9-h?J7}ewTSn6zTSY5?lfM>mVExfa)LUCXd%}VyQW**;$I~6;gXPJZSuHui z)G&`GPzBPAcTwGRYMBwrQgkb2*?E#O3#9C@{H9T%wqrZBN)SphYIU+H!?Me3#ll3T zMq|7k7v7oIz}QAjJo1_y;n60(c#O3}tnH3A@I)JO$d#;85jwd2RYr_8n+C{P-Cu>O zE;#Gp?4V^~jPHeIUO|6Bwb;AMFX@G^3)*5puuctE|H4P35Y(^-peuK7&_{CU93=h! z&_c8yw$T9)j1Qx#G|mH;0>M3Y1H?A^Di1c5Wc}{j^qcgNa>s*b{1A`tK5O79nt&Qt zguYtH9kA9-2T{H!d0O8B>=!9+jR~RztirV#%e8KA5iWxRQr_k@Hv;9@b7d8>Ns}nHi&KX zMUEUK)X%coR0fZg$wOuG-GhcQ^S$fMHI#cEbXwe%{HIAdecTJcUH?Toq|7*ml+(eL zwt(g(=1(nm-mQP>B6Fz6Wf!m7cFDg(KF}WWTKJPI_u+(UiKOXGNby(1o`YzKf*_or zkrQ<52TGoxR7(m8sk+!gpmy;YSFMOBOxHJC2-N#IPwg+AajzA_!c1L07<>3hebhqW L>@WS|%4_@^#yug5 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_config.cpython-39.pyc b/tests/src/agents/__pycache__/_config.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3821305611c6e607af0331713fe7753ed3e8f6ff GIT binary patch literal 1004 zcmZ`%&2G~`5Z?7Vw)5KrP^F@txcP!#098~-NJuCg3YT0g*SksG_)oJNsgZI6-lB(a z?5n^V?3DxRD{x}QPC|vsN;96F8PCo)-z39fkKy_L^~d6u!`M%{JUlL5UZd!1RFX-a zvx3h!C)>%LLd*o(Lb|zIbY`8#?&MzK&wS2QAU)~dIx>*mFYc_Xxay&&cPnIH4$#wA zLJm(D`bL;_B**9*NbiD$lY4xRh1~Yf^}0+y)T%sxhgbi7W)vj(J#WLKxWZ79#ri4% zB{AcBT~b+ z&W3$!Fki4cJ`DwU7>5ojdrZ$sHBVN#+4q7^gdBVf-!bUp=l5CkNh{D16j5496(tK* z8ZDP`4V9_Py574Y4e2f##c5T}v&GAL4Mb9aqKOa21KxN-VZAiZu#`PoX%){a*dqtZ zPLa{WA^U*u6fYYNBf@yi8N+7H@lCQU=O X!7Hq^%GDW7N|S4Vy`YG3=ZW_Rk@e{d literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_debug.cpython-313.pyc b/tests/src/agents/__pycache__/_debug.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e95877370b8fc0fd7fb04f0fe8668af27feab52b GIT binary patch literal 744 zcmZ{i&ui0Q7{{MCNxRxw!If3!!F5BJ*}*W_U`QtxTBci;NYg?)3$blp1DZAEO){CM z;oXZe>~U9b9{nG@r1VgE_2$W2NBskQ-}Hwt^npC@^M2mv`^^*bj??KRu&BTLYCjhM ze)7#Mq-imJ#ENq;z(5B;zz`b95F5glWJtH+r9{MrxCTaS8%_F&bO$05pOcu4*v0ljd@CSE+iKcnUdI+OCC_rj0wkXT37J#PwbOv^ztJN7~a<#k6oMu20w0QJ}$ps?rnWoIoto1TIyB8)ao!*zj*W6=&$b# z*K1$)hk3Q1R|k15EZ6$^S}4`WObc{Xe_GNCPt3wzNvjxUQP(P_s_&VHdaH9!!$DRHAu|#LBwZ#TD~IyIU`Y*OF+$N> F{s8&&ueSgI literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_debug.cpython-39.pyc b/tests/src/agents/__pycache__/_debug.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b32ba66b7cae4ee1a76d182b8b67cf9c45d754df GIT binary patch literal 523 zcmZ`$%}T>S5Z<38hH63V3n+*X5QBFWi!DW{iO_g3mo@FKIW%U&Zc-`KlU{uT5AD%s z@eTIs$ye~?Zd*$aIxye2GduIm4x7~LHK6VG^&`DQ0DPKY-LMYMwaY^d6i}Ff7%<2X zMQ?~=YQ0!7W|-Q~Ft#*1nr#Yuz}xuJ9U$80t3z)ZN%E)O)6 zUbOQaLFrSw2#B#vhsRkTBA! zW+szSl2z;-2(#(;sedDxkiK~E2b?lLNm*V>IwXbQB`?RtZ_1Zqu)OsNW&J8WD#og@ zIFh+A%aeY_sAXPn0Kgg)Tbkp&@VXr|FG;i04x%1ug+WBRVTW9Wt#+5Rno%>}{ih@f z!$pPX2uEuvEK8xRKIhp&yYYvb+F2!6{qud-VAF`XCl8!f89OuIAYBbE-nL!ag+Bp? BgH!+j literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_run_impl.cpython-313.pyc b/tests/src/agents/__pycache__/_run_impl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76570e7de1a4967033c4c8b30e85e5c61d683535 GIT binary patch literal 32581 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

LKhsdV$ zO-o!IYOS;T0St(sA99-Wo5MG3MVa08>BnVE$+l19IP9M2FAZ#9-zcvPG#lR7v1cQ^ zEmU*%WU3Pb~{VaGl4D5w@-^IePac~)?FRn0>QJN)A;O4jE~1}P=~ zrhG|W`e(A}U!NC&{q{HEMe>%pF{{k=`HwbQ=5LArSWC(9KW5RfKF3i^>9_;Oktgq~ zS$VI|W9+7R{~Pcad1#)@qIrF;qK4A^xw(owCAMUhxIP~N;}S;S9r%c~dEc1_&W(?1 z5cmMCoqmA!k7=HRzW))0VG7GYRdN&l&cokvRGYw`Pce)p2XSwwmtVjVOSR;Z{rUsA zr`OzNPp3MK|Am|SHHeOyfjeyKPWZy)nbwQ$mpm_cE^U6f>v9*|x^?uGqX}n!lIy?D z^)FkBGWT((H|ebBo%NS=FB>i!uGGES@Jd6%*^}gYu5&#!uCmua)-BJp;3Zx^7)$v2 zelT{0eX06|>Wg1~DgHwIyWUsEcwb+_xiiV_yw2?ulghg_mQu|-t6|?Ad|@!*Y)^9S z*SYq5`Crt%WO%`FsqW>5%RtDcB)5s@Hc3RwdopHM%R6fm&iW)*&vW%+M)Eccgtk4? zmdTl1nS6EjmD#Hgzw^~=Urjghd$t%Kj11qur+=f ze*-t=K6jvwz2;#92Fu6C0?~ z{8e)i;D4=V12vkz)@Ttovk0v^gl>u#Q&>u2IfXt7t0=5NC>d{M8Z;s2D&BiYb!2xo zXhKN`j&vyBi*l>@MIdqw;=#+$GQv^X%^RNgV1~5ed_z4OWts`jG7`YoT?k;)m26!8 z9x()XHW7aX5Xr`+O*A?Ha|^`a0=&8vBVwZc24bT924bT924Y~p;aGJ5CYckU-*J}O z6BKJ}Z#dzU2cD?x0-FTH3~*KXsu3_L0s3v0;y2xJR$zSm{7j7Bq{MHc@mEz4D%J+; z0B!jnus){{Jf&c>h@n*HKaM|(IXMqM`UXFXNl=|bzt<4VsM@6lMA10WfVZH7BR=>( zmEdY6{#KbAen}}7nQq0izj!Mg71H0;jf>Y}8Zep>0V+ zLGDM?{=$yW5NhKEIw3|JxpZXW@D)sO5GwxcZJd;0II=L}-ArgfMtt~Mi2!*DQjK67 z0c@NMK|X@~2mx{h^b=QlVU1|Vin;>PX0;iy76iBx6JUnmLMp}hf&gnP+O-OH`b$gk z)@eN4o&?(tItT?x8m<>>d~+QhekIB zZbbmxJ|mvUZ%3>b0X>*4z*^sjuR8$(80W_nF$;8Xc?pH3Q(b)dYY6@M{X(3_A!>`~H@3{haaqoN0QWX?dU7!!vu{ zXT}rE`1?%TZIv05$l%u=hV}iN@&24?e4pufpV|68(|lWHz$gH2BKX7wpLYISQ+`{= zvc{VX0s~Xhp73s1WYADdWxlOau?=wbCyVw$D)S0`HAw+J4DcxdK3Imt6zc@oSkPqy zkW%1l4Nbwa-W4bv&7H6ed^Jb`b_}p90d^(8hyg|=z$gV2vfV2T04V@p+YK!I0)lr% wjnA#{r6oLXXdSn-@VxDg>e+^y48q&@+p5@&HyHr81MD=*Hhs(>q)XZV0Xwp~w*UYD literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_run_impl.cpython-39.pyc b/tests/src/agents/__pycache__/_run_impl.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d04233a0ae46ae13b22e79b6d4b7541c8bae89b GIT binary patch literal 17933 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

GA+jW81vHXK{)`1KvW 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_strict_schema.cpython-313.pyc b/tests/src/agents/__pycache__/_strict_schema.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54b08cff58225e3fbc176d5fa1ad9d502cfe5b00 GIT binary patch literal 5560 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 D<$kG> literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/_utils.cpython-313.pyc b/tests/src/agents/__pycache__/_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a45d8f77f312949bf574a1a3d32295196ec7be69 GIT binary patch literal 2493 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 I_1#|WUoWlVY5)KL literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/agent.cpython-311.pyc b/tests/src/agents/__pycache__/agent.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3579bbedebf410a9eb5b04b483091f892b424e7b GIT binary patch literal 4695 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/agent.cpython-39.pyc b/tests/src/agents/__pycache__/agent.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d7f8f1d280e642ed7c81c6ea119564cb93aa424 GIT binary patch literal 4875 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/call_agent_tool.cpython-39.pyc b/tests/src/agents/__pycache__/call_agent_tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3502ff97892fd12223d71e5f97ae4d2af0c7fdb8 GIT binary patch literal 2546 zcmaJ@&2QX96!+Nc{oZ`E=?CS|fdgo1v=JN-s;WX%30kS-&;}tniY&)7n{_(2mzi;w zjkc#$YA(H$YY*+Q{{$zF`~`F6fch_hK)g5hE@_(9Q6}$=-@K3C`^|HrUa#%IbLqF= z_n$Q#=MPj)J{~CFI1W+IKwu6d!bwR$2+FQ-Q!ntW?1@_H2R_I(=8JmT2pU$di)Pvi zT2^j|cG?L#R&I)J+6#JCZi%IIIas!GTdbt3!D@OgIA`^aIG?TsYw3mH0&%``SeNx4 zJFIu)1{c}VeW$>gKzz;|^}GA{eBSq14N}9?`pZ zzo%f&Wu!TyNj5I3qiqTgcwotJ$R&8BLz$;^6lE+Q4i%VEJ;~`LnyTy8_VWR~lP%t_ zLsFb(d{U-EC69UXkSn_#=h;Jm+%}FvnmU(Lds<`8SkkHEHUQ+a@Nzn3Yw*Q;Gtkik z2%kgXw{m!IYy<(Z1LNmc-)dsAzW%Va;vB2jh6y4pHiw(Z=R zvuXFRt+b>wx|L`AaDC3;ot*_Sch~8H(ZE1JA;kDUYW zC7F5Si;OV$sXOQIbMJ}sgbZEe@#F3hdF0Q0U~n_~^GwPjQTBlwbLO_^IMYqJUy%#R zh>xxAI&hm;ysB6|Se!wtMe(I2!a(z(?E}7ob`L2G4my_k!-R2Gvd#MbR8ea_Nra$# zoED0+Gn}7*vy5?K<7B9HIvFMLh^kRu2)th`iQ%TfF5K8eQvWUOrw`N$0jkcHm#*w~d5aNpTkaf+x8lDEjfj+{DOU;(o$>%<?><<5OadoOA;_rQNifQ#-{-wT$MjwD>u z{m^0|uYf=K0WAHx#4n`rWRckoiF4bmVQOIoLv?$UX~B>S6Znv*zE>KfA5$;!e&xy) zP~~}8UO9h#z43#B$+scXjW}m~!v<>-oUX_=4QVpgYnd~Af6T*c)hSWNr>1TKTFy0AFs zU9v>#|JG_(I)|6vm2aPHL9k|M2U!@Jb{M8PD+C9*8-_m>k+2L4O(P6h9>aDg2Ck}< z>ru|+ClIL2hAt#bNiM^JzY6=>B(~-=gfJ4xXO`m}=D+>Ot3k9E zOQfNxL%RSYSp!90zy(1pVQHX&Z&^`t2SzLYVhox-Y5aN36s@PcwM?~m)Ktdf8l3^ta$$cio)1G literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/computer.cpython-313.pyc b/tests/src/agents/__pycache__/computer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..871935a268770f9c751f90d386e08c026e06377e GIT binary patch literal 5846 zcmeHLO>7%Q6yEi&e`3eZ|E~~T8dMs7k^(8HYG_H*w1q+n-4>}}wA!pU&QjZJX4g&A zQv{-d6Xk$NZ|=>x<=A77aBCzaszFGVIOP`Pg2avYX1w;UTgOR@;L?@+=4anG-kY8NR`lyNKlhm zO&%8Z3u<^&P$OyKJR7|>POUgB@d}AB&nS4t+IU8@;&{gv!sf?C8hy`lO`_+$EF|wqrzwM;o5DlBpo}T;^{;> zF%`nZ0Una}2z+Nt&Lm(Wehf*q@QtPukmQ8gk?v9@H9i$n69HUz8ysRHYG(k`(*~0S zz7+fr8&|sm*xnY{?sSs*%W6-+zpsV=h}s+Q>u=%b&7aSsFMt_nf$2{Vrjz(&Y7q?t zP(v+HgMjiqRUdvRfFD+e$w)RnK1yS=cD`7G6}=S$vTI+>C`PSAvGT>7nKuirnNgN4 zcS&(p3Wj1AVfDL~U2rCp3rnWr;S~!U$)awUioW2uL^oVVahJ?^1E3U4GXsOUHjort zV&@dSL1z=C_U25`2r=CfDHvjHIb(r!e0i%fnD2$GurAK8gQ2ox<`x~it7va@& zCP{wMurubQo`v|G%w?@eY}a;IieA^GLyX#8@Z>3&D6Y_iS-4^myMXhi{odpze7&6* zLln%aupJRPl2xb*J4Z@-!G-jp=?BAOC4*3<@r(sj4(5~g)#)0YJqQ{>GYaPMX&i%? zz_Vn#KAJ`+#!|)QvMStfA_Nh zXb{sFnih;TDuAPDjHw8mp0Th`40bwD2QeO*j7y&;>r~i00QVcSe(a?sFYA`;8ycH028Z6|lOzZWK0P^a zU-8F{O-yDZZYp0kSDd{j=om8j6BL6{io|pr+EY)G*Y#QxVNrLN_DVS0OeklFp7o^- zn{Nb&*oLtlBHhqCzYbfqrWN$OscAH>X?Z(Sf>o{2PEGr~q~~gPx-@OkB95E03TDBE z+fhx+*alQ$umi%@?XFNsUodFO+obcRyJTl*Vy?08ks)BDVYdYPKb2r#C(mGq1iL!f zhqcHFwkMDmpg~?j^C}t^{t>KAg8}tp0b&N*r?5SsX^sm=hoR}NODxz|Oh?oBw#Z(6 z+=K`U9A@x5a{hwFeqHz@B!-W|W*bS}3&(y54{e5rwxKJz_DNNMqACfIVKk|28JdrB zoGXu&o$|q(=UM?e{KL*RyciFT=y$l`Y$NX)FDj>4Yr}1WS~HkmnHQBzJ9mk@^P{clQ(Vnb~< z>uG*IHIX%B?mIXd9xPh|6S{x1kBKirve=|ogK~E_fuNcSs$6zF(!SutnGAg&e8~bD z+}jKZjCbvZ>LHoL&R{wkWbzB#4`KKQVMCJ!WbJOK55Yi9LYAHVv_DABrR>EZEvnLh zZrTka{*&QXYD{I&-XI;y*-HVkz&o)Uo~92of;y&|2Bqz%L7FFjc1$ZZ%?D+!9v~v9 zSofT~w*eGL9;z`q7)+igb4|GCTO_m4+%w@^Ga*xcO|wI(YX!;JxA7V5!&kIf6Ml?+ zXmIQOe>7ovtevP)x#Hr*#sSv4eW4AiJj*2$FE&oLare|1*Jc}>>kbG1*cR;Y2;J5s zZ8YH;XYzt>M_8V!NVkqK(vAMsq+Aoa<+;jO#i<;;b#8Ot6wjUy5XNKV11M+& z-h8W&CHNYW^JqT8ZfWMkEdP>VONXtlQM`+v7&JD`zn+efE6{{548b`8W?K?P@mC@K zn=pDmHE?(E=w@nqHF2+_`)<$lX2)Brkq41OV)AR}+vRVT9|%x=uqcAPG5f>m?@s@T I9Za+S1B+mKH2?qr literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/computer.cpython-39.pyc b/tests/src/agents/__pycache__/computer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bfc37b5e6e98cb2728d5bcd42d5830a46a1ee68 GIT binary patch literal 4174 zcmeHLOK;;g5Z24K?AUoY&U0Vtd);p1whI&}T68y?E{X!%0L69>HBbn$$i&*nl1tI+ z*w^f({SEm8{vAE_SfD3fd+J~4sWY@C%U(H)Bt=gpK#H6PzafX5A>>plB?Zd8-+w#) zqo^o<;viiN7(9d;J%NTOMD>*>el=h1XiZI3s7CZ1rK!W$fUohjLUJVkQXzS&JkOo! z%{)*F!~}{tp%jSvRH+yLf*F;=x+)4!JWiS8*A1a}99IWrB=WshFW@~4>+1sFVp8r@YDg(^*zC{2y1V6s7T z&-1iEv~!i{#CU0(sb^}_oY0H3L`z574lEYonYu(1vpitBJ^o-DM#U`Jfw<8WD3RT#y{^#a?$HmT2(8 zV_|^1P)0-z8bu%0;#KdZ1YU0^TZv6$Ye`RuZ*d&f( za``#e_uLcYj90D=my?xC)<{pyAZ^e=I4;<~$Oe7hhB^2$341M{+7jwEOcqx$#O84gyfVm$Q7Z{5a;|+SjlSdY_cP~5j(Z7rGxXpq_IJV; zbT4y$E!aRAhZJ2UJ3H(vwt!hDs2+Mb1a&0FAC z)Wo!m@1toZ&U$yq3;9igqz&hgQZE6v1!9?*e-YjdP)J={d1LX;f1AQvpIw3q&C1kB3UivZsOh54mn5PJqSPLQ=(z;SM7?;uoGPF@7{ zUIr9~${$it)mf;833(Al#*0)&;vW}dEKJIaFtU7vf!B>ui&3OmYFS|dhH%)2#!1x@Mah5BjG}Nz~>@)a?JRAfs wIA(a<7*5JCehGd5(Sb~T424r$l~^InFUAjoFIA9;@aKW4mNio~_3Hh90Qtr>yZ`_I literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/exceptions.cpython-311.pyc b/tests/src/agents/__pycache__/exceptions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d851c8e1c85e16b01a87d107315e6ba4ea0df5c GIT binary patch literal 3660 zcmd5;%WoS+7@zU3pN*R|^pzHxLE=$EY8NU5q9rORN&0jVNe`IAYVl4Sw`;GNS+{9a zL69mDq#k-fMM6k9wX*t`a6vALM6?o8B~HC1at^2`zHc_Wjn{90Cxx{C2EmU%%UaDiov{^*Z*;a*IhY2SLO}3>eT0tj+v6PvTaJq zW*osx+h#mqC9Bq%JIk3b#il+vmoMgYFJ%~Yv&Id>%NRz@v6?p4`wZi&rfF|5NyD(5 zvSGAH2bc8tY~hmRLKZ}=P&uH#;B1Wg5(OIH^1`GOSXjc$eRlJbq~ zA_W5~0H+~xFTg_hThTvk2anhpbnH=6;2NE`fbOd461Z4N<2sL9u@k^pF|N;mo88n) z2}=j{#l3+G=G=5s)Rh9c#jW|#P?Zd9CAr0}&2nF9HFK_7Yt~r3S^JU;=FBh`MOjrU z4WoG8OV+rQpo?-0W(*3}agm+f0C&g@dV?4N2azyETC~`r0S-j4&9kE!6#M zu=A(s{#ERQFbwZD@ifuaC!QysaLDbo{w^@zMKUDjMN7jBnTF$bk<3syc>79Fo1>)! z}CjcI`)JK zcG2MNd=2me-I_pM zJalY%=%eMKi8lOI&zcrXXVwV-g!^Vtxg!_zmxl`4{umK21LbcuyRpAAw{5sCP=8aX z$CLh+RBRv|hF4Alz-qg{6@9F&k3HNw($?{LppUHRqpP|ehowCO_-6}FQ-(Jzf!#aL z9SdyBy)6<9;&qr!BoNRK{;#eqQ2io@a0Fl*x63_kr=i~+$5GJvdfY{+pn$FK-~^jo z;21U{yobaSUEoMt$LE25|1H{`1=+4ScUOG%){#_KWBJ&s+r z!tLrBwyt-nRD#02Dhjv1S;N-#E;X3Ko>BO5cnw?EyVTz3aB7O%{gE*<6rwQ-OU@d$ Vu6L;e(N0Qm3D()$Gn7$2^f#FXEXx1@ literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/exceptions.cpython-313.pyc b/tests/src/agents/__pycache__/exceptions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ae5d4cf9b01960c0ef315eefe67a659f0abfacf GIT binary patch literal 3240 zcmd5;&2JM&6rc63?X`)MkOGaNCDWD)ECm}i&<`LfA58*D2~{?!+LCs)*b`^Vde@!V zg^&wKm5O?(#09BGuDSNV=q(YIpew2(RjQO5m5o3>^}Shpi9`5MMWwE^Z)V@TH#_hB z-uz}7xm=dOQ(gPHdMr)IANbLJlC4hTC+OTD1~HULGNEvVk~w8xYtYgOF%pJ4nV3+y zI-zlml78|gF|?h;NX{h>sgWU1No@+W>A6&_O-ro~+RQqwF11rh2ToXm(XwL+LFYYAEytm(Z6c`cQh$Nb$dwu&O%|8QR3R1Wrs-NWW}0EvG;3ZZ zaIoKHn%@ML(;De9&3T&(-?3fhdN73uc{rzUN=Aw^b#<%dOGU<7F4?mpK#ykpjaMSUlg6yV_yC zwXN?+Vyi4j;~g08Oyb*6P+Xf!&ZP`>FN_-MT=HN73+ii)r;Q}ntEoad>^);G&IH_* z31$^mS=$1}OCxcSn_0gZ&LLz{36vK3l2?61k z6EIu*!9K3I(F@`R`K;GzkB}-^R;HE}z5_JNiBJ>FnU@~|ohapf&>(!aYx@92e&e!> z?IE1Ifyui{?!3DDhps4dc~DF(XE+AOyHMm%bff425vs!HAwX}ru7?9#E0($u^w-k{&3fmk;Y)} zLbu&0izq@mO*xi zpu#mS9ep}f0a!w2F91}QOBupT1A*yAUPD5A*=g>J7Z+!D4=nP9vWv$UM}c9*yJ74p zy8#V1&+G_{e6=Z$A$u7b^4M-T`ew;N#Yh5^>A+AVeS-o%r{ztfQ{N3#KcpUNov0MaRGnJN#)mRpdgXBd zaahYKegNhPlOm`?fg#m}nK-O4W18}K1r|h(TUNQ;Z0N$@1tBm%O0P<0j=c9hC-ZS( z6~VRf9*9Rk()7SD`s;V~-1q5Kz5h;k|4P1aJ3q9VAG)0%Ud<2R&X29;$5#5sSH{0s z={om7O=PliW5a2Iizy^zBVl<4!Sgr3_A#G_xmI{(<~XJabIF)6g~y_Z_M<%tEEj>y z@(3yZ#JwGZx3s}K+Q>sqOJ*Ms5E0Ly?SEL-bi_KxXg2yiJ2t2yx|EWj~dB{vV`P6HylZ!el)s(j_^_Nz*UM*LEKjr zMfr`;Kh@sfwXtSOQI1@B&`UuhC(nti%5@Z&&}K-1iT`kt0Pt7LC_O#)4G MC-$#Az`o4VKcKa|ZU6uP literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/exceptions.cpython-39.pyc b/tests/src/agents/__pycache__/exceptions.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b82c80ccda50a12d857b08c9480f6e06b6e6b544 GIT binary patch literal 2630 zcmcImTW=dh6rR~#Ut>3JS}vtPhk~GyD!VU8NJLfI5QS3JMI?QwUq*{(;%vGXb7r?q zt-Li-`41Ec9{p4J3;W7b{z6~ioN>HP;y{ZO#+vb*Gq*F}cjlb+o6S1IGC2HWuv%g4 zPZEw74~12%<^UVT6c1U%dz>@PK`2MLp%b}1H}ZNOXB$j;s&v3qNta%@y)w#WRYAF; z%P3b+uBsZ!H7i$9uB!!<7pz=ExuKdUH?3Sp`GQ(RdC|%Xs$@i0p_G9MK9LHL6DSyiR@MxqWrAqozju|YVEw*`mr}M~YFdc|G{Y2@G zZ>>>J|_a-5#_>A03+pe32$eh)(UNc1`?6S{GRKUSQIj0$%{S;?qFU z+{h2{Nkqu`cbgBpZ98v51X1(0R06SM$%Kp4n_+6|S=8 z_i%s)r>uC$3hB;`l^VU=2^lN@Oh$&25Vt2th93(}+=#xp6)faA__6spDb(xnU=STX%cM8z=%VBHRq-! zRJvFVfi{UaDSyF!!!)tacimlYxAY5M^u>#dsNit-nS$-hf|0_SsPm z-o?Epb~TTT>FY2=gJ$pXQ<`F5(-rJd>X^PJr|JUf&fP1-skA=l7VrTXMVwi*;6t+F zbQ1Z`l)^`};m6cmr^ep=Bm-Zg{v-p-Z?d(Z0LZn@1om?r_>y)ml7|bV>n=C)GUk7b z6hOBFK@B;!y?b6O9*0f%HsRR#W*@8N#_n_M(Be9Ib!GC6a+|)6?3Yo)@p#K^d97M& H0c-Pb2fjqx literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/function_schema.cpython-313.pyc b/tests/src/agents/__pycache__/function_schema.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97ee533f316aef514061482c6d4af0e04c2b6de2 GIT binary patch literal 12146 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%0A}za5*0Ew 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrail.cpython-311.pyc b/tests/src/agents/__pycache__/guardrail.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a7e4dfeb2279a091a510376c5a59daca5d66e9a GIT binary patch literal 10844 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrail.cpython-313.pyc b/tests/src/agents/__pycache__/guardrail.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac7e9800a739d468c81427587c5167f0d2222e24 GIT binary patch literal 9977 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrail.cpython-39.pyc b/tests/src/agents/__pycache__/guardrail.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99fcf761b01080c3f6fbced72ef1bfbf5417f5f3 GIT binary patch literal 7823 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 TYH961(`Pk|)b3e4r04$t3HCH3 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrail_base.cpython-313.pyc b/tests/src/agents/__pycache__/guardrail_base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..525e91e8b4e93f9cc37c20618e454960217831bb GIT binary patch literal 3401 zcmcH+O>Y~=b(XvQ{*ow4re#YV%eEN8mXyYpZM3dk#|a#hiCUOa8-OxatL2c|db!KK zS=yG+L(v}6V=e}K$RURu5}>~#NPu37id7303%L%E_@J8z8$f)IY?idTOf2e}qv@u`c`1wstaUebTQg0u>qhS|i-DoL(Kii;fo8gju5kxXMHkoG>}MJX z21}g5>i{D0#k*F!<;7ZMqz-h9TMTb8*c{m5JO5gX?Glag=$z0+f`O=+jMgd80|*}FcpDjlL4!LL>5XB zH>RqV)?%vaCRDX$*E^yyQj%N0EvZ~rJfIFsPG0VnnL>#aeB1Tn}3+w5s=HLaZ z9fxsMg>jUohd<)JJ|}d|=b$yG+jTal3C;5Q1GNpdV>@V)K5CAG9)N>qsjC`iv+Z@r zBQI|f8sS1|?~u>3#Vw_{7Yc=Adohw5-BR*SB+J}o#@W)q8RY@OhTayusnI~DXnWdXoFgy)Uc1qlaqPCC59mA*d^e#OAT5p@t)fF88n&r9D2BC8 z+qP-w2IF{WQG39s{g46GF&e-&sogkUTp5V0F53xtbe1HF?{zkn(NKF(CZ&an@Pi4rZP;in-wMamJPb9Y^V~QNlBRC)2 ztgYb9SZV!HOAVmR02&%Vxd0NP&fvRiBJX&kHWol)Z!T2B7x|+mlASjW(+IlDAu)f8 zqAj4aEhG!|H%I>QvX*HD@)2jiO-Q3Sy;L%H>6T% zVWTwgHyXGTZFRUKvY6g4(-&#ec4#T%#sUmXqoP0<#f=&k7b(OYF}Q96CMzsI0V@y| zgt(xSF%n%x1R@9%f+!6jObEhrylR2{fx?;DaKm0Qa-$-PVNJ&lBM3y#=iQuTTk2sP z7p9|Pz!XBRjEUP#4<0v*%bSSj|G;UN{F;0fAwz|y%64I{TbSFPAj27Oz z{6V67>Oyz)!e3^7{K-Uj<|mu^pYG%*-m`z->P}wo=6|#&OQSdUWqCM<#cz0Yd#KnQ zD)y3*$;|H-_A_Msr4PefC$DagT-{7v{o>5||H$_22XA%ruLzEpkKs6#*-T9$%l!2F zY-?nGGc~_ILh{%C#Ygbvzj^i2Yia4@dFi#J^6`~hW4Q3DKeiY!i~Ad6$LC9!6Pr`t zoiyEuUnw>w2dek$#F30yioq#l38%$0A*M;3+^DM7Z5>;Z?+Mf^VxnRa?(g>o!nhy` zw>JrZU&84fvaiIjnHY=46FoT|j_l>V-r_{~%HKvW?8)ML{Vz1r`^jhjSIR{Mc*Ac(w{ z%hStYUAeLAe!F-Bg#U|7&)0B5_mQM$Wa@L0c}~tgBj=xyGtbGjzmjV&$oUtf_<~G4 zCqLYakWligP*#e)+kD#Uj$P~#gnKe#_XKwDoVWjd>B_s&r|Is{^oMef;L4-e4Zqfh% literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrail_base.cpython-39.pyc b/tests/src/agents/__pycache__/guardrail_base.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..400cd36c8737e392fa6c3c2b47c005130cf38bb7 GIT binary patch literal 3458 zcmZu!TW=h<6`tGPFIvg+O7bNgJ8hLsW$7h-F^s^D;J5Q%K| z61BMQ)#HZOh?`zBZh5V^?X~q-SuDjJucPIPSdLe`706Xq6F1^jZ&k~6aWn3EUDMzz ztijiKn|Juyz}hfa^9S2o=MBEWS2v7fvv|{pcZ*k_8LY+HuMO7bO}@TiT-X<;cU$)_ zu@3q>)X%%5&+Q8bqj%Wy*G6yU9U;&&RU=4}G!OD9O|qV)>?dFBs&;UcJ!|K^}x6$g*AuA0+3hx*G^VqpI?ZCtOCMs{A&}xeSCVJx^epKKmlb z`0rB21saw8^CABxknhaGjr#sxG92Z5Igc^g+JAD)lYBQ#a{e+`o&9H{Kr$Ib;`gH* zYH+l|_#haGT$PT}QNlu6&SJ6AcuYb4B?NAGCNn(iIfBb=W<4^Pjkkk$3GXuA6@XGz zOY@1o;u)-u-Fe5sRVTJwEd5Z`j%U8fWlW{YsdiC0M`dIQP^)LMXO}=W-PWZ{W$QsOna=grBKl>Rhl}kIXRw@X=I)Cq6+sS{l5=;M ziYSbDHvMovIOT5of=f5VZjh;P)4@->PoBW-QNmvid6>iV!mkLk@Lc!Qwb5_WkzODS z4x-}`&=`bi?3X?3eGfb^bEsLyA&i2EhRxAUxZ}ny&cAQp5qgHe1Tf z(JjDU7$^oszTn>?u-lXeMT+R@DTkz{gyM4wMu4QEbzWjuXdnYJ_lyfsB$TQl?R*`J z@uMj#9&FD0Hn-f(IL&fj@(_F2r@YuabSLia2>8UB7Qc)H$W%485q~gkC22yeqUmM) zY;Jk6yD&5*=Ywk$b_foe5&Hr0)#0I9nwj*A{OFJG5}jQC7B2SFgsVo8&^~F6yEr&7 zAfhZkxTN&M@#+VRKK*ojZyxo6Zm&<$vsFp2T2%;gIt|K_=OdYfHh5@O$`FX4ek6i= z2w`IU-I$nf%~v)gb7D`dx7HpG^xqh-9Q0Td=dC$ymvZP#oJon9KiHratKYNbP1ulK zs@73~v0GEvi$JO>62gz-p-@Y{=9+mVG<){S%E`DG$X}2NI)c1*5jM_rgtJTIm#i*7 zf}*lv`YD90gUYb$=8D-dt>X8;acd6QHuUt7BiEqUvkHrwkVEj{ypT*#X=olnm>A#V z7(BIPn^|8Qxk>E{Q+_gotbhe076UNf8-F$aW?h(n#sLa&oW8sd8()q$_L3KYh(M3i z3?zO6oji1b-p5c;)o_}3s{TzNM*OLisf3%q!eHU+{5aDKm!D$b4|p z`~(W$;>qX=U;ya0lkGRK@pM8v1?#%pL0g zn5v&swM7->nO?snrzB0ZFu6`j+KBRJs0hc5c%W`M?WSYBuh*I-5%h%2t&0>0WKLw4dJ}NG)pHqA@0=a2*%qD(!EY$kxT3y>RyUUjOf4k(fL;wH) literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/guardrails.cpython-313.pyc b/tests/src/agents/__pycache__/guardrails.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bec8e3f80154cd21374ad81c0992830bbc2961ce GIT binary patch literal 4141 zcmb7H-EZ606~7coiK4!2+45KX!8jj|TE|Y-XhE7ZiILesk75M&QafCBYP&|oj?WBoD&-Jh^M?9oD2#2^+Vz|e=h$xwl`dD=Ob674v}irt69 zbI(0IydUTMewWQeLMG78-uuT=BTmSFu+wf)U*VbxN614`AO&ugjjpQXN<)bv3m#LhO(O5oCZ)%2?7IG(s@8r4cR?D139x+h)I zs#SFIB9{%zpr-CcJ~Ul}YE@4xS`f<z#g=vvK!k3u(bo%Ju-mTTN`XEr%c zFfG&7{RR87wR*1kIrKk<%qSDe6$lL#ILaS^%%B3>NxmSQ;b^!ZmLrAm82Ap8LWD{M z3HnjLKMXdoCj4hx7LrABiYv$ijO@&QNVhe@{6 zOS0()i+-7e&wlpzDNC8UqAX~RQBjuKo1?hPnyb_t!%;L#(QrQ}l zQfA&0*_>$t>>h7gu9sp4bw#_Qx;1J!I;MHMp$?V7HQxv2Jok{4+9Tw;SS0f>UgV2H zfjbjY$jy#ws6BjBslt1HTOymGImqKU+YEKlPq>sz&DAxN8mewvi{?^R@IaMP4FgqurJWT`Ti02_phKFA_~;S?haoI zb`u6$ZIl$rwN@>)ZTP&I20KRE*V?Fa1<-dHzI>Z|$j^y!V(~@(nzYXux|UFiwXG$< zcE1&cTx%|eiA3g<)BGa0$<1XWv6F_q!I;ikHTza0cE8`@oQmiOx^3UJ(7RNS&HwVmNv z$@d+AFf5(vAZiMT*XLS*T^b8s9vfH2R&2+Gz0d()R6z8Vv3cbW%3G+-^8fYaAjIB} z`fs!dqEyGR`Cz%iu-+pf;fQ_SYgFg*^B&pN86$4(pC&r^<-6Du`9JI>Ty;5eNC&jMp7yO0d?G}X&IIc!x2@j*g6zFW-;3@ z4}A8}A`BrEu&SgdLGvA^Y2sk^)ffrd8(LM}= z0)gK70>lr0B0B;vN;@*?AO0#=@5|nuzW@FcA-gS{cyVCx?$wRL6Jc;$xY>}2a&Y@2 z?L~Z`kw}V@FH-#t5uT7k14E4nY7*%!aE&OcG8xdgS20uxic#_#uE;g`h&Zu5JmEjj z{F*&qMEhTfU~du-^jLd_b2?~23rO@fnEpQ!Wt1uJp+g`*pvy==ml9B9e9w5IAhqZ+ zT8L7a(Pa$iGU=uKJPaW5C6a@dgbkijbat`W&zFqifG@Ffjg^2iKMCMgU94F;UVc9D zDRjY5bVyxb-Zj%*My8W}CdFDn>a8fwGUH4?^B5Nf(SQ(r`1pCIrN)wR=ch^j=h;?9 zdCgTB)?ZrpA?@DyKI}^R2p__tL>agKg9IQbIu5 zSh4#^TWFDXsK~!f+Dn9vz$*Y}qww$xZ3YcKa#PfIdDkziHbCF?OY0E0czyd4B;d@; zFzR46B4>9056(Q4`&JrZRqzH~;wl+kUIfLbOQ1ch?0xNKx=mR7iH{MOfCT2l9miG$ zOFj%C81ylqbAT(6c>Gl{w0Ub)ZOXX;uFfzBq#OW(o`I*C@p@+b>yMvgrk|#!x1{NM zYWhb9nebj_nER3&6dsAg9L<9Pc;r2J&{L>~{VkqGgMtEoM;OS{U!sNpO;3Z!#_1W< zvGV9y6z5QUfP&#CJF4^8#S>w0NiU#w5ycdUy?}TLjVLgl^8f?`L;(P?15RlpSC;_h zh}hd8q5g1_gv0?@r+9;FB*_hKfkQM*CBgL_vVQ9Q-083IbZ@5CwAq z3i9F%$6$~th=ZswAVj4}Q2J1abIT4Roh;UXX@E{B4<;|A+kg@bIh+Y?L8q7@UM%=w zG8~Et%J#$y!==x(mS_Ymc=w_Bvr1(YckR(_DBc2S2MWB-cp}3h znnnZOI{dp(46~wjqoLhsMxyZqEPXG%Tg@{qz+>CKN&ICVMOW#ZPnqZGN6^LOcD@18 z;5m+aMn=CQN1l^o&&h?SWiWN&XobdPWAHk)u0d5{hkzb*|_8&>?PO_6wX&i=0cUa3iNh<#)ygeqIpe*xqy B5CQ-I literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/handoff.cpython-313.pyc b/tests/src/agents/__pycache__/handoff.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5442e41960003daa28abb8cdcdf0fa11d9631895 GIT binary patch literal 8542 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/handoff.cpython-39.pyc b/tests/src/agents/__pycache__/handoff.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..753a02eaf2f8a324916fecc9db20662a8505540c GIT binary patch literal 6070 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 R23njLjI4eqAjr1a{{V2=R_6c! literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/handoffs.cpython-39.pyc b/tests/src/agents/__pycache__/handoffs.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2f2c7577d9f635b60e70e6a793f6514780f61a1 GIT binary patch literal 6152 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 E2a=wc@Bjb+ literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/items.cpython-311.pyc b/tests/src/agents/__pycache__/items.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b0149fe51cd3320717aead8e17d34cf1f81c7a0 GIT binary patch literal 9200 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 Qnw6>Ae^6T0)RBq*0aO+AF#rGn literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/lifecycle.cpython-313.pyc b/tests/src/agents/__pycache__/lifecycle.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76fad066a5c130007feb5a642ead60297d2210b1 GIT binary patch literal 3988 zcmcgv&2Jl35P$2B^?Gf`ar0$`k3I!j8dU63`pIWQg+PgvBDRPkS!_1z=fsV(8)nyO zlM4tW1h@2n)FVgwAHaXoC=}>gl}MnKvJAc78A0 zxtz}M`TpFG3+oxiex*hGQ6pyCN9J>8FhiMP(+VilF^F+y#Eke%d^!OM$tPx#(<-Rb zDM-n6az>j@Lz=T9n_z}I&WuzgR!Jpsue0%RXVB$44d12ryvr=;lbJ12oQwtWDQ7K6 zy)GO9b$d%8p%Nsg7KG#D+rnE-=d$bg;->!&SSu?60_|!fK)u9O*KOFL%aF>Pk=_0o z$>*%XKrtA^3|>Sm>GrboByGgrw*I}S*#+<{v8%7E0=jX|lIHwrMgpiUkMG%U^=uDj%IX_(OZ z+7!1NmgjMQ(egP6Thv#D$8D?8n78aDkGl?EwV>`cJ>IC-guP}r1Q)AVX^+<(Zs9*F zJBOxI;zn~m+|6>Ty1Oa>>Q#YT3%)G;MYrnlHMhwfA*v-kNZH{T1xdL>pn~w5;LO@l zJ79Q>#qjqr{SzWqUJ*ACXA&{p{=@0=!Qo+!m<~+ zUtbQ@>?QyO9je=lma`zd((D~}Cmtj`(WpTV-N677kD}q62N%n4djh;NESGJ!D$3H< zt1g)<;QFq=wj#vSuZ-F0-wa=i`|AL%))Qnr~L)Ow*I``Lh z0+%1f5=x-DO@F27+azc|2Uh1!Y`qj^eq&KM@(99YG1_X=#v`_Pt?pP29&THjg#zw+ zCjmqFUpIXy;?g6ZSOX5j$OPF=)WWF-BYTrUG3~P5No9aifiW`HYv3-2)d)1-4Y|W5 zg-8BaZ&DV=gR8o=8h33xQ9MAI!(?v`N6{y6B)f9ZF^9-H80yU+57vH8I9c5o)19r1S2Mu*{|EC<@->dZ~DT+-RR5M#vhaY4+xA*5^~^W5Pm*kzsF)pt)(TB zxu3>Pd|BKaJKc)Y;)P4!6u*8&^7qrZog~Uy*;F!jzcBH|h0VgrRuUI2l?{$;T>I>U zR*F^{D-5^N#OX{Q*vSxg1j%ljk@4FnZe3~H3>9x{x5|gH>-Uu(+GC^j`ocv|e6-RS z*$}5wq#5F}80num$SQaLRN{ti#Tdqj_wH`hdYiI#OXOX;Y*8z z?sNDJx8b}E*R+#_`CXgodM|GChv0S(Hsmq6!^8DlG-UocTtm}w>drz0*O+XG*j37r zlGnq;!>I~KFse3chk&KwGzyV-sCzVoy;69I^gB?cb|(=*-{EFHjH9X?6YvP_){mDe zdZIa4=y#5y*T}4kGOFh`!4`+}iD-De7Y*r_CF?*yGcTxKgk^X?B82qa62FQXbD@hl zQtDdBu0RPHprIaGgpJi(+dgQe+Y!9FCtaZF8jSSk8lmbenVcd*s0suhhf5ZYlRnL1 zcnV2>fx{_Y?DyEk#%pBNDYAsC!vQ0l>^Oafoc3c}h!~^$c*TRY$g-O#1wL7TXGwaR z$a6%_5IIX^7hJtWG6`3gi0g+dI7c#qD>zR?7DWzSx}3rDZ9~?XpF!2XNUSjFr8_~4V literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/lifecycle.cpython-39.pyc b/tests/src/agents/__pycache__/lifecycle.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f9b1a14c9b0101488bdc049dbd3f15e9ffa5007 GIT binary patch literal 3308 zcmdT`OK;>v5Vk!Y+w;mMo5yYzmTevjgvX5FfV2`qiB@Pi1&Iv;=HfWrJ(*Gb7`p91 z=7!jxkptKK4vt8ik*}QgFDw$Gs@vnS^Ux0BL~Ev0-PP6g=<2T=^m=Ur#&4heemv_K z#-BK;jwVcY;V&5wW-v1}B6Da)*3dEyX0b-ti0q-Qcsp!H&d`b6p{wT2(2H8bmT5dO zn8Vy>26MT^-EHIN#;^@*9&5o`t6b}__ETf0^BgQ0J67JAJcC{Fr9Lddk4h=?6VJ&B&2v!bvQ8Dg}?j-2sef%Glmv3hYfD? z=C;8dq!`tCgW0V4%pST`3HLzeoZHN09>`iX*<7~;@;2*$yj_!bK(`CB9$Nufw=Uy7 zkls3PvQ@SQ(iLVwCT`~*1o=ghoXFWvdxVC8lq4MoDG{9V$tjnF24Q#<&=X0Ln4AV; zl4O#ElM$z9G~|SzLb4>8#3X4sJWWo(5GnTXfR0HziS#y_34yNb?Q|T($6WUJFAVU=mOLB@s9ZyIk*v(gi${a6 zB^Poaq5(}9A1H0fPW-7z(j+~b@_`hzSRRx)>rc<}l_d7nc3-A}NPBRmw161)?9Ti~ z`t>~*_QHwWNwRdBr2-WxreHphzml5^Yt}E~{$b2xbp<+9@RVf~t~ns1NgRYkpWEM8 zVS>q3IB#KBY~ZxafLMcR-Yxy)F>Jyc!AY*wJz1?U6_#orK&=6U{9d5M1{C zYCr*jD`UxML=p;j30J3_1XBzD@1pD5*ZAKC{|oh3E8eeHy)WdoJFrs#fF&J<8<x9Mdr^)B3C1K@RwWTdVf0u$AZ7GIi)07Q`)ZtWuo{c|CYQ zs(iDIga)2m##4%@x!_eUo>C>c74q(TjwsDA?78?1**!u8l1{7@Um*IP}&3gDnmk_VOmL;-%zqIgL$(7kw!1Qft%bne=+gd=oAh3(R z6v4$CrI9yFZeb45FM$6Xgtx9Sr(jq-MD4ecyo2OjB$v?keOyy$`vG!wv=uwJb|c!p z2hvCImwh0Bw$45G8l>&k*9xRX-Tj?z?!dPtVLtzr~rJTH*s_rxx8}1Bs;; ztz`HN(savKKeRZts93)!S3fyFB~>4yHzhm1peVm2zofDNCZu0nlnj;ChZvz(P4BO~K&2EGTJ0@pdEE^$iDu)oZy-N;tN4wM7{k1Ip> literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/logger.cpython-39.pyc b/tests/src/agents/__pycache__/logger.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5642df7d75169a22ee8c9fee8145c8bab66ff1e2 GIT binary patch literal 217 zcmYe~<>g`kf{$IN(`|tCV-N=!FatRbKwK;aBvKes7;_k+7*ZJ)Fs3jqWQ<};VGL%_ zWPS;hWALlu%`ZsJOU%?uOi#@#Db{4Z#h#O&o}QVPev30bwZsR=PA$5{1`lfwfC+DZ6>O%}j$&N25$}h<;sVsmA=@%CzLuK_L=I9kv-r}&y T%}*)KNws4Hnpn&PBsiD=<>oti literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/model_settings.cpython-313.pyc b/tests/src/agents/__pycache__/model_settings.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88efd70a7573685a883174aa7fa73de45e15ce78 GIT binary patch literal 2129 zcma)7O-vg{6rQ!$Kdf;eKxzbmPEv?tdTh2`cAS|p zk*yp!wKBj=5K=uzXl+)tssxawFo9Yu~Jw2GUiPk^KC=nfJ|m^Y*=& zqij|~Fn(Y7qxLv~&|eZ955RILM>jz}K|0cvGFnlXf~6iS$5!Gj9_aD1x{_cCjPAup z(M6=IV@OZT$0Qq}`Q9I5$y&0IdL>ao@iV6DQeg_4x*T9%#T2IHm|TL?sx1gJ9V=u8 zfAJnrj=lxu3A%@vq9Yd56&9a{Xqc+USwdIuCG|ueEC8hflnhZiK&cS*1t=XNP45Gq z7N88v=>34Q;ab0b2GBr&hV;QP^tH;e`dN0S7B39@+1s>29GwVZyEXnQ1)f_^k0BNm zH#or$wuy@^({aECn=W2myQp$Ub(sy*!8>wKY*Dvr*BUIy5w4re z+#!$_jxUo^t%L>HsY8-PgOQ>pXx*q6ah(8N*n}6cU=7y_s6|YKRVtLUwN`)Cp{BqO z@fvlBpE7);6^*a<17rgr3TY;r{CzBAzRHPH_0z|m zxhy)2{p+)v92$9+?aW%x>a%8zxPn){G3ty8Dw=gN%b68U&jz=F!NV)2RB!qzXh`bp z693$(WR$|`55Nom3B(>cOrrjgc4oXgoO@LMbKrb;6nmq^_Gr-?UH(n==9b%Y%iW=| zr=`awZ>aRcjh|P3TH?h#3Snwv+pHF#L*V|Xu zBlp6WJ-pP$OCG-SLiLvKw3qL6b02!SnRaf*%U%1W@+*BtUk{`QwL_Hd(~clY^b8eL zHUvs*Up!EA-;a0HVt0OVZ{=X{la9LhM$IO)o>%E-t&ozrs5azd05Qp@ z($D<+q7u&JUF0XBvY<A;g6u`;*(8>B}>?AEZ5?NdUs?;e%5d;Pbq(I>q1)MBb6a;;wq)VwIL-HI* z@RqC@vS;hmza>EbfLlBDPiUvUN6U@e0!4Wc^&|QCzW7Kn9uEk#4-bD`zRL;u4F~%z zgTV_}$bMU52Wm0A`Rk_R_ zfk$8sIgeaK?nUlLE+QYuKFAyZ56XcY0*~VSP>z8=0lov#9llTGk-YmRD@QVc2<~;` zr$#D$u0p7rrQiO1Lo689dc|I?RKqHvH7qmHu+!70v%!Etd$y`Qi&3(b(b6+xF#$re zHL7HlX%_Xebulf>3MbYIGU3@5YPOuQpw<@h5?ZIGQC~2Y+bOdORD@djDGRP`sStX;e4dQ`O~BO?R|#>Se2%YQwJ;Zn{J3l!qC0Gqe3_cQij7 zi#C|!DLckZwNnzg0o}(xf)O9wM^naVUad@BsVCdv1&nT%?qP&lE6qcc0ruMm|FR%! zFD^7#K+Vzp!N)MS_ivrXPGXXDeRHXttEK9CPC?3=D%mR~_*T{`VQ_o`9G3LmwBZZZVd9j5O)ZFe?Fc+ z_fVX3x1Lu}zH_luP4Mz3Zk-7xY^<7lSM8?fvBJ6E-I|%*bOjWb(U-r zw1tVICzII d?DcVAa{pnl>p!}V?=gi%j|VzRz+#%vzW|%tg^K_H literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/output_tool.cpython-313.pyc b/tests/src/agents/__pycache__/output_tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3569e3351fdf38a63ed7d7bdf021128f880718cc GIT binary patch literal 933 zcmY*X&r2IY6rTN&WTUZGu~wzDtDq$y1`6J!;7?FvB5rIifnnTC(xvOp_RYki*B<&8 z_z(2fzoPe^3SMNPkWzZ^mNti8`(`&OI52O%_r14o-}hz)6BA>!ZuIOXD< z_R2<5Y%#_BkV$!GWX$wthexX3RdH9n1{TL0l2SW}SV%=mULBuG?y5I|bd?Yx8W*_g zLWCT5(@ZzZbVWPmn69|$vZ&>9+&aw{(FF&|R3M@?f7n`jSVL#9f#8}n2^by$(=)(Y zMzLTwbIrVGJ;&%%l45&^3&`g*_VVC#3S}o*NVV+g<}*PJYvX>o;*xpi%TDZSSw5( zWCNk^vw?mlwQ@0WXqrxGGDhjSXmvx5xJ2o@E(>)b^d>272Ow1#L_88m6ew*8AAuzm z=rD^Sq0%`@N|U?Umll%8kA9 z$24;e%#OaPl?%!E|EI?#=vz-pX&=L9mPwh~+1=Ybtu|<*Ua!*H=Fx64caPfH^|nv; zj~n~-TGoUouIX%KlYI$K(A95TzChEr48!b`@E-9NZ+}I+eT#2{ti?O$sNcPVZ&5$6UCs>4OQ!W*&}5#C zR-!WVjj1>0gJN|pRF*7@ij`FQvPaT?wU`DP%Rs9hiqFgx~-~zXT;v7I2i|3!s{XA`&sjL*#7;wgquCL_7vs zE&IZwKXz~%3JZXED63_$x|y7bS_utUG+_;PcEyU4eJp{G1bk#VJpp*kkWxTVVeXl+ zN4y5M>zLDut%cq9E*CmiMeXR=Ck=c0rJ3HW-?kmE(u%t_?(O zO1aFz3AIsnk5X1u()bB!N|n3z??~-|@#@#R6z)OkRISroa*_HeYyO$mN*Zad>-$uy zRla?jZlO8MgZf&<&}|1qyOrzV2zNqw0-txdcmA!1&-z`4*mk~ C^WuI0 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/result.cpython-313.pyc b/tests/src/agents/__pycache__/result.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbe643e189bc141670d200969dc0ad24bcf3a26f GIT binary patch literal 10434 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 IYhp+LzjDc0^8f$< literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/result.cpython-39.pyc b/tests/src/agents/__pycache__/result.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..638fdbb37365ded0c82376ba166046d064d4fd75 GIT binary patch literal 6885 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/run.cpython-313.pyc b/tests/src/agents/__pycache__/run.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f5f3c1e0fce7388b42dbda74c5712732cbd8f55 GIT binary patch literal 31817 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/run_context.cpython-313.pyc b/tests/src/agents/__pycache__/run_context.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ac6e93c2854802559a6ccd8e78c66c0f89a14ea GIT binary patch literal 1142 zcmYjQ&5zqe6dyZw>^R-6w$-9l5p+Q6M?{sJ_RtTI5UMJ&Wsz_Smq^oDdy+Nl%%pF| zD!KIrM=q5(_JRa|4O;bre5oqb!``-pKY;ff7cr7&-g`g4kKep`>#ePX;rig$KjvwN zvA;-px1v^9FHv~TQkHrLZ0vzI_QB_*`v;wI0Kqtfu+cjQ-EjmF%0U_)#N!_HIGaWv zv$Q)6(&)Ai$<*6psK-}W+M9;cB*fVJn3>l8=3vmDCxiYOX#>CR<AnNG zTq-#0BQ8I}nkL7EEP(f2BIXp)YvOvRT-Qsvwk{uqNHxgk{g=HT&= zf9bd^t!X@Ep>0y=nSq6Lh0$CdnTnz)UJ-3>W{rZPb{r#cbeu^TCduK~llymgTcza^ z6xYW6FE0go`0%SCpB$^z4TL3sDpyny)y8wRR618WD-?R`oKtTI%}lNmqEIZBrCMO( zTf8hz6k?|2vr1=FslCNBS(ZmKJ3;Z-7&?^8hDjZ@cY>X|*mg?HWabR4>ad~pJ3Aco z>sSaa7fOga5n^HTswDY>5Kk&uw#Y>xW(8PR7Fub8a3n--G9iE-dyTNvNV~Z_i&lCC z(4%dK@LeG+ftirbL2*<$MSopFY>gkQ?~Wc>1$G3B5%C^17j5$su>|9c!>2g1khSI! zHbAuGhRYRfVT5L*$6>GI_9J^6gkgNX&9>ir8SMTUJa~P1@3-C4%cE!af7*W;jQ;lh zForAd91jN<>i&O2p)An&G$iUSCNK0HXsV!#TfMWf2pj^XE%g9st{bKs&U7c6s)*qx z&HLmU-`m{pXK)o&nk@S%Zs#4(^Iox=uh`W$?AjZ)_qu=S=Q}U#Y5$XF@p+f|{eS#z XFa9NZ5uff2)(qu!2d!_(+JyfXtcpKC literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/run_context.cpython-39.pyc b/tests/src/agents/__pycache__/run_context.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cad1ca87cdb66b0fb3a5017208905294949026c9 GIT binary patch literal 995 zcmYjQO>Yx15cPg#H=C5U2o4;Oe1k|;BCb#&5JDAFDH2UV+KcP1C(RPCz1UuroXfR8 zg9E>Y`(8QqFK}YKiJ*>lGV{DMes9LhcXo1u>+z3Ym!}aSzkP6b4KR3xk3B~thzKO9 zgCeNIA`E;S%BYTuxK4_sk0Uv#(;`Jb7KzO2VKEE{q+$TENN>U-hv0~aObovhF@yy2 zN8~!X4vLY;PswC-i>HuD*bN1Dyp-J9E?HC{1!B=_weHd*P++QZ5_IW&-M}YqZUdha zbjg|Jm(XSNH(ELP=KL!H7QB_N^kwfZl)901fDVu%5TpptP+k#142cL22@H^SbQ2UA zq=>|aL?lRY(CvS0)%J+bhBplu{tPM1mzAYg2w3WtK+6pYsxQ7k=@8?(fy!DPbTAT^zROXH6gyy^XLXxv{IKQ_DF)J+utAz?<^?m`)2%S*OT4Q h_OklA|Cw*Rhdw_$+_dGT{}AjL&0eqyB~E*BX4la$eyPX*)nDGHC-GIN=0tg(gx1}8bUmTQ<`YS~<~8hOY!3Q%a+U~8MT zR&11@gmT_3tT~M`l(lTHO*AH9vM~iyoHfmPc7eIYMdp^S=F*H;(?15&8R{TfehZz+ z&CHP* z^)RM%V~}3=*7xY(Q_j(6rh;*N1%=0~$-rBfKkN2=9vV{Od{p}s+U$GpZgo#DPOXe_|fNb|dXC9o*ig=Gw zy#ZFX5-Y}vK4BYAib+w3(CbPe5=V$`)arLgo)F?e-|J+^v=Hq8RNM(d8Ab@(LbM_u z1w_WqQwW%&b)6AH(GmN?i(?4B>Bmwbb_%g${ITSV`duYK)uCJWqn50DbeyX7o#;Un zN3^)Q0zWg?l|IpQXR6lQgDH%m2daYNI6Pv1S-E_1SY+j;1MA$O_1V$N^@lh2oyviA zJ+U}^IGXZqPTWBF{hUBP6f%C;$d4lGCCv{9`BAP;By$-Z$EmS^p$EUdK@ElH>cxB) zydL-X{cPI(8eb&@SwaQjSPfqy3sM`Bh>wmLS%b5KPRBtJ4M$`+Z3LD662XV`>_WqF_7~g?L{CUit1xeB>t)56W1%#3Cp;1N|^{8Yjbm zpDgvAcx6lTQ4?e6VSQ&LqNPMBnu3&+gP7ltz|fBwjs{pt3h_0iGC zH;Gj%&#jwj6bkTeO8uWUqublv9$s(R8s}0Svkk84-j)&wTWQb^G(nI~NCE6FfkH~r z;in|R%20`G14l=_hdp*HDIqR2o-FD=(NDAC#ijO{wUOnM`u?}m`-{Acs|Ffq`HCw2 zi2Y&RIGSEKuoe!j`J+!ZsHpYl)<#-XJ)wwxNqKDd8`aa)quTu#*@f-I)D9=_hg4vZ zrGV`N_Y3+F!Y7u2P3GCuEaVTd3>CdTpflw4&VXL!Bu`vZns>Uvrj15(L@$Z>0gE(H zEBl8=N%8Zvb6$tb2+$*?e!*dA8iw&ZyYP~of59$3V;5ht%1bu?f?XaKSg!m!S2E5I z84kxLsza^LYhcc}WegdO8Key>8Y>uIAx2|{X~P^_T-sl{w*P5kf8F0-@}DiV9+rRp N`hc|#*+sq7zX3cyS3dv% literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/stream_events.cpython-39.pyc b/tests/src/agents/__pycache__/stream_events.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54f102cbdc47560a85be09ec2dfbddf2faae6961 GIT binary patch literal 1841 zcmb7F%WfMt6rG1gqo*aic7g;gz)dR%2&7$BNf9rKqJZm#V{axfn8A>;2c3sPa_UIC zBK?*E+4eUI^aEzwReqtXp39MC#i&tqfT1q$y>m$3b9faGhdl?+AJ6~1>bD)|FPiK= zElgfHj;nuzap4F@xS5l?k?WG}WnS(_zP0_Vl?PFfx1)9*Mxph!vQFNOy08P$&U$%2 z>RUU^2Kg`==6lhe>q!55M|4E@S4VU|cpuzoWIa96ho^5nW9u1+Av{Ct*_WXl$+6sD z_)nbZK)}P!4Bsjj3g_(MFcvclZ7@60H=Vwwcfhk`bCFA_NpOKKzn!AsW z$t$YdH!!3Vxx$G&RM3rl;mMZpvD;P@hyZpQc8HyK>c?mC%`Lj#fnTd;k``A?(t2#6 z%AA>N$xcpwoU!w3sU?e*WD^zNOqv}1jHNRAZ#Q4Oov~wzfKWOc!@&{>!Wtj=X17d$V0@isnI#O#P_vQr zDb!$D(ri&GR>&Kc0|Iyug(=I7C2^MB+GS;ymBH*fE=0Llz>+4JSdB*L{X=zTNH|p zR8=%dyZo-%cB}sypIllsovIO#?iAHnkI45J!xOxB;^=jJ>wQ=p|L=a=ZgR22rIKP- z8P9DOY2<*Wl`c|CH^c!lajjDgfe?+2Byc_|)NI|efN#*vF6%Dh+{UA)_J>!^Lj1ET zeZ8&H!%6>Ls;4N^oJxhi4Z24j3PnB@VyOE&XFHu9(#>B~1~R~n_H4qo8B48v^0aYv zaU?~5IVzxmkn8=%&EZ h^P*`=-%`idbcQ`|;2wGdjAP$(#{;}ac)O3>e*l#x{B-~T literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/strict_schema.cpython-313.pyc b/tests/src/agents/__pycache__/strict_schema.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbc86f245c83333b5b2cb21ca95a03d18cdba24f GIT binary patch literal 6123 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 J&8E||{{uwP3F`m= literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/tool.cpython-313.pyc b/tests/src/agents/__pycache__/tool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3380a6b5c35b317af237c87346c69b01153875c GIT binary patch literal 11729 zcmb_CTWlLwc6Z3(Ye^VaCU1XKW)8jfjUKjRe=KaU!7oqP4=ghl|bC7-6-7vi~P|mqsb;M&~xt0 zP?RjiPSLS-dFGyb&pr3vbI@Um4_c^&;rL-|&_-T)A5AO*2s4v(=o7j8VaC190?7e)rE!ale7+f*j9_*kU46YpR z40h2jkxbOKl2Fw?PFo4uJt0hVPdN6PEKouH@K!U?G2y_siEgd7i0zqhP539e8LmhB z)%)$7u+un&R>V2@b`0Ueu2<_FeHGr*s3fre+>i}E-6gHq^8vxt*6t*8Y>YeDD z2u$?v<=G5&HIYzjXh#FN#?##}8)Ggn^qVFcC-z_)o#(w1?vRQ7^Fi4Zur!hF{)O5? z%}DW@sW_*7&(}ND9uhbA1P(ilktcRPW8}_Ii`M@dNB2#e{9Rw*s`@$ADJ7Gsw3Lpg zlFA6YI|gq`@w9X`A>+%Wl$K%%Nm2Y3)jXJ-SM4XHL;|s@?QA?PQz@ZZB1!11+EX`V znn+2Lev@h&iYEZ6sAXeP@_IZuJvzq*0FthVBGa>}7#k7ZJr|@z9EJfBrzlNP=O+gjHsZ@gTt;BGdjmNA3+}g_k+$Ix*av=h$ zf}`dRP#csFnI|kEz6-Fhxd>Zy*b1;shwT76bXcI)kQ3fr_#Sdo+jN=Vqq>H4=8=yt zngNyln?W%RFJgK|N{csvX+@MoV~zpwOj4XpDQPh#DYDWl&Zjb>GLyJ@%kzG&-A_ z%p?$YMWf%(NC_R|iAJa5R7oe|NjaGUv@IH)OvL~IOv!nRMk(%_S{;olcm!inDGkG3 z1({U<*A2Ke_#^7xempfwKb`H5f%NuE({eJcOkR)9(NsE>o}ZKZ6&lm4`*D8)bMq88 zo)riv$DsHLdCZ$E_JW-_%icJX=U-l}@GkS-Jb!Stu5+31%=3F!y~E4=aKU7UW?mw; z=lGg;`DMQC_a@FVKzA7=V*nH%vj%Z%Hvo91Tw6e9H^?)!g&ZKmf-ch*YG0RWI0qK$ z2)O}Yrr{@qkf&kYC^$pq)HQAKSEyy6bmWjM(bx==`=h{2_RT?Y1_WE4ED?W@M<$u# z*|X?WyrK!yH49MjTIhIaQkd_pc1LBDcrLfi@wy7i(N@i{@MUmXiczQ+{os*M; zXW;07MuB6KQ9ysN!!jl-bD&UVMI3zTOhCvw5^;rz@Flfe8vu4328hNdm65CvPo~8m ziX*8cD4tpAwP-Rk8>O<6Nu-q_+6nVeYvRdRA~PvRHGU0jwtAz2EmEPH-i@X-#hb;& zL5*xQuuW<2G2MW(9Yh%Rl3U{exJ{-xziC8uLQ7*-4vWELVK{-iA66KeOLsr*pI~lEMCOoAQY+{#2s`_ zdY+9(`w@Y}G_s75J@|?R?!NLW6zeiyD=zcRd4Bh5Yv;n~tyh`6zp&cYo#PwcJ-N(x z=lQYKzTFF>x$2%}es`YlSZ!)uIQ`b;Wxh4f?_0IFAHx~&t(l3nYz-l8SR8EY(+~vA z)~A7jmY=Y$Yj-#SHh8z|5^fJUN+jG70ta#0?02f}i}KYH0Y@v6z46=vE{iwitIv>V zaic^F+e-9EwyM;S8)D@Kb3vmC-Mp*TS$Q&^nazs2uQ1+~NZst}6}z;0gl6K?GhLU| zDqXH)npur1@vQu8*{p;a87E?n$~WTjO-&$w8Zfr)$8zE&86BJ*rpDNLZatsU#zI^uV1zpqT~ zeEV+ViKbZK3MuVS{DgeQ53H7<@hq4$(`qe4J<(a*xlV0->=x5Q8UP7FC2K})+XMgv zk=!^cRQ5zTBd{tXdra-%^=8k8!3Ud^rz9{6nk|#jT`u}hz2eQ8cx*jPYBgB^y;J3+Qn$C;T zv=mPUMCLOe68#))0dBz~H^(HX?N(i3fJU)6=D(G=Lj~u2(>K zAs~X$+&~ao6a^GX&ZXuu2@Ix6NR$YBK-lvgXh?KAGYhf^vnxhZz(yD~iE&iG(S@TT z#(@vuGKQ|Llu4&%F&u$Vf4*0QaD;_#F)1k;XHAT!1A=g7YQtQ_v~(RjUP-+2R0wuO z4+cdR7l{UAy+B9onFW>ABf^NFzF^vd?gNMz*N7=?3>NVK7NS=;)SCOw^phYA(rM+8 zfWnHqbV$VM755946r9LgJ@P$toL$2Es#z(yN3QH&=cfRH8)Rt(y( zb=X!TU9!SOg|yuWC2hZLAjYyWElfp}%8EX!v5T6*+ipN@i&1ZtN|n(UqW>uoazN{g zQ5;!63?hW3^6VVi9VQ%ony4^pjJC$j)7q9X6s8&3Sl{)XDV@wVf?s5#bit$~28R z*aIR>w#=AVnS*p1i}iXDm4u})65mJz^^kZ~)(GLe4MBv1c%zwI?V~QiVRoA^=A%mkFxl+hkqghC#W8`LHEy4{--Av*wT$6f~qZ4dps(f*Ml< zYWJE$W?kWOXYClKsuR+cF5tTC&Gm+2YFEGwh_OD zjstcpySc%aLW}e>oM!-Rz)@2hP`?d1v`BG8sfG}wq&;J$ewbbdCZ_0hjdHt4(llP2 z!&Y1xbCKGCvQVV#GNAQeNNAhG_OY_{8UsS_&M}>dTX1WNO9B*tn1u`=UcbQ9nZ0DmWZW{ zfCO4M&TYKTU}3DmfYyIoN!VemLvaNll>pqsn46zsi)v`$Ku4pWi&)Nc2duCzR$P}e zaH%r`5`TlcZ62|>NYXZXfVH%ION-8DjON~MG@x$ZNIhwLz7>@^o?}IAz}J?N7_#p% z8>{wQvba4ZN5r;izc2&Io`}sDqn)&n@uKdq5ov*tr1?2#`aEZj+is@c@yu;YCuG`u z=Ef`ZX+ku&-RPTpMa(i>7ADPu<|%H9449kA6bCEgH7LQ#w6K+VKHBEemEVfCot)-I zVyuiDfHD2fk?f8$$s2eLBQi%eo`hQy%`*WXR?M0XiPNNZ2%6 z9>z*$5y3Vq&|c^juiw;R3&UoeEKdN*SFlo9L61?v-P9taa+Sw?zG}fMBt>-;4ID+S zrB_PY#9$DIR^*!Y+ff1PtmXwOYR&n46!Jl9*Fx*;$G*ue#Ey zC~_G^&Qu%NQ@B@3t5tCYf`$}aCnaUWB~abWn~mzus21u9^gJ7rO+ZNX<@K@0u7(T!IU(fI6KLJb;> zu|P9$1aoW&!C zfe(-0ay;>p@|qP-H~fECUh|IYr>=KjzdQAd>+fB^dnxBT{7Lzd&o@`!>%E`3Kb`Z9 zd|Wc$oCt_R*-OWuLSgAcp|tM$z*_4}6U_bt~SDA-6vZK0gB zbghVcmc%`aL(AgfC7185lb=`BuT=HIf1QC`bN?cL_r~I@_nr5Cl=GeYxP0Vu5AfLY zz|(X0#TC!qCC}bG_UVHEziHcf_e8F3XtDi4+t9t8OKn3dzM;2=K7;CTu5EB}e5q}4 z#W(o&&}y-Vuj5WE=R0=y>pB>J*n!bAS3P-%YHDYtuKP*nu z+wjiWpPtRNeeb^YSI+lwk}c;x@s51{RQ-p?Kg@hMool)9vG?M`n(lnf3yb^zadin}QPt`sPVD@wTlI4t555<#wL_(E4xAkDT{^ zlnY6u#*4f(c?An@++@!rpf%ProqE!>dhb3 zTj1rRCNm&DY7v4i^G7>(1a0PD+bjV8+QmctuPbq!4Ne{&7Utv~f_%eKv*F~0`Bw5W zXWZ#;HrDOn1CQ{aP@%K-fI__q3e^<0psOsCum$>?jQ(JPiq7Fyt}QqSMKf7+9m)yR zvXQ}{X>Nj@xQJqE2G=2J8iW+yw;Q%rGVV0^1Jhw0+ro_;_ZTfAMW-=j3YbFXgT;3d z_$VD!8-3ldFon;LWSdU0KS+r!!Ef&bT5OpAAs-Ak`fZGD@)-J^O;(u*Rd8w zjD8=BG!~c~qZA63bkfwMt|vX4^rU*~|3yXq0SEhYD0J;up!dSUo?X@yJdY?)v!|4`Jk%v&W)9-z*1Ep-_rHh zN8Ua1iMI=s!%F#{2jzPndVP8E*s8z(7me>VKJ4heTk*FS9$U;!4Ua9>>Y68JdqriT zlGHWbc7Ic7gJJ%g-DJJN9dG2`bk>qvTs8l;xt9C|R@~-l$veXFI`hwak5`%BcXNZ~ z*7wVJgey6ye!t2Bt=_NO2Q?qKvF3v^toZ=y0j@IxtP(%3?MEA~H+WZsf-_0{R<<)= z*F2TbVR!1UQ0|ns4q{X?}63`Ne!= z&tr$BTqqEWL(r16pl6B?hR$$AxHa_fj&iq2nA@1$e2>$U#&8JX!-@nm?exdcT0zYu z>g(W`G%}rPG2OMv_!nC=0s{(qKwmA8-+0>Zyt3@+&)NGKbFlNcC0|#dmo}t-I5G|i z;Y)M@>e((rn>P`D3ybwyPPL;q#Xc6YZyTsd(W3v&a$guC9dim@6~3b zNi6*%B#DYk(^3lvwLsx7)A(dFK{@+cfGY$2`cjj_KgG5e5mkNC$dQFul2p5**RuAV z*b1ff>pEOGrcaI4GsrDq8$1PmmzKd`q*?mEI0*eSY{V`CiXd zf8c8tV?@MVqJNA-X%oyct_(EWPJ=6Uy1qLnhYVsk7SQ?*jQRKD$1+nH%$vh^EqDT$ z-JlXcswb98B)|=a45t#1uEr?XP%tUQPtRn9TCm0n`Cte@C}w3SCVVJL5=9k31t`=$ z_&6Xb#RHf(Rs!I2YoA~QN-`{d$4?RT@InhL zDm31r@I{!MRB$E~tu+P5fY8^msKf$IBgJ!1&td@?2SRJ0PzBk@=P4AU7)^m1yp}oPltaA~cn~2PqC>5k#^) zwqVzF+F8P5q%v)>LFlzRqHgTQz7pDv&Fn|CPo$30zl2u~{*)9bG4L!o?h$GIEh+nV z;(SCp9+8$$$)Qikq2H17kI1P3Shfk2@n@Kt08f$J;~D0BqAHW0h}t%-M!-W|Btn{y8>*-kI;dD8R5YT_<) z1%Xuia!ue!EH&+2j6*1LKae{gUOFDh`65f+3prAOObYP%R!c{|YiGW1cfPqTf8g+A zkHFdULSw-UztF_nRInjrCygBi2SNg=*;#NRI=HgVlMVwTQ?0u%_CC7^uyNf!dT9sLhFiFsWnQmlX&zf&5=fV|^O{ literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/tool.cpython-39.pyc b/tests/src/agents/__pycache__/tool.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d78bad327b035fc7df5c80e13686e801f7152f24 GIT binary patch literal 8570 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≀QnZva3zQT{%FRj&#~avVGT}x}{-H+BP|IN$dZ)dX1&@TY zG^J`s_%0z2VMvNm72QK`QUl>genJH$YMQ3BsIJ1lUrp6Zk+-`3wWQl#HY zhiyfdKN|NvW;eBH&$>P5nGk#VHiHeX5ppYA9|`7^}@!?Pe5$!}$;!I9a;ZQ-gMxAE~VQcaN6|+?ALy^=(o}`N;*_ssj`>CdE z(F9UqLxXXU-YCb?X%jn?0yvB&>mZrHE7^C$+xWZ!yK^bcI9vO-_vIb+(T?=2QJP;R zb5&w7DbNU&6-(6=oe@i=uEz3DD%X)BRB4wj6;rv8eq(!=;2wD>>aZXcy^jB3V_My( z)t&0;Ge*GL%p0>vEKeW7bpY2iuEum>rZ{gR^_d^iZ&(AiDu4R+NJIO6Zl^tdwmx3PiH@f(|t8{DqCR_&`sP;UeTzUjFe)lFUIeYYE~9l78t z+3eoFJ&DH^qwC_d!`2x{1_RlUKv`D}c|B$*>+xCDP{-K=^?C?~gs>$;TkMoAg|_&* zww@-}Ap#G4;_E&m3H@|v-XF&Ly#s**uPF;$4KPL?2PbHNYlkF>jpHa4e7e7(*kNnL zg&3>S+7T|I_5ILheTY#=tJ1wFk>NT`xJo|X8H-Z%Z3Vqz3T4D4-vQyPp_DM`C3%vq zr6a2mTkDVXRq1*dVTR4fk?u%(e3>UV!nAOiU7@+73{xaDl`S3O*=Y`Q`)Pb4}mg za<(n5eQnhm&Mg2BU<5(9hho=!px}MtGN0$SrdNI3ej2u?ZLi`?TU2L$<)K*zFYsvR zaB12HJ5nhs)Arn6Moaa_JQaARS9e^i%12J6yL4nA`h2=yPuXAT;U6nM-)*J!W?DZS zEgVkdpWk_Qr(K+C6=#~onO1RrAKHa0t-^e>F#jf@ULJfCOAKW?EYY9&XONv6JGfN( zHQ4|BmnXZmgRxIuWEKvR3tjZhPSp|>r>LbGDtN7_LJ0KEz}2J5IyT|z8P#+zag2zx z=*Ac_)`~*4vZpSKM8Y6q;35iLC616bQ3V4+Jy{AFPh4yB5m;gop_#kmTBjTt=w!gH`~q@TG?yO z?6p>Qv6)?bk^KzwFqXkQ3}~2#lm>Z?DRZ@~@#dqAS-L)tjSwJ9nj(n``Fg+S&J7 z*|}zR?kt}6*s`ACNmHcXG`VJ)+g_znb5QRy&F>mkjS2(b&Y750@Pk^_bzBb@(ss@A zee4ojUIkKgDwRZ*1QjDpk)Rd_RO@gR1s^s|>=QxNHmx9l>bH%+@l6v-1jv)1G6K4i zLQqfk>F4kRYsUsUayqv7)BO&^>EOy@m*%d*;^&2-GcL4bOgs4Ciw>jtuHB_M#Guw0 ziZHu#`!jnBJ=lVA4C~4W>xDv5EbDzdj@_o}Zi-XztG#Ygq7LMjZ2VCQfn6v1U5eD1 zqU{MrcrwhFFUa8O6StyA6!pDc-xM^8)1PL9Cl`8Y`4r|6Or6~)qC1KtNiW&OS8U}K VyZn;P{J|#Q#*32nH$x$g@h_{P`d|P6 literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/tool_converter.cpython-39.pyc b/tests/src/agents/__pycache__/tool_converter.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ee20bd4589e44efb70a46fb62e87a249daff87c GIT binary patch literal 1534 zcmZ`(UvC^W5VyU1dwYAA5>liz<&Q#DflflXcT}NPQ40?!pqBEmFV<@9OE%rLm+>A| zqC9c^76b|2$Oqv|_?4%A0wg35Grm2zpvEhY$KLVyH@_LX*xJe%wx@smcKqvrvEOO3 z*&>`g#O|J=P)zZfH9Y5>bWw{Y$|KYx71wby$Ol0m)JZeUhe1#3v>D~2Cd)Iv*8zoQFcB@ z)4Pw3wdtNzrl`MKbnT)e<3nra)$vsL$&-aC*Dica|4KS)v&A@hh~0gOLbIGJmJ1!} zSPvAx6iTS*RlMYRf@{NmEmniDmg3q-XL}6S6SSqk$hHs5Z}n)t*U)J|Kyil# zfGGu@xNf8)_v{e6dw`;27rf(30Us%T!A}MDsJ)|Tj8$}rY+s6JtQ)97mt62A|B?M9 zmiz~VkBlZqKArcmdv+E1*c6TSW2IdQRZEoktR+r00&`b{?5x!=!Gq6lKQ}LoeQEXy z>^`l3x<4KIta6ocU13TML$rgpNNl6V<5ZV}gR{0@>R^-DLch4J9gHLeV1Ge!RgK?2;E^WhL&S=J)zA<(vv!_S?xX z89K@qizrMC6vI6K_$Ws}wN#w2#jPIDWj5{}og^esvc^ bAcW{mP<{GfMaF0JfI8YW5%}>5-;VwPO)rb~ literal 0 HcmV?d00001 diff --git a/tests/src/agents/__pycache__/usage.cpython-313.pyc b/tests/src/agents/__pycache__/usage.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16617abff1cac4b3b56eac19fedbd7e8e193e4ce GIT binary patch literal 1362 zcmaJ>J!soN6uy&WNB(O_sUbFvN&jdyBqF4yi)*?RLYp{H;mvSWZN+utNY0%!WU9B8 zLTQEuLYJ=Xl#HD_WsO5>a}Wr0%9i3GONYLb72_DuhxBycdr$9s_w-KvR4R@@{yzL! zKamjnEx@Hjf<=EG7CXp527Zp7VTxx_1y7^f$dIzgkn`}Kko@3*s8m<9$eBPI=CPXT zn$@PsST*qb4>&>8P3ge0T9FT)XA&jNX~i4;Bv9O|ecAvgw)4 zz%xn6T7xpT*|06!A;fPfmm9Xr!DZSuoD;X<*bH1(8%-w#U78RkBC5o6UD|l%xt0hO z9imf0T@y-F<|{EstN`jAI=zu@FTJlGDe0avcRHPEFK^#DQZhZ|(P<{zUfEteQnEdz zFzP8@C`x1n4-$)*FEesk^q&Fc%oCpTQ6C z8+?W(Dk^>fQ842@qKL8l@@8H#nHjrTuUA0!=hgSC9}?gvJ9A6Oxuor05FnuF;2>%t zz=-#N64fK1dXNvImY95iGW|vGK`Fu#8{f2D<6O&;xhZlkX}c|ffm$Hca!0vqWfGI3 z+%V4~SCKCwPa@AFPl*F&xcDkD$K_*EFEOM4uR^+W&6Tl&a);cS+tE1hLb30Uqt|}8 zF}(}xVe~V%@Plu$a1Hn38tge^FHyV( z3Hqc6iV6FYaZ*J5Mp8`LcZ`!FqoOFL>_^5)Q3%wY+{VVr#PY$|lp$kS5K!TXGuAF})?uafVZgCtUTA$gHr+{y#Rj$SeR!sI)AuxPzaM(1^?xnc zHT@= z7Zl}}Eg`k0{y9n)9(Z6#~=E z&X6a?!T=JuI^Gb@VSPk@mP?PPJ zP-bpHeo;wIW|CfRYDr>BVoBmHPPo`D4!9tDS!z*nW`5o+?)Z2pGd}*7NI+t8HqcDJ z{1UhP(!3PcqN4nwl?+9kK-<8?FFXCvVxUg_qFnvt{FGFEhyzlx;|q%NOY%!93t&R} o#YM?bS$(JldIgoYIBatBQ%ZAE?HGYZ7K1{AgNcbzfKh-60Mja1VgLXD literal 0 HcmV?d00001 diff --git a/tests/src/agents/_config.py b/tests/src/agents/_config.py new file mode 100644 index 0000000..55ded64 --- /dev/null +++ b/tests/src/agents/_config.py @@ -0,0 +1,23 @@ +from openai import AsyncOpenAI +from typing_extensions import Literal + +from .models import _openai_shared +from .tracing import set_tracing_export_api_key + + +def set_default_openai_key(key: str) -> None: + set_tracing_export_api_key(key) + _openai_shared.set_default_openai_key(key) + + +def set_default_openai_client(client: AsyncOpenAI, use_for_tracing: bool) -> None: + if use_for_tracing: + set_tracing_export_api_key(client.api_key) + _openai_shared.set_default_openai_client(client) + + +def set_default_openai_api(api: Literal["chat_completions", "responses"]) -> None: + if api == "chat_completions": + _openai_shared.set_use_responses_by_default(False) + else: + _openai_shared.set_use_responses_by_default(True) diff --git a/tests/src/agents/_debug.py b/tests/src/agents/_debug.py new file mode 100644 index 0000000..4da91be --- /dev/null +++ b/tests/src/agents/_debug.py @@ -0,0 +1,17 @@ +import os + + +def _debug_flag_enabled(flag: str) -> bool: + flag_value = os.getenv(flag) + return flag_value is not None and (flag_value == "1" or flag_value.lower() == "true") + + +DONT_LOG_MODEL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_MODEL_DATA") +"""By default we don't log LLM inputs/outputs, to prevent exposing sensitive information. Set this +flag to enable logging them. +""" + +DONT_LOG_TOOL_DATA = _debug_flag_enabled("OPENAI_AGENTS_DONT_LOG_TOOL_DATA") +"""By default we don't log tool call inputs/outputs, to prevent exposing sensitive information. Set +this flag to enable logging them. +""" diff --git a/tests/src/agents/_run_impl.py b/tests/src/agents/_run_impl.py new file mode 100644 index 0000000..112819c --- /dev/null +++ b/tests/src/agents/_run_impl.py @@ -0,0 +1,792 @@ +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +from openai.types.responses import ( + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseOutputMessage, +) +from openai.types.responses.response_computer_tool_call import ( + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, +) +from openai.types.responses.response_input_param import ComputerCallOutput +from openai.types.responses.response_output_item import Reasoning + +from . import _utils +from .agent import Agent +from .agent_output import AgentOutputSchema +from .computer import AsyncComputer, Computer +from .exceptions import AgentsException, ModelBehaviorError, UserError +from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult +from .handoffs import Handoff, HandoffInputData +from .items import ( + HandoffCallItem, + HandoffOutputItem, + ItemHelpers, + MessageOutputItem, + ModelResponse, + ReasoningItem, + RunItem, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, +) +from .lifecycle import RunHooks +from .logger import logger +from .models.interface import ModelTracing +from .run_context import RunContextWrapper, TContext +from .stream_events import RunItemStreamEvent, StreamEvent +from .tool import ComputerTool, FunctionTool +from .tracing import ( + SpanError, + Trace, + function_span, + get_current_trace, + guardrail_span, + handoff_span, + trace, +) + +if TYPE_CHECKING: + from .run import RunConfig + + +class QueueCompleteSentinel: + pass + + +QUEUE_COMPLETE_SENTINEL = QueueCompleteSentinel() + + +@dataclass +class ToolRunHandoff: + handoff: Handoff + tool_call: ResponseFunctionToolCall + + +@dataclass +class ToolRunFunction: + tool_call: ResponseFunctionToolCall + function_tool: FunctionTool + + +@dataclass +class ToolRunComputerAction: + tool_call: ResponseComputerToolCall + computer_tool: ComputerTool + + +@dataclass +class ProcessedResponse: + new_items: list[RunItem] + handoffs: list[ToolRunHandoff] + functions: list[ToolRunFunction] + computer_actions: list[ToolRunComputerAction] + + def has_tools_to_run(self) -> bool: + # Handoffs, functions and computer actions need local processing + # Hosted tools have already run, so there's nothing to do. + return any( + [ + self.handoffs, + self.functions, + self.computer_actions, + ] + ) + + +@dataclass +class NextStepHandoff: + new_agent: Agent[Any] + + +@dataclass +class NextStepFinalOutput: + output: Any + + +@dataclass +class NextStepRunAgain: + pass + + +@dataclass +class SingleStepResult: + original_input: str | list[TResponseInputItem] + """The input items i.e. the items before run() was called. May be mutated by handoff input + filters.""" + + model_response: ModelResponse + """The model response for the current step.""" + + pre_step_items: list[RunItem] + """Items generated before the current step.""" + + new_step_items: list[RunItem] + """Items generated during this current step.""" + + next_step: NextStepHandoff | NextStepFinalOutput | NextStepRunAgain + """The next step to take.""" + + @property + def generated_items(self) -> list[RunItem]: + """Items generated during the agent run (i.e. everything generated after + `original_input`).""" + return self.pre_step_items + self.new_step_items + + +def get_model_tracing_impl( + tracing_disabled: bool, trace_include_sensitive_data: bool +) -> ModelTracing: + if tracing_disabled: + return ModelTracing.DISABLED + elif trace_include_sensitive_data: + return ModelTracing.ENABLED + else: + return ModelTracing.ENABLED_WITHOUT_DATA + + +class RunImpl: + @classmethod + async def execute_tools_and_side_effects( + cls, + *, + agent: Agent[TContext], + # The original input to the Runner + original_input: str | list[TResponseInputItem], + # Eveything generated by Runner since the original input, but before the current step + pre_step_items: list[RunItem], + new_response: ModelResponse, + processed_response: ProcessedResponse, + output_schema: AgentOutputSchema | None, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + # Make a copy of the generated items + pre_step_items = list(pre_step_items) + + new_step_items: list[RunItem] = [] + new_step_items.extend(processed_response.new_items) + + # First, lets run the tool calls - function tools and computer actions + function_results, computer_results = await asyncio.gather( + cls.execute_function_tool_calls( + agent=agent, + tool_runs=processed_response.functions, + hooks=hooks, + context_wrapper=context_wrapper, + config=run_config, + ), + cls.execute_computer_actions( + agent=agent, + actions=processed_response.computer_actions, + hooks=hooks, + context_wrapper=context_wrapper, + config=run_config, + ), + ) + new_step_items.extend(function_results) + new_step_items.extend(computer_results) + + # Second, check if there are any handoffs + if run_handoffs := processed_response.handoffs: + return await cls.execute_handoffs( + agent=agent, + original_input=original_input, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + new_response=new_response, + run_handoffs=run_handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + # Now we can check if the model also produced a final output + message_items = [item for item in new_step_items if isinstance(item, MessageOutputItem)] + + # We'll use the last content output as the final output + potential_final_output_text = ( + ItemHelpers.extract_last_text(message_items[-1].raw_item) if message_items else None + ) + + # There are two possibilities that lead to a final output: + # 1. Structured output schema => always leads to a final output + # 2. Plain text output schema => only leads to a final output if there are no tool calls + if output_schema and not output_schema.is_plain_text() and potential_final_output_text: + final_output = output_schema.validate_json(potential_final_output_text) + return await cls.execute_final_output( + agent=agent, + original_input=original_input, + new_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + final_output=final_output, + hooks=hooks, + context_wrapper=context_wrapper, + ) + elif ( + not output_schema or output_schema.is_plain_text() + ) and not processed_response.has_tools_to_run(): + return await cls.execute_final_output( + agent=agent, + original_input=original_input, + new_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + final_output=potential_final_output_text or "", + hooks=hooks, + context_wrapper=context_wrapper, + ) + else: + # If there's no final output, we can just run again + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepRunAgain(), + ) + + @classmethod + def process_model_response( + cls, + *, + agent: Agent[Any], + response: ModelResponse, + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + ) -> ProcessedResponse: + items: list[RunItem] = [] + + run_handoffs = [] + functions = [] + computer_actions = [] + + handoff_map = {handoff.tool_name: handoff for handoff in handoffs} + function_map = {tool.name: tool for tool in agent.tools if isinstance(tool, FunctionTool)} + computer_tool = next((tool for tool in agent.tools if isinstance(tool, ComputerTool)), None) + + for output in response.output: + if isinstance(output, ResponseOutputMessage): + items.append(MessageOutputItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseFileSearchToolCall): + items.append(ToolCallItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseFunctionWebSearch): + items.append(ToolCallItem(raw_item=output, agent=agent)) + elif isinstance(output, Reasoning): + items.append(ReasoningItem(raw_item=output, agent=agent)) + elif isinstance(output, ResponseComputerToolCall): + items.append(ToolCallItem(raw_item=output, agent=agent)) + if not computer_tool: + _utils.attach_error_to_current_span( + SpanError( + message="Computer tool not found", + data={}, + ) + ) + raise ModelBehaviorError( + "Model produced computer action without a computer tool." + ) + computer_actions.append( + ToolRunComputerAction(tool_call=output, computer_tool=computer_tool) + ) + elif not isinstance(output, ResponseFunctionToolCall): + logger.warning(f"Unexpected output type, ignoring: {type(output)}") + continue + + # At this point we know it's a function tool call + if not isinstance(output, ResponseFunctionToolCall): + continue + + # Handoffs + if output.name in handoff_map: + items.append(HandoffCallItem(raw_item=output, agent=agent)) + handoff = ToolRunHandoff( + tool_call=output, + handoff=handoff_map[output.name], + ) + run_handoffs.append(handoff) + # Regular function tool call + else: + if output.name not in function_map: + _utils.attach_error_to_current_span( + SpanError( + message="Tool not found", + data={"tool_name": output.name}, + ) + ) + raise ModelBehaviorError(f"Tool {output.name} not found in agent {agent.name}") + items.append(ToolCallItem(raw_item=output, agent=agent)) + functions.append( + ToolRunFunction( + tool_call=output, + function_tool=function_map[output.name], + ) + ) + + return ProcessedResponse( + new_items=items, + handoffs=run_handoffs, + functions=functions, + computer_actions=computer_actions, + ) + + @classmethod + async def execute_function_tool_calls( + cls, + *, + agent: Agent[TContext], + tool_runs: list[ToolRunFunction], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> list[RunItem]: + async def run_single_tool( + func_tool: FunctionTool, tool_call: ResponseFunctionToolCall + ) -> str: + with function_span(func_tool.name) as span_fn: + if config.trace_include_sensitive_data: + span_fn.span_data.input = tool_call.arguments + try: + _, _, result = await asyncio.gather( + hooks.on_tool_start(context_wrapper, agent, func_tool), + ( + agent.hooks.on_tool_start(context_wrapper, agent, func_tool) + if agent.hooks + else _utils.noop_coroutine() + ), + func_tool.on_invoke_tool(context_wrapper, tool_call.arguments), + ) + + await asyncio.gather( + hooks.on_tool_end(context_wrapper, agent, func_tool, result), + ( + agent.hooks.on_tool_end(context_wrapper, agent, func_tool, result) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + except Exception as e: + _utils.attach_error_to_current_span( + SpanError( + message="Error running tool", + data={"tool_name": func_tool.name, "error": str(e)}, + ) + ) + if isinstance(e, AgentsException): + raise e + raise UserError(f"Error running tool {func_tool.name}: {e}") from e + + if config.trace_include_sensitive_data: + span_fn.span_data.output = result + return result + + tasks = [] + for tool_run in tool_runs: + function_tool = tool_run.function_tool + tasks.append(run_single_tool(function_tool, tool_run.tool_call)) + + results = await asyncio.gather(*tasks) + + return [ + ToolCallOutputItem( + output=str(result), + raw_item=ItemHelpers.tool_call_output_item(tool_run.tool_call, str(result)), + agent=agent, + ) + for tool_run, result in zip(tool_runs, results) + ] + + @classmethod + async def execute_computer_actions( + cls, + *, + agent: Agent[TContext], + actions: list[ToolRunComputerAction], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> list[RunItem]: + results: list[RunItem] = [] + # Need to run these serially, because each action can affect the computer state + for action in actions: + results.append( + await ComputerAction.execute( + agent=agent, + action=action, + hooks=hooks, + context_wrapper=context_wrapper, + config=config, + ) + ) + + return results + + @classmethod + async def execute_handoffs( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + pre_step_items: list[RunItem], + new_step_items: list[RunItem], + new_response: ModelResponse, + run_handoffs: list[ToolRunHandoff], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + # If there is more than one handoff, add tool responses that reject those handoffs + if len(run_handoffs) > 1: + output_message = "Multiple handoffs detected, ignoring this one." + new_step_items.extend( + [ + ToolCallOutputItem( + output=output_message, + raw_item=ItemHelpers.tool_call_output_item( + handoff.tool_call, output_message + ), + agent=agent, + ) + for handoff in run_handoffs[1:] + ] + ) + + actual_handoff = run_handoffs[0] + with handoff_span(from_agent=agent.name) as span_handoff: + handoff = actual_handoff.handoff + new_agent: Agent[Any] = await handoff.on_invoke_handoff( + context_wrapper, actual_handoff.tool_call.arguments + ) + span_handoff.span_data.to_agent = new_agent.name + + # Append a tool output item for the handoff + new_step_items.append( + HandoffOutputItem( + agent=agent, + raw_item=ItemHelpers.tool_call_output_item( + actual_handoff.tool_call, + handoff.get_transfer_message(new_agent), + ), + source_agent=agent, + target_agent=new_agent, + ) + ) + + # Execute handoff hooks + await asyncio.gather( + hooks.on_handoff( + context=context_wrapper, + from_agent=agent, + to_agent=new_agent, + ), + ( + agent.hooks.on_handoff( + context_wrapper, + agent=new_agent, + source=agent, + ) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + # If there's an input filter, filter the input for the next agent + input_filter = handoff.input_filter or ( + run_config.handoff_input_filter if run_config else None + ) + if input_filter: + logger.debug("Filtering inputs for handoff") + handoff_input_data = HandoffInputData( + input_history=tuple(original_input) + if isinstance(original_input, list) + else original_input, + pre_handoff_items=tuple(pre_step_items), + new_items=tuple(new_step_items), + ) + if not callable(input_filter): + _utils.attach_error_to_span( + span_handoff, + SpanError( + message="Invalid input filter", + data={"details": "not callable()"}, + ), + ) + raise UserError(f"Invalid input filter: {input_filter}") + filtered = input_filter(handoff_input_data) + if not isinstance(filtered, HandoffInputData): + _utils.attach_error_to_span( + span_handoff, + SpanError( + message="Invalid input filter result", + data={"details": "not a HandoffInputData"}, + ), + ) + raise UserError(f"Invalid input filter result: {filtered}") + + original_input = ( + filtered.input_history + if isinstance(filtered.input_history, str) + else list(filtered.input_history) + ) + pre_step_items = list(filtered.pre_handoff_items) + new_step_items = list(filtered.new_items) + + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepHandoff(new_agent), + ) + + @classmethod + async def execute_final_output( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + new_response: ModelResponse, + pre_step_items: list[RunItem], + new_step_items: list[RunItem], + final_output: Any, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + ) -> SingleStepResult: + # Run the on_end hooks + await cls.run_final_output_hooks(agent, hooks, context_wrapper, final_output) + + return SingleStepResult( + original_input=original_input, + model_response=new_response, + pre_step_items=pre_step_items, + new_step_items=new_step_items, + next_step=NextStepFinalOutput(final_output), + ) + + @classmethod + async def run_final_output_hooks( + cls, + agent: Agent[TContext], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + final_output: Any, + ): + await asyncio.gather( + hooks.on_agent_end(context_wrapper, agent, final_output), + agent.hooks.on_end(context_wrapper, agent, final_output) + if agent.hooks + else _utils.noop_coroutine(), + ) + + @classmethod + async def run_single_input_guardrail( + cls, + agent: Agent[Any], + guardrail: InputGuardrail[TContext], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> InputGuardrailResult: + with guardrail_span(guardrail.get_name()) as span_guardrail: + result = await guardrail.run(agent, input, context) + span_guardrail.span_data.triggered = result.output.tripwire_triggered + return result + + @classmethod + async def run_single_output_guardrail( + cls, + guardrail: OutputGuardrail[TContext], + agent: Agent[Any], + agent_output: Any, + context: RunContextWrapper[TContext], + ) -> OutputGuardrailResult: + with guardrail_span(guardrail.get_name()) as span_guardrail: + result = await guardrail.run(agent=agent, agent_output=agent_output, context=context) + span_guardrail.span_data.triggered = result.output.tripwire_triggered + return result + + @classmethod + def stream_step_result_to_queue( + cls, + step_result: SingleStepResult, + queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel], + ): + for item in step_result.new_step_items: + if isinstance(item, MessageOutputItem): + event = RunItemStreamEvent(item=item, name="message_output_created") + elif isinstance(item, HandoffCallItem): + event = RunItemStreamEvent(item=item, name="handoff_requested") + elif isinstance(item, HandoffOutputItem): + event = RunItemStreamEvent(item=item, name="handoff_occured") + elif isinstance(item, ToolCallItem): + event = RunItemStreamEvent(item=item, name="tool_called") + elif isinstance(item, ToolCallOutputItem): + event = RunItemStreamEvent(item=item, name="tool_output") + elif isinstance(item, ReasoningItem): + event = RunItemStreamEvent(item=item, name="reasoning_item_created") + else: + logger.warning(f"Unexpected item type: {type(item)}") + event = None + + if event: + queue.put_nowait(event) + + +class TraceCtxManager: + """Creates a trace only if there is no current trace, and manages the trace lifecycle.""" + + def __init__( + self, + workflow_name: str, + trace_id: str | None, + group_id: str | None, + metadata: dict[str, Any] | None, + disabled: bool, + ): + self.trace: Trace | None = None + self.workflow_name = workflow_name + self.trace_id = trace_id + self.group_id = group_id + self.metadata = metadata + self.disabled = disabled + + def __enter__(self) -> TraceCtxManager: + current_trace = get_current_trace() + if not current_trace: + self.trace = trace( + workflow_name=self.workflow_name, + trace_id=self.trace_id, + group_id=self.group_id, + metadata=self.metadata, + disabled=self.disabled, + ) + self.trace.start(mark_as_current=True) + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.trace: + self.trace.finish(reset_current=True) + + +class ComputerAction: + @classmethod + async def execute( + cls, + *, + agent: Agent[TContext], + action: ToolRunComputerAction, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + config: RunConfig, + ) -> RunItem: + output_func = ( + cls._get_screenshot_async(action.computer_tool.computer, action.tool_call) + if isinstance(action.computer_tool.computer, AsyncComputer) + else cls._get_screenshot_sync(action.computer_tool.computer, action.tool_call) + ) + + _, _, output = await asyncio.gather( + hooks.on_tool_start(context_wrapper, agent, action.computer_tool), + ( + agent.hooks.on_tool_start(context_wrapper, agent, action.computer_tool) + if agent.hooks + else _utils.noop_coroutine() + ), + output_func, + ) + + await asyncio.gather( + hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output), + ( + agent.hooks.on_tool_end(context_wrapper, agent, action.computer_tool, output) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + # TODO: don't send a screenshot every single time, use references + image_url = f"data:image/png;base64,{output}" + return ToolCallOutputItem( + agent=agent, + output=image_url, + raw_item=ComputerCallOutput( + call_id=action.tool_call.call_id, + output={ + "type": "computer_screenshot", + "image_url": image_url, + }, + type="computer_call_output", + ), + ) + + @classmethod + async def _get_screenshot_sync( + cls, + computer: Computer, + tool_call: ResponseComputerToolCall, + ) -> str: + action = tool_call.action + if isinstance(action, ActionClick): + computer.click(action.x, action.y, action.button) + elif isinstance(action, ActionDoubleClick): + computer.double_click(action.x, action.y) + elif isinstance(action, ActionDrag): + computer.drag([(p.x, p.y) for p in action.path]) + elif isinstance(action, ActionKeypress): + computer.keypress(action.keys) + elif isinstance(action, ActionMove): + computer.move(action.x, action.y) + elif isinstance(action, ActionScreenshot): + computer.screenshot() + elif isinstance(action, ActionScroll): + computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) + elif isinstance(action, ActionType): + computer.type(action.text) + elif isinstance(action, ActionWait): + computer.wait() + + return computer.screenshot() + + @classmethod + async def _get_screenshot_async( + cls, + computer: AsyncComputer, + tool_call: ResponseComputerToolCall, + ) -> str: + action = tool_call.action + if isinstance(action, ActionClick): + await computer.click(action.x, action.y, action.button) + elif isinstance(action, ActionDoubleClick): + await computer.double_click(action.x, action.y) + elif isinstance(action, ActionDrag): + await computer.drag([(p.x, p.y) for p in action.path]) + elif isinstance(action, ActionKeypress): + await computer.keypress(action.keys) + elif isinstance(action, ActionMove): + await computer.move(action.x, action.y) + elif isinstance(action, ActionScreenshot): + await computer.screenshot() + elif isinstance(action, ActionScroll): + await computer.scroll(action.x, action.y, action.scroll_x, action.scroll_y) + elif isinstance(action, ActionType): + await computer.type(action.text) + elif isinstance(action, ActionWait): + await computer.wait() + + return await computer.screenshot() diff --git a/tests/src/agents/_utils.py b/tests/src/agents/_utils.py new file mode 100644 index 0000000..2a0293a --- /dev/null +++ b/tests/src/agents/_utils.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import re +from collections.abc import Awaitable +from typing import Any, Literal, Union + +from pydantic import TypeAdapter, ValidationError +from typing_extensions import TypeVar + +from .exceptions import ModelBehaviorError +from .logger import logger +from .tracing import Span, SpanError, get_current_span + +T = TypeVar("T") + +MaybeAwaitable = Union[Awaitable[T], T] + + +def transform_string_function_style(name: str) -> str: + # Replace spaces with underscores + name = name.replace(" ", "_") + + # Replace non-alphanumeric characters with underscores + name = re.sub(r"[^a-zA-Z0-9]", "_", name) + + return name.lower() + + +def validate_json(json_str: str, type_adapter: TypeAdapter[T], partial: bool) -> T: + partial_setting: bool | Literal["off", "on", "trailing-strings"] = ( + "trailing-strings" if partial else False + ) + try: + validated = type_adapter.validate_json(json_str, experimental_allow_partial=partial_setting) + return validated + except ValidationError as e: + attach_error_to_current_span( + SpanError( + message="Invalid JSON provided", + data={}, + ) + ) + raise ModelBehaviorError( + f"Invalid JSON when parsing {json_str} for {type_adapter}; {e}" + ) from e + + +def attach_error_to_span(span: Span[Any], error: SpanError) -> None: + span.set_error(error) + + +def attach_error_to_current_span(error: SpanError) -> None: + span = get_current_span() + if span: + attach_error_to_span(span, error) + else: + logger.warning(f"No span to add error {error} to") + + +async def noop_coroutine() -> None: + pass diff --git a/tests/src/agents/agent.py b/tests/src/agents/agent.py new file mode 100644 index 0000000..61c0a89 --- /dev/null +++ b/tests/src/agents/agent.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +import dataclasses +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Callable, Generic, cast + +from . import _utils +from ._utils import MaybeAwaitable +from .guardrail import InputGuardrail, OutputGuardrail +from .handoffs import Handoff +from .items import ItemHelpers +from .logger import logger +from .model_settings import ModelSettings +from .models.interface import Model +from .run_context import RunContextWrapper, TContext +from .tool import Tool, function_tool + +if TYPE_CHECKING: + from .lifecycle import AgentHooks + from .result import RunResult + + +@dataclass +class Agent(Generic[TContext]): + """An agent is an AI model configured with instructions, tools, guardrails, handoffs and more. + + We strongly recommend passing `instructions`, which is the "system prompt" for the agent. In + addition, you can pass `description`, which is a human-readable description of the agent, used + when the agent is used inside tools/handoffs. + + Agents are generic on the context type. The context is a (mutable) object you create. It is + passed to tool functions, handoffs, guardrails, etc. + """ + + name: str + """The name of the agent.""" + + instructions: ( + str + | Callable[ + [RunContextWrapper[TContext], Agent[TContext]], + MaybeAwaitable[str], + ] + | None + ) = None + """The instructions for the agent. Will be used as the "system prompt" when this agent is + invoked. Describes what the agent should do, and how it responds. + + Can either be a string, or a function that dynamically generates instructions for the agent. If + you provide a function, it will be called with the context and the agent instance. It must + return a string. + """ + + handoff_description: str | None = None + """A description of the agent. This is used when the agent is used as a handoff, so that an + LLM knows what it does and when to invoke it. + """ + + handoffs: list[Agent[Any] | Handoff[TContext]] = field(default_factory=list) + """Handoffs are sub-agents that the agent can delegate to. You can provide a list of handoffs, + and the agent can choose to delegate to them if relevant. Allows for separation of concerns and + modularity. + """ + + model: str | Model | None = None + """The model implementation to use when invoking the LLM. + + By default, if not set, the agent will use the default model configured in + `model_settings.DEFAULT_MODEL`. + """ + + model_settings: ModelSettings = field(default_factory=ModelSettings) + """Configures model-specific tuning parameters (e.g. temperature, top_p). + """ + + tools: list[Tool] = field(default_factory=list) + """A list of tools that the agent can use.""" + + input_guardrails: list[InputGuardrail[TContext]] = field(default_factory=list) + """A list of checks that run in parallel to the agent's execution, before generating a + response. Runs only if the agent is the first agent in the chain. + """ + + output_guardrails: list[OutputGuardrail[TContext]] = field(default_factory=list) + """A list of checks that run on the final output of the agent, after generating a response. + Runs only if the agent produces a final output. + """ + + output_type: type[Any] | None = None + """The type of the output object. If not provided, the output will be `str`.""" + + hooks: AgentHooks[TContext] | None = None + """A class that receives callbacks on various lifecycle events for this agent. + """ + + def clone(self, **kwargs: Any) -> Agent[TContext]: + """Make a copy of the agent, with the given arguments changed. For example, you could do: + ``` + new_agent = agent.clone(instructions="New instructions") + ``` + """ + return dataclasses.replace(self, **kwargs) + + def as_tool( + self, + tool_name: str | None, + tool_description: str | None, + custom_output_extractor: Callable[[RunResult], Awaitable[str]] | None = None, + ) -> Tool: + """Transform this agent into a tool, callable by other agents. + + This is different from handoffs in two ways: + 1. In handoffs, the new agent receives the conversation history. In this tool, the new agent + receives generated input. + 2. In handoffs, the new agent takes over the conversation. In this tool, the new agent is + called as a tool, and the conversation is continued by the original agent. + + Args: + tool_name: The name of the tool. If not provided, the agent's name will be used. + tool_description: The description of the tool, which should indicate what it does and + when to use it. + custom_output_extractor: A function that extracts the output from the agent. If not + provided, the last message from the agent will be used. + """ + + @function_tool( + name_override=tool_name or _utils.transform_string_function_style(self.name), + description_override=tool_description or "", + ) + async def run_agent(context: RunContextWrapper, input: str) -> str: + from .run import Runner + + output = await Runner.run( + starting_agent=self, + input=input, + context=context.context, + ) + if custom_output_extractor: + return await custom_output_extractor(output) + + return ItemHelpers.text_message_outputs(output.new_items) + + return run_agent + + async def get_system_prompt(self, run_context: RunContextWrapper[TContext]) -> str | None: + """Get the system prompt for the agent.""" + if isinstance(self.instructions, str): + return self.instructions + elif callable(self.instructions): + if inspect.iscoroutinefunction(self.instructions): + return await cast(Awaitable[str], self.instructions(run_context, self)) + else: + return cast(str, self.instructions(run_context, self)) + elif self.instructions is not None: + logger.error(f"Instructions must be a string or a function, got {self.instructions}") + + return None diff --git a/tests/src/agents/agent_output.py b/tests/src/agents/agent_output.py new file mode 100644 index 0000000..8140d8c --- /dev/null +++ b/tests/src/agents/agent_output.py @@ -0,0 +1,144 @@ +from dataclasses import dataclass +from typing import Any + +from pydantic import BaseModel, TypeAdapter +from typing_extensions import TypedDict, get_args, get_origin + +from . import _utils +from .exceptions import ModelBehaviorError, UserError +from .strict_schema import ensure_strict_json_schema +from .tracing import SpanError + +_WRAPPER_DICT_KEY = "response" + + +@dataclass(init=False) +class AgentOutputSchema: + """An object that captures the JSON schema of the output, as well as validating/parsing JSON + produced by the LLM into the output type. + """ + + output_type: type[Any] + """The type of the output.""" + + _type_adapter: TypeAdapter[Any] + """A type adapter that wraps the output type, so that we can validate JSON.""" + + _is_wrapped: bool + """Whether the output type is wrapped in a dictionary. This is generally done if the base + output type cannot be represented as a JSON Schema object. + """ + + _output_schema: dict[str, Any] + """The JSON schema of the output.""" + + strict_json_schema: bool + """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, + as it increases the likelihood of correct JSON input. + """ + + def __init__(self, output_type: type[Any], strict_json_schema: bool = True): + """ + Args: + output_type: The type of the output. + strict_json_schema: Whether the JSON schema is in strict mode. We **strongly** recommend + setting this to True, as it increases the likelihood of correct JSON input. + """ + self.output_type = output_type + self.strict_json_schema = strict_json_schema + + if output_type is None or output_type is str: + self._is_wrapped = False + self._type_adapter = TypeAdapter(output_type) + self._output_schema = self._type_adapter.json_schema() + return + + # We should wrap for things that are not plain text, and for things that would definitely + # not be a JSON Schema object. + self._is_wrapped = not _is_subclass_of_base_model_or_dict(output_type) + + if self._is_wrapped: + OutputType = TypedDict( + "OutputType", + { + _WRAPPER_DICT_KEY: output_type, # type: ignore + }, + ) + self._type_adapter = TypeAdapter(OutputType) + self._output_schema = self._type_adapter.json_schema() + else: + self._type_adapter = TypeAdapter(output_type) + self._output_schema = self._type_adapter.json_schema() + + if self.strict_json_schema: + self._output_schema = ensure_strict_json_schema(self._output_schema) + + def is_plain_text(self) -> bool: + """Whether the output type is plain text (versus a JSON object).""" + return self.output_type is None or self.output_type is str + + def json_schema(self) -> dict[str, Any]: + """The JSON schema of the output type.""" + if self.is_plain_text(): + raise UserError("Output type is plain text, so no JSON schema is available") + return self._output_schema + + def validate_json(self, json_str: str, partial: bool = False) -> Any: + """Validate a JSON string against the output type. Returns the validated object, or raises + a `ModelBehaviorError` if the JSON is invalid. + """ + validated = _utils.validate_json(json_str, self._type_adapter, partial) + if self._is_wrapped: + if not isinstance(validated, dict): + _utils.attach_error_to_current_span( + SpanError( + message="Invalid JSON", + data={"details": f"Expected a dict, got {type(validated)}"}, + ) + ) + raise ModelBehaviorError( + f"Expected a dict, got {type(validated)} for JSON: {json_str}" + ) + + if _WRAPPER_DICT_KEY not in validated: + _utils.attach_error_to_current_span( + SpanError( + message="Invalid JSON", + data={"details": f"Could not find key {_WRAPPER_DICT_KEY} in JSON"}, + ) + ) + raise ModelBehaviorError( + f"Could not find key {_WRAPPER_DICT_KEY} in JSON: {json_str}" + ) + return validated[_WRAPPER_DICT_KEY] + return validated + + def output_type_name(self) -> str: + """The name of the output type.""" + return _type_to_str(self.output_type) + + +def _is_subclass_of_base_model_or_dict(t: Any) -> bool: + if not isinstance(t, type): + return False + + # If it's a generic alias, 'origin' will be the actual type, e.g. 'list' + origin = get_origin(t) + + allowed_types = (BaseModel, dict) + # If it's a generic alias e.g. list[str], then we should check the origin type i.e. list + return issubclass(origin or t, allowed_types) + + +def _type_to_str(t: type[Any]) -> str: + origin = get_origin(t) + args = get_args(t) + + if origin is None: + # It's a simple type like `str`, `int`, etc. + return t.__name__ + elif args: + args_str = ', '.join(_type_to_str(arg) for arg in args) + return f"{origin.__name__}[{args_str}]" + else: + return str(t) diff --git a/tests/src/agents/computer.py b/tests/src/agents/computer.py new file mode 100644 index 0000000..1b9224d --- /dev/null +++ b/tests/src/agents/computer.py @@ -0,0 +1,107 @@ +import abc +from typing import Literal + +Environment = Literal["mac", "windows", "ubuntu", "browser"] +Button = Literal["left", "right", "wheel", "back", "forward"] + + +class Computer(abc.ABC): + """A computer implemented with sync operations. The Computer interface abstracts the + operations needed to control a computer or browser.""" + + @property + @abc.abstractmethod + def environment(self) -> Environment: + pass + + @property + @abc.abstractmethod + def dimensions(self) -> tuple[int, int]: + pass + + @abc.abstractmethod + def screenshot(self) -> str: + pass + + @abc.abstractmethod + def click(self, x: int, y: int, button: Button) -> None: + pass + + @abc.abstractmethod + def double_click(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + pass + + @abc.abstractmethod + def type(self, text: str) -> None: + pass + + @abc.abstractmethod + def wait(self) -> None: + pass + + @abc.abstractmethod + def move(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + def keypress(self, keys: list[str]) -> None: + pass + + @abc.abstractmethod + def drag(self, path: list[tuple[int, int]]) -> None: + pass + + +class AsyncComputer(abc.ABC): + """A computer implemented with async operations. The Computer interface abstracts the + operations needed to control a computer or browser.""" + + @property + @abc.abstractmethod + def environment(self) -> Environment: + pass + + @property + @abc.abstractmethod + def dimensions(self) -> tuple[int, int]: + pass + + @abc.abstractmethod + async def screenshot(self) -> str: + pass + + @abc.abstractmethod + async def click(self, x: int, y: int, button: Button) -> None: + pass + + @abc.abstractmethod + async def double_click(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + pass + + @abc.abstractmethod + async def type(self, text: str) -> None: + pass + + @abc.abstractmethod + async def wait(self) -> None: + pass + + @abc.abstractmethod + async def move(self, x: int, y: int) -> None: + pass + + @abc.abstractmethod + async def keypress(self, keys: list[str]) -> None: + pass + + @abc.abstractmethod + async def drag(self, path: list[tuple[int, int]]) -> None: + pass diff --git a/tests/src/agents/exceptions.py b/tests/src/agents/exceptions.py new file mode 100644 index 0000000..78898f0 --- /dev/null +++ b/tests/src/agents/exceptions.py @@ -0,0 +1,63 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .guardrail import InputGuardrailResult, OutputGuardrailResult + + +class AgentsException(Exception): + """Base class for all exceptions in the Agents SDK.""" + + +class MaxTurnsExceeded(AgentsException): + """Exception raised when the maximum number of turns is exceeded.""" + + message: str + + def __init__(self, message: str): + self.message = message + + +class ModelBehaviorError(AgentsException): + """Exception raised when the model does something unexpected, e.g. calling a tool that doesn't + exist, or providing malformed JSON. + """ + + message: str + + def __init__(self, message: str): + self.message = message + + +class UserError(AgentsException): + """Exception raised when the user makes an error using the SDK.""" + + message: str + + def __init__(self, message: str): + self.message = message + + +class InputGuardrailTripwireTriggered(AgentsException): + """Exception raised when a guardrail tripwire is triggered.""" + + guardrail_result: "InputGuardrailResult" + """The result data of the guardrail that was triggered.""" + + def __init__(self, guardrail_result: "InputGuardrailResult"): + self.guardrail_result = guardrail_result + super().__init__( + f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" + ) + + +class OutputGuardrailTripwireTriggered(AgentsException): + """Exception raised when a guardrail tripwire is triggered.""" + + guardrail_result: "OutputGuardrailResult" + """The result data of the guardrail that was triggered.""" + + def __init__(self, guardrail_result: "OutputGuardrailResult"): + self.guardrail_result = guardrail_result + super().__init__( + f"Guardrail {guardrail_result.guardrail.__class__.__name__} triggered tripwire" + ) diff --git a/tests/src/agents/extensions/__init__.py b/tests/src/agents/extensions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/src/agents/extensions/__pycache__/__init__.cpython-313.pyc b/tests/src/agents/extensions/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc837fb110529ee91165368c4e1e4cb1a6915d3e GIT binary patch literal 171 zcmey&%ge<81l;cqr-SInAOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkla$ylvMr1^whkP;*{+8f};GA{F2H7n2>&PQ8H9kKeeJHHLo}`Kd)FnK0Y%qvm`!V jub}c4hfQvNN@-52T@fqLXpq&#AjU^#Mn=XWW*`dy)@>}f literal 0 HcmV?d00001 diff --git a/tests/src/agents/extensions/__pycache__/__init__.cpython-39.pyc b/tests/src/agents/extensions/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e72c0c897ae2a73bec9dc4524016b56dfda9c85 GIT binary patch literal 158 zcmYe~<>g`k0`7N*(?RrO5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HserR!OQL%ne zu6}ZUN~(ThdTL%taY}Z4K~a85eo19Ps(x`%GE`VUwW1_7uQ)S5uUJ1mJ~J<~BtBlR Wpz;=nO>TZlX-=vg$neiV%m4tgY$uZd literal 0 HcmV?d00001 diff --git a/tests/src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc b/tests/src/agents/extensions/__pycache__/handoff_filters.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc28a3cf09236083e9ebc47792d8ac6fd2c464d6 GIT binary patch literal 2318 zcmZ`)O>7%g5Ps|Z@y0)~<0gTm#BAD9FeEm0DN;kBLLg|}G!@=jP+4?k?3dKV*|pxT zDdps2kL6I&0|yRVxN(F#w;~iP5jIPaK$JsoE(xksabV`{Pn<+N%kRybnfGSid^5A# z$z+VcxcBN$Yu`r+`4cz6he^3TvC`) zin3@)Wol`Rjg?|7rjabs$8>p?T$fl}kLe1h5?SEmPmk+S&PlGRxx_;>nG@W2%``2i z<}@wSeu#bmFMY0NHmudv^Jcr_yj^o@ImJ!+ytitt)_I4nyAwX+QpW);&We^hCMOb? zEvpr3W`f;_YUXm8+HJ5&xe+{77tketD6ooz2-`mbafeh$IgpI3rT~0Db|Y+JMyhhK zuLxDVi;q*j?hF3D7AK@S=5I(vIOThANvH^>SOuv!z)B*Zch^AMltwfoAy=-Z0^N#O zkt(t-oEDFfT;#KrcbYASGF!7cjt1e>9Eg_Igb>(E+G?{!HJjF0{n~Jj-c#CdZBJLXrZ=7O~-D0 zY_yr>Sons5&FVh8KtFY;X=B0{0yn+u?R@*DoA#d%A?6{l*(?}J!{0Vx>XM;CQuF=s zd~ZD8eRq&P(oZk+(hJ@5I|@1Z`giaDK>NkzUU7L#eeSEZEj7PUzA_YvJO$z-OB+ke z{iUnDrK_9s#)g!=KXc;S+~&-g4Qcv8{P1A<_+a*xLFU+_sFYBKM3R+lut@e41_qcH z#=k(^Aq9d(aEP3{PB87m5~@V1VPWh~`^H&F`%z5^t>8GI`Qkp!|1G7))ILqsyYb+i zHSp`=6$0f}4ht;M1#VbC8oAgaBu+F^#eC&sw?v_kgq2*BAubHPV$f3w!(wLGXI-V% zZd0?tCSU^p9_Ep^m-qtl#7tr{c=}K<9RZ$I^uj#2<(u2hGZRbm) zpIGcA7VoEK?jGCHPV`g7UaH7aD!(eF-(ufL2`W6s~v@X0Sm;xXk;fOB);$lTChe(2&KlCt) zru4y^uK|}w9ZXarWqj?vQ4xc-1yRrYQQvK$;cXCgaRtgd8FV58JCQrNXKQ$U$xYU+ zb?8cz`AU1XffBX>YIyz5(@R0q+ryBsegQ&pjXG=&Johk_Sr%wFw%exIvnbS%3vgrO z!YA+!;X#{0IZh|wZ!g2tC4Vc?jM6O)Gvwu>kMSTq`GxwqdUtv=t=*Cb@$_aq`^y0Y z#}L8Tegs!?5}QObXxdd6bvn%Sx}wA#esYMn*{c7kT+s6Q{D%y%By15TIF8SHlJ&F> zekDf_08%73?#2vbwF7pjVK4;ORsFVRbHn_?ks86u+$|J_MaWy6m-!f;ZwSvu9yC`y zvu-syE&2v4f(Vn%J`K}Q6a?Wna^x@a{NqSeh&(1RK@^3vJ4#wmc8l z@MTh@J9D_PcBT#= zJ>+tUnA`?2tAuN!Q$0MbqsI#qK28x|`+}>LD5k_@-CRE29-g}fz|Wyn&Uen5K$108 zv?7XKQgKtc6}cn@>@`y!M*T~&cA-(cJ=LmM<&$L23nkO<Txv{)6I$G}N1H5UID8MlOymQr|XIycLeeysoyXIw)t%WIG97DX1N8W&NhzkgUSCj_-P!_zT zZtyqtDd`eB@8V_+$LsA@BeREv$#RRu9T;;QNxzUa0VcF^fXRDnR=xy2}Hf^D=?U`}iB)T^(b2JMV&7?hZGs#et$9417 z^A6F0dk)v|3mwPiT#2`le~qnYv5h+a^^7pciFY6(@B)ke5AhH~AK|f-b6C&@$OK#i z(*Ud)JsI_NCyr+;oJ%~8g*iHP(6CtARbJKtZJUxTHr{ZMw(oit203G=yi Za3?R*RmMLxho3a7y0lBe?%;UP{})%owz~iT literal 0 HcmV?d00001 diff --git a/tests/src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc b/tests/src/agents/extensions/__pycache__/handoff_prompt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b1154bdaee6473333b7ed6b593cf68cba773d31 GIT binary patch literal 1071 zcmY*YO>fgc5M9S<8>MP{L#3yY%Av7JA_46Is!A0NYC-)_-69Z`!p5E?rtz+|yKd^9 z_!InxaN~a60Qu$4seLm zzKI_5E(!y>IL!Nf48ime_Uyg}fB(%Ugs7vgumK4%ze1Q^3ZztFB)EyB04_qn8w}EX zgo zNYM2(zpnE;M#}zl7H|PQON9VPB8k67rKb#uJQtF*sRDD*LxN8dC5ad}Sch5a+$kRP zBD^Nm?F<4kCK6qOwCEPRlZu?dCJ;YV6wg5Ln2vuWZ9;cYwTx`-lkD*RrmcEM5dRcQ+-suI+`aV;>3*R6`E9uz?pR(jc)NyR`cfj@GrDXO?bhU z*;DrstCenFSNFmY6jHH>U`S%_6>Cb3`oJcF4skE!n;thnJZARldiAzct=62;!?Whg zlcS^N@qTl^b$WJkbb3xh^VQ+Inxm-EnYKJ(blA8c8M>k3Mj%6MOo79z79}&%WC=F3 z3X1Ip{ShcM`KKF2LG3vrRZp@}!LMZvYR$s`>re}na>eF;K%dX-cZIEN+%0Vk%fq+B zgS*A8yYkkABJ8;4syP}*E_G~v>nl?g+EDX$x3Uw7kg$?8vp HandoffInputData: + """Filters out all tool items: file search, web search and function calls+output.""" + + history = handoff_input_data.input_history + new_items = handoff_input_data.new_items + + filtered_history = ( + _remove_tool_types_from_input(history) if isinstance(history, tuple) else history + ) + filtered_pre_handoff_items = _remove_tools_from_items(handoff_input_data.pre_handoff_items) + filtered_new_items = _remove_tools_from_items(new_items) + + return HandoffInputData( + input_history=filtered_history, + pre_handoff_items=filtered_pre_handoff_items, + new_items=filtered_new_items, + ) + + +def _remove_tools_from_items(items: tuple[RunItem, ...]) -> tuple[RunItem, ...]: + filtered_items = [] + for item in items: + if ( + isinstance(item, HandoffCallItem) + or isinstance(item, HandoffOutputItem) + or isinstance(item, ToolCallItem) + or isinstance(item, ToolCallOutputItem) + ): + continue + filtered_items.append(item) + return tuple(filtered_items) + + +def _remove_tool_types_from_input( + items: tuple[TResponseInputItem, ...], +) -> tuple[TResponseInputItem, ...]: + tool_types = [ + "function_call", + "function_call_output", + "computer_call", + "computer_call_output", + "file_search_call", + "web_search_call", + ] + + filtered_items: list[TResponseInputItem] = [] + for item in items: + itype = item.get("type") + if itype in tool_types: + continue + filtered_items.append(item) + return tuple(filtered_items) diff --git a/tests/src/agents/extensions/handoff_prompt.py b/tests/src/agents/extensions/handoff_prompt.py new file mode 100644 index 0000000..cfb5ca7 --- /dev/null +++ b/tests/src/agents/extensions/handoff_prompt.py @@ -0,0 +1,19 @@ +# A recommended prompt prefix for agents that use handoffs. We recommend including this or +# similar instructions in any agents that use handoffs. +RECOMMENDED_PROMPT_PREFIX = ( + "# System context\n" + "You are part of a multi-agent system called the Agents SDK, designed to make agent " + "coordination and execution easy. Agents uses two primary abstraction: **Agents** and " + "**Handoffs**. An agent encompasses instructions and tools and can hand off a " + "conversation to another agent when appropriate. " + "Handoffs are achieved by calling a handoff function, generally named " + "`transfer_to_`. Transfers between agents are handled seamlessly in the background;" + " do not mention or draw attention to these transfers in your conversation with the user.\n" +) + + +def prompt_with_handoff_instructions(prompt: str) -> str: + """ + Add recommended instructions to the prompt for agents that use handoffs. + """ + return f"{RECOMMENDED_PROMPT_PREFIX}\n\n{prompt}" diff --git a/tests/src/agents/function_schema.py b/tests/src/agents/function_schema.py new file mode 100644 index 0000000..a4b5767 --- /dev/null +++ b/tests/src/agents/function_schema.py @@ -0,0 +1,340 @@ +from __future__ import annotations + +import contextlib +import inspect +import logging +import re +from dataclasses import dataclass +from typing import Any, Callable, Literal, get_args, get_origin, get_type_hints + +from griffe import Docstring, DocstringSectionKind +from pydantic import BaseModel, Field, create_model + +from .exceptions import UserError +from .run_context import RunContextWrapper +from .strict_schema import ensure_strict_json_schema + + +@dataclass +class FuncSchema: + """ + Captures the schema for a python function, in preparation for sending it to an LLM as a tool. + """ + + name: str + """The name of the function.""" + description: str | None + """The description of the function.""" + params_pydantic_model: type[BaseModel] + """A Pydantic model that represents the function's parameters.""" + params_json_schema: dict[str, Any] + """The JSON schema for the function's parameters, derived from the Pydantic model.""" + signature: inspect.Signature + """The signature of the function.""" + takes_context: bool = False + """Whether the function takes a RunContextWrapper argument (must be the first argument).""" + + def to_call_args(self, data: BaseModel) -> tuple[list[Any], dict[str, Any]]: + """ + Converts validated data from the Pydantic model into (args, kwargs), suitable for calling + the original function. + """ + positional_args: list[Any] = [] + keyword_args: dict[str, Any] = {} + seen_var_positional = False + + # Use enumerate() so we can skip the first parameter if it's context. + for idx, (name, param) in enumerate(self.signature.parameters.items()): + # If the function takes a RunContextWrapper and this is the first parameter, skip it. + if self.takes_context and idx == 0: + continue + + value = getattr(data, name, None) + if param.kind == param.VAR_POSITIONAL: + # e.g. *args: extend positional args and mark that *args is now seen + positional_args.extend(value or []) + seen_var_positional = True + elif param.kind == param.VAR_KEYWORD: + # e.g. **kwargs handling + keyword_args.update(value or {}) + elif param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): + # Before *args, add to positional args. After *args, add to keyword args. + if not seen_var_positional: + positional_args.append(value) + else: + keyword_args[name] = value + else: + # For KEYWORD_ONLY parameters, always use keyword args. + keyword_args[name] = value + return positional_args, keyword_args + + +@dataclass +class FuncDocumentation: + """Contains metadata about a python function, extracted from its docstring.""" + + name: str + """The name of the function, via `__name__`.""" + description: str | None + """The description of the function, derived from the docstring.""" + param_descriptions: dict[str, str] | None + """The parameter descriptions of the function, derived from the docstring.""" + + +DocstringStyle = Literal["google", "numpy", "sphinx"] + + +# As of Feb 2025, the automatic style detection in griffe is an Insiders feature. This +# code approximates it. +def _detect_docstring_style(doc: str) -> DocstringStyle: + scores: dict[DocstringStyle, int] = {"sphinx": 0, "numpy": 0, "google": 0} + + # Sphinx style detection: look for :param, :type, :return:, and :rtype: + sphinx_patterns = [r"^:param\s", r"^:type\s", r"^:return:", r"^:rtype:"] + for pattern in sphinx_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["sphinx"] += 1 + + # Numpy style detection: look for headers like 'Parameters', 'Returns', or 'Yields' followed by + # a dashed underline + numpy_patterns = [ + r"^Parameters\s*\n\s*-{3,}", + r"^Returns\s*\n\s*-{3,}", + r"^Yields\s*\n\s*-{3,}", + ] + for pattern in numpy_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["numpy"] += 1 + + # Google style detection: look for section headers with a trailing colon + google_patterns = [r"^(Args|Arguments):", r"^(Returns):", r"^(Raises):"] + for pattern in google_patterns: + if re.search(pattern, doc, re.MULTILINE): + scores["google"] += 1 + + max_score = max(scores.values()) + if max_score == 0: + return "google" + + # Priority order: sphinx > numpy > google in case of tie + styles: list[DocstringStyle] = ["sphinx", "numpy", "google"] + + for style in styles: + if scores[style] == max_score: + return style + + return "google" + + +@contextlib.contextmanager +def _suppress_griffe_logging(): + # Supresses warnings about missing annotations for params + logger = logging.getLogger("griffe") + previous_level = logger.getEffectiveLevel() + logger.setLevel(logging.ERROR) + try: + yield + finally: + logger.setLevel(previous_level) + + +def generate_func_documentation( + func: Callable[..., Any], style: DocstringStyle | None = None +) -> FuncDocumentation: + """ + Extracts metadata from a function docstring, in preparation for sending it to an LLM as a tool. + + Args: + func: The function to extract documentation from. + style: The style of the docstring to use for parsing. If not provided, we will attempt to + auto-detect the style. + + Returns: + A FuncDocumentation object containing the function's name, description, and parameter + descriptions. + """ + name = func.__name__ + doc = inspect.getdoc(func) + if not doc: + return FuncDocumentation(name=name, description=None, param_descriptions=None) + + with _suppress_griffe_logging(): + docstring = Docstring(doc, lineno=1, parser=style or _detect_docstring_style(doc)) + parsed = docstring.parse() + + description: str | None = next( + (section.value for section in parsed if section.kind == DocstringSectionKind.text), None + ) + + param_descriptions: dict[str, str] = { + param.name: param.description + for section in parsed + if section.kind == DocstringSectionKind.parameters + for param in section.value + } + + return FuncDocumentation( + name=func.__name__, + description=description, + param_descriptions=param_descriptions or None, + ) + + +def function_schema( + func: Callable[..., Any], + docstring_style: DocstringStyle | None = None, + name_override: str | None = None, + description_override: str | None = None, + use_docstring_info: bool = True, + strict_json_schema: bool = True, +) -> FuncSchema: + """ + Given a python function, extracts a `FuncSchema` from it, capturing the name, description, + parameter descriptions, and other metadata. + + Args: + func: The function to extract the schema from. + docstring_style: The style of the docstring to use for parsing. If not provided, we will + attempt to auto-detect the style. + name_override: If provided, use this name instead of the function's `__name__`. + description_override: If provided, use this description instead of the one derived from the + docstring. + use_docstring_info: If True, uses the docstring to generate the description and parameter + descriptions. + strict_json_schema: Whether the JSON schema is in strict mode. If True, we'll ensure that + the schema adheres to the "strict" standard the OpenAI API expects. We **strongly** + recommend setting this to True, as it increases the likelihood of the LLM providing + correct JSON input. + + Returns: + A `FuncSchema` object containing the function's name, description, parameter descriptions, + and other metadata. + """ + + # 1. Grab docstring info + if use_docstring_info: + doc_info = generate_func_documentation(func, docstring_style) + param_descs = doc_info.param_descriptions or {} + else: + doc_info = None + param_descs = {} + + func_name = name_override or doc_info.name if doc_info else func.__name__ + + # 2. Inspect function signature and get type hints + sig = inspect.signature(func) + type_hints = get_type_hints(func) + params = list(sig.parameters.items()) + takes_context = False + filtered_params = [] + + if params: + first_name, first_param = params[0] + # Prefer the evaluated type hint if available + ann = type_hints.get(first_name, first_param.annotation) + if ann != inspect._empty: + origin = get_origin(ann) or ann + if origin is RunContextWrapper: + takes_context = True # Mark that the function takes context + else: + filtered_params.append((first_name, first_param)) + else: + filtered_params.append((first_name, first_param)) + + # For parameters other than the first, raise error if any use RunContextWrapper. + for name, param in params[1:]: + ann = type_hints.get(name, param.annotation) + if ann != inspect._empty: + origin = get_origin(ann) or ann + if origin is RunContextWrapper: + raise UserError( + f"RunContextWrapper param found at non-first position in function" + f" {func.__name__}" + ) + filtered_params.append((name, param)) + + # We will collect field definitions for create_model as a dict: + # field_name -> (type_annotation, default_value_or_Field(...)) + fields: dict[str, Any] = {} + + for name, param in filtered_params: + ann = type_hints.get(name, param.annotation) + default = param.default + + # If there's no type hint, assume `Any` + if ann == inspect._empty: + ann = Any + + # If a docstring param description exists, use it + field_description = param_descs.get(name, None) + + # Handle different parameter kinds + if param.kind == param.VAR_POSITIONAL: + # e.g. *args: extend positional args + if get_origin(ann) is tuple: + # e.g. def foo(*args: tuple[int, ...]) -> treat as List[int] + args_of_tuple = get_args(ann) + if len(args_of_tuple) == 2 and args_of_tuple[1] is Ellipsis: + ann = list[args_of_tuple[0]] # type: ignore + else: + ann = list[Any] + else: + # If user wrote *args: int, treat as List[int] + ann = list[ann] # type: ignore + + # Default factory to empty list + fields[name] = ( + ann, + Field(default_factory=list, description=field_description), # type: ignore + ) + + elif param.kind == param.VAR_KEYWORD: + # **kwargs handling + if get_origin(ann) is dict: + # e.g. def foo(**kwargs: dict[str, int]) + dict_args = get_args(ann) + if len(dict_args) == 2: + ann = dict[dict_args[0], dict_args[1]] # type: ignore + else: + ann = dict[str, Any] + else: + # e.g. def foo(**kwargs: int) -> Dict[str, int] + ann = dict[str, ann] # type: ignore + + fields[name] = ( + ann, + Field(default_factory=dict, description=field_description), # type: ignore + ) + + else: + # Normal parameter + if default == inspect._empty: + # Required field + fields[name] = ( + ann, + Field(..., description=field_description), + ) + else: + # Parameter with a default value + fields[name] = ( + ann, + Field(default=default, description=field_description), + ) + + # 3. Dynamically build a Pydantic model + dynamic_model = create_model(f"{func_name}_args", __base__=BaseModel, **fields) + + # 4. Build JSON schema from that model + json_schema = dynamic_model.model_json_schema() + if strict_json_schema: + json_schema = ensure_strict_json_schema(json_schema) + + # 5. Return as a FuncSchema dataclass + return FuncSchema( + name=func_name, + description=description_override or doc_info.description if doc_info else None, + params_pydantic_model=dynamic_model, + params_json_schema=json_schema, + signature=sig, + takes_context=takes_context, + ) diff --git a/tests/src/agents/guardrail.py b/tests/src/agents/guardrail.py new file mode 100644 index 0000000..fcae0b8 --- /dev/null +++ b/tests/src/agents/guardrail.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Generic, Union, overload + +from typing_extensions import TypeVar + +from ._utils import MaybeAwaitable +from .exceptions import UserError +from .items import TResponseInputItem +from .run_context import RunContextWrapper, TContext + +if TYPE_CHECKING: + from .agent import Agent + + +@dataclass +class GuardrailFunctionOutput: + """The output of a guardrail function.""" + + output_info: Any + """ + Optional information about the guardrail's output. For example, the guardrail could include + information about the checks it performed and granular results. + """ + + tripwire_triggered: bool + """ + Whether the tripwire was triggered. If triggered, the agent's execution will be halted. + """ + + +@dataclass +class InputGuardrailResult: + """The result of a guardrail run.""" + + guardrail: InputGuardrail[Any] + """ + The guardrail that was run. + """ + + output: GuardrailFunctionOutput + """The output of the guardrail function.""" + + +@dataclass +class OutputGuardrailResult: + """The result of a guardrail run.""" + + guardrail: OutputGuardrail[Any] + """ + The guardrail that was run. + """ + + agent_output: Any + """ + The output of the agent that was checked by the guardrail. + """ + + agent: Agent[Any] + """ + The agent that was checked by the guardrail. + """ + + output: GuardrailFunctionOutput + """The output of the guardrail function.""" + + +@dataclass +class InputGuardrail(Generic[TContext]): + """Input guardrails are checks that run in parallel to the agent's execution. + They can be used to do things like: + - Check if input messages are off-topic + - Take over control of the agent's execution if an unexpected input is detected + + You can use the `@input_guardrail()` decorator to turn a function into an `InputGuardrail`, or + create an `InputGuardrail` manually. + + Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, the agent + execution will immediately stop and a `InputGuardrailTripwireTriggered` exception will be raised + """ + + guardrail_function: Callable[ + [RunContextWrapper[TContext], Agent[Any], str | list[TResponseInputItem]], + MaybeAwaitable[GuardrailFunctionOutput], + ] + """A function that receives the the agent input and the context, and returns a + `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally + include information about the guardrail's output. + """ + + name: str | None = None + """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail + function's name. + """ + + def get_name(self) -> str: + if self.name: + return self.name + + return self.guardrail_function.__name__ + + async def run( + self, + agent: Agent[Any], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> InputGuardrailResult: + if not callable(self.guardrail_function): + raise UserError(f"Guardrail function must be callable, got {self.guardrail_function}") + + output = self.guardrail_function(context, agent, input) + if inspect.isawaitable(output): + return InputGuardrailResult( + guardrail=self, + output=await output, + ) + + return InputGuardrailResult( + guardrail=self, + output=output, + ) + + +@dataclass +class OutputGuardrail(Generic[TContext]): + """Output guardrails are checks that run on the final output of an agent. + They can be used to do check if the output passes certain validation criteria + + You can use the `@output_guardrail()` decorator to turn a function into an `OutputGuardrail`, + or create an `OutputGuardrail` manually. + + Guardrails return a `GuardrailResult`. If `result.tripwire_triggered` is `True`, a + `OutputGuardrailTripwireTriggered` exception will be raised. + """ + + guardrail_function: Callable[ + [RunContextWrapper[TContext], Agent[Any], Any], + MaybeAwaitable[GuardrailFunctionOutput], + ] + """A function that receives the final agent, its output, and the context, and returns a + `GuardrailResult`. The result marks whether the tripwire was triggered, and can optionally + include information about the guardrail's output. + """ + + name: str | None = None + """The name of the guardrail, used for tracing. If not provided, we'll use the guardrail + function's name. + """ + + def get_name(self) -> str: + if self.name: + return self.name + + return self.guardrail_function.__name__ + + async def run( + self, context: RunContextWrapper[TContext], agent: Agent[Any], agent_output: Any + ) -> OutputGuardrailResult: + if not callable(self.guardrail_function): + raise UserError(f"Guardrail function must be callable, got {self.guardrail_function}") + + output = self.guardrail_function(context, agent, agent_output) + if inspect.isawaitable(output): + return OutputGuardrailResult( + guardrail=self, + agent=agent, + agent_output=agent_output, + output=await output, + ) + + return OutputGuardrailResult( + guardrail=self, + agent=agent, + agent_output=agent_output, + output=output, + ) + + +TContext_co = TypeVar("TContext_co", bound=Any, covariant=True) + +# For InputGuardrail +_InputGuardrailFuncSync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Union[str, list[TResponseInputItem]]], + GuardrailFunctionOutput, +] +_InputGuardrailFuncAsync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Union[str, list[TResponseInputItem]]], + Awaitable[GuardrailFunctionOutput], +] + + +@overload +def input_guardrail( + func: _InputGuardrailFuncSync[TContext_co], +) -> InputGuardrail[TContext_co]: ... + + +@overload +def input_guardrail( + func: _InputGuardrailFuncAsync[TContext_co], +) -> InputGuardrail[TContext_co]: ... + + +@overload +def input_guardrail( + *, + name: str | None = None, +) -> Callable[ + [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], + InputGuardrail[TContext_co], +]: ... + + +def input_guardrail( + func: _InputGuardrailFuncSync[TContext_co] + | _InputGuardrailFuncAsync[TContext_co] + | None = None, + *, + name: str | None = None, +) -> ( + InputGuardrail[TContext_co] + | Callable[ + [_InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co]], + InputGuardrail[TContext_co], + ] +): + """ + Decorator that transforms a sync or async function into an `InputGuardrail`. + It can be used directly (no parentheses) or with keyword args, e.g.: + + @input_guardrail + def my_sync_guardrail(...): ... + + @input_guardrail(name="guardrail_name") + async def my_async_guardrail(...): ... + """ + + def decorator( + f: _InputGuardrailFuncSync[TContext_co] | _InputGuardrailFuncAsync[TContext_co], + ) -> InputGuardrail[TContext_co]: + return InputGuardrail(guardrail_function=f, name=name) + + if func is not None: + # Decorator was used without parentheses + return decorator(func) + + # Decorator used with keyword arguments + return decorator + + +_OutputGuardrailFuncSync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Any], + GuardrailFunctionOutput, +] +_OutputGuardrailFuncAsync = Callable[ + [RunContextWrapper[TContext_co], "Agent[Any]", Any], + Awaitable[GuardrailFunctionOutput], +] + + +@overload +def output_guardrail( + func: _OutputGuardrailFuncSync[TContext_co], +) -> OutputGuardrail[TContext_co]: ... + + +@overload +def output_guardrail( + func: _OutputGuardrailFuncAsync[TContext_co], +) -> OutputGuardrail[TContext_co]: ... + + +@overload +def output_guardrail( + *, + name: str | None = None, +) -> Callable[ + [_OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co]], + OutputGuardrail[TContext_co], +]: ... + + +def output_guardrail( + func: _OutputGuardrailFuncSync[TContext_co] + | _OutputGuardrailFuncAsync[TContext_co] + | None = None, + *, + name: str | None = None, +) -> ( + OutputGuardrail[TContext_co] + | Callable[ + [_OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co]], + OutputGuardrail[TContext_co], + ] +): + """ + Decorator that transforms a sync or async function into an `OutputGuardrail`. + It can be used directly (no parentheses) or with keyword args, e.g.: + + @output_guardrail + def my_sync_guardrail(...): ... + + @output_guardrail(name="guardrail_name") + async def my_async_guardrail(...): ... + """ + + def decorator( + f: _OutputGuardrailFuncSync[TContext_co] | _OutputGuardrailFuncAsync[TContext_co], + ) -> OutputGuardrail[TContext_co]: + return OutputGuardrail(guardrail_function=f, name=name) + + if func is not None: + # Decorator was used without parentheses + return decorator(func) + + # Decorator used with keyword arguments + return decorator diff --git a/tests/src/agents/handoffs.py b/tests/src/agents/handoffs.py new file mode 100644 index 0000000..ac15740 --- /dev/null +++ b/tests/src/agents/handoffs.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +import inspect +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Generic, cast, overload + +from pydantic import TypeAdapter +from typing_extensions import TypeAlias, TypeVar + +from . import _utils +from .exceptions import ModelBehaviorError, UserError +from .items import RunItem, TResponseInputItem +from .run_context import RunContextWrapper, TContext +from .strict_schema import ensure_strict_json_schema +from .tracing.spans import SpanError + +if TYPE_CHECKING: + from .agent import Agent + + +# The handoff input type is the type of data passed when the agent is called via a handoff. +THandoffInput = TypeVar("THandoffInput", default=Any) + +OnHandoffWithInput = Callable[[RunContextWrapper[Any], THandoffInput], Any] +OnHandoffWithoutInput = Callable[[RunContextWrapper[Any]], Any] + + +@dataclass(frozen=True) +class HandoffInputData: + input_history: str | tuple[TResponseInputItem, ...] + """ + The input history before `Runner.run()` was called. + """ + + pre_handoff_items: tuple[RunItem, ...] + """ + The items generated before the agent turn where the handoff was invoked. + """ + + new_items: tuple[RunItem, ...] + """ + The new items generated during the current agent turn, including the item that triggered the + handoff and the tool output message representing the response from the handoff output. + """ + + +HandoffInputFilter: TypeAlias = Callable[[HandoffInputData], HandoffInputData] +"""A function that filters the input data passed to the next agent.""" + + +@dataclass +class Handoff(Generic[TContext]): + """A handoff is when an agent delegates a task to another agent. + For example, in a customer support scenario you might have a "triage agent" that determines + which agent should handle the user's request, and sub-agents that specialize in different + areas like billing, account management, etc. + """ + + tool_name: str + """The name of the tool that represents the handoff.""" + + tool_description: str + """The description of the tool that represents the handoff.""" + + input_json_schema: dict[str, Any] + """The JSON schema for the handoff input. Can be empty if the handoff does not take an input. + """ + + on_invoke_handoff: Callable[[RunContextWrapper[Any], str], Awaitable[Agent[TContext]]] + """The function that invokes the handoff. The parameters passed are: + 1. The handoff run context + 2. The arguments from the LLM, as a JSON string. Empty string if input_json_schema is empty. + + Must return an agent. + """ + + agent_name: str + """The name of the agent that is being handed off to.""" + + input_filter: HandoffInputFilter | None = None + """A function that filters the inputs that are passed to the next agent. By default, the new + agent sees the entire conversation history. In some cases, you may want to filter inputs e.g. + to remove older inputs, or remove tools from existing inputs. + + The function will receive the entire conversation history so far, including the input item + that triggered the handoff and a tool call output item representing the handoff tool's output. + + You are free to modify the input history or new items as you see fit. The next agent that + runs will receive `handoff_input_data.all_items`. + + IMPORTANT: in streaming mode, we will not stream anything as a result of this function. The + items generated before will already have been streamed. + """ + + strict_json_schema: bool = True + """Whether the input JSON schema is in strict mode. We **strongly** recommend setting this to + True, as it increases the likelihood of correct JSON input. + """ + + def get_transfer_message(self, agent: Agent[Any]) -> str: + base = f"{{'assistant': '{agent.name}'}}" + return base + + @classmethod + def default_tool_name(cls, agent: Agent[Any]) -> str: + return _utils.transform_string_function_style(f"transfer_to_{agent.name}") + + @classmethod + def default_tool_description(cls, agent: Agent[Any]) -> str: + return ( + f"Handoff to the {agent.name} agent to handle the request. " + f"{agent.handoff_description or ''}" + ) + + +@overload +def handoff( + agent: Agent[TContext], + *, + tool_name_override: str | None = None, + tool_description_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +@overload +def handoff( + agent: Agent[TContext], + *, + on_handoff: OnHandoffWithInput[THandoffInput], + input_type: type[THandoffInput], + tool_description_override: str | None = None, + tool_name_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +@overload +def handoff( + agent: Agent[TContext], + *, + on_handoff: OnHandoffWithoutInput, + tool_description_override: str | None = None, + tool_name_override: str | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: ... + + +def handoff( + agent: Agent[TContext], + tool_name_override: str | None = None, + tool_description_override: str | None = None, + on_handoff: OnHandoffWithInput[THandoffInput] | OnHandoffWithoutInput | None = None, + input_type: type[THandoffInput] | None = None, + input_filter: Callable[[HandoffInputData], HandoffInputData] | None = None, +) -> Handoff[TContext]: + """Create a handoff from an agent. + + Args: + agent: The agent to handoff to, or a function that returns an agent. + tool_name_override: Optional override for the name of the tool that represents the handoff. + tool_description_override: Optional override for the description of the tool that + represents the handoff. + on_handoff: A function that runs when the handoff is invoked. + input_type: the type of the input to the handoff. If provided, the input will be validated + against this type. Only relevant if you pass a function that takes an input. + input_filter: a function that filters the inputs that are passed to the next agent. + """ + assert (on_handoff and input_type) or not (on_handoff and input_type), ( + "You must provide either both on_input and input_type, or neither" + ) + type_adapter: TypeAdapter[Any] | None + if input_type is not None: + assert callable(on_handoff), "on_handoff must be callable" + sig = inspect.signature(on_handoff) + if len(sig.parameters) != 2: + raise UserError("on_handoff must take two arguments: context and input") + + type_adapter = TypeAdapter(input_type) + input_json_schema = type_adapter.json_schema() + else: + type_adapter = None + input_json_schema = {} + if on_handoff is not None: + sig = inspect.signature(on_handoff) + if len(sig.parameters) != 1: + raise UserError("on_handoff must take one argument: context") + + async def _invoke_handoff( + ctx: RunContextWrapper[Any], input_json: str | None = None + ) -> Agent[Any]: + if input_type is not None and type_adapter is not None: + if input_json is None: + _utils.attach_error_to_current_span( + SpanError( + message="Handoff function expected non-null input, but got None", + data={"details": "input_json is None"}, + ) + ) + raise ModelBehaviorError("Handoff function expected non-null input, but got None") + + validated_input = _utils.validate_json( + json_str=input_json, + type_adapter=type_adapter, + partial=False, + ) + input_func = cast(OnHandoffWithInput[THandoffInput], on_handoff) + if inspect.iscoroutinefunction(input_func): + await input_func(ctx, validated_input) + else: + input_func(ctx, validated_input) + elif on_handoff is not None: + no_input_func = cast(OnHandoffWithoutInput, on_handoff) + if inspect.iscoroutinefunction(no_input_func): + await no_input_func(ctx) + else: + no_input_func(ctx) + + return agent + + tool_name = tool_name_override or Handoff.default_tool_name(agent) + tool_description = tool_description_override or Handoff.default_tool_description(agent) + + # Always ensure the input JSON schema is in strict mode + # If there is a need, we can make this configurable in the future + input_json_schema = ensure_strict_json_schema(input_json_schema) + + return Handoff( + tool_name=tool_name, + tool_description=tool_description, + input_json_schema=input_json_schema, + on_invoke_handoff=_invoke_handoff, + input_filter=input_filter, + agent_name=agent.name, + ) diff --git a/tests/src/agents/items.py b/tests/src/agents/items.py new file mode 100644 index 0000000..bbaf49d --- /dev/null +++ b/tests/src/agents/items.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +import abc +import copy +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, Union + +from openai.types.responses import ( + Response, + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionToolCall, + ResponseFunctionWebSearch, + ResponseInputItemParam, + ResponseOutputItem, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, + ResponseStreamEvent, +) +from openai.types.responses.response_input_item_param import ComputerCallOutput, FunctionCallOutput +from openai.types.responses.response_output_item import Reasoning +from pydantic import BaseModel +from typing_extensions import TypeAlias + +from .exceptions import AgentsException, ModelBehaviorError +from .usage import Usage + +if TYPE_CHECKING: + from .agent import Agent + +TResponse = Response +"""A type alias for the Response type from the OpenAI SDK.""" + +TResponseInputItem = ResponseInputItemParam +"""A type alias for the ResponseInputItemParam type from the OpenAI SDK.""" + +TResponseOutputItem = ResponseOutputItem +"""A type alias for the ResponseOutputItem type from the OpenAI SDK.""" + +TResponseStreamEvent = ResponseStreamEvent +"""A type alias for the ResponseStreamEvent type from the OpenAI SDK.""" + +T = TypeVar("T", bound=Union[TResponseOutputItem, TResponseInputItem]) + + +@dataclass +class RunItemBase(Generic[T], abc.ABC): + agent: Agent[Any] + """The agent whose run caused this item to be generated.""" + + raw_item: T + """The raw Responses item from the run. This will always be a either an output item (i.e. + `openai.types.responses.ResponseOutputItem` or an input item + (i.e. `openai.types.responses.ResponseInputItemParam`). + """ + + def to_input_item(self) -> TResponseInputItem: + """Converts this item into an input item suitable for passing to the model.""" + if isinstance(self.raw_item, dict): + # We know that input items are dicts, so we can ignore the type error + return self.raw_item # type: ignore + elif isinstance(self.raw_item, BaseModel): + # All output items are Pydantic models that can be converted to input items. + return self.raw_item.model_dump(exclude_unset=True) # type: ignore + else: + raise AgentsException(f"Unexpected raw item type: {type(self.raw_item)}") + + +@dataclass +class MessageOutputItem(RunItemBase[ResponseOutputMessage]): + """Represents a message from the LLM.""" + + raw_item: ResponseOutputMessage + """The raw response output message.""" + + type: Literal["message_output_item"] = "message_output_item" + + +@dataclass +class HandoffCallItem(RunItemBase[ResponseFunctionToolCall]): + """Represents a tool call for a handoff from one agent to another.""" + + raw_item: ResponseFunctionToolCall + """The raw response function tool call that represents the handoff.""" + + type: Literal["handoff_call_item"] = "handoff_call_item" + + +@dataclass +class HandoffOutputItem(RunItemBase[TResponseInputItem]): + """Represents the output of a handoff.""" + + raw_item: TResponseInputItem + """The raw input item that represents the handoff taking place.""" + + source_agent: Agent[Any] + """The agent that made the handoff.""" + + target_agent: Agent[Any] + """The agent that is being handed off to.""" + + type: Literal["handoff_output_item"] = "handoff_output_item" + + +ToolCallItemTypes: TypeAlias = Union[ + ResponseFunctionToolCall, + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, +] +"""A type that represents a tool call item.""" + + +@dataclass +class ToolCallItem(RunItemBase[ToolCallItemTypes]): + """Represents a tool call e.g. a function call or computer action call.""" + + raw_item: ToolCallItemTypes + """The raw tool call item.""" + + type: Literal["tool_call_item"] = "tool_call_item" + + +@dataclass +class ToolCallOutputItem(RunItemBase[Union[FunctionCallOutput, ComputerCallOutput]]): + """Represents the output of a tool call.""" + + raw_item: FunctionCallOutput | ComputerCallOutput + """The raw item from the model.""" + + output: str + """The output of the tool call.""" + + type: Literal["tool_call_output_item"] = "tool_call_output_item" + + +@dataclass +class ReasoningItem(RunItemBase[Reasoning]): + """Represents a reasoning item.""" + + raw_item: Reasoning + """The raw reasoning item.""" + + type: Literal["reasoning_item"] = "reasoning_item" + + +RunItem: TypeAlias = Union[ + MessageOutputItem, + HandoffCallItem, + HandoffOutputItem, + ToolCallItem, + ToolCallOutputItem, + ReasoningItem, +] +"""An item generated by an agent.""" + + +@dataclass +class ModelResponse: + output: list[TResponseOutputItem] + """A list of outputs (messages, tool calls, etc) generated by the model""" + + usage: Usage + """The usage information for the response.""" + + referenceable_id: str | None + """An ID for the response which can be used to refer to the response in subsequent calls to the + model. Not supported by all model providers. + """ + + def to_input_items(self) -> list[TResponseInputItem]: + """Convert the output into a list of input items suitable for passing to the model.""" + # We happen to know that the shape of the Pydantic output items are the same as the + # equivalent TypedDict input items, so we can just convert each one. + # This is also tested via unit tests. + return [it.model_dump(exclude_unset=True) for it in self.output] # type: ignore + + +class ItemHelpers: + @classmethod + def extract_last_content(cls, message: TResponseOutputItem) -> str: + """Extracts the last text content or refusal from a message.""" + if not isinstance(message, ResponseOutputMessage): + return "" + + last_content = message.content[-1] + if isinstance(last_content, ResponseOutputText): + return last_content.text + elif isinstance(last_content, ResponseOutputRefusal): + return last_content.refusal + else: + raise ModelBehaviorError(f"Unexpected content type: {type(last_content)}") + + @classmethod + def extract_last_text(cls, message: TResponseOutputItem) -> str | None: + """Extracts the last text content from a message, if any. Ignores refusals.""" + if isinstance(message, ResponseOutputMessage): + last_content = message.content[-1] + if isinstance(last_content, ResponseOutputText): + return last_content.text + + return None + + @classmethod + def input_to_new_input_list( + cls, input: str | list[TResponseInputItem] + ) -> list[TResponseInputItem]: + """Converts a string or list of input items into a list of input items.""" + if isinstance(input, str): + return [ + { + "content": input, + "role": "user", + } + ] + return copy.deepcopy(input) + + @classmethod + def text_message_outputs(cls, items: list[RunItem]) -> str: + """Concatenates all the text content from a list of message output items.""" + text = "" + for item in items: + if isinstance(item, MessageOutputItem): + text += cls.text_message_output(item) + return text + + @classmethod + def text_message_output(cls, message: MessageOutputItem) -> str: + """Extracts all the text content from a single message output item.""" + text = "" + for item in message.raw_item.content: + if isinstance(item, ResponseOutputText): + text += item.text + return text + + @classmethod + def tool_call_output_item( + cls, tool_call: ResponseFunctionToolCall, output: str + ) -> FunctionCallOutput: + """Creates a tool call output item from a tool call and its output.""" + return { + "call_id": tool_call.call_id, + "output": output, + "type": "function_call_output", + } diff --git a/tests/src/agents/lifecycle.py b/tests/src/agents/lifecycle.py new file mode 100644 index 0000000..8643248 --- /dev/null +++ b/tests/src/agents/lifecycle.py @@ -0,0 +1,105 @@ +from typing import Any, Generic + +from .agent import Agent +from .run_context import RunContextWrapper, TContext +from .tool import Tool + + +class RunHooks(Generic[TContext]): + """A class that receives callbacks on various lifecycle events in an agent run. Subclass and + override the methods you need. + """ + + async def on_agent_start( + self, context: RunContextWrapper[TContext], agent: Agent[TContext] + ) -> None: + """Called before the agent is invoked. Called each time the current agent changes.""" + pass + + async def on_agent_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + """Called when the agent produces a final output.""" + pass + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + from_agent: Agent[TContext], + to_agent: Agent[TContext], + ) -> None: + """Called when a handoff occurs.""" + pass + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + """Called before a tool is invoked.""" + pass + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + """Called after a tool is invoked.""" + pass + + +class AgentHooks(Generic[TContext]): + """A class that receives callbacks on various lifecycle events for a specific agent. You can + set this on `agent.hooks` to receive events for that specific agent. + + Subclass and override the methods you need. + """ + + async def on_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext]) -> None: + """Called before the agent is invoked. Called each time the running agent is changed to this + agent.""" + pass + + async def on_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + """Called when the agent produces a final output.""" + pass + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + source: Agent[TContext], + ) -> None: + """Called when the agent is being handed off to. The `source` is the agent that is handing + off to this agent.""" + pass + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + """Called before a tool is invoked.""" + pass + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + """Called after a tool is invoked.""" + pass diff --git a/tests/src/agents/logger.py b/tests/src/agents/logger.py new file mode 100644 index 0000000..bd81a82 --- /dev/null +++ b/tests/src/agents/logger.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("openai.agents") diff --git a/tests/src/agents/model_settings.py b/tests/src/agents/model_settings.py new file mode 100644 index 0000000..78cf9a8 --- /dev/null +++ b/tests/src/agents/model_settings.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Literal + + +@dataclass +class ModelSettings: + """Settings to use when calling an LLM. + + This class holds optional model configuration parameters (e.g. temperature, + top_p, penalties, truncation, etc.). + """ + temperature: float | None = None + top_p: float | None = None + frequency_penalty: float | None = None + presence_penalty: float | None = None + tool_choice: Literal["auto", "required", "none"] | str | None = None + parallel_tool_calls: bool | None = False + truncation: Literal["auto", "disabled"] | None = None + + def resolve(self, override: ModelSettings | None) -> ModelSettings: + """Produce a new ModelSettings by overlaying any non-None values from the + override on top of this instance.""" + if override is None: + return self + return ModelSettings( + temperature=override.temperature or self.temperature, + top_p=override.top_p or self.top_p, + frequency_penalty=override.frequency_penalty or self.frequency_penalty, + presence_penalty=override.presence_penalty or self.presence_penalty, + tool_choice=override.tool_choice or self.tool_choice, + parallel_tool_calls=override.parallel_tool_calls or self.parallel_tool_calls, + truncation=override.truncation or self.truncation, + ) diff --git a/tests/src/agents/models/__init__.py b/tests/src/agents/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/src/agents/models/__pycache__/__init__.cpython-311.pyc b/tests/src/agents/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..809c19dff1f3cc9d75ee9c3cbb609c8a65e0043a GIT binary patch literal 1098 zcmaJ=&u`N(6tnt?@fbR| zZA0({dC0>73h@AAQHWRuXJ_;ODNq(eJBLK(5y+=!TLMod9n!a9T<%Lb&e;4`0J$cM`~ zr_fVifh8*}^I&zbHi}cyjpT0r*Vbx^b%>r2+TVbBgxT_e7P?cggXb05{vTpwDpF?_v%wTtT?xvai#m7jK@WlXn;U}OH+u-sCTX8{| literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/__init__.cpython-313.pyc b/tests/src/agents/models/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcd3895dd00249af22c8c23b9a10f2e332ec733b GIT binary patch literal 160 zcmey&%ge<81iS0br-SInAOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=E{m|mnqGJ7` zT>a$ylvMr1^whkP;*{+8f};GA{F2IoRQ=+jWT>!yE>KBMv3`7fW?p7Ve7s&kg`kg57oJ(?RrO5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!H$erR!OQL%ne zu6}ZUN~(ThdTL%taY}Z4K~a85eo19Ps(x`%GE`VU7pNqsSU)~KGcU6wK3=b&@)m~; QP_Q&7)edCvXCP((04`%D4*&oF literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/_openai_shared.cpython-311.pyc b/tests/src/agents/models/__pycache__/_openai_shared.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31d955795bcdbe98d94ba451c69ea55a29e27378 GIT binary patch literal 1651 zcmbVMJx?1!5Z(1Hhp}-W7#VDA$9#my1wPTENJweYkb=&^~|tcJjs`XHsUq!h;)H`6f=4Hz2^<4jEPsU2X#%;YA_ru8iAO!FC-<@9Ws z&wXNtSzhMzU}t!R7s2NE3SR`9=T%+;JIiZ)32cEI`=nX_g=sWtly^MO51ioC_Y_Wd zRo`pB>k4o8?cOk_0@*lk?D?LEiZ&O=PR|W&A9kHn`%LtQ3n_X+EIlN)-BaNO?<02P z`)*XWdrH_+s4hel%0B8R{Kx>8kpVFyQ;MJ`y@+AxHpN^=4aXoNy+e&J$vJUAwtaF% z_pYckVXaAJ9*ja_V|ZX4C?S<4J67B0!g5X^6~)i&uJi*x=y#JPD-bGBR;y(x*&flX z4t(M&D>z`MI0{xM5U9-ftO%&4t^>Na6!K3>mjS_?;mjSeU8nNY(^tTkEFt_6pgcivKqcUi0g$ybI`+o`10ACM2^I4R$?c*N2 zVqx17UMz{s_>Uv@i%&J0v+dO15Ag8jBucJdlEkW(SXC15R?8~!WDm3$W_RGF_FVB= zzJgsWR`nBLU{FeL$XfWQ-;#Pb-f!17LsAS0Aogprd9$@0l2S+jv0szz8|0MJoPn94 n^#K`&9HlP?WE`>?x`Dhj0B;R>X#ided1(ONOXN)ieW$+xn(cUU literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/_openai_shared.cpython-313.pyc b/tests/src/agents/models/__pycache__/_openai_shared.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad69a8718fff0911ae7be93a093101d1ce411501 GIT binary patch literal 1434 zcmbVM&rcIU6rSlWrL>e^#X=ieODUL|Kttlun2>l83?4=z4>Oo_ujmjZaf}kI6i;sMefDQw(YoO zch|8MnQN+LS6(!vz4mPLBC1@-z0Ykrw)7HKRqm9Vb=PumwY+QX$=1bGA3Y!@ZxSo@ zU1_^7J#*V}>R!rfDrtdK4OEoM+HM8>3a6NQgD{qjcOwW6*%musHFm@|k2r+UgP|H2 zfFfGXdtTa75>yHHOBJUoOXV60s_LHA0LOLQRwHng6jXYvrF~@9RVheEy)A>Rt~6R+ zQb{+6DOg1>V8Nwz_+PgsB9%I;NWN2|@AcMTcF)wOaJ55xOmId@}d13##`{&Zr^TsW!@6$jY z^%zj$^%DVVTirrH7phANIYKu&A@UfXV6!ft0DWlGOlkwC-v;OWJInuM*-n0`%@Utp vov@|T<<&Mzwr3xnu+>v?r@lNw_jx$Pv*%IH@1I9Pyg(>VXyp&0MP0tXX9Pi| literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/_openai_shared.cpython-39.pyc b/tests/src/agents/models/__pycache__/_openai_shared.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb45fa3dd723a96090da5c3504c8b904bc09239d GIT binary patch literal 1313 zcmbVLy>8nu5GE!0-?AM$apKl3($P!x0zuKBT?!QLkY@2hC_2V9B1;A-4-PW+89Mm| z{2E>R23$M!6*~1!iX6Fc0i!fbeaAcU`|f!3UAN9~eEa@m`rc;jCmF?oYwRs!T(1#a zGho0&Hs=%030t8xwQjlf&-m3 zha>2sRf8USkI{PqeYERPPQ9mahE@Y==pCT<89Yb3i8k#W!V9!qu&>zYqMPDoR8y3nw|i8>o3DZ#OU>B;!dvayM^#Pu>W^p z5YA+jd^XNa9EWBgmRbrW^#U2C7B{Or-q=WI9AX<;#>VqaS;=InqJWdy$3d9>@2JpK z1P{!|@?NnGA8)vFaAm|5UDSrD7Jhzz^scp3+Ea5ch(UV(6!mDh6AKk5ak5(EOP*H2 zqt}~bWT?G7Tm8jX5?(J>rmtm^OU}Khl?b(k6SGtYXVlQZPe3chv6W6j6O^r*2$V|` zsVz)xG);fHmyUcH9al%Rjd~BuR?e!OaUWE+lphOMq64_<(qT_RQVoMWDQ7%AjLx$@ zMto?xw6DOD@?}(e(#@#Owp3i5AjV|fomTpfoGWw=SN&ZzsgNP%ymNk`Rfk(GmLgq; zrhU3g^py6H(*76Qt1TN>1H_}2sS9zt#HS&JP}Fp$oW07l4bnGY+CmiGHpbBfE6OsYH literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/fake_id.cpython-313.pyc b/tests/src/agents/models/__pycache__/fake_id.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b15d6e475ce5709eb59b87ea08f9ab24e4c54f84 GIT binary patch literal 210 zcmey&%ge<81b<8prSmW{FgylvU;xMmgU=j5YAQo8LlHwdqbAcW?)dn$#O&1g%#`?e zKTXD4f^Lr9uJJ*x!2$k$!LGsao-QjHK7(}La@7wlPAw|dFUr+V&QD3zPfSnED=ALN zjxQ+6FUc>dEPx5=7Z)W%W%YA`DszhUp;qV>RNmsS0g9F8q}mm+15E`vt5^U?d|+l| VWV|7wc!5Fi0~0eNOA#wj5CB-EIcER> literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/fake_id.cpython-39.pyc b/tests/src/agents/models/__pycache__/fake_id.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77f6bef514902cc7e2ed196112ada07b31deec93 GIT binary patch literal 195 zcmYe~<>g`kfve7#@Q-$N(tD-~hzM96%z4A&Mb|F_=MN`@jPpdK*s%T+(LIJKx)zbIEfIX@*;KQTQu zucSC7JHDVOza+n;vH&KeUtE+7mDSG$s>~_YhuWi8Pu>y~eXYRQ=k$?0T7)PAU197gGnkQ8>y1La2*vt1jMqz0lC)f7qCN?eoNZ*k z8e3sy7-`8jrs~r$&Djh)$BePF%qVQ6*Ak(`Vtl`yjy~YDlq{m;_(p1N?ubZ?4kgP{ zvV@Wo|3q@8ovKXk)B1`Mj#-}P2Ug(vo>;~2Vohv&_DbM@75G4e_3ypEXf7--E;LqF zm-bW9vdLQ8@q)X(V5=7x_5-JDp=fs5@|u3DRY`}%JATvYtT|!}^*Z6ydV;QaxB}I8 z!&5_=5rAWL7ayWV)S|BYeup-eDhwwGT(2$c_`LE(M&P3it|NTPHW(-dLq96$!xX5q z=ts~Hs*pjLGqkhp2P$L{%ScQ&vXE=*m3&x`o2-Ln<0ku)s18W^KE?_IbQ-e>OCDmO zXOscC>?ym)K1euhEw*V$K;-%|V{fo$%4#JACHxNcTPsHG=IzB>&+wSS>9hoC=i!a{ zd%^)R58Zhi?`__q%N5NJ%`NZ)KiJ-K<^|X>dA>_mE9PAfoz${yr@FNrj=92Yy29G* zIL&b8=%K1dIIrMtVj96?wp*J1#Ydl&=XUUaY3{i`H$d`#;Iu&Mlk;X5SLJcxG!mn; zLK)Gp;Cgna*L2LL6J(*t0q@r(~Kf7evr;J;(D+6HcKzc$KU>6`O5-cV&I~ z?!9&MR&BioGsJ(5_@_-%P|Db*6$IdJ_5w$sf-jQ*870psJuU%7mG}yRCwS`kk~hX$gl9W0$vl;I`F4 zFSNPk@!PlWRBippIVi1uLMcbbJI^?ty{%kLx0FiC`W0r+k`r)?LtJQh3;Zq*a2xcx z)2Zh3HE4_Ld5(|R7Fc#n*AoHsY+3NGbFwI6piPGi9{9W`oT^;tQpt)$CCXjYBS>Vp z(HySkcefm`w!)K2C+KWlD~OaxdrT2mvq+ny@q{+AXMlk$A}NV->!Ia#D7{Dajdc66 z44XpM;b>7r;t^>?^Mj~H$I6qhm+B9q@`#!vgxkK?a@##@5!g2HlGkMV#rI5C+Uj*W zkqd^dspeN@hCI{ZOX$jQ`UHQ3n!Mm}eDt(L%yabt~6~Y zFq7~Ir|!tGI7*|$6f{2UV^!?G#d5{J2^BwooKlsMNTgDYS*Q$%OHqgsxQoQ7d;LEM z|EGem=24c)8HW3dDceZ3N&+_5Lcy0(_v_xjza4g`39}dB8X!v zs_xV&Rx1;53EzLu;}ZT6U-dl)szjKL2pv}h9}H)t;oNi|=1~ZW1TM?ris`Do_b1Sa< zIJ!z{Y!5OsctYT=Nb=F8ent~O$M-*^381&drLf?e+nffqYTl*^AbC!x!H?`{1n5#_ zKU{XC@k@>z2&3LedZ{Q3yEpYmYzZedGPyQsT{zat)|EfhD9E~`*W{?LNM2aJL*^tm zRvj`nQCzP1Q9LR}r9g#6WJC$a2_u(QawjDPyi5FV6SzU(Jpv~sz)fNhAa{w^5j$@V)PCmN!vn43U7 zG0fmE74#x#^ihyMV$7&z1C_w2L{N*yK2IZ*EH*&B7siOHQkw@F^bTuHs4n7$(a)!J z?dF2K8F{Q@2_Ne3;ML=#|1Nw7HBh98?<44^6-C)&=RRlo-?OnjR@r0xk8Ey_&HjNk zc35MNmH(8U*i)+qnQ>+A<13$D`{ddIL-gR>Ri*gTg`Y3|Z0Ud@dJrfjrSS2>r%RtK K{e>YaZ|fiWsxV9d literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/interface.cpython-39.pyc b/tests/src/agents/models/__pycache__/interface.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc80baca175b78d6d2865fe074e62ca08ead1ab9 GIT binary patch literal 3637 zcmeGe+in|0bZ@pdj_q9ACI!kspo>qW?d;( z-`Y2Rp+Arx;0Jg_;sfTDr~X2pIA_M|OX`O_BR0zMnKNh3oH_TPUauPP{q^nNN8i;9 z;~!j1e-13R4Z{?F074CF#zta#rir)}Tb`wUZO?{ZJ9d(iS5kXUTuv%p1@ICr$8NIV zEhxMaSCd6=QQ>Y}OX^-dX?Trf$y-vig?Kqx@m5TORrm`yTa8zfHE+!{?i+NG)?OL3 z#_W^zm$r8WD0SKZN`qM^SAlX3C`)u1D9a9tAAwDQXF%A=J_7hqMPRj|RLtf?!yJP6?m zZi1jU2zUvAbb4BkGH3aKry=eXAL3|<9aI3L<%N+OC+0IS?YY@$TD%T4>E7ErXy1Lf zd+!`xC0N`)H~1CUc)PvzR4^{Kc(N6Oowforrl8OKQ=a8nes;>X1P@2#tprUewxSfA z(how`Iz5w%k??6Gf?mw%dmvJvHikLen0`@fis`cI^A(U&JfO1MQTp>WAS%^0xC%>I zi_$P2Q0CJh4{iXZ&|z~{hbgX3)0M98r$NGeUsin|o;HXPulfFOgCN#Bd=;ppn&wCmq%cia3L?r$PMb4~$NOxYe^p&a9yj*Wi}ThZSMBXr(2da(=0bQm$` zq(m)hW8h#AQ3xGceeJ50b6%QWc@9TiaWv@CE99uqWlS|$QU)2`>K-%F3j`x$SCW3l zNf^X2#2*P#^6=q)>upu!oV*~NEM>zR8jmB9|1!>(aSk1Fs87bQs<$Cr4~HA?BxWJ1 zuDC`idZ?_yT{sRtSrcahQqqr7xNZ=tVjxQqrmAd!k67%BQ69=t4)+LI&s6UFLM6X+ zk9Fz`S;o6bplzib&QU$1j2E0>cYoIK7C=6$%~c{MMWxUee;malAbh$5(V5 zU*LeA%CDm&90U0K0A$7YX%<3PwS!(L8$l0_1Yw@A{5YfhGvxam!6bncrkdvmuz|@| zG*8PhZ(Mgwj`R@zXTI?yj8B>0JYQSbK2v1(8Iea7BdG68mYKWzVpm4o-<;7NmDZaI(;~hU2WF-qW;XHBsFSce!d< zkj~=|aco!@a(?)`?pEsPME1C;yxXHDm*}#K*I^Ki8w{3es85%vp-R@pstE&|s+wPc zwo`6Zb#Va;pE}ipU0oEd;kA8yc;N(992HNeS-B8Syoo};K=37k7J_-9a~n2(g?~{7 z0CQIf!Uk#0O&s<51DK*=)Z=JXB~Cgl9VBuo%;K2op`!%>%WYtgWtf6-D8~?!H8mjn zy1Q`{uiHQcH%qEtt5Jq8;1-T_(!n~!7jdl`2o6fp*@X+$Qb)U9)D^EciFq;ny*1_Ug*&oqqwrdaWz~ literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/map.cpython-313.pyc b/tests/src/agents/models/__pycache__/map.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f648281cc1b2aa9998e99c33d74ea97a0a86d0bf GIT binary patch literal 760 zcmZ8eziZo25WXkLvSeyEg9#+0U_zkL5USo$@A(G&Bc> zW&Wg>z)}+PIPCLr+neyD=7tduyrC;DJmh_gC8aD$VDlAanlFzoy!a@bL}MNg!$1_- z`Z9aMMMP1)j3gxi`WCOrTtlyj!-=wjE3geua~!ZP+s(0O z0`y8nQJ03Z&vu|!{y$q-l2rM`7c{_NiOj8}J|&{WoreVI0P@s0i5t{XA=DGK4+xqP=LARHcsP zsKT=m-X~)DxOhzF628^9zt#^w*AKtco0rGG4Y;>)r-J$5-LqR`FIBaYey2li_hxik z-A~KpF6cLCTa^S*4tLqBISgWs=bq0|PAgf@7wywN{lELn8cx>o8nu5GE;EmJKWL5M(Gabk$P5KoKPHk_>f#1X;8QjClTOMM*f)g8^p=y5k=d$J<}?C(hV!m%KNRl4CmNl$>IU z7fkb%Ymo}>a#48NPyIIbik=SAppE^aufsIt3^@3zcP~_+`rrL@pu$TQ51wfki$yc| zQYk2=QbV(sRW+1ynVE$|P%&?MZIOZx=Ttd4n=GWARJty}F00aX)%|Vt0!&RP>~PTx zwVR!lZg_xTS6Jq5ZD)c4CvQT>d?aVt5B`(g@D0ZUD>fwdj$bnCbx|y)j#WE0_R;;@ z9L~XJD+3vJ>$B^1reG#lg~fncutzQy*mv#Gf2|J;M}+=4Nv;f_Nl+&_aZcnMN^8`& ztj5Y#c3pQ+VldxTla_`_w5&hX>n3tMwt}ncuWpxVAC`x{cpZuG1@{P>#>0l86TW3X zSe6!WCUZca!1Q4E1UcPw{_mz+3F&xO{Ht^;&&_RRymJv@P57DktO_`Cmu^CGV?J`9 F_XZ?frB(m{ literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc b/tests/src/agents/models/__pycache__/openai_chatcompletions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88538e8feca83b180f6366dad087b02616d72271 GIT binary patch literal 34087 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 Ngg<-tPXY~h+~4TYn|uHO literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/openai_provider.cpython-39.pyc b/tests/src/agents/models/__pycache__/openai_provider.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d52a6bb6445c8f170015b870fdff795ec5ec7bdd GIT binary patch literal 2042 zcmZuy&2Jk;6rb5|uh)*_geDYJtpF8#z?Kjfq^N3?v_RFS2*_nGqm5^sINc93I|ghm z97wLcam=AP^4D;mE2sViAP_3=&Dz0fvnxNpH#7U@z2EQ6o4D2T2|S}uen0-xBjgV> zRv!zD`-IT)S5OWKC!D4vqYtZ)@NcGOW<^$JM>dRG+)ka$ja;oesh2gPM&?Jp zw%xRu1yP`NFKuP*s114}TH}7w;msZJ8n5#J^cLuCzIH)*hp(SoQTKDgyL{uE@QuVj zGouY|d_#JhFEQR8Rn0ih3mMCCk(UQOLp68HSw4I^N%Gw<)z*G8il?d6V_!%)`Ef5D zC%Jq{>yO*4fQ9jxl_#-CIDBqDDR`2qmj3%n6yJ||B4C@_wa2}aSnd_sBu(^&dKJuW z*0WzHL~txI5;d5Q4T~l7y<%=_!wX+`7<^Vg)HQ3k_<{QTnrB~1gRV>z=I_krv7B6x;2uk zIc$Lc(g8sXk2+MWgZ4(~4H&$Sp+5vAAra*SYK*SD#)ZR8cr7h$?r`_Q`s%RRIMp27fXicK9IVgY~Ed|x#d{%s3_7Urxr5Xa|J>nHc`BV;ueZ66x%4;Ack0( z_#j~J;-@+N8H~yVPO12)0(gMi?Fx?Jl$7BkIG|_r7h0JWVI;pR=?mkMOdd(IqLUr4 ztV>cEl~vi&K7*t7+{SekIVGpgsr$m?<|T!ZM&)S#lga_h&wco^315QFv3{(B`Mv!j z|4@bt%EJYiVIjgLoZ)yB&Wh>Pl561h>=g{I(pSyZl5d04e*IN{SY*9Gxr@^v5ES+H zaUvP75BBo>*3=0T=WpFG4}|Nw)8H%Xq$s5V24f-1AI9<|>acOS^reeiwb?bk)w}5R z+7LUMEyL)!$|{p|w36IS1YaFD(ZN7H#>V+rGWHHwWe9@!9_>=&m1TN{Pkn0W*ZRlv zy40hdG2gmg(EX(cfu#cN%h$oPJkFB3oUkPvHOYvM8fKlAnP7Qz}AfRH&Jp zK8G$VtyQaMV1Kps4!VAU%mBQI5CvW+0uvErD4hMe3StjB}ZYQ9MjE69O((D8za- z8^Walg}l5pG(b^jF?rQdY6KAtwZ_=Wtp+jZo{fD|2Y6Fn%G;^oxQ7j-#2(Xzb_;;g O4h?8v1ZH5j-Twgj8xVv5 literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/openai_responses.cpython-313.pyc b/tests/src/agents/models/__pycache__/openai_responses.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e53854728eba07f5723cde7ac3b18f1f22bbdde8 GIT binary patch literal 15413 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/models/__pycache__/openai_responses.cpython-39.pyc b/tests/src/agents/models/__pycache__/openai_responses.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0865febff7a7f70ed00ced43f15657b9ba51c613 GIT binary patch literal 9519 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 None: + global _default_openai_key + _default_openai_key = key + + +def get_default_openai_key() -> str | None: + return _default_openai_key + + +def set_default_openai_client(client: AsyncOpenAI) -> None: + global _default_openai_client + _default_openai_client = client + + +def get_default_openai_client() -> AsyncOpenAI | None: + return _default_openai_client + + +def set_use_responses_by_default(use_responses: bool) -> None: + global _use_responses_by_default + _use_responses_by_default = use_responses + + +def get_use_responses_by_default() -> bool: + return _use_responses_by_default diff --git a/tests/src/agents/models/fake_id.py b/tests/src/agents/models/fake_id.py new file mode 100644 index 0000000..0565b0a --- /dev/null +++ b/tests/src/agents/models/fake_id.py @@ -0,0 +1,5 @@ +FAKE_RESPONSES_ID = "__fake_id__" +"""This is a placeholder ID used to fill in the `id` field in Responses API related objects. It's +useful when you're creating Responses objects from non-Responses APIs, e.g. the OpenAI Chat +Completions API or other LLM providers. +""" diff --git a/tests/src/agents/models/interface.py b/tests/src/agents/models/interface.py new file mode 100644 index 0000000..e9a8700 --- /dev/null +++ b/tests/src/agents/models/interface.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import abc +import enum +from collections.abc import AsyncIterator +from typing import TYPE_CHECKING + +from ..agent_output import AgentOutputSchema +from ..handoffs import Handoff +from ..items import ModelResponse, TResponseInputItem, TResponseStreamEvent +from ..tool import Tool + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +class ModelTracing(enum.Enum): + DISABLED = 0 + """Tracing is disabled entirely.""" + + ENABLED = 1 + """Tracing is enabled, and all data is included.""" + + ENABLED_WITHOUT_DATA = 2 + """Tracing is enabled, but inputs/outputs are not included.""" + + def is_disabled(self) -> bool: + return self == ModelTracing.DISABLED + + def include_data(self) -> bool: + return self == ModelTracing.ENABLED + + +class Model(abc.ABC): + """The base interface for calling an LLM.""" + + @abc.abstractmethod + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + """Get a response from the model. + + Args: + system_instructions: The system instructions to use. + input: The input items to the model, in OpenAI Responses format. + model_settings: The model settings to use. + tools: The tools available to the model. + output_schema: The output schema to use. + handoffs: The handoffs available to the model. + tracing: Tracing configuration. + + Returns: + The full model response. + """ + pass + + @abc.abstractmethod + def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[TResponseStreamEvent]: + """Stream a response from the model. + + Args: + system_instructions: The system instructions to use. + input: The input items to the model, in OpenAI Responses format. + model_settings: The model settings to use. + tools: The tools available to the model. + output_schema: The output schema to use. + handoffs: The handoffs available to the model. + tracing: Tracing configuration. + + Returns: + An iterator of response stream events, in OpenAI Responses format. + """ + pass + + +class ModelProvider(abc.ABC): + """The base interface for a model provider. + + Model provider is responsible for looking up Models by name. + """ + + @abc.abstractmethod + def get_model(self, model_name: str | None) -> Model: + """Get a model by name. + + Args: + model_name: The name of the model to get. + + Returns: + The model. + """ diff --git a/tests/src/agents/models/openai_chatcompletions.py b/tests/src/agents/models/openai_chatcompletions.py new file mode 100644 index 0000000..a7340d0 --- /dev/null +++ b/tests/src/agents/models/openai_chatcompletions.py @@ -0,0 +1,952 @@ +from __future__ import annotations + +import dataclasses +import json +import time +from collections.abc import AsyncIterator, Iterable +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, Literal, cast, overload + +from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, NotGiven +from openai.types import ChatModel +from openai.types.chat import ( + ChatCompletion, + ChatCompletionAssistantMessageParam, + ChatCompletionChunk, + ChatCompletionContentPartImageParam, + ChatCompletionContentPartParam, + ChatCompletionContentPartTextParam, + ChatCompletionDeveloperMessageParam, + ChatCompletionMessage, + ChatCompletionMessageParam, + ChatCompletionMessageToolCallParam, + ChatCompletionSystemMessageParam, + ChatCompletionToolChoiceOptionParam, + ChatCompletionToolMessageParam, + ChatCompletionUserMessageParam, +) +from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam +from openai.types.chat.completion_create_params import ResponseFormat +from openai.types.completion_usage import CompletionUsage +from openai.types.responses import ( + EasyInputMessageParam, + Response, + ResponseCompletedEvent, + ResponseContentPartAddedEvent, + ResponseContentPartDoneEvent, + ResponseCreatedEvent, + ResponseFileSearchToolCallParam, + ResponseFunctionCallArgumentsDeltaEvent, + ResponseFunctionToolCall, + ResponseFunctionToolCallParam, + ResponseInputContentParam, + ResponseInputImageParam, + ResponseInputTextParam, + ResponseOutputItem, + ResponseOutputItemAddedEvent, + ResponseOutputItemDoneEvent, + ResponseOutputMessage, + ResponseOutputMessageParam, + ResponseOutputRefusal, + ResponseOutputText, + ResponseRefusalDeltaEvent, + ResponseTextDeltaEvent, +) +from openai.types.responses.response_input_param import FunctionCallOutput, ItemReference, Message + +from .. import _debug +from ..agent_output import AgentOutputSchema +from ..exceptions import AgentsException, UserError +from ..handoffs import Handoff +from ..items import ModelResponse, TResponseInputItem, TResponseOutputItem, TResponseStreamEvent +from ..logger import logger +from ..tool import FunctionTool, Tool +from ..tracing import generation_span +from ..tracing.span_data import GenerationSpanData +from ..tracing.spans import Span +from ..usage import Usage +from ..version import __version__ +from .fake_id import FAKE_RESPONSES_ID +from .interface import Model, ModelTracing + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +_USER_AGENT = f"Agents/Python {__version__}" +_HEADERS = {"User-Agent": _USER_AGENT} + + +@dataclass +class _StreamingState: + started: bool = False + text_content_index_and_output: tuple[int, ResponseOutputText] | None = None + refusal_content_index_and_output: tuple[int, ResponseOutputRefusal] | None = None + function_calls: dict[int, ResponseFunctionToolCall] = field(default_factory=dict) + + +class OpenAIChatCompletionsModel(Model): + def __init__( + self, + model: str | ChatModel, + openai_client: AsyncOpenAI, + ) -> None: + self.model = model + self._client = openai_client + + def _non_null_or_not_given(self, value: Any) -> Any: + return value if value is not None else NOT_GIVEN + + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + with generation_span( + model=str(self.model), + model_config=dataclasses.asdict(model_settings) + | {"base_url": str(self._client.base_url)}, + disabled=tracing.is_disabled(), + ) as span_generation: + response = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + span_generation, + tracing, + stream=False, + ) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Received model response") + else: + logger.debug( + f"LLM resp:\n{json.dumps(response.choices[0].message.model_dump(), indent=2)}\n" + ) + + usage = ( + Usage( + requests=1, + input_tokens=response.usage.prompt_tokens, + output_tokens=response.usage.completion_tokens, + total_tokens=response.usage.total_tokens, + ) + if response.usage + else Usage() + ) + if tracing.include_data(): + span_generation.span_data.output = [response.choices[0].message.model_dump()] + span_generation.span_data.usage = { + "input_tokens": usage.input_tokens, + "output_tokens": usage.output_tokens, + } + + items = _Converter.message_to_output_items(response.choices[0].message) + + return ModelResponse( + output=items, + usage=usage, + referenceable_id=None, + ) + + async def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[TResponseStreamEvent]: + """ + Yields a partial message as it is generated, as well as the usage information. + """ + with generation_span( + model=str(self.model), + model_config=dataclasses.asdict(model_settings) + | {"base_url": str(self._client.base_url)}, + disabled=tracing.is_disabled(), + ) as span_generation: + response, stream = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + span_generation, + tracing, + stream=True, + ) + + usage: CompletionUsage | None = None + state = _StreamingState() + + async for chunk in stream: + if not state.started: + state.started = True + yield ResponseCreatedEvent( + response=response, + type="response.created", + ) + + # The usage is only available in the last chunk + usage = chunk.usage + + if not chunk.choices or not chunk.choices[0].delta: + continue + + delta = chunk.choices[0].delta + + # Handle text + if delta.content: + if not state.text_content_index_and_output: + # Initialize a content tracker for streaming text + state.text_content_index_and_output = ( + 0 if not state.refusal_content_index_and_output else 1, + ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + ) + # Start a new assistant message stream + assistant_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="in_progress", + ) + # Notify consumers of the start of a new output message + first content part + yield ResponseOutputItemAddedEvent( + item=assistant_item, + output_index=0, + type="response.output_item.added", + ) + yield ResponseContentPartAddedEvent( + content_index=state.text_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + type="response.content_part.added", + ) + # Emit the delta for this segment of content + yield ResponseTextDeltaEvent( + content_index=state.text_content_index_and_output[0], + delta=delta.content, + item_id=FAKE_RESPONSES_ID, + output_index=0, + type="response.output_text.delta", + ) + # Accumulate the text into the response part + state.text_content_index_and_output[1].text += delta.content + + # Handle refusals (model declines to answer) + if delta.refusal: + if not state.refusal_content_index_and_output: + # Initialize a content tracker for streaming refusal text + state.refusal_content_index_and_output = ( + 0 if not state.text_content_index_and_output else 1, + ResponseOutputRefusal(refusal="", type="refusal"), + ) + # Start a new assistant message if one doesn't exist yet (in-progress) + assistant_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="in_progress", + ) + # Notify downstream that assistant message + first content part are starting + yield ResponseOutputItemAddedEvent( + item=assistant_item, + output_index=0, + type="response.output_item.added", + ) + yield ResponseContentPartAddedEvent( + content_index=state.refusal_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=ResponseOutputText( + text="", + type="output_text", + annotations=[], + ), + type="response.content_part.added", + ) + # Emit the delta for this segment of refusal + yield ResponseRefusalDeltaEvent( + content_index=state.refusal_content_index_and_output[0], + delta=delta.refusal, + item_id=FAKE_RESPONSES_ID, + output_index=0, + type="response.refusal.delta", + ) + # Accumulate the refusal string in the output part + state.refusal_content_index_and_output[1].refusal += delta.refusal + + # Handle tool calls + # Because we don't know the name of the function until the end of the stream, we'll + # save everything and yield events at the end + if delta.tool_calls: + for tc_delta in delta.tool_calls: + if tc_delta.index not in state.function_calls: + state.function_calls[tc_delta.index] = ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + arguments="", + name="", + type="function_call", + call_id="", + ) + tc_function = tc_delta.function + + state.function_calls[tc_delta.index].arguments += ( + tc_function.arguments if tc_function else "" + ) or "" + state.function_calls[tc_delta.index].name += ( + tc_function.name if tc_function else "" + ) or "" + state.function_calls[tc_delta.index].call_id += tc_delta.id or "" + + function_call_starting_index = 0 + if state.text_content_index_and_output: + function_call_starting_index += 1 + # Send end event for this content part + yield ResponseContentPartDoneEvent( + content_index=state.text_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=state.text_content_index_and_output[1], + type="response.content_part.done", + ) + + if state.refusal_content_index_and_output: + function_call_starting_index += 1 + # Send end event for this content part + yield ResponseContentPartDoneEvent( + content_index=state.refusal_content_index_and_output[0], + item_id=FAKE_RESPONSES_ID, + output_index=0, + part=state.refusal_content_index_and_output[1], + type="response.content_part.done", + ) + + # Actually send events for the function calls + for function_call in state.function_calls.values(): + # First, a ResponseOutputItemAdded for the function call + yield ResponseOutputItemAddedEvent( + item=ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=function_call.call_id, + arguments=function_call.arguments, + name=function_call.name, + type="function_call", + ), + output_index=function_call_starting_index, + type="response.output_item.added", + ) + # Then, yield the args + yield ResponseFunctionCallArgumentsDeltaEvent( + delta=function_call.arguments, + item_id=FAKE_RESPONSES_ID, + output_index=function_call_starting_index, + type="response.function_call_arguments.delta", + ) + # Finally, the ResponseOutputItemDone + yield ResponseOutputItemDoneEvent( + item=ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=function_call.call_id, + arguments=function_call.arguments, + name=function_call.name, + type="function_call", + ), + output_index=function_call_starting_index, + type="response.output_item.done", + ) + + # Finally, send the Response completed event + outputs: list[ResponseOutputItem] = [] + if state.text_content_index_and_output or state.refusal_content_index_and_output: + assistant_msg = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="completed", + ) + if state.text_content_index_and_output: + assistant_msg.content.append(state.text_content_index_and_output[1]) + if state.refusal_content_index_and_output: + assistant_msg.content.append(state.refusal_content_index_and_output[1]) + outputs.append(assistant_msg) + + # send a ResponseOutputItemDone for the assistant message + yield ResponseOutputItemDoneEvent( + item=assistant_msg, + output_index=0, + type="response.output_item.done", + ) + + for function_call in state.function_calls.values(): + outputs.append(function_call) + + final_response = response.model_copy(update={"output": outputs, "usage": usage}) + + yield ResponseCompletedEvent( + response=final_response, + type="response.completed", + ) + if tracing.include_data(): + span_generation.span_data.output = [final_response.model_dump()] + + if usage: + span_generation.span_data.usage = { + "input_tokens": usage.prompt_tokens, + "output_tokens": usage.completion_tokens, + } + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: Literal[True], + ) -> tuple[Response, AsyncStream[ChatCompletionChunk]]: ... + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: Literal[False], + ) -> ChatCompletion: ... + + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + span: Span[GenerationSpanData], + tracing: ModelTracing, + stream: bool = False, + ) -> ChatCompletion | tuple[Response, AsyncStream[ChatCompletionChunk]]: + converted_messages = _Converter.items_to_messages(input) + + if system_instructions: + converted_messages.insert( + 0, + { + "content": system_instructions, + "role": "system", + }, + ) + if tracing.include_data(): + span.span_data.input = converted_messages + + parallel_tool_calls = ( + True if model_settings.parallel_tool_calls and tools and len(tools) > 0 else NOT_GIVEN + ) + tool_choice = _Converter.convert_tool_choice(model_settings.tool_choice) + response_format = _Converter.convert_response_format(output_schema) + + converted_tools = [ToolConverter.to_openai(tool) for tool in tools] if tools else [] + + for handoff in handoffs: + converted_tools.append(ToolConverter.convert_handoff_tool(handoff)) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Calling LLM") + else: + logger.debug( + f"{json.dumps(converted_messages, indent=2)}\n" + f"Tools:\n{json.dumps(converted_tools, indent=2)}\n" + f"Stream: {stream}\n" + f"Tool choice: {tool_choice}\n" + f"Response format: {response_format}\n" + ) + + ret = await self._get_client().chat.completions.create( + model=self.model, + messages=converted_messages, + tools=converted_tools or NOT_GIVEN, + temperature=self._non_null_or_not_given(model_settings.temperature), + top_p=self._non_null_or_not_given(model_settings.top_p), + frequency_penalty=self._non_null_or_not_given(model_settings.frequency_penalty), + presence_penalty=self._non_null_or_not_given(model_settings.presence_penalty), + tool_choice=tool_choice, + response_format=response_format, + parallel_tool_calls=parallel_tool_calls, + stream=stream, + stream_options={"include_usage": True} if stream else NOT_GIVEN, + extra_headers=_HEADERS, + ) + + if isinstance(ret, ChatCompletion): + return ret + + response = Response( + id=FAKE_RESPONSES_ID, + created_at=time.time(), + model=self.model, + object="response", + output=[], + tool_choice=cast(Literal["auto", "required", "none"], tool_choice) + if tool_choice != NOT_GIVEN + else "auto", + top_p=model_settings.top_p, + temperature=model_settings.temperature, + tools=[], + parallel_tool_calls=parallel_tool_calls or False, + ) + return response, ret + + def _get_client(self) -> AsyncOpenAI: + if self._client is None: + self._client = AsyncOpenAI() + return self._client + + +class _Converter: + @classmethod + def convert_tool_choice( + cls, tool_choice: Literal["auto", "required", "none"] | str | None + ) -> ChatCompletionToolChoiceOptionParam | NotGiven: + if tool_choice is None: + return NOT_GIVEN + elif tool_choice == "auto": + return "auto" + elif tool_choice == "required": + return "required" + elif tool_choice == "none": + return "none" + else: + return { + "type": "function", + "function": { + "name": tool_choice, + }, + } + + @classmethod + def convert_response_format( + cls, final_output_schema: AgentOutputSchema | None + ) -> ResponseFormat | NotGiven: + if not final_output_schema or final_output_schema.is_plain_text(): + return NOT_GIVEN + + return { + "type": "json_schema", + "json_schema": { + "name": "final_output", + "strict": final_output_schema.strict_json_schema, + "schema": final_output_schema.json_schema(), + }, + } + + @classmethod + def message_to_output_items(cls, message: ChatCompletionMessage) -> list[TResponseOutputItem]: + items: list[TResponseOutputItem] = [] + + message_item = ResponseOutputMessage( + id=FAKE_RESPONSES_ID, + content=[], + role="assistant", + type="message", + status="completed", + ) + if message.content: + message_item.content.append( + ResponseOutputText(text=message.content, type="output_text", annotations=[]) + ) + if message.refusal: + message_item.content.append( + ResponseOutputRefusal(refusal=message.refusal, type="refusal") + ) + if message.audio: + raise AgentsException("Audio is not currently supported") + + if message_item.content: + items.append(message_item) + + if message.tool_calls: + for tool_call in message.tool_calls: + items.append( + ResponseFunctionToolCall( + id=FAKE_RESPONSES_ID, + call_id=tool_call.id, + arguments=tool_call.function.arguments, + name=tool_call.function.name, + type="function_call", + ) + ) + + return items + + @classmethod + def maybe_easy_input_message(cls, item: Any) -> EasyInputMessageParam | None: + if not isinstance(item, dict): + return None + + keys = item.keys() + # EasyInputMessageParam only has these two keys + if keys != {"content", "role"}: + return None + + role = item.get("role", None) + if role not in ("user", "assistant", "system", "developer"): + return None + + if "content" not in item: + return None + + return cast(EasyInputMessageParam, item) + + @classmethod + def maybe_input_message(cls, item: Any) -> Message | None: + if ( + isinstance(item, dict) + and item.get("type") == "message" + and item.get("role") + in ( + "user", + "system", + "developer", + ) + ): + return cast(Message, item) + + return None + + @classmethod + def maybe_file_search_call(cls, item: Any) -> ResponseFileSearchToolCallParam | None: + if isinstance(item, dict) and item.get("type") == "file_search_call": + return cast(ResponseFileSearchToolCallParam, item) + return None + + @classmethod + def maybe_function_tool_call(cls, item: Any) -> ResponseFunctionToolCallParam | None: + if isinstance(item, dict) and item.get("type") == "function_call": + return cast(ResponseFunctionToolCallParam, item) + return None + + @classmethod + def maybe_function_tool_call_output( + cls, + item: Any, + ) -> FunctionCallOutput | None: + if isinstance(item, dict) and item.get("type") == "function_call_output": + return cast(FunctionCallOutput, item) + return None + + @classmethod + def maybe_item_reference(cls, item: Any) -> ItemReference | None: + if isinstance(item, dict) and item.get("type") == "item_reference": + return cast(ItemReference, item) + return None + + @classmethod + def maybe_response_output_message(cls, item: Any) -> ResponseOutputMessageParam | None: + # ResponseOutputMessage is only used for messages with role assistant + if ( + isinstance(item, dict) + and item.get("type") == "message" + and item.get("role") == "assistant" + ): + return cast(ResponseOutputMessageParam, item) + return None + + @classmethod + def extract_text_content( + cls, content: str | Iterable[ResponseInputContentParam] + ) -> str | list[ChatCompletionContentPartTextParam]: + all_content = cls.extract_all_content(content) + if isinstance(all_content, str): + return all_content + out: list[ChatCompletionContentPartTextParam] = [] + for c in all_content: + if c.get("type") == "text": + out.append(cast(ChatCompletionContentPartTextParam, c)) + return out + + @classmethod + def extract_all_content( + cls, content: str | Iterable[ResponseInputContentParam] + ) -> str | list[ChatCompletionContentPartParam]: + if isinstance(content, str): + return content + out: list[ChatCompletionContentPartParam] = [] + + for c in content: + if isinstance(c, dict) and c.get("type") == "input_text": + casted_text_param = cast(ResponseInputTextParam, c) + out.append( + ChatCompletionContentPartTextParam( + type="text", + text=casted_text_param["text"], + ) + ) + elif isinstance(c, dict) and c.get("type") == "input_image": + casted_image_param = cast(ResponseInputImageParam, c) + if "image_url" not in casted_image_param or not casted_image_param["image_url"]: + raise UserError( + f"Only image URLs are supported for input_image {casted_image_param}" + ) + out.append( + ChatCompletionContentPartImageParam( + type="image_url", + image_url={ + "url": casted_image_param["image_url"], + "detail": casted_image_param["detail"], + }, + ) + ) + elif isinstance(c, dict) and c.get("type") == "input_file": + raise UserError(f"File uploads are not supported for chat completions {c}") + else: + raise UserError(f"Unknonw content: {c}") + return out + + @classmethod + def items_to_messages( + cls, + items: str | Iterable[TResponseInputItem], + ) -> list[ChatCompletionMessageParam]: + """ + Convert a sequence of 'Item' objects into a list of ChatCompletionMessageParam. + + Rules: + - EasyInputMessage or InputMessage (role=user) => ChatCompletionUserMessageParam + - EasyInputMessage or InputMessage (role=system) => ChatCompletionSystemMessageParam + - EasyInputMessage or InputMessage (role=developer) => ChatCompletionDeveloperMessageParam + - InputMessage (role=assistant) => Start or flush a ChatCompletionAssistantMessageParam + - response_output_message => Also produces/flushes a ChatCompletionAssistantMessageParam + - tool calls get attached to the *current* assistant message, or create one if none. + - tool outputs => ChatCompletionToolMessageParam + """ + + if isinstance(items, str): + return [ + ChatCompletionUserMessageParam( + role="user", + content=items, + ) + ] + + result: list[ChatCompletionMessageParam] = [] + current_assistant_msg: ChatCompletionAssistantMessageParam | None = None + + def flush_assistant_message() -> None: + nonlocal current_assistant_msg + if current_assistant_msg is not None: + # The API doesn't support empty arrays for tool_calls + if not current_assistant_msg.get("tool_calls"): + del current_assistant_msg["tool_calls"] + result.append(current_assistant_msg) + current_assistant_msg = None + + def ensure_assistant_message() -> ChatCompletionAssistantMessageParam: + nonlocal current_assistant_msg + if current_assistant_msg is None: + current_assistant_msg = ChatCompletionAssistantMessageParam(role="assistant") + current_assistant_msg["tool_calls"] = [] + return current_assistant_msg + + for item in items: + # 1) Check easy input message + if easy_msg := cls.maybe_easy_input_message(item): + role = easy_msg["role"] + content = easy_msg["content"] + + if role == "user": + flush_assistant_message() + msg_user: ChatCompletionUserMessageParam = { + "role": "user", + "content": cls.extract_all_content(content), + } + result.append(msg_user) + elif role == "system": + flush_assistant_message() + msg_system: ChatCompletionSystemMessageParam = { + "role": "system", + "content": cls.extract_text_content(content), + } + result.append(msg_system) + elif role == "developer": + flush_assistant_message() + msg_developer: ChatCompletionDeveloperMessageParam = { + "role": "developer", + "content": cls.extract_text_content(content), + } + result.append(msg_developer) + else: + raise UserError(f"Unexpected role in easy_input_message: {role}") + + # 2) Check input message + elif in_msg := cls.maybe_input_message(item): + role = in_msg["role"] + content = in_msg["content"] + flush_assistant_message() + + if role == "user": + msg_user = { + "role": "user", + "content": cls.extract_all_content(content), + } + result.append(msg_user) + elif role == "system": + msg_system = { + "role": "system", + "content": cls.extract_text_content(content), + } + result.append(msg_system) + elif role == "developer": + msg_developer = { + "role": "developer", + "content": cls.extract_text_content(content), + } + result.append(msg_developer) + else: + raise UserError(f"Unexpected role in input_message: {role}") + + # 3) response output message => assistant + elif resp_msg := cls.maybe_response_output_message(item): + flush_assistant_message() + new_asst = ChatCompletionAssistantMessageParam(role="assistant") + contents = resp_msg["content"] + + text_segments = [] + for c in contents: + if c["type"] == "output_text": + text_segments.append(c["text"]) + elif c["type"] == "refusal": + new_asst["refusal"] = c["refusal"] + elif c["type"] == "output_audio": + # Can't handle this, b/c chat completions expects an ID which we dont have + raise UserError( + f"Only audio IDs are supported for chat completions, but got: {c}" + ) + else: + raise UserError(f"Unknown content type in ResponseOutputMessage: {c}") + + if text_segments: + combined = "\n".join(text_segments) + new_asst["content"] = combined + + new_asst["tool_calls"] = [] + current_assistant_msg = new_asst + + # 4) function/file-search calls => attach to assistant + elif file_search := cls.maybe_file_search_call(item): + asst = ensure_assistant_message() + tool_calls = list(asst.get("tool_calls", [])) + new_tool_call = ChatCompletionMessageToolCallParam( + id=file_search["id"], + type="function", + function={ + "name": "file_search_call", + "arguments": json.dumps( + { + "queries": file_search.get("queries", []), + "status": file_search.get("status"), + } + ), + }, + ) + tool_calls.append(new_tool_call) + asst["tool_calls"] = tool_calls + + elif func_call := cls.maybe_function_tool_call(item): + asst = ensure_assistant_message() + tool_calls = list(asst.get("tool_calls", [])) + new_tool_call = ChatCompletionMessageToolCallParam( + id=func_call["call_id"], + type="function", + function={ + "name": func_call["name"], + "arguments": func_call["arguments"], + }, + ) + tool_calls.append(new_tool_call) + asst["tool_calls"] = tool_calls + # 5) function call output => tool message + elif func_output := cls.maybe_function_tool_call_output(item): + flush_assistant_message() + msg: ChatCompletionToolMessageParam = { + "role": "tool", + "tool_call_id": func_output["call_id"], + "content": func_output["output"], + } + result.append(msg) + + # 6) item reference => handle or raise + elif item_ref := cls.maybe_item_reference(item): + raise UserError( + f"Encountered an item_reference, which is not supported: {item_ref}" + ) + + # 7) If we haven't recognized it => fail or ignore + else: + raise UserError(f"Unhandled item type or structure: {item}") + + flush_assistant_message() + return result + + +class ToolConverter: + @classmethod + def to_openai(cls, tool: Tool) -> ChatCompletionToolParam: + if isinstance(tool, FunctionTool): + return { + "type": "function", + "function": { + "name": tool.name, + "description": tool.description or "", + "parameters": tool.params_json_schema, + }, + } + + raise UserError( + f"Hosted tools are not supported with the ChatCompletions API. FGot tool type: " + f"{type(tool)}, tool: {tool}" + ) + + @classmethod + def convert_handoff_tool(cls, handoff: Handoff[Any]) -> ChatCompletionToolParam: + return { + "type": "function", + "function": { + "name": handoff.tool_name, + "description": handoff.tool_description, + "parameters": handoff.input_json_schema, + }, + } diff --git a/tests/src/agents/models/openai_provider.py b/tests/src/agents/models/openai_provider.py new file mode 100644 index 0000000..5194663 --- /dev/null +++ b/tests/src/agents/models/openai_provider.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +import httpx +from openai import AsyncOpenAI, DefaultAsyncHttpxClient + +from . import _openai_shared +from .interface import Model, ModelProvider +from .openai_chatcompletions import OpenAIChatCompletionsModel +from .openai_responses import OpenAIResponsesModel + +DEFAULT_MODEL: str = "gpt-4o" + + +_http_client: httpx.AsyncClient | None = None + + +# If we create a new httpx client for each request, that would mean no sharing of connection pools, +# which would mean worse latency and resource usage. So, we share the client across requests. +def shared_http_client() -> httpx.AsyncClient: + global _http_client + if _http_client is None: + _http_client = DefaultAsyncHttpxClient() + return _http_client + + +class OpenAIProvider(ModelProvider): + def __init__( + self, + *, + api_key: str | None = None, + base_url: str | None = None, + openai_client: AsyncOpenAI | None = None, + organization: str | None = None, + project: str | None = None, + use_responses: bool | None = None, + ) -> None: + if openai_client is not None: + assert api_key is None and base_url is None, ( + "Don't provide api_key or base_url if you provide openai_client" + ) + self._client = openai_client + else: + self._client = _openai_shared.get_default_openai_client() or AsyncOpenAI( + api_key=api_key or _openai_shared.get_default_openai_key(), + base_url=base_url, + organization=organization, + project=project, + http_client=shared_http_client(), + ) + + self._is_openai_model = self._client.base_url.host.startswith("api.openai.com") + if use_responses is not None: + self._use_responses = use_responses + else: + self._use_responses = _openai_shared.get_use_responses_by_default() + + def get_model(self, model_name: str | None) -> Model: + if model_name is None: + model_name = DEFAULT_MODEL + + return ( + OpenAIResponsesModel(model=model_name, openai_client=self._client) + if self._use_responses + else OpenAIChatCompletionsModel(model=model_name, openai_client=self._client) + ) diff --git a/tests/src/agents/models/openai_responses.py b/tests/src/agents/models/openai_responses.py new file mode 100644 index 0000000..a10d7b9 --- /dev/null +++ b/tests/src/agents/models/openai_responses.py @@ -0,0 +1,384 @@ +from __future__ import annotations + +import json +from collections.abc import AsyncIterator +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Literal, overload + +from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream, NotGiven +from openai.types import ChatModel +from openai.types.responses import ( + Response, + ResponseCompletedEvent, + ResponseStreamEvent, + ResponseTextConfigParam, + ToolParam, + WebSearchToolParam, + response_create_params, +) + +from .. import _debug +from ..agent_output import AgentOutputSchema +from ..exceptions import UserError +from ..handoffs import Handoff +from ..items import ItemHelpers, ModelResponse, TResponseInputItem +from ..logger import logger +from ..tool import ComputerTool, FileSearchTool, FunctionTool, Tool, WebSearchTool +from ..tracing import SpanError, response_span +from ..usage import Usage +from ..version import __version__ +from .interface import Model, ModelTracing + +if TYPE_CHECKING: + from ..model_settings import ModelSettings + + +_USER_AGENT = f"Agents/Python {__version__}" +_HEADERS = {"User-Agent": _USER_AGENT} + +# From the Responses API +IncludeLiteral = Literal[ + "file_search_call.results", + "message.input_image.image_url", + "computer_call_output.output.image_url", +] + + +class OpenAIResponsesModel(Model): + """ + Implementation of `Model` that uses the OpenAI Responses API. + """ + + def __init__( + self, + model: str | ChatModel, + openai_client: AsyncOpenAI, + ) -> None: + self.model = model + self._client = openai_client + + def _non_null_or_not_given(self, value: Any) -> Any: + return value if value is not None else NOT_GIVEN + + async def get_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> ModelResponse: + with response_span(disabled=tracing.is_disabled()) as span_response: + try: + response = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + stream=False, + ) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("LLM responsed") + else: + logger.debug( + "LLM resp:\n" + f"{json.dumps([x.model_dump() for x in response.output], indent=2)}\n" + ) + + usage = ( + Usage( + requests=1, + input_tokens=response.usage.input_tokens, + output_tokens=response.usage.output_tokens, + total_tokens=response.usage.total_tokens, + ) + if response.usage + else Usage() + ) + + if tracing.include_data(): + span_response.span_data.response = response + span_response.span_data.input = input + except Exception as e: + span_response.set_error( + SpanError( + message="Error getting response", + data={ + "error": str(e) if tracing.include_data() else e.__class__.__name__, + }, + ) + ) + logger.error(f"Error getting response: {e}") + raise + + return ModelResponse( + output=response.output, + usage=usage, + referenceable_id=response.id, + ) + + async def stream_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + tracing: ModelTracing, + ) -> AsyncIterator[ResponseStreamEvent]: + """ + Yields a partial message as it is generated, as well as the usage information. + """ + with response_span(disabled=tracing.is_disabled()) as span_response: + try: + stream = await self._fetch_response( + system_instructions, + input, + model_settings, + tools, + output_schema, + handoffs, + stream=True, + ) + + final_response: Response | None = None + + async for chunk in stream: + if isinstance(chunk, ResponseCompletedEvent): + final_response = chunk.response + yield chunk + + if final_response and tracing.include_data(): + span_response.span_data.response = final_response + span_response.span_data.input = input + + except Exception as e: + span_response.set_error( + SpanError( + message="Error streaming response", + data={ + "error": str(e) if tracing.include_data() else e.__class__.__name__, + }, + ) + ) + logger.error(f"Error streaming response: {e}") + raise + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[True], + ) -> AsyncStream[ResponseStreamEvent]: ... + + @overload + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[False], + ) -> Response: ... + + async def _fetch_response( + self, + system_instructions: str | None, + input: str | list[TResponseInputItem], + model_settings: ModelSettings, + tools: list[Tool], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + stream: Literal[True] | Literal[False] = False, + ) -> Response | AsyncStream[ResponseStreamEvent]: + list_input = ItemHelpers.input_to_new_input_list(input) + + parallel_tool_calls = ( + True if model_settings.parallel_tool_calls and tools and len(tools) > 0 else NOT_GIVEN + ) + + tool_choice = Converter.convert_tool_choice(model_settings.tool_choice) + converted_tools = Converter.convert_tools(tools, handoffs) + response_format = Converter.get_response_format(output_schema) + + if _debug.DONT_LOG_MODEL_DATA: + logger.debug("Calling LLM") + else: + logger.debug( + f"Calling LLM {self.model} with input:\n" + f"{json.dumps(list_input, indent=2)}\n" + f"Tools:\n{json.dumps(converted_tools.tools, indent=2)}\n" + f"Stream: {stream}\n" + f"Tool choice: {tool_choice}\n" + f"Response format: {response_format}\n" + ) + + return await self._client.responses.create( + instructions=self._non_null_or_not_given(system_instructions), + model=self.model, + input=list_input, + include=converted_tools.includes, + tools=converted_tools.tools, + temperature=self._non_null_or_not_given(model_settings.temperature), + top_p=self._non_null_or_not_given(model_settings.top_p), + truncation=self._non_null_or_not_given(model_settings.truncation), + tool_choice=tool_choice, + parallel_tool_calls=parallel_tool_calls, + stream=stream, + extra_headers=_HEADERS, + text=response_format, + ) + + def _get_client(self) -> AsyncOpenAI: + if self._client is None: + self._client = AsyncOpenAI() + return self._client + + +@dataclass +class ConvertedTools: + tools: list[ToolParam] + includes: list[IncludeLiteral] + + +class Converter: + @classmethod + def convert_tool_choice( + cls, tool_choice: Literal["auto", "required", "none"] | str | None + ) -> response_create_params.ToolChoice | NotGiven: + if tool_choice is None: + return NOT_GIVEN + elif tool_choice == "required": + return "required" + elif tool_choice == "auto": + return "auto" + elif tool_choice == "none": + return "none" + elif tool_choice == "file_search": + return { + "type": "file_search", + } + elif tool_choice == "web_search_preview": + return { + "type": "web_search_preview", + } + elif tool_choice == "computer_use_preview": + return { + "type": "computer_use_preview", + } + else: + return { + "type": "function", + "name": tool_choice, + } + + @classmethod + def get_response_format( + cls, output_schema: AgentOutputSchema | None + ) -> ResponseTextConfigParam | NotGiven: + if output_schema is None or output_schema.is_plain_text(): + return NOT_GIVEN + else: + return { + "format": { + "type": "json_schema", + "name": "final_output", + "schema": output_schema.json_schema(), + "strict": output_schema.strict_json_schema, + } + } + + @classmethod + def convert_tools( + cls, + tools: list[Tool], + handoffs: list[Handoff[Any]], + ) -> ConvertedTools: + converted_tools: list[ToolParam] = [] + includes: list[IncludeLiteral] = [] + + computer_tools = [tool for tool in tools if isinstance(tool, ComputerTool)] + if len(computer_tools) > 1: + raise UserError(f"You can only provide one computer tool. Got {len(computer_tools)}") + + for tool in tools: + converted_tool, include = cls._convert_tool(tool) + converted_tools.append(converted_tool) + if include: + includes.append(include) + + for handoff in handoffs: + converted_tools.append(cls._convert_handoff_tool(handoff)) + + return ConvertedTools(tools=converted_tools, includes=includes) + + @classmethod + def _convert_tool(cls, tool: Tool) -> tuple[ToolParam, IncludeLiteral | None]: + """Returns converted tool and includes""" + + if isinstance(tool, FunctionTool): + converted_tool: ToolParam = { + "name": tool.name, + "parameters": tool.params_json_schema, + "strict": tool.strict_json_schema, + "type": "function", + "description": tool.description, + } + includes: IncludeLiteral | None = None + elif isinstance(tool, WebSearchTool): + ws: WebSearchToolParam = { + "type": "web_search_preview", + "user_location": tool.user_location, + "search_context_size": tool.search_context_size, + } + converted_tool = ws + includes = None + elif isinstance(tool, FileSearchTool): + converted_tool = { + "type": "file_search", + "vector_store_ids": tool.vector_store_ids, + } + if tool.max_num_results: + converted_tool["max_num_results"] = tool.max_num_results + if tool.ranking_options: + converted_tool["ranking_options"] = tool.ranking_options + if tool.filters: + converted_tool["filters"] = tool.filters + + includes = "file_search_call.results" if tool.include_search_results else None + elif isinstance(tool, ComputerTool): + converted_tool = { + "type": "computer-preview", + "environment": tool.computer.environment, + "display_width": tool.computer.dimensions[0], + "display_height": tool.computer.dimensions[1], + } + includes = None + + else: + raise UserError(f"Unknown tool type: {type(tool)}, tool") + + return converted_tool, includes + + @classmethod + def _convert_handoff_tool(cls, handoff: Handoff) -> ToolParam: + return { + "name": handoff.tool_name, + "parameters": handoff.input_json_schema, + "strict": handoff.strict_json_schema, + "type": "function", + "description": handoff.tool_description, + } diff --git a/tests/src/agents/result.py b/tests/src/agents/result.py new file mode 100644 index 0000000..5683827 --- /dev/null +++ b/tests/src/agents/result.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import abc +import asyncio +from collections.abc import AsyncIterator +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any, cast + +from typing_extensions import TypeVar + +from ._run_impl import QueueCompleteSentinel +from .agent import Agent +from .agent_output import AgentOutputSchema +from .exceptions import InputGuardrailTripwireTriggered, MaxTurnsExceeded +from .guardrail import InputGuardrailResult, OutputGuardrailResult +from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem +from .logger import logger +from .stream_events import StreamEvent +from .tracing import Trace + +if TYPE_CHECKING: + from ._run_impl import QueueCompleteSentinel + from .agent import Agent + +T = TypeVar("T") + + +@dataclass +class RunResultBase(abc.ABC): + input: str | list[TResponseInputItem] + """The original input items i.e. the items before run() was called. This may be a mutated + version of the input, if there are handoff input filters that mutate the input. + """ + + new_items: list[RunItem] + """The new items generated during the agent run. These include things like new messages, tool + calls and their outputs, etc. + """ + + raw_responses: list[ModelResponse] + """The raw LLM responses generated by the model during the agent run.""" + + final_output: Any + """The output of the last agent.""" + + input_guardrail_results: list[InputGuardrailResult] + """Guardrail results for the input messages.""" + + output_guardrail_results: list[OutputGuardrailResult] + """Guardrail results for the final output of the agent.""" + + @property + @abc.abstractmethod + def last_agent(self) -> Agent[Any]: + """The last agent that was run.""" + + def final_output_as(self, cls: type[T], raise_if_incorrect_type: bool = False) -> T: + """A convenience method to cast the final output to a specific type. By default, the cast + is only for the typechecker. If you set `raise_if_incorrect_type` to True, we'll raise a + TypeError if the final output is not of the given type. + + Args: + cls: The type to cast the final output to. + raise_if_incorrect_type: If True, we'll raise a TypeError if the final output is not of + the given type. + + Returns: + The final output casted to the given type. + """ + if raise_if_incorrect_type and not isinstance(self.final_output, cls): + raise TypeError(f"Final output is not of type {cls.__name__}") + + return cast(T, self.final_output) + + def to_input_list(self) -> list[TResponseInputItem]: + """Creates a new input list, merging the original input with all the new items generated.""" + original_items: list[TResponseInputItem] = ItemHelpers.input_to_new_input_list(self.input) + new_items = [item.to_input_item() for item in self.new_items] + + return original_items + new_items + + +@dataclass +class RunResult(RunResultBase): + _last_agent: Agent[Any] + + @property + def last_agent(self) -> Agent[Any]: + """The last agent that was run.""" + return self._last_agent + + +@dataclass +class RunResultStreaming(RunResultBase): + """The result of an agent run in streaming mode. You can use the `stream_events` method to + receive semantic events as they are generated. + + The streaming method will raise: + - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. + - A GuardrailTripwireTriggered exception if a guardrail is tripped. + """ + + current_agent: Agent[Any] + """The current agent that is running.""" + + current_turn: int + """The current turn number.""" + + max_turns: int + """The maximum number of turns the agent can run for.""" + + final_output: Any + """The final output of the agent. This is None until the agent has finished running.""" + + _current_agent_output_schema: AgentOutputSchema | None = field(repr=False) + + _trace: Trace | None = field(repr=False) + + is_complete: bool = False + """Whether the agent has finished running.""" + + # Queues that the background run_loop writes to + _event_queue: asyncio.Queue[StreamEvent | QueueCompleteSentinel] = field( + default_factory=asyncio.Queue, repr=False + ) + _input_guardrail_queue: asyncio.Queue[InputGuardrailResult] = field( + default_factory=asyncio.Queue, repr=False + ) + + # Store the asyncio tasks that we're waiting on + _run_impl_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _input_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _output_guardrails_task: asyncio.Task[Any] | None = field(default=None, repr=False) + _stored_exception: Exception | None = field(default=None, repr=False) + + @property + def last_agent(self) -> Agent[Any]: + """The last agent that was run. Updates as the agent run progresses, so the true last agent + is only available after the agent run is complete. + """ + return self.current_agent + + async def stream_events(self) -> AsyncIterator[StreamEvent]: + """Stream deltas for new items as they are generated. We're using the types from the + OpenAI Responses API, so these are semantic events: each event has a `type` field that + describes the type of the event, along with the data for that event. + + This will raise: + - A MaxTurnsExceeded exception if the agent exceeds the max_turns limit. + - A GuardrailTripwireTriggered exception if a guardrail is tripped. + """ + while True: + self._check_errors() + if self._stored_exception: + logger.debug("Breaking due to stored exception") + self.is_complete = True + break + + if self.is_complete and self._event_queue.empty(): + break + + try: + item = await self._event_queue.get() + except asyncio.CancelledError: + break + + if isinstance(item, QueueCompleteSentinel): + self._event_queue.task_done() + # Check for errors, in case the queue was completed due to an exception + self._check_errors() + break + + yield item + self._event_queue.task_done() + + if self._trace: + self._trace.finish(reset_current=True) + + self._cleanup_tasks() + + if self._stored_exception: + raise self._stored_exception + + def _check_errors(self): + if self.current_turn > self.max_turns: + self._stored_exception = MaxTurnsExceeded(f"Max turns ({self.max_turns}) exceeded") + + # Fetch all the completed guardrail results from the queue and raise if needed + while not self._input_guardrail_queue.empty(): + guardrail_result = self._input_guardrail_queue.get_nowait() + if guardrail_result.output.tripwire_triggered: + self._stored_exception = InputGuardrailTripwireTriggered(guardrail_result) + + # Check the tasks for any exceptions + if self._run_impl_task and self._run_impl_task.done(): + exc = self._run_impl_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + if self._input_guardrails_task and self._input_guardrails_task.done(): + exc = self._input_guardrails_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + if self._output_guardrails_task and self._output_guardrails_task.done(): + exc = self._output_guardrails_task.exception() + if exc and isinstance(exc, Exception): + self._stored_exception = exc + + def _cleanup_tasks(self): + if self._run_impl_task and not self._run_impl_task.done(): + self._run_impl_task.cancel() + + if self._input_guardrails_task and not self._input_guardrails_task.done(): + self._input_guardrails_task.cancel() + + if self._output_guardrails_task and not self._output_guardrails_task.done(): + self._output_guardrails_task.cancel() + self._output_guardrails_task.cancel() + self._output_guardrails_task.cancel() diff --git a/tests/src/agents/run.py b/tests/src/agents/run.py new file mode 100644 index 0000000..dfff7e3 --- /dev/null +++ b/tests/src/agents/run.py @@ -0,0 +1,904 @@ +from __future__ import annotations + +import asyncio +import copy +from dataclasses import dataclass, field +from typing import Any, cast + +from openai.types.responses import ResponseCompletedEvent + +from . import Model, _utils +from ._run_impl import ( + NextStepFinalOutput, + NextStepHandoff, + NextStepRunAgain, + QueueCompleteSentinel, + RunImpl, + SingleStepResult, + TraceCtxManager, + get_model_tracing_impl, +) +from .agent import Agent +from .agent_output import AgentOutputSchema +from .exceptions import ( + AgentsException, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + OutputGuardrailTripwireTriggered, +) +from .guardrail import InputGuardrail, InputGuardrailResult, OutputGuardrail, OutputGuardrailResult +from .handoffs import Handoff, HandoffInputFilter, handoff +from .items import ItemHelpers, ModelResponse, RunItem, TResponseInputItem +from .lifecycle import RunHooks +from .logger import logger +from .model_settings import ModelSettings +from .models.interface import ModelProvider +from .models.openai_provider import OpenAIProvider +from .result import RunResult, RunResultStreaming +from .run_context import RunContextWrapper, TContext +from .stream_events import AgentUpdatedStreamEvent, RawResponsesStreamEvent +from .tracing import Span, SpanError, agent_span, get_current_trace, trace +from .tracing.span_data import AgentSpanData +from .usage import Usage + +DEFAULT_MAX_TURNS = 10 + + +@dataclass +class RunConfig: + """Configures settings for the entire agent run.""" + + model: str | Model | None = None + """The model to use for the entire agent run. If set, will override the model set on every + agent. The model_provider passed in below must be able to resolve this model name. + """ + + model_provider: ModelProvider = field(default_factory=OpenAIProvider) + """The model provider to use when looking up string model names. Defaults to OpenAI.""" + + model_settings: ModelSettings | None = None + """Configure global model settings. Any non-null values will override the agent-specific model + settings. + """ + + handoff_input_filter: HandoffInputFilter | None = None + """A global input filter to apply to all handoffs. If `Handoff.input_filter` is set, then that + will take precedence. The input filter allows you to edit the inputs that are sent to the new + agent. See the documentation in `Handoff.input_filter` for more details. + """ + + input_guardrails: list[InputGuardrail[Any]] | None = None + """A list of input guardrails to run on the initial run input.""" + + output_guardrails: list[OutputGuardrail[Any]] | None = None + """A list of output guardrails to run on the final output of the run.""" + + tracing_disabled: bool = False + """Whether tracing is disabled for the agent run. If disabled, we will not trace the agent run. + """ + + trace_include_sensitive_data: bool = True + """Whether we include potentially sensitive data (for example: inputs/outputs of tool calls or + LLM generations) in traces. If False, we'll still create spans for these events, but the + sensitive data will not be included. + """ + + workflow_name: str = "Agent workflow" + """The name of the run, used for tracing. Should be a logical name for the run, like + "Code generation workflow" or "Customer support agent". + """ + + trace_id: str | None = None + """A custom trace ID to use for tracing. If not provided, we will generate a new trace ID.""" + + group_id: str | None = None + """ + A grouping identifier to use for tracing, to link multiple traces from the same conversation + or process. For example, you might use a chat thread ID. + """ + + trace_metadata: dict[str, Any] | None = None + """ + An optional dictionary of additional metadata to include with the trace. + """ + + +class Runner: + @classmethod + async def run( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + *, + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResult: + """Run a workflow starting at the given agent. The agent will run in a loop until a final + output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A run result containing all the inputs, guardrail results and the output of the last + agent. Agents may perform handoffs, so we don't know the specific type of the output. + """ + if hooks is None: + hooks = RunHooks[Any]() + if run_config is None: + run_config = RunConfig() + + with TraceCtxManager( + workflow_name=run_config.workflow_name, + trace_id=run_config.trace_id, + group_id=run_config.group_id, + metadata=run_config.trace_metadata, + disabled=run_config.tracing_disabled, + ): + current_turn = 0 + original_input: str | list[TResponseInputItem] = copy.deepcopy(input) + generated_items: list[RunItem] = [] + model_responses: list[ModelResponse] = [] + + context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( + context=context, # type: ignore + ) + + input_guardrail_results: list[InputGuardrailResult] = [] + + current_span: Span[AgentSpanData] | None = None + current_agent = starting_agent + should_run_agent_start_hooks = True + + try: + while True: + # Start an agent span if we don't have one. This span is ended if the current + # agent changes, or if the agent loop ends. + if current_span is None: + handoff_names = [h.agent_name for h in cls._get_handoffs(current_agent)] + tool_names = [t.name for t in current_agent.tools] + if output_schema := cls._get_output_schema(current_agent): + output_type_name = output_schema.output_type_name() + else: + output_type_name = "str" + + current_span = agent_span( + name=current_agent.name, + handoffs=handoff_names, + tools=tool_names, + output_type=output_type_name, + ) + current_span.start(mark_as_current=True) + + current_turn += 1 + if current_turn > max_turns: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Max turns exceeded", + data={"max_turns": max_turns}, + ), + ) + raise MaxTurnsExceeded(f"Max turns ({max_turns}) exceeded") + + logger.debug( + f"Running agent {current_agent.name} (turn {current_turn})", + ) + + if current_turn == 1: + input_guardrail_results, turn_result = await asyncio.gather( + cls._run_input_guardrails( + starting_agent, + starting_agent.input_guardrails + + (run_config.input_guardrails or []), + copy.deepcopy(input), + context_wrapper, + ), + cls._run_single_turn( + agent=current_agent, + original_input=original_input, + generated_items=generated_items, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + should_run_agent_start_hooks=should_run_agent_start_hooks, + ), + ) + else: + turn_result = await cls._run_single_turn( + agent=current_agent, + original_input=original_input, + generated_items=generated_items, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + should_run_agent_start_hooks=should_run_agent_start_hooks, + ) + should_run_agent_start_hooks = False + + model_responses.append(turn_result.model_response) + original_input = turn_result.original_input + generated_items = turn_result.generated_items + + if isinstance(turn_result.next_step, NextStepFinalOutput): + output_guardrail_results = await cls._run_output_guardrails( + current_agent.output_guardrails + (run_config.output_guardrails or []), + current_agent, + turn_result.next_step.output, + context_wrapper, + ) + return RunResult( + input=original_input, + new_items=generated_items, + raw_responses=model_responses, + final_output=turn_result.next_step.output, + _last_agent=current_agent, + input_guardrail_results=input_guardrail_results, + output_guardrail_results=output_guardrail_results, + ) + elif isinstance(turn_result.next_step, NextStepHandoff): + current_agent = cast(Agent[TContext], turn_result.next_step.new_agent) + current_span.finish(reset_current=True) + current_span = None + should_run_agent_start_hooks = True + elif isinstance(turn_result.next_step, NextStepRunAgain): + pass + else: + raise AgentsException( + f"Unknown next step type: {type(turn_result.next_step)}" + ) + finally: + if current_span: + current_span.finish(reset_current=True) + + @classmethod + def run_sync( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + *, + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResult: + """Run a workflow synchronously, starting at the given agent. Note that this just wraps the + `run` method, so it will not work if there's already an event loop (e.g. inside an async + function, or in a Jupyter notebook or async context like FastAPI). For those cases, use + the `run` method instead. + + The agent will run in a loop until a final output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A run result containing all the inputs, guardrail results and the output of the last + agent. Agents may perform handoffs, so we don't know the specific type of the output. + """ + return asyncio.get_event_loop().run_until_complete( + cls.run( + starting_agent, + input, + context=context, + max_turns=max_turns, + hooks=hooks, + run_config=run_config, + ) + ) + + @classmethod + def run_streamed( + cls, + starting_agent: Agent[TContext], + input: str | list[TResponseInputItem], + context: TContext | None = None, + max_turns: int = DEFAULT_MAX_TURNS, + hooks: RunHooks[TContext] | None = None, + run_config: RunConfig | None = None, + ) -> RunResultStreaming: + """Run a workflow starting at the given agent in streaming mode. The returned result object + contains a method you can use to stream semantic events as they are generated. + + The agent will run in a loop until a final output is generated. The loop runs like so: + 1. The agent is invoked with the given input. + 2. If there is a final output (i.e. the agent produces something of type + `agent.output_type`, the loop terminates. + 3. If there's a handoff, we run the loop again, with the new agent. + 4. Else, we run tool calls (if any), and re-run the loop. + + In two cases, the agent may raise an exception: + 1. If the max_turns is exceeded, a MaxTurnsExceeded exception is raised. + 2. If a guardrail tripwire is triggered, a GuardrailTripwireTriggered exception is raised. + + Note that only the first agent's input guardrails are run. + + Args: + starting_agent: The starting agent to run. + input: The initial input to the agent. You can pass a single string for a user message, + or a list of input items. + context: The context to run the agent with. + max_turns: The maximum number of turns to run the agent for. A turn is defined as one + AI invocation (including any tool calls that might occur). + hooks: An object that receives callbacks on various lifecycle events. + run_config: Global settings for the entire agent run. + + Returns: + A result object that contains data about the run, as well as a method to stream events. + """ + if hooks is None: + hooks = RunHooks[Any]() + if run_config is None: + run_config = RunConfig() + + # If there's already a trace, we don't create a new one. In addition, we can't end the + # trace here, because the actual work is done in `stream_events` and this method ends + # before that. + new_trace = ( + None + if get_current_trace() + else trace( + workflow_name=run_config.workflow_name, + trace_id=run_config.trace_id, + group_id=run_config.group_id, + metadata=run_config.trace_metadata, + disabled=run_config.tracing_disabled, + ) + ) + # Need to start the trace here, because the current trace contextvar is captured at + # asyncio.create_task time + if new_trace: + new_trace.start(mark_as_current=True) + + output_schema = cls._get_output_schema(starting_agent) + context_wrapper: RunContextWrapper[TContext] = RunContextWrapper( + context=context # type: ignore + ) + + streamed_result = RunResultStreaming( + input=copy.deepcopy(input), + new_items=[], + current_agent=starting_agent, + raw_responses=[], + final_output=None, + is_complete=False, + current_turn=0, + max_turns=max_turns, + input_guardrail_results=[], + output_guardrail_results=[], + _current_agent_output_schema=output_schema, + _trace=new_trace, + ) + + # Kick off the actual agent loop in the background and return the streamed result object. + streamed_result._run_impl_task = asyncio.create_task( + cls._run_streamed_impl( + starting_input=input, + streamed_result=streamed_result, + starting_agent=starting_agent, + max_turns=max_turns, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + ) + return streamed_result + + @classmethod + async def _run_input_guardrails_with_queue( + cls, + agent: Agent[Any], + guardrails: list[InputGuardrail[TContext]], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + streamed_result: RunResultStreaming, + parent_span: Span[Any], + ): + queue = streamed_result._input_guardrail_queue + + # We'll run the guardrails and push them onto the queue as they complete + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_input_guardrail(agent, guardrail, input, context) + ) + for guardrail in guardrails + ] + guardrail_results = [] + try: + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + _utils.attach_error_to_span( + parent_span, + SpanError( + message="Guardrail tripwire triggered", + data={ + "guardrail": result.guardrail.get_name(), + "type": "input_guardrail", + }, + ), + ) + queue.put_nowait(result) + guardrail_results.append(result) + except Exception: + for t in guardrail_tasks: + t.cancel() + raise + + streamed_result.input_guardrail_results = guardrail_results + + @classmethod + async def _run_streamed_impl( + cls, + starting_input: str | list[TResponseInputItem], + streamed_result: RunResultStreaming, + starting_agent: Agent[TContext], + max_turns: int, + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ): + current_span: Span[AgentSpanData] | None = None + current_agent = starting_agent + current_turn = 0 + should_run_agent_start_hooks = True + + streamed_result._event_queue.put_nowait(AgentUpdatedStreamEvent(new_agent=current_agent)) + + try: + while True: + if streamed_result.is_complete: + break + + # Start an agent span if we don't have one. This span is ended if the current + # agent changes, or if the agent loop ends. + if current_span is None: + handoff_names = [h.agent_name for h in cls._get_handoffs(current_agent)] + tool_names = [t.name for t in current_agent.tools] + if output_schema := cls._get_output_schema(current_agent): + output_type_name = output_schema.output_type_name() + else: + output_type_name = "str" + + current_span = agent_span( + name=current_agent.name, + handoffs=handoff_names, + tools=tool_names, + output_type=output_type_name, + ) + current_span.start(mark_as_current=True) + + current_turn += 1 + streamed_result.current_turn = current_turn + + if current_turn > max_turns: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Max turns exceeded", + data={"max_turns": max_turns}, + ), + ) + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + break + + if current_turn == 1: + # Run the input guardrails in the background and put the results on the queue + streamed_result._input_guardrails_task = asyncio.create_task( + cls._run_input_guardrails_with_queue( + starting_agent, + starting_agent.input_guardrails + (run_config.input_guardrails or []), + copy.deepcopy(ItemHelpers.input_to_new_input_list(starting_input)), + context_wrapper, + streamed_result, + current_span, + ) + ) + try: + turn_result = await cls._run_single_turn_streamed( + streamed_result, + current_agent, + hooks, + context_wrapper, + run_config, + should_run_agent_start_hooks, + ) + should_run_agent_start_hooks = False + + streamed_result.raw_responses = streamed_result.raw_responses + [ + turn_result.model_response + ] + streamed_result.input = turn_result.original_input + streamed_result.new_items = turn_result.generated_items + + if isinstance(turn_result.next_step, NextStepHandoff): + current_agent = turn_result.next_step.new_agent + current_span.finish(reset_current=True) + current_span = None + should_run_agent_start_hooks = True + streamed_result._event_queue.put_nowait( + AgentUpdatedStreamEvent(new_agent=current_agent) + ) + elif isinstance(turn_result.next_step, NextStepFinalOutput): + streamed_result._output_guardrails_task = asyncio.create_task( + cls._run_output_guardrails( + current_agent.output_guardrails + + (run_config.output_guardrails or []), + current_agent, + turn_result.next_step.output, + context_wrapper, + ) + ) + + try: + output_guardrail_results = await streamed_result._output_guardrails_task + except Exception: + # Exceptions will be checked in the stream_events loop + output_guardrail_results = [] + + streamed_result.output_guardrail_results = output_guardrail_results + streamed_result.final_output = turn_result.next_step.output + streamed_result.is_complete = True + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + elif isinstance(turn_result.next_step, NextStepRunAgain): + pass + except Exception as e: + if current_span: + _utils.attach_error_to_span( + current_span, + SpanError( + message="Error in agent run", + data={"error": str(e)}, + ), + ) + streamed_result.is_complete = True + streamed_result._event_queue.put_nowait(QueueCompleteSentinel()) + raise + + streamed_result.is_complete = True + finally: + if current_span: + current_span.finish(reset_current=True) + + @classmethod + async def _run_single_turn_streamed( + cls, + streamed_result: RunResultStreaming, + agent: Agent[TContext], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + should_run_agent_start_hooks: bool, + ) -> SingleStepResult: + if should_run_agent_start_hooks: + await asyncio.gather( + hooks.on_agent_start(context_wrapper, agent), + ( + agent.hooks.on_start(context_wrapper, agent) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + output_schema = cls._get_output_schema(agent) + + streamed_result.current_agent = agent + streamed_result._current_agent_output_schema = output_schema + + system_prompt = await agent.get_system_prompt(context_wrapper) + + handoffs = cls._get_handoffs(agent) + + model = cls._get_model(agent, run_config) + model_settings = agent.model_settings.resolve(run_config.model_settings) + final_response: ModelResponse | None = None + + input = ItemHelpers.input_to_new_input_list(streamed_result.input) + input.extend([item.to_input_item() for item in streamed_result.new_items]) + + # 1. Stream the output events + async for event in model.stream_response( + system_prompt, + input, + model_settings, + agent.tools, + output_schema, + handoffs, + get_model_tracing_impl( + run_config.tracing_disabled, run_config.trace_include_sensitive_data + ), + ): + if isinstance(event, ResponseCompletedEvent): + usage = ( + Usage( + requests=1, + input_tokens=event.response.usage.input_tokens, + output_tokens=event.response.usage.output_tokens, + total_tokens=event.response.usage.total_tokens, + ) + if event.response.usage + else Usage() + ) + final_response = ModelResponse( + output=event.response.output, + usage=usage, + referenceable_id=event.response.id, + ) + + streamed_result._event_queue.put_nowait(RawResponsesStreamEvent(data=event)) + + # 2. At this point, the streaming is complete for this turn of the agent loop. + if not final_response: + raise ModelBehaviorError("Model did not produce a final response!") + + # 3. Now, we can process the turn as we do in the non-streaming case + single_step_result = await cls._get_single_step_result_from_response( + agent=agent, + original_input=streamed_result.input, + pre_step_items=streamed_result.new_items, + new_response=final_response, + output_schema=output_schema, + handoffs=handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + RunImpl.stream_step_result_to_queue(single_step_result, streamed_result._event_queue) + return single_step_result + + @classmethod + async def _run_single_turn( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + generated_items: list[RunItem], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + should_run_agent_start_hooks: bool, + ) -> SingleStepResult: + # Ensure we run the hooks before anything else + if should_run_agent_start_hooks: + await asyncio.gather( + hooks.on_agent_start(context_wrapper, agent), + ( + agent.hooks.on_start(context_wrapper, agent) + if agent.hooks + else _utils.noop_coroutine() + ), + ) + + system_prompt = await agent.get_system_prompt(context_wrapper) + + output_schema = cls._get_output_schema(agent) + handoffs = cls._get_handoffs(agent) + input = ItemHelpers.input_to_new_input_list(original_input) + input.extend([generated_item.to_input_item() for generated_item in generated_items]) + + new_response = await cls._get_new_response( + agent, + system_prompt, + input, + output_schema, + handoffs, + context_wrapper, + run_config, + ) + + return await cls._get_single_step_result_from_response( + agent=agent, + original_input=original_input, + pre_step_items=generated_items, + new_response=new_response, + output_schema=output_schema, + handoffs=handoffs, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + @classmethod + async def _get_single_step_result_from_response( + cls, + *, + agent: Agent[TContext], + original_input: str | list[TResponseInputItem], + pre_step_items: list[RunItem], + new_response: ModelResponse, + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + hooks: RunHooks[TContext], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> SingleStepResult: + processed_response = RunImpl.process_model_response( + agent=agent, + response=new_response, + output_schema=output_schema, + handoffs=handoffs, + ) + return await RunImpl.execute_tools_and_side_effects( + agent=agent, + original_input=original_input, + pre_step_items=pre_step_items, + new_response=new_response, + processed_response=processed_response, + output_schema=output_schema, + hooks=hooks, + context_wrapper=context_wrapper, + run_config=run_config, + ) + + @classmethod + async def _run_input_guardrails( + cls, + agent: Agent[Any], + guardrails: list[InputGuardrail[TContext]], + input: str | list[TResponseInputItem], + context: RunContextWrapper[TContext], + ) -> list[InputGuardrailResult]: + if not guardrails: + return [] + + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_input_guardrail(agent, guardrail, input, context) + ) + for guardrail in guardrails + ] + + guardrail_results = [] + + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + # Cancel all guardrail tasks if a tripwire is triggered. + for t in guardrail_tasks: + t.cancel() + _utils.attach_error_to_current_span( + SpanError( + message="Guardrail tripwire triggered", + data={"guardrail": result.guardrail.get_name()}, + ) + ) + raise InputGuardrailTripwireTriggered(result) + else: + guardrail_results.append(result) + + return guardrail_results + + @classmethod + async def _run_output_guardrails( + cls, + guardrails: list[OutputGuardrail[TContext]], + agent: Agent[TContext], + agent_output: Any, + context: RunContextWrapper[TContext], + ) -> list[OutputGuardrailResult]: + if not guardrails: + return [] + + guardrail_tasks = [ + asyncio.create_task( + RunImpl.run_single_output_guardrail(guardrail, agent, agent_output, context) + ) + for guardrail in guardrails + ] + + guardrail_results = [] + + for done in asyncio.as_completed(guardrail_tasks): + result = await done + if result.output.tripwire_triggered: + # Cancel all guardrail tasks if a tripwire is triggered. + for t in guardrail_tasks: + t.cancel() + _utils.attach_error_to_current_span( + SpanError( + message="Guardrail tripwire triggered", + data={"guardrail": result.guardrail.get_name()}, + ) + ) + raise OutputGuardrailTripwireTriggered(result) + else: + guardrail_results.append(result) + + return guardrail_results + + @classmethod + async def _get_new_response( + cls, + agent: Agent[TContext], + system_prompt: str | None, + input: list[TResponseInputItem], + output_schema: AgentOutputSchema | None, + handoffs: list[Handoff], + context_wrapper: RunContextWrapper[TContext], + run_config: RunConfig, + ) -> ModelResponse: + model = cls._get_model(agent, run_config) + model_settings = agent.model_settings.resolve(run_config.model_settings) + new_response = await model.get_response( + system_instructions=system_prompt, + input=input, + model_settings=model_settings, + tools=agent.tools, + output_schema=output_schema, + handoffs=handoffs, + tracing=get_model_tracing_impl( + run_config.tracing_disabled, run_config.trace_include_sensitive_data + ), + ) + + context_wrapper.usage.add(new_response.usage) + + return new_response + + @classmethod + def _get_output_schema(cls, agent: Agent[Any]) -> AgentOutputSchema | None: + if agent.output_type is None or agent.output_type is str: + return None + + return AgentOutputSchema(agent.output_type) + + @classmethod + def _get_handoffs(cls, agent: Agent[Any]) -> list[Handoff]: + handoffs = [] + for handoff_item in agent.handoffs: + if isinstance(handoff_item, Handoff): + handoffs.append(handoff_item) + elif isinstance(handoff_item, Agent): + handoffs.append(handoff(handoff_item)) + return handoffs + + @classmethod + def _get_model(cls, agent: Agent[Any], run_config: RunConfig) -> Model: + if isinstance(run_config.model, Model): + return run_config.model + elif isinstance(run_config.model, str): + return run_config.model_provider.get_model(run_config.model) + elif isinstance(agent.model, Model): + return agent.model + + return run_config.model_provider.get_model(agent.model) diff --git a/tests/src/agents/run_context.py b/tests/src/agents/run_context.py new file mode 100644 index 0000000..579a215 --- /dev/null +++ b/tests/src/agents/run_context.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass, field +from typing import Any, Generic + +from typing_extensions import TypeVar + +from .usage import Usage + +TContext = TypeVar("TContext", default=Any) + + +@dataclass +class RunContextWrapper(Generic[TContext]): + """This wraps the context object that you passed to `Runner.run()`. It also contains + information about the usage of the agent run so far. + + NOTE: Contexts are not passed to the LLM. They're a way to pass dependencies and data to code + you implement, like tool functions, callbacks, hooks, etc. + """ + + context: TContext + """The context object (or None), passed by you to `Runner.run()`""" + + usage: Usage = field(default_factory=Usage) + """The usage of the agent run so far. For streamed responses, the usage will be stale until the + last chunk of the stream is processed. + """ diff --git a/tests/src/agents/stream_events.py b/tests/src/agents/stream_events.py new file mode 100644 index 0000000..bd37d11 --- /dev/null +++ b/tests/src/agents/stream_events.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal, Union + +from typing_extensions import TypeAlias + +from .agent import Agent +from .items import RunItem, TResponseStreamEvent + + +@dataclass +class RawResponsesStreamEvent: + """Streaming event from the LLM. These are 'raw' events, i.e. they are directly passed through + from the LLM. + """ + + data: TResponseStreamEvent + """The raw responses streaming event from the LLM.""" + + type: Literal["raw_response_event"] = "raw_response_event" + """The type of the event.""" + + +@dataclass +class RunItemStreamEvent: + """Streaming events that wrap a `RunItem`. As the agent processes the LLM response, it will + generate these events for new messages, tool calls, tool outputs, handoffs, etc. + """ + + name: Literal[ + "message_output_created", + "handoff_requested", + "handoff_occured", + "tool_called", + "tool_output", + "reasoning_item_created", + ] + """The name of the event.""" + + item: RunItem + """The item that was created.""" + + type: Literal["run_item_stream_event"] = "run_item_stream_event" + + +@dataclass +class AgentUpdatedStreamEvent: + """Event that notifies that there is a new agent running.""" + + new_agent: Agent[Any] + """The new agent.""" + + type: Literal["agent_updated_stream_event"] = "agent_updated_stream_event" + + +StreamEvent: TypeAlias = Union[RawResponsesStreamEvent, RunItemStreamEvent, AgentUpdatedStreamEvent] +"""A streaming event from an agent.""" diff --git a/tests/src/agents/strict_schema.py b/tests/src/agents/strict_schema.py new file mode 100644 index 0000000..910ad85 --- /dev/null +++ b/tests/src/agents/strict_schema.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +from typing import Any + +from openai import NOT_GIVEN +from typing_extensions import TypeGuard + +from .exceptions import UserError + +_EMPTY_SCHEMA = { + "additionalProperties": False, + "type": "object", + "properties": {}, + "required": [], +} + + +def ensure_strict_json_schema( + schema: dict[str, Any], +) -> dict[str, Any]: + """Mutates the given JSON schema to ensure it conforms to the `strict` standard + that the OpenAI API expects. + """ + if schema == {}: + return _EMPTY_SCHEMA + return _ensure_strict_json_schema(schema, path=(), root=schema) + + +# Adapted from https://github.com/openai/openai-python/blob/main/src/openai/lib/_pydantic.py +def _ensure_strict_json_schema( + json_schema: object, + *, + path: tuple[str, ...], + root: dict[str, object], +) -> dict[str, Any]: + if not is_dict(json_schema): + raise TypeError(f"Expected {json_schema} to be a dictionary; path={path}") + + defs = json_schema.get("$defs") + if is_dict(defs): + for def_name, def_schema in defs.items(): + _ensure_strict_json_schema(def_schema, path=(*path, "$defs", def_name), root=root) + + definitions = json_schema.get("definitions") + if is_dict(definitions): + for definition_name, definition_schema in definitions.items(): + _ensure_strict_json_schema( + definition_schema, path=(*path, "definitions", definition_name), root=root + ) + + typ = json_schema.get("type") + if typ == "object" and "additionalProperties" not in json_schema: + json_schema["additionalProperties"] = False + elif ( + typ == "object" + and "additionalProperties" in json_schema + and json_schema["additionalProperties"] is True + ): + raise UserError( + "additionalProperties should not be set for object types. This could be because " + "you're using an older version of Pydantic, or because you configured additional " + "properties to be allowed. If you really need this, update the function or output tool " + "to not use a strict schema." + ) + + # object types + # { 'type': 'object', 'properties': { 'a': {...} } } + properties = json_schema.get("properties") + if is_dict(properties): + json_schema["required"] = list(properties.keys()) + json_schema["properties"] = { + key: _ensure_strict_json_schema(prop_schema, path=(*path, "properties", key), root=root) + for key, prop_schema in properties.items() + } + + # arrays + # { 'type': 'array', 'items': {...} } + items = json_schema.get("items") + if is_dict(items): + json_schema["items"] = _ensure_strict_json_schema(items, path=(*path, "items"), root=root) + + # unions + any_of = json_schema.get("anyOf") + if is_list(any_of): + json_schema["anyOf"] = [ + _ensure_strict_json_schema(variant, path=(*path, "anyOf", str(i)), root=root) + for i, variant in enumerate(any_of) + ] + + # intersections + all_of = json_schema.get("allOf") + if is_list(all_of): + if len(all_of) == 1: + json_schema.update( + _ensure_strict_json_schema(all_of[0], path=(*path, "allOf", "0"), root=root) + ) + json_schema.pop("allOf") + else: + json_schema["allOf"] = [ + _ensure_strict_json_schema(entry, path=(*path, "allOf", str(i)), root=root) + for i, entry in enumerate(all_of) + ] + + # strip `None` defaults as there's no meaningful distinction here + # the schema will still be `nullable` and the model will default + # to using `None` anyway + if json_schema.get("default", NOT_GIVEN) is None: + json_schema.pop("default") + + # we can't use `$ref`s if there are also other properties defined, e.g. + # `{"$ref": "...", "description": "my description"}` + # + # so we unravel the ref + # `{"type": "string", "description": "my description"}` + ref = json_schema.get("$ref") + if ref and has_more_than_n_keys(json_schema, 1): + assert isinstance(ref, str), f"Received non-string $ref - {ref}" + + resolved = resolve_ref(root=root, ref=ref) + if not is_dict(resolved): + raise ValueError( + f"Expected `$ref: {ref}` to resolved to a dictionary but got {resolved}" + ) + + # properties from the json schema take priority over the ones on the `$ref` + json_schema.update({**resolved, **json_schema}) + json_schema.pop("$ref") + # Since the schema expanded from `$ref` might not have `additionalProperties: false` applied + # we call `_ensure_strict_json_schema` again to fix the inlined schema and ensure it's valid + return _ensure_strict_json_schema(json_schema, path=path, root=root) + + return json_schema + + +def resolve_ref(*, root: dict[str, object], ref: str) -> object: + if not ref.startswith("#/"): + raise ValueError(f"Unexpected $ref format {ref!r}; Does not start with #/") + + path = ref[2:].split("/") + resolved = root + for key in path: + value = resolved[key] + assert is_dict(value), ( + f"encountered non-dictionary entry while resolving {ref} - {resolved}" + ) + resolved = value + + return resolved + + +def is_dict(obj: object) -> TypeGuard[dict[str, object]]: + # just pretend that we know there are only `str` keys + # as that check is not worth the performance cost + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool: + i = 0 + for _ in obj.keys(): + i += 1 + if i > n: + return True + return False diff --git a/tests/src/agents/tool.py b/tests/src/agents/tool.py new file mode 100644 index 0000000..7587268 --- /dev/null +++ b/tests/src/agents/tool.py @@ -0,0 +1,286 @@ +from __future__ import annotations + +import inspect +import json +from collections.abc import Awaitable +from dataclasses import dataclass +from typing import Any, Callable, Literal, Union, overload + +from openai.types.responses.file_search_tool_param import Filters, RankingOptions +from openai.types.responses.web_search_tool_param import UserLocation +from pydantic import ValidationError +from typing_extensions import Concatenate, ParamSpec + +from . import _debug, _utils +from ._utils import MaybeAwaitable +from .computer import AsyncComputer, Computer +from .exceptions import ModelBehaviorError +from .function_schema import DocstringStyle, function_schema +from .logger import logger +from .run_context import RunContextWrapper +from .tracing import SpanError + +ToolParams = ParamSpec("ToolParams") + +ToolFunctionWithoutContext = Callable[ToolParams, Any] +ToolFunctionWithContext = Callable[Concatenate[RunContextWrapper[Any], ToolParams], Any] + +ToolFunction = Union[ToolFunctionWithoutContext[ToolParams], ToolFunctionWithContext[ToolParams]] + + +@dataclass +class FunctionTool: + """A tool that wraps a function. In most cases, you should use the `function_tool` helpers to + create a FunctionTool, as they let you easily wrap a Python function. + """ + + name: str + """The name of the tool, as shown to the LLM. Generally the name of the function.""" + + description: str + """A description of the tool, as shown to the LLM.""" + + params_json_schema: dict[str, Any] + """The JSON schema for the tool's parameters.""" + + on_invoke_tool: Callable[[RunContextWrapper[Any], str], Awaitable[str]] + """A function that invokes the tool with the given context and parameters. The params passed + are: + 1. The tool run context. + 2. The arguments from the LLM, as a JSON string. + + You must return a string representation of the tool output. In case of errors, you can either + raise an Exception (which will cause the run to fail) or return a string error message (which + will be sent back to the LLM). + """ + + strict_json_schema: bool = True + """Whether the JSON schema is in strict mode. We **strongly** recommend setting this to True, + as it increases the likelihood of correct JSON input.""" + + +@dataclass +class FileSearchTool: + """A hosted tool that lets the LLM search through a vector store. Currently only supported with + OpenAI models, using the Responses API. + """ + + vector_store_ids: list[str] + """The IDs of the vector stores to search.""" + + max_num_results: int | None = None + """The maximum number of results to return.""" + + include_search_results: bool = False + """Whether to include the search results in the output produced by the LLM.""" + + ranking_options: RankingOptions | None = None + """Ranking options for search.""" + + filters: Filters | None = None + """A filter to apply based on file attributes.""" + + @property + def name(self): + return "file_search" + + +@dataclass +class WebSearchTool: + """A hosted tool that lets the LLM search the web. Currently only supported with OpenAI models, + using the Responses API. + """ + + user_location: UserLocation | None = None + """Optional location for the search. Lets you customize results to be relevant to a location.""" + + search_context_size: Literal["low", "medium", "high"] = "medium" + """The amount of context to use for the search.""" + + @property + def name(self): + return "web_search_preview" + + +@dataclass +class ComputerTool: + """A hosted tool that lets the LLM control a computer.""" + + computer: Computer | AsyncComputer + """The computer implementation, which describes the environment and dimensions of the computer, + as well as implements the computer actions like click, screenshot, etc. + """ + + @property + def name(self): + return "computer_use_preview" + + +Tool = Union[FunctionTool, FileSearchTool, WebSearchTool, ComputerTool] +"""A tool that can be used in an agent.""" + + +def default_tool_error_function(ctx: RunContextWrapper[Any], error: Exception) -> str: + """The default tool error function, which just returns a generic error message.""" + return f"An error occurred while running the tool. Please try again. Error: {str(error)}" + + +ToolErrorFunction = Callable[[RunContextWrapper[Any], Exception], MaybeAwaitable[str]] + + +@overload +def function_tool( + func: ToolFunction[...], + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = None, +) -> FunctionTool: + """Overload for usage as @function_tool (no parentheses).""" + ... + + +@overload +def function_tool( + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = None, +) -> Callable[[ToolFunction[...]], FunctionTool]: + """Overload for usage as @function_tool(...).""" + ... + + +def function_tool( + func: ToolFunction[...] | None = None, + *, + name_override: str | None = None, + description_override: str | None = None, + docstring_style: DocstringStyle | None = None, + use_docstring_info: bool = True, + failure_error_function: ToolErrorFunction | None = default_tool_error_function, +) -> FunctionTool | Callable[[ToolFunction[...]], FunctionTool]: + """ + Decorator to create a FunctionTool from a function. By default, we will: + 1. Parse the function signature to create a JSON schema for the tool's parameters. + 2. Use the function's docstring to populate the tool's description. + 3. Use the function's docstring to populate argument descriptions. + The docstring style is detected automatically, but you can override it. + + If the function takes a `RunContextWrapper` as the first argument, it *must* match the + context type of the agent that uses the tool. + + Args: + func: The function to wrap. + name_override: If provided, use this name for the tool instead of the function's name. + description_override: If provided, use this description for the tool instead of the + function's docstring. + docstring_style: If provided, use this style for the tool's docstring. If not provided, + we will attempt to auto-detect the style. + use_docstring_info: If True, use the function's docstring to populate the tool's + description and argument descriptions. + failure_error_function: If provided, use this function to generate an error message when + the tool call fails. The error message is sent to the LLM. If you pass None, then no + error message will be sent and instead an Exception will be raised. + """ + + def _create_function_tool(the_func: ToolFunction[...]) -> FunctionTool: + schema = function_schema( + func=the_func, + name_override=name_override, + description_override=description_override, + docstring_style=docstring_style, + use_docstring_info=use_docstring_info, + ) + + async def _on_invoke_tool_impl(ctx: RunContextWrapper[Any], input: str) -> str: + try: + json_data: dict[str, Any] = json.loads(input) if input else {} + except Exception as e: + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Invalid JSON input for tool {schema.name}") + else: + logger.debug(f"Invalid JSON input for tool {schema.name}: {input}") + raise ModelBehaviorError( + f"Invalid JSON input for tool {schema.name}: {input}" + ) from e + + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Invoking tool {schema.name}") + else: + logger.debug(f"Invoking tool {schema.name} with input {input}") + + try: + parsed = ( + schema.params_pydantic_model(**json_data) + if json_data + else schema.params_pydantic_model() + ) + except ValidationError as e: + raise ModelBehaviorError(f"Invalid JSON input for tool {schema.name}: {e}") from e + + args, kwargs_dict = schema.to_call_args(parsed) + + if not _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Tool call args: {args}, kwargs: {kwargs_dict}") + + if inspect.iscoroutinefunction(the_func): + if schema.takes_context: + result = await the_func(ctx, *args, **kwargs_dict) + else: + result = await the_func(*args, **kwargs_dict) + else: + if schema.takes_context: + result = the_func(ctx, *args, **kwargs_dict) + else: + result = the_func(*args, **kwargs_dict) + + if _debug.DONT_LOG_TOOL_DATA: + logger.debug(f"Tool {schema.name} completed.") + else: + logger.debug(f"Tool {schema.name} returned {result}") + + return str(result) + + async def _on_invoke_tool(ctx: RunContextWrapper[Any], input: str) -> str: + try: + return await _on_invoke_tool_impl(ctx, input) + except Exception as e: + if failure_error_function is None: + raise + + result = failure_error_function(ctx, e) + if inspect.isawaitable(result): + return await result + + _utils.attach_error_to_current_span( + SpanError( + message="Error running tool (non-fatal)", + data={ + "tool_name": schema.name, + "error": str(e), + }, + ) + ) + return result + + return FunctionTool( + name=schema.name, + description=schema.description or "", + params_json_schema=schema.params_json_schema, + on_invoke_tool=_on_invoke_tool, + ) + + # If func is actually a callable, we were used as @function_tool with no parentheses + if callable(func): + return _create_function_tool(func) + + # Otherwise, we were used as @function_tool(...), so return a decorator + def decorator(real_func: ToolFunction[...]) -> FunctionTool: + return _create_function_tool(real_func) + + return decorator diff --git a/tests/src/agents/tracing/__init__.py b/tests/src/agents/tracing/__init__.py new file mode 100644 index 0000000..8e80201 --- /dev/null +++ b/tests/src/agents/tracing/__init__.py @@ -0,0 +1,97 @@ +import atexit + +from .create import ( + agent_span, + custom_span, + function_span, + generation_span, + get_current_span, + get_current_trace, + guardrail_span, + handoff_span, + response_span, + trace, +) +from .processor_interface import TracingProcessor +from .processors import default_exporter, default_processor +from .setup import GLOBAL_TRACE_PROVIDER +from .span_data import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + ResponseSpanData, + SpanData, +) +from .spans import Span, SpanError +from .traces import Trace +from .util import gen_span_id, gen_trace_id + +__all__ = [ + "add_trace_processor", + "agent_span", + "custom_span", + "function_span", + "generation_span", + "get_current_span", + "get_current_trace", + "guardrail_span", + "handoff_span", + "response_span", + "set_trace_processors", + "set_tracing_disabled", + "trace", + "Trace", + "SpanError", + "Span", + "SpanData", + "AgentSpanData", + "CustomSpanData", + "FunctionSpanData", + "GenerationSpanData", + "GuardrailSpanData", + "HandoffSpanData", + "ResponseSpanData", + "TracingProcessor", + "gen_trace_id", + "gen_span_id", +] + + +def add_trace_processor(span_processor: TracingProcessor) -> None: + """ + Adds a new trace processor. This processor will receive all traces/spans. + """ + GLOBAL_TRACE_PROVIDER.register_processor(span_processor) + + +def set_trace_processors(processors: list[TracingProcessor]) -> None: + """ + Set the list of trace processors. This will replace the current list of processors. + """ + GLOBAL_TRACE_PROVIDER.set_processors(processors) + + +def set_tracing_disabled(disabled: bool) -> None: + """ + Set whether tracing is globally disabled. + """ + GLOBAL_TRACE_PROVIDER.set_disabled(disabled) + + +def set_tracing_export_api_key(api_key: str) -> None: + """ + Set the OpenAI API key for the backend exporter. + """ + default_exporter().set_api_key(api_key) + + +# Add the default processor, which exports traces and spans to the backend in batches. You can +# change the default behavior by either: +# 1. calling add_trace_processor(), which adds additional processors, or +# 2. calling set_trace_processors(), which replaces the default processor. +add_trace_processor(default_processor()) + +atexit.register(GLOBAL_TRACE_PROVIDER.shutdown) diff --git a/tests/src/agents/tracing/__pycache__/__init__.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d78e7663953b1e6170c28faaa9f181d511f3603 GIT binary patch literal 2896 zcmcgt&2Jk;6rXk0A94JdIG_DsNt-rqgWW<4RU1H|-%@F#CU95@RvXXQal7laX4j-a z93s&_fLpH-hZ4kvBS-EWSR+eBb3)?Oo280NPrNtwt`k!Phr(v|xAWe-*?qsC-@JU2 zPA3UmUw!#g?dJp`zoQXcYJc+lhC;{_LJ3tYq7xdSBbH)E^oTv8kJzfN+EG1f$Ml#T z*W-3VPuQdSsGZc43dX9|n4Qv7avrtPc1F+GSv@Q5n3c2hdS1@s*0?>PPuK;$U{C6k z_LM$lPwUh6j6P$}>a#K?Va?e`^dt6B{iw7@tz-6a{kTGEgeJdM^w;PZOVJcnswz!C zR&|YL*y}7c8!#gMwJfa7(;Pd|xAS11q~mO%Z%=@IDnghBclIgl{w{pOQ_$i&xO1wM{@#;>a zXJ#RcadY6v)tF~gTAYX5^9Ma1H!DnJYAusfZq}{dTT+{5gF4k}&x*>Rq9StZ5M6K7 zR=HDQuIun(MC2%|nk~yS*rTSyJ;p^o@HE3PMNHnjbMJ$dJI4C`m8;i{)%*88zJ2Zb z{bF3CSCH5m@VaJtrpR2C+(J8dqfamNjNc3yhQ9pGApg*nz15=-IugMQl2&mpMiH-b z4m=bQMQ*G(EhbD#4awLD32>}%9z}>D#1Y^W5I%~4Cxedxh=L1h4{de_UeL~EF*wFnL0} z18Wb$`4{A+)ff4EEvdn^LaD2nT7zwAl9d)pQqtBp>+ZnTw(6FpaaLjV&zNSyTyAj7 zn5kQmZ@XKJ@EoQ;&RMPQLa_`e6cv6PmR!tCd$C&n$YtCubGuw|C@ahAcj-fn_Z)A# z8Mw+Gb3M0QDwSPc>4%l^%OG{xFzSuEXBegCHa`OIm2e5ag4PcCBSE5b9d&8%)3&k|A056$<-_lwyr}nzzX`3uMro>aeA+uF+NZ4DC8st#xY?hUi@kTs|l zbl8Cg8Tf)2Lk;>#g9K;5cR&I$hs*sF(Qnd0G=BqPKg4*fjox{fY^zfpb?R4j{ugzA z_no$Swxgc)gCQj<7DdeT*rU29VinF{`fvNA?0I4$EK4|_u(=AwE!&zbQI4c|7C=NH z^;WajGDc;`qwqiCANnJf))JG(OW+f8tX3L|_R^wYDg^=gyMPk`@Xv<>n@n!fD{{HT4gplrtqA2?$y(9m+ zYEp@G$^QjYM0wLE=MRp3a>aky*e8qr;n9sGFxUSA#))#mC-Z+D`()NXJi6-47-SK^ zPQ3fJs=VtjUg{Ef{;)0|!;n#xi@WRonaiDpE4X&?M^!$C0piZ@UO?~py=(F@3=pxj zH-g@!@8vs|z literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/__init__.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a7836a26d7a85e3718ada54177d2ca69d20f79e GIT binary patch literal 2574 zcmb_dOK%%h6u#q(9Y5kn5+}{0kEu)2xQXnhY5>tv2z}9UX^WjA1Qx@@bK{u09_!4Q zB&;en{RQ1;8FuVo$uB@nOH}Eisz|K53#2Gp&bc!lr-cPb9m(IE``vRN-zTN(@*fROu@zdI&;#AN1)esCpQ}dITbRKlJNS zi0Uzj>2Zkb2}o$6os8Chd6XxSoTYegaNttjH>fj}>^M zGEhxck`?8wT1ic?uT+?-q^r}_WEds-d`9voD}&V;pBa)&zA{`rgN$eQ*435#$a5lZJg2?#aS=D`yyY5B+iZzMt>d`% zU2l+F>9lHY!)|%QG`evxdqk$rU8B|k2$l~XWLz+7T%_t96D%+rO>dWhRkLN;D=YpH zz@4_;a=14P%QbmLWR`GoqgB5Jc8xoZ4f&ABSbW9oG+l$Qw{37ah`}z?4%`$ma`pPn z4;HT*OXbCjmyKKHo1a{}bh(_5isT~ctf0(GrfZ7SMJY8HXRi2a17_%IAUNO#uXg1G zRQf|tQ9xwMzNmnSb}f{lk}YC{mjO_jBEqDW<6Hu7C?jDsEHOX>84w)hC&jR7S^hZC z3sgBkxdjn|eu5|gmKX!7I7ko-Ac(AkAI#e^*um+s7;nRBIOcMbTactxhY2PLCJ4p} zh6siUvIHXpqXdTt#t2dbM+uG~h?Ij`jo?HP0q%C7Rr7NhT^Q^0e|x9Vc)&g^6HTqeI`6;)|B$Xr|WUYnqg(1qT+irPYSh8*6KgW>W)RmIh=Cn?wWYNc-!IN6ya{MW?Q@{E6K6$kiToY8*N^6 zpyty>It61~G>k^8;TlGvy#dE?9|`Z#t`ysQ>_wD?C!VNtkJP!XFLu=Pd&n{o5A-;% zHFCG(74B;8D%YB*RkK%K4WHx3&yQNWiMcBM{n8KC^)7fIhAGOq-VDZXhLpRw4ycB|&N9xSh#EyCvPU4)1cC+)f24GeOU~QFS3_yylv~|3bo@lnzGr+wkP@x0~5zEWMvDL3QMNBP?v4HDf2?~Atys6qTMzlqRwi^wd}Rl zcT9#z7Kn(RziV5aCcglScrvAe^8>F2nbaOE2iPn^Kj9&83H@-r1fyM-vM_ zsWh=b6Z4yAiJITKMASS{b6b5x&3!Mu=KdIEN_IOt^*DR-7dG=7i`-9tn|_#?dXgz@ LX9_#4D24qE&^>=X literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/__init__.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14152d8616d9bdb79e0f9e1f5656969d4f6fd609 GIT binary patch literal 2210 zcmbVMOK;mo5GG|ks5d3c@7QMSII;S$3iMVKZB)l`4WzMQyFDyW5VUqBv*wb*E>+t_ zZ!XY3(L>r>|4e_tUVF-4=&3U+k}`ZrQv&?B-|U0)&CZNhuU8CseieU>Zpw!7Hv17y@Fr<;o7kEcgBEX-wjP&)dEOx%zCafEB3a~1WQi}6WxhgI_$pcDYh+EY zvw}x_oviZ>vY~l7*yIjz^tci{=38WoZ`7B>$eakqgzoGZ^Xjbm18ed)xku@5ucL!a1st;KMJR`3Co!9W(Ir2Vs|hRBHP}& zZ;6FBV5%{ic!CPg52mNoF1?UO!(s9Z!Q?m!C7aTM-qSUeeGZ%baC9P~0h2Nk-JG&1 z8+wxM@|H?f-?e8DBC#T2X9lba>?OLkdN1bON^Mx0C zsX1u1{4Bi4F*`?wXqSC{1{#S76SIJVF7Xh)_bX5THs7fkh*#0IDP5!lox?C&-x< z;_XsjdKUqsqK3QH5LOXZ5S9^I2yKLUgbu<2!Xm;FLLFfp;Sm7nYDiG)X8%-0!QzPs z2bh8BgJV>)53g(h#Ks%*XCuqn+)rl}2cABq((#;-T{~KelO4O~oL~BK#+_?F2pqu% z%>R)&9*p${*+XySo<8k$Hz#cP8EFeP@+D-|%+0PT9>dh1hS-MBKhJyLN+x7a@ZKPz ztf$LH(kt8$NAc~L^`sai<{thE0@`z3KlEeQ-5cME4LIgYcx4X2$ZHPKsbmVI*n)}o z49oXg$Qg^B_>wsRh;X9e!`R3qI7vvx0VJGWpVV)*Z|2K`05vdqX0s5S9q?6$W(rS` zZUVf7Ha+a3kc1zhc|lm6j>+ck7Modc(zqL+A| z_ndZsegkjnG;(md3-iq0oqfbY-1_P}?wic6AYOFCn^;~bW!l6GO--BU#`W;cGUiUYx5 z_+ITpw*)eur2r&0oG^q$nijlH2XtFB0aO7Tn~bN$Ro3-Sq3!;c_JM5%wqub_`wn&> zQk)jA?nx>?iTyx#7u{!6$#o$KTvwNr%3~4eR8;wkC<=swC(eOQU>uZ{*1EFfs+v~$*`UFwwVl3lIj{sounJy-w$ literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/_strict_schema.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/_strict_schema.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79c5357f56d60d2cc51a177daa2c028c2308b81f GIT binary patch literal 5568 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 J#!eaj|1Y3dtg!$9 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/create.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/create.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62b33cf3542d1eb37007e981b0972fbde07091a1 GIT binary patch literal 10389 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/create.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/create.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9296d5f0e9a61b8d06a55d53951331bf7acb8c1a GIT binary patch literal 12633 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/logger.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/logger.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01611fce61e792df0ba146181f33b5b92da6cfd6 GIT binary patch literal 283 zcmey&%ge<81b<8prMm#>#~=<2Fhd!i4SPX{t@ai*u1_yF0d zMYq^MVyQ(d89swF-*VLtElw>e)-TG{PtH$C)rXjok{w@AlwXoxQds~K(l0JbhRW(g zZP14prB_gSi^C>2KczG$)vkyWXe!8&#gahc12ZEd<821M2b==eIi)UfN?njPy3A?Z K$X3J-lm!5ZBuX~` literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/logger.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/logger.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25e02e5f91ac9964cba37742c507b2de43b6f2e8 GIT binary patch literal 233 zcmYe~<>g`kf(;|q%NOY%!93t&R}#YM?bS$(KA d`VjN<3My}L*yQG?l;)(`F#=64W&#o%OaSD`L23X1 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/processor_interface.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ea4cfd5decd0bb2ed1214e58b8aaa9614fb8655 GIT binary patch literal 2644 zcma)7J8v6D5Z-(8AyJa^aO^~~){c#UfI~KRV+27IMX`+lga9c;XatAT?ebyWgWf$t z7Lh_lkfd~Bz(Ib16T@|Y)cFN0TnJYgaOF;dTp1}cv&SPvkx{hd&F#+4?#_Js&D`&$ zQjtLW=f>a7S0*8^aF8`?&^h`EI!_2AO!G+{-@2~_dR-5Ux}g!Si&DXFZfOMi zmTw2SdQKxcxlNe;BVjpC?&q_OpBjWbhbL|Ig0jkkRpAt?BGWcVx%e6v)yjHO+Wc;P z%~`#(w))N8+U>+x33p#>Fjv+RbED&i@U}LE>v1nVCY9N#N58@N3E`x!F;dr=RyVkL zKZ}^KS@7C?3O3J&tLyfdMTr$AEOKKObF4UFkw0NEGhtC+7Q{K9ETHr4u(>WGk4qVe z-tyfr=Ar>1(MBX_C!GKjDj{B3Nvs&-6*+J@u?3I2BCL7(FaSajA4K5D1_3vUi0Zwq zRoC}9qdP4gQkSYtXj@VlyCUYSQY_LzTM9X z1D1V%21tJ%hZHwJL>uj}En5I7-DpK!pV2Ui>6RaP+Y~NTxNXTPf9UZ}+>YQvPXive zOmSD6E7*tI53i^uN)SO=7jy6u?}8X0FXl0_82|v6J3M5)#cA+lv+v{jb12>c;UST- zHl0*gjw28QJ={*mZ+k9A2KIGsKhJ*jJ(Q%(ljP^lIKHDdoR)*s?!=-5u z=(Y#Xuw~`^=nkk(DfT+InSrpRjhW19m{UFgIaQi-$^3woYY#gSR7lZVPkS=0fk>?Z zdny#j1}W_Z9iLyPo=SQ@YEsCE0CQeK=B0x;g4(;~dfT9ZQr?bvAbaQVBu;;z8^A?X zd{RoBp#*uEPcHS_GDdDA9~AL3?@lW)%7L`Ps4fKuxeB=QALZp5z&BQ2?9{$@{sdl? zRDT`pM>VCK7fYzX#1_jaD2mG{u7F5P3`2Z^;}gWaiaHc!c;q^WWv7N(Oaogf~g~BluL;XA1Cr!b3EQ2q(xz|b^ppA`4hZ=YQ2Py4?#79cOQOZ(e_qW;Zj@(Znq#*^Za_qch8`ZrT16-@sE+DCNn literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/processor_interface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a25145efc3a858f509539f8b63f5d22c887be414 GIT binary patch literal 2941 zcmcIm&2Jk;6rcUHy@``l(697sT9pq&u>mzb6sS}}D2*kn>V^XlwA!pE_R{sPJ2O_A zz=0EIdMM?_IsXCw0}`Sq#G8wVo0<3Vd+%+(Ty_XN zKVA8`^`%Y7pZGC&teok82h0QF5?8Aej&HrLt?FD~HMl{^y8dde=+6@38m?J4S1oQ) zi{nx#WDot9Q{_HbLHqNnAxPj0Pv z%O9;Q*Kam%q{j7ldrt#dMN3V$>&Ni6*0>+AAiF4|`K_d11lo2 zt=r(!SxxXDU#{U+aG{oa zlW;E_cv&`=gP(=*fk0P#^g;RPfw0y)(D8{_698m46xWm!^{)AO0g zLxW9)sw}CQ3uSNt7xP@4J;;Y=^0Uw9L~^hb!xT`ro%Et6jT1>Xq9nLOp$51g3P#zz zfOTbCOj{Llkx`Zony;Z1R3!IkZ;IuX5|m7_^WmNk#)SU z+g7=65c|YFa{CL!F8x0eeRvOW`^Axa29mxGT$Ty6c5Fxwe?%G}`OibzSTDFHZgnl! zzHGYVqqFE1d8uVrO6hzK=aqZi1V#|wdk6OH+{!*hHhB=7IsU2?;0pEbce)X~L<7|k zqNGKko;uhN1k`2rArqKz8-8#HR;sBLO4boONANmEzoHXxI<&>s2NwA>3WdblK)|TN z8Q3=3d(?GV^HD4XbC~k6ns**RFt%BK8OT@zvNOH(*hFZm+4?oyG@K1t z9mypmZy>26sR2n%G@M_-Z)_Elczqk!kYEPHTR;%6pAPY=Q2PA`zns7P==9P)0m%M% zv5zl>VTI-lL3l=GMqQlRFz#WxbNmtE_#={9s*iB^-Gw60LYP&&asB<}tZ0-aYKRW2 zDJ{O6RrWRh9_+-XCa{I<8=9v5PMjy?rN4}Y$L6WW=9#B8xcmUsW)mPw(bH+*5nbq5)!G`^*1@(xn_BB#)3)f2*4DJvrn|3A+NOJ2S1;-I=?$%GP#X~5%-iU8oQ+Nd zkC>EP%nl!CiishBAww?6r1$_PL;_OT6%Z6nN<|^!yegQQiY&59uyKt1^x>6Af#%bi z4f+O5~g{COxw^8Rv$N^N|foQ>Ihof?PgYUN_#@uhKT z<&I>@PxCnhDM`-*z^J&on6RD{(Olib^8j^kP5>7{ zydb7KxyW~T7HF3O?R*oFY6{%h;n@nO-RrhnQ59{VY8Yp+9RE*T734G)3>DKBO!MZ_ zWtP$RO5pBn1_ze28kWP)P#?cS**KUnq6VwCf*f3jjElET4zw+DL$BJ98XDiFB`{c` zjbfP`LZfa0YTTfR>D?0SyX#Nim{ua>$?;TC{yh5}4k!xFo*lg9-NGu`#;1GVa{2O%IP%0aWe>>O~YjV#SH?=BT(DnAj)^bfmG0KRLaztQ?Y~I?4sC5aRUWb zuegQcic3Ahlou`ttWfzO2+wpZ$K3a>zir1cwcY01?NeC1db_bIwGQy+E#GjizIWVw zcOJ5%7ZVPxU(8O5kt~`!24l{5mq2)+0K#;Vu+K=OOC;eV0@;&dg-A%jVxl-)DuXaO z2Tgajhet5-J2{20>6V07c_mgXmD@L^XyI2#l2|It2qavm%fiEVm83qdXia)Zz)7Y- zcUKCsGjB}Tc1oerjj38Jd4PeM-!ivW80;1%9Z$TE8+?G`00jnCe1zgQh}=OK;t=QS z^5G7A_!?e`*L;x=0(G5+-XpKVJqg#MKre(1BCqJO5EUF(3vTp(1o)fuU0>&o-eBEn Zx%W+x<`1w=CH7^{Y(ok*&8EHo#eXXMWEKDb literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/processors.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/processors.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..719f46870b76ae6cdc060a57603e56ab4aa35d92 GIT binary patch literal 13530 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/processors.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/processors.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd3c5b89c2049f8f44f13dca4d276640de98083a GIT binary patch literal 12772 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/processors.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/processors.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..631993a2e20bd5834d0d0b1bb813050bd8f94dfa GIT binary patch literal 8799 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 B|I7dY literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/scope.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/scope.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca52084c4744ee1710a07791e6ab00e7b26ad412 GIT binary patch literal 3108 zcmb_d&2Jk;6rcUDGx66tX_I~=ZMSig#t7^KsG?CoLTy!0MTp`Q372Z+dKc5yUZ=C` zik7I#p$85exHS?2NMW`0Vi69mScpMR}=9wX#;dvBUb%K|Y(mV)P4J=Tbqf3+3PdCZxiuVw*N1fB8GS4g@)Z94QBH(b3?B;& z#7OFid3frJksJ^s6^fAx#Yp=xE)zZTkmyR^+ta#GBKhn~eCtA9^wP_pEndx8j_yX zrHArkK%WL{Nm&MbM2|=C7~rG&a0HJ7en?M7@FBnt1C9OhDd1ze62XUc3gnJ^u~N0U zVt8?8*ezzVBz##FNeu=z17@`U4v0s@4siJ0=v~*v!}*oSrU12LDX@D|&9FBGPpZ}} z3w_mtkBe6=gIPt^C{~-gQLNOUR+fHSTVYMRY2RB3prUP9wpA)EH`f(=C%IDxnAvV81O@`IA7;L`8Ry63Xg${r(G^fgyDyEIkOBK^dkVV z2@n>(OjX%Y`0=t}9GGuEj=Rnp2ho%7#^JUy<0>-_ongl@TFl1$76zv?2P^(dI&kKY zzLa9jfbZMvB&9;&P*S7Z98T+~U&g5A1bR<`fW+w7Hl5g_6YH1SbjqbzN`Lb7SO!-8 zzpWhTZ$pYerx*$8tH93epK8)675KCIE}7BOP<(PPH!ov+e*$=at_S}1>#51 zsc26wqoH#+h5He&4c?D;L4g8x`;3rVU0oMe1va!Q-0F6+31`;#e?xo>TKNh7rA(Zro_qJj!A1rp`F0C&3gn|5?yOeKOM9 zZqqQ z?M|yw?^u%6dd0FDhW%wzXLuO0Qz*`$m_hL-iUNpD!oQzyUp9g$3f!007KpX(-%c#M zCknCL9tp%NiNr@9Uh~m7=%}4VyDxg|XZHg8Klz_Qe_!lgPV@jdYs0r48eTKb!>U1i#t zdDm6WV?>b)SO6L&bBn_B3HSUbJMy9RWpJHC*Ew{Z6W21|CEe`I#sfEduHQ3pOn@>( N#Vf>K4=`}P{sO48t*`(9 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/scope.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/scope.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f45326ff9a446d72ea5542d2674c0e259a89f74a GIT binary patch literal 2769 zcmb_d&2Jl35P$pSZ2YxO+B9v`(rrF#K5S5FMXG>=(x{L~DR>hkNUGJ=-kMOyo6fFL z3#jS=A%u|NQYptCNiX~tg`?fHaA833FZj~i z)f>+Qv}FZ9R&mN@8&H#tHLHrVvH(`m_LIdLfL--WH-LLe_7$sE@wPMMw}c@s>0jvc zPSy5PW~Z;8idG&4+D`I#k#D-~%dN#A-#|*?qV+8*w~-tW32y09L^Qbw!`QxQuWJzZEYVw$0LVR54u5@k7?@fD-!tl7^F;@68r zT{MVr%DeRkFrO3K}KELSN;O1d9 zUvx@#-YVlcxTUM+8aSTg-B^pDybHz9nkPC}s^z>(dFIw`_!*4bCYuh?Kg4Uu?ZyZ2 zqi!FHTkNTJtf}b@P2bdp>*{a|K~@ZVigaEoFZX3!{8zb)!kLh&Q3k|ZyJXk)P#;9d zmnk)uzM<=In2-!w_f32^egwVU11LHYzjJU?8?US5FogL0{<-JS^!&N&tlXWw0ybu{ z5$3OW&Q-g+fPU`KUBcmj`@NRqR755yLLF$TLk)FkedeJ$%q#dmdM%<}!;b(RHXNdF z@?HxV@9S&{elPvOy}iN>CuwWcEXSML$%b}vQyZzPBSF5M%_5^OZR5%Fx>~lcvo*OnB5;pk3?sR(qQItA)88e(N!AB* z_Xd8b{dn{4&7aTLbMMy&3J+A{u@XKYhDRaZ~#<(IhS{CB=*WI|f}7|ub&2yGk_K`Q)cj-h zP*XkDP>-!IJ`6i<1M~A^V0$&FLISs7N-yBVb$A`M?UABW_3Z23rxv)m1-hRA zaMvj9q9>Z~qW770LmCBIUy1Y}=83&A7wC5v{^#29&hErZ`l@MGtySAJ{kUnaI;C2L z^rUHCt67!MqM7EE6>z=EO4Y79XigL>mg}zC-p5V}=*fcZK*t-s04m35w##G$|sw~I(JUVkKtbqFDX!a1NCZnIr+~5 zsAbWG_;x|1MSMEg{DB3*UlnPzs#S;BaH>QCzs9`uZaRE*DmaO^U>YOQtLVBYwj@Ci zp0fU5*x2vv=pRz%i9GZ~KK|>}h2~V@{#4F*QG%y=U+e&=Q*bC!Gl9X}$B{>4ymI{pFN CpHUzH literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/scope.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/scope.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a7d6c077ffa5589c38d96b57293b42159cad949 GIT binary patch literal 1965 zcma)6&2HO95Z+yq5=BWi{S!M*f1)DkK|NUa!YJChL0S|ripnWarUAinw~7<~D7#cq z3qBM`j_ngP1?;2yB7Fy5d&(>L(#{MiL6%&!1a>$(GaP>N&Fn_quE*ec@yDN|?;OVd z!p-7i!{%dH<`)o;m}HVCEWo!&cq#&sT7kuxw4|NbsU6syDWN}wy^}a;BWRGXku*~` zaKXo(wPaIr>HaDLk9Ms+rrOdwsC@0(*CAhreBA@~kjXW<{))-8BhG-&p49A)r!j&&5PvJ+3VR#_36 z%G{LdhiH-%S3br)h7?bFXT765Q)M??j4ivfU4ecL}xLQH+xVQunu7v7ev9%`2`!YOAg%Y zHdsn)q>4*kT5)17nZ5<4tIr0HjM8SH(?Og|HHbz)wvo@nvCfOUI33SS1Eb^GJ3!7( zveCdGsQvM2=|kc4X0o^gXaHL0=BR zVRi~c{2#d1AlD2R95Cp{HEfPnkovMl_tqR3K_qmW6mbpTI}7-3SJbWs=`KY5zxkLU zLacfd#TJSsnl={OM`z|eh??=VyV_!knciLP@i899`W#|^9g4h}NIZtr;QOE;krG<0 z{WJbdWJ0pv*ze+kb5>aAcGdd6IGXMbRZ+lr_Ua3uM?d(m*LO7BoUC-D`hGGhT{4F! zQd8yF_Su~DKwlwXaR}(s_aVw4LMHBT_$wWTm8gFoTAaqJCaOW4TUbLiCRE4QI325% z^lhp`V*@JeZ=;5Du?%|a`eYF74YXrr zF^Q7e(~gtKm{b){bE$U`!yOdwqPUCV9t!N=0=W5r>veG8*)+(8%LVr>!8hwgx-B?G z_1DVw!Zei)xSXKN8ZQEkH$mN9ye^DxfB`~q){JiA#yhO8n{Ogr-G=L77-d;rM8!#- knJ}!lL_jwoE!~5PfsgQq1lQxf=m?v4I7<8PZJVU?53mE$tpET3 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/setup.cpython-311.pyc b/tests/src/agents/tracing/__pycache__/setup.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3aed1e68d40a0fdee8576907d313b7be9593b8d9 GIT binary patch literal 10043 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 EA6|6;!2kdN literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/span_data.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/span_data.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cae2b71988dc6324d3bcea0aa4c0246b5f9858f GIT binary patch literal 7609 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 Rc3^sDJ$*xYgnEX^{{s20lMny^ literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/span_data.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/span_data.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1d71fc3b76e59b26a565dae870e2786bbfe781e GIT binary patch literal 5702 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 literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/spans.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/spans.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c538b810617358b2f2663e1d9290334bcb5a40a GIT binary patch literal 8282 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 Z3~@>`Jrli|Wq@B>y&d#!{S(5DegPd7*ChY| literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/util.cpython-313.pyc b/tests/src/agents/tracing/__pycache__/util.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..723c198e1ff3e90df30afdfe4b187952e34660ed GIT binary patch literal 1074 zcmbtT&ubG=5PolWlcwFoA59Onh%BaUOe&8~5(snlzR@-l+N$ zg#^bABh~fRIM$EUz_+7bM|hox1JcIiQPF%N7#AjQn6_7AX0;A!MeTW|$vxlm*PF}~ z+>W13pI2?it($@GtWGr7J9DZ)Ql5ci3Ao}Kzy|rMXWtZG7vH7cr{AWJ^pRuTJk-r2 zeKOQ0xeg(JA87np>AyzW9ZdObw2(4B7swq22Bx&GJYp{6@JWSHHC(o4VE4?Jo1KV~ zl2#7XNDBg|79|j;B6Wqm;JAIVMV{!(nzL)JH>Y^iPr4s@$3dAh5gtCDKwXRU@%ftu=UFs%TbWDL`ogS!MBL5Q_3k z2vIqg$u Se$z&tM?Su3eFr`+>;DA(ywwx{ literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/__pycache__/util.cpython-39.pyc b/tests/src/agents/tracing/__pycache__/util.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8bcc53764fbd50ce253e502d1418125cf0cf28f GIT binary patch literal 769 zcmaJytn;@!yW3U}P6>pN!kB7MWaoU zj}hDIPuUb2*SL$RNh}n7I&qK}Wj#(>S58LLi^XyoB8LcQX)F^!y8nMg&1uOwI}_j7 z4}mL++1^`I8$i*l%2jR76^%0L==CTabZ&MdebBb0_95BlUY?rI4~s0kA&&OzxI(t* zDK(L~C${w%u>g~u_Pgjj5Ys}-E3wCVg28O}UxId;qxi@#BriL@BLG+<^H<&_R%^tn zt1LAUxhJ)Qe!K$f)JFpLzsx%5gaoPuDtZ((+OHbjPRs#5ab{_5#JA;UxEcNdgDI-U literal 0 HcmV?d00001 diff --git a/tests/src/agents/tracing/create.py b/tests/src/agents/tracing/create.py new file mode 100644 index 0000000..8d7fc49 --- /dev/null +++ b/tests/src/agents/tracing/create.py @@ -0,0 +1,306 @@ +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any + +from .logger import logger +from .setup import GLOBAL_TRACE_PROVIDER +from .span_data import ( + AgentSpanData, + CustomSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailSpanData, + HandoffSpanData, + ResponseSpanData, +) +from .spans import Span +from .traces import Trace + +if TYPE_CHECKING: + from openai.types.responses import Response + + +def trace( + workflow_name: str, + trace_id: str | None = None, + group_id: str | None = None, + metadata: dict[str, Any] | None = None, + disabled: bool = False, +) -> Trace: + """ + Create a new trace. The trace will not be started automatically; you should either use + it as a context manager (`with trace(...):`) or call `trace.start()` + `trace.finish()` + manually. + + In addition to the workflow name and optional grouping identifier, you can provide + an arbitrary metadata dictionary to attach additional user-defined information to + the trace. + + Args: + workflow_name: The name of the logical app or workflow. For example, you might provide + "code_bot" for a coding agent, or "customer_support_agent" for a customer support agent. + trace_id: The ID of the trace. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_trace_id()` to generate a trace ID, to guarantee that IDs are + correctly formatted. + group_id: Optional grouping identifier to link multiple traces from the same conversation + or process. For instance, you might use a chat thread ID. + metadata: Optional dictionary of additional metadata to attach to the trace. + disabled: If True, we will return a Trace but the Trace will not be recorded. This will + not be checked if there's an existing trace and `even_if_trace_running` is True. + + Returns: + The newly created trace object. + """ + current_trace = GLOBAL_TRACE_PROVIDER.get_current_trace() + if current_trace: + logger.warning( + "Trace already exists. Creating a new trace, but this is probably a mistake." + ) + + return GLOBAL_TRACE_PROVIDER.create_trace( + name=workflow_name, + trace_id=trace_id, + group_id=group_id, + metadata=metadata, + disabled=disabled, + ) + + +def get_current_trace() -> Trace | None: + """Returns the currently active trace, if present.""" + return GLOBAL_TRACE_PROVIDER.get_current_trace() + + +def get_current_span() -> Span[Any] | None: + """Returns the currently active span, if present.""" + return GLOBAL_TRACE_PROVIDER.get_current_span() + + +def agent_span( + name: str, + handoffs: list[str] | None = None, + tools: list[str] | None = None, + output_type: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[AgentSpanData]: + """Create a new agent span. The span will not be started automatically, you should either do + `with agent_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the agent. + handoffs: Optional list of agent names to which this agent could hand off control. + tools: Optional list of tool names available to this agent. + output_type: Optional name of the output type produced by the agent. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created agent span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=AgentSpanData(name=name, handoffs=handoffs, tools=tools, output_type=output_type), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def function_span( + name: str, + input: str | None = None, + output: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[FunctionSpanData]: + """Create a new function span. The span will not be started automatically, you should either do + `with function_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the function. + input: The input to the function. + output: The output of the function. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created function span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=FunctionSpanData(name=name, input=input, output=output), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def generation_span( + input: Sequence[Mapping[str, Any]] | None = None, + output: Sequence[Mapping[str, Any]] | None = None, + model: str | None = None, + model_config: Mapping[str, Any] | None = None, + usage: dict[str, Any] | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[GenerationSpanData]: + """Create a new generation span. The span will not be started automatically, you should either + do `with generation_span() ...` or call `span.start()` + `span.finish()` manually. + + This span captures the details of a model generation, including the + input message sequence, any generated outputs, the model name and + configuration, and usage data. If you only need to capture a model + response identifier, use `response_span()` instead. + + Args: + input: The sequence of input messages sent to the model. + output: The sequence of output messages received from the model. + model: The model identifier used for the generation. + model_config: The model configuration (hyperparameters) used. + usage: A dictionary of usage information (input tokens, output tokens, etc.). + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created generation span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=GenerationSpanData( + input=input, output=output, model=model, model_config=model_config, usage=usage + ), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def response_span( + response: Response | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[ResponseSpanData]: + """Create a new response span. The span will not be started automatically, you should either do + `with response_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + response: The OpenAI Response object. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=ResponseSpanData(response=response), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def handoff_span( + from_agent: str | None = None, + to_agent: str | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[HandoffSpanData]: + """Create a new handoff span. The span will not be started automatically, you should either do + `with handoff_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + from_agent: The name of the agent that is handing off. + to_agent: The name of the agent that is receiving the handoff. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created handoff span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=HandoffSpanData(from_agent=from_agent, to_agent=to_agent), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def custom_span( + name: str, + data: dict[str, Any] | None = None, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[CustomSpanData]: + """Create a new custom span, to which you can add your own metadata. The span will not be + started automatically, you should either do `with custom_span() ...` or call + `span.start()` + `span.finish()` manually. + + Args: + name: The name of the custom span. + data: Arbitrary structured data to associate with the span. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + + Returns: + The newly created custom span. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=CustomSpanData(name=name, data=data or {}), + span_id=span_id, + parent=parent, + disabled=disabled, + ) + + +def guardrail_span( + name: str, + triggered: bool = False, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, +) -> Span[GuardrailSpanData]: + """Create a new guardrail span. The span will not be started automatically, you should either + do `with guardrail_span() ...` or call `span.start()` + `span.finish()` manually. + + Args: + name: The name of the guardrail. + triggered: Whether the guardrail was triggered. + span_id: The ID of the span. Optional. If not provided, we will generate an ID. We + recommend using `util.gen_span_id()` to generate a span ID, to guarantee that IDs are + correctly formatted. + parent: The parent span or trace. If not provided, we will automatically use the current + trace/span as the parent. + disabled: If True, we will return a Span but the Span will not be recorded. + """ + return GLOBAL_TRACE_PROVIDER.create_span( + span_data=GuardrailSpanData(name=name, triggered=triggered), + span_id=span_id, + parent=parent, + disabled=disabled, + ) diff --git a/tests/src/agents/tracing/logger.py b/tests/src/agents/tracing/logger.py new file mode 100644 index 0000000..661d09b --- /dev/null +++ b/tests/src/agents/tracing/logger.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("openai.agents.tracing") diff --git a/tests/src/agents/tracing/processor_interface.py b/tests/src/agents/tracing/processor_interface.py new file mode 100644 index 0000000..4dcd897 --- /dev/null +++ b/tests/src/agents/tracing/processor_interface.py @@ -0,0 +1,69 @@ +import abc +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from .spans import Span + from .traces import Trace + + +class TracingProcessor(abc.ABC): + """Interface for processing spans.""" + + @abc.abstractmethod + def on_trace_start(self, trace: "Trace") -> None: + """Called when a trace is started. + + Args: + trace: The trace that started. + """ + pass + + @abc.abstractmethod + def on_trace_end(self, trace: "Trace") -> None: + """Called when a trace is finished. + + Args: + trace: The trace that started. + """ + pass + + @abc.abstractmethod + def on_span_start(self, span: "Span[Any]") -> None: + """Called when a span is started. + + Args: + span: The span that started. + """ + pass + + @abc.abstractmethod + def on_span_end(self, span: "Span[Any]") -> None: + """Called when a span is finished. Should not block or raise exceptions. + + Args: + span: The span that finished. + """ + pass + + @abc.abstractmethod + def shutdown(self) -> None: + """Called when the application stops.""" + pass + + @abc.abstractmethod + def force_flush(self) -> None: + """Forces an immediate flush of all queued spans/traces.""" + pass + + +class TracingExporter(abc.ABC): + """Exports traces and spans. For example, could log them or send them to a backend.""" + + @abc.abstractmethod + def export(self, items: list["Trace | Span[Any]"]) -> None: + """Exports a list of traces and spans. + + Args: + items: The items to export. + """ + pass diff --git a/tests/src/agents/tracing/processors.py b/tests/src/agents/tracing/processors.py new file mode 100644 index 0000000..282bc23 --- /dev/null +++ b/tests/src/agents/tracing/processors.py @@ -0,0 +1,261 @@ +from __future__ import annotations + +import os +import queue +import random +import threading +import time +from typing import Any + +import httpx + +from .logger import logger +from .processor_interface import TracingExporter, TracingProcessor +from .spans import Span +from .traces import Trace + + +class ConsoleSpanExporter(TracingExporter): + """Prints the traces and spans to the console.""" + + def export(self, items: list[Trace | Span[Any]]) -> None: + for item in items: + if isinstance(item, Trace): + print(f"[Exporter] Export trace_id={item.trace_id}, name={item.name}, ") + else: + print(f"[Exporter] Export span: {item.export()}") + + +class BackendSpanExporter(TracingExporter): + def __init__( + self, + api_key: str | None = None, + organization: str | None = None, + project: str | None = None, + endpoint: str = "https://api.openai.com/v1/traces/ingest", + max_retries: int = 3, + base_delay: float = 1.0, + max_delay: float = 30.0, + ): + """ + Args: + api_key: The API key for the "Authorization" header. Defaults to + `os.environ["OPENAI_TRACE_API_KEY"]` if not provided. + organization: The OpenAI organization to use. Defaults to + `os.environ["OPENAI_ORG_ID"]` if not provided. + project: The OpenAI project to use. Defaults to + `os.environ["OPENAI_PROJECT_ID"]` if not provided. + endpoint: The HTTP endpoint to which traces/spans are posted. + max_retries: Maximum number of retries upon failures. + base_delay: Base delay (in seconds) for the first backoff. + max_delay: Maximum delay (in seconds) for backoff growth. + """ + self.api_key = api_key or os.environ.get("OPENAI_API_KEY") + self.organization = organization or os.environ.get("OPENAI_ORG_ID") + self.project = project or os.environ.get("OPENAI_PROJECT_ID") + self.endpoint = endpoint + self.max_retries = max_retries + self.base_delay = base_delay + self.max_delay = max_delay + + # Keep a client open for connection pooling across multiple export calls + self._client = httpx.Client(timeout=httpx.Timeout(timeout=60, connect=5.0)) + + def set_api_key(self, api_key: str): + """Set the OpenAI API key for the exporter. + + Args: + api_key: The OpenAI API key to use. This is the same key used by the OpenAI Python + client. + """ + self.api_key = api_key + + def export(self, items: list[Trace | Span[Any]]) -> None: + if not items: + return + + if not self.api_key: + logger.warning("OPENAI_API_KEY is not set, skipping trace export") + return + + traces: list[dict[str, Any]] = [] + spans: list[dict[str, Any]] = [] + + data = [item.export() for item in items if item.export()] + payload = {"data": data} + + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + "OpenAI-Beta": "traces=v1", + } + + # Exponential backoff loop + attempt = 0 + delay = self.base_delay + while True: + attempt += 1 + try: + response = self._client.post(url=self.endpoint, headers=headers, json=payload) + + # If the response is successful, break out of the loop + if response.status_code < 300: + logger.debug(f"Exported {len(traces)} traces, {len(spans)} spans") + return + + # If the response is a client error (4xx), we wont retry + if 400 <= response.status_code < 500: + logger.error(f"Tracing client error {response.status_code}: {response.text}") + return + + # For 5xx or other unexpected codes, treat it as transient and retry + logger.warning(f"Server error {response.status_code}, retrying.") + except httpx.RequestError as exc: + # Network or other I/O error, we'll retry + logger.warning(f"Request failed: {exc}") + + # If we reach here, we need to retry or give up + if attempt >= self.max_retries: + logger.error("Max retries reached, giving up on this batch.") + return + + # Exponential backoff + jitter + sleep_time = delay + random.uniform(0, 0.1 * delay) # 10% jitter + time.sleep(sleep_time) + delay = min(delay * 2, self.max_delay) + + def close(self): + """Close the underlying HTTP client.""" + self._client.close() + + +class BatchTraceProcessor(TracingProcessor): + """Some implementation notes: + 1. Using Queue, which is thread-safe. + 2. Using a background thread to export spans, to minimize any performance issues. + 3. Spans are stored in memory until they are exported. + """ + + def __init__( + self, + exporter: TracingExporter, + max_queue_size: int = 8192, + max_batch_size: int = 128, + schedule_delay: float = 5.0, + export_trigger_ratio: float = 0.7, + ): + """ + Args: + exporter: The exporter to use. + max_queue_size: The maximum number of spans to store in the queue. After this, we will + start dropping spans. + max_batch_size: The maximum number of spans to export in a single batch. + schedule_delay: The delay between checks for new spans to export. + export_trigger_ratio: The ratio of the queue size at which we will trigger an export. + """ + self._exporter = exporter + self._queue: queue.Queue[Trace | Span[Any]] = queue.Queue(maxsize=max_queue_size) + self._max_queue_size = max_queue_size + self._max_batch_size = max_batch_size + self._schedule_delay = schedule_delay + self._shutdown_event = threading.Event() + + # The queue size threshold at which we export immediately. + self._export_trigger_size = int(max_queue_size * export_trigger_ratio) + + # Track when we next *must* perform a scheduled export + self._next_export_time = time.time() + self._schedule_delay + + self._shutdown_event = threading.Event() + self._worker_thread = threading.Thread(target=self._run, daemon=True) + self._worker_thread.start() + + def on_trace_start(self, trace: Trace) -> None: + try: + self._queue.put_nowait(trace) + except queue.Full: + logger.warning("Queue is full, dropping trace.") + + def on_trace_end(self, trace: Trace) -> None: + # We send traces via on_trace_start, so we don't need to do anything here. + pass + + def on_span_start(self, span: Span[Any]) -> None: + # We send spans via on_span_end, so we don't need to do anything here. + pass + + def on_span_end(self, span: Span[Any]) -> None: + try: + self._queue.put_nowait(span) + except queue.Full: + logger.warning("Queue is full, dropping span.") + + def shutdown(self, timeout: float | None = None): + """ + Called when the application stops. We signal our thread to stop, then join it. + """ + self._shutdown_event.set() + self._worker_thread.join(timeout=timeout) + + def force_flush(self): + """ + Forces an immediate flush of all queued spans. + """ + self._export_batches(force=True) + + def _run(self): + while not self._shutdown_event.is_set(): + current_time = time.time() + queue_size = self._queue.qsize() + + # If it's time for a scheduled flush or queue is above the trigger threshold + if current_time >= self._next_export_time or queue_size >= self._export_trigger_size: + self._export_batches(force=False) + # Reset the next scheduled flush time + self._next_export_time = time.time() + self._schedule_delay + else: + # Sleep a short interval so we don't busy-wait. + time.sleep(0.2) + + # Final drain after shutdown + self._export_batches(force=True) + + def _export_batches(self, force: bool = False): + """Drains the queue and exports in batches. If force=True, export everything. + Otherwise, export up to `max_batch_size` repeatedly until the queue is empty or below a + certain threshold. + """ + while True: + items_to_export: list[Span[Any] | Trace] = [] + + # Gather a batch of spans up to max_batch_size + while not self._queue.empty() and ( + force or len(items_to_export) < self._max_batch_size + ): + try: + items_to_export.append(self._queue.get_nowait()) + except queue.Empty: + # Another thread might have emptied the queue between checks + break + + # If we collected nothing, we're done + if not items_to_export: + break + + # Export the batch + self._exporter.export(items_to_export) + + +# Create a shared global instance: +_global_exporter = BackendSpanExporter() +_global_processor = BatchTraceProcessor(_global_exporter) + + +def default_exporter() -> BackendSpanExporter: + """The default exporter, which exports traces and spans to the backend in batches.""" + return _global_exporter + + +def default_processor() -> BatchTraceProcessor: + """The default processor, which exports traces and spans to the backend in batches.""" + return _global_processor diff --git a/tests/src/agents/tracing/scope.py b/tests/src/agents/tracing/scope.py new file mode 100644 index 0000000..9ccd9f8 --- /dev/null +++ b/tests/src/agents/tracing/scope.py @@ -0,0 +1,45 @@ +# Holds the current active span +import contextvars +from typing import TYPE_CHECKING, Any + +from .logger import logger + +if TYPE_CHECKING: + from .spans import Span + from .traces import Trace + +_current_span: contextvars.ContextVar["Span[Any] | None"] = contextvars.ContextVar( + "current_span", default=None +) + +_current_trace: contextvars.ContextVar["Trace | None"] = contextvars.ContextVar( + "current_trace", default=None +) + + +class Scope: + @classmethod + def get_current_span(cls) -> "Span[Any] | None": + return _current_span.get() + + @classmethod + def set_current_span(cls, span: "Span[Any] | None") -> "contextvars.Token[Span[Any] | None]": + return _current_span.set(span) + + @classmethod + def reset_current_span(cls, token: "contextvars.Token[Span[Any] | None]") -> None: + _current_span.reset(token) + + @classmethod + def get_current_trace(cls) -> "Trace | None": + return _current_trace.get() + + @classmethod + def set_current_trace(cls, trace: "Trace | None") -> "contextvars.Token[Trace | None]": + logger.debug(f"Setting current trace: {trace.trace_id if trace else None}") + return _current_trace.set(trace) + + @classmethod + def reset_current_trace(cls, token: "contextvars.Token[Trace | None]") -> None: + logger.debug("Resetting current trace") + _current_trace.reset(token) diff --git a/tests/src/agents/tracing/setup.py b/tests/src/agents/tracing/setup.py new file mode 100644 index 0000000..bc340c9 --- /dev/null +++ b/tests/src/agents/tracing/setup.py @@ -0,0 +1,211 @@ +from __future__ import annotations + +import os +import threading +from typing import Any + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope +from .spans import NoOpSpan, Span, SpanImpl, TSpanData +from .traces import NoOpTrace, Trace, TraceImpl + + +class SynchronousMultiTracingProcessor(TracingProcessor): + """ + Forwards all calls to a list of TracingProcessors, in order of registration. + """ + + def __init__(self): + # Using a tuple to avoid race conditions when iterating over processors + self._processors: tuple[TracingProcessor, ...] = () + self._lock = threading.Lock() + + def add_tracing_processor(self, tracing_processor: TracingProcessor): + """ + Add a processor to the list of processors. Each processor will receive all traces/spans. + """ + with self._lock: + self._processors += (tracing_processor,) + + def set_processors(self, processors: list[TracingProcessor]): + """ + Set the list of processors. This will replace the current list of processors. + """ + with self._lock: + self._processors = tuple(processors) + + def on_trace_start(self, trace: Trace) -> None: + """ + Called when a trace is started. + """ + for processor in self._processors: + processor.on_trace_start(trace) + + def on_trace_end(self, trace: Trace) -> None: + """ + Called when a trace is finished. + """ + for processor in self._processors: + processor.on_trace_end(trace) + + def on_span_start(self, span: Span[Any]) -> None: + """ + Called when a span is started. + """ + for processor in self._processors: + processor.on_span_start(span) + + def on_span_end(self, span: Span[Any]) -> None: + """ + Called when a span is finished. + """ + for processor in self._processors: + processor.on_span_end(span) + + def shutdown(self) -> None: + """ + Called when the application stops. + """ + for processor in self._processors: + logger.debug(f"Shutting down trace processor {processor}") + processor.shutdown() + + def force_flush(self): + """ + Force the processors to flush their buffers. + """ + for processor in self._processors: + processor.force_flush() + + +class TraceProvider: + def __init__(self): + self._multi_processor = SynchronousMultiTracingProcessor() + self._disabled = os.environ.get("OPENAI_AGENTS_DISABLE_TRACING", "false").lower() in ( + "true", + "1", + ) + + def register_processor(self, processor: TracingProcessor): + """ + Add a processor to the list of processors. Each processor will receive all traces/spans. + """ + self._multi_processor.add_tracing_processor(processor) + + def set_processors(self, processors: list[TracingProcessor]): + """ + Set the list of processors. This will replace the current list of processors. + """ + self._multi_processor.set_processors(processors) + + def get_current_trace(self) -> Trace | None: + """ + Returns the currently active trace, if any. + """ + return Scope.get_current_trace() + + def get_current_span(self) -> Span[Any] | None: + """ + Returns the currently active span, if any. + """ + return Scope.get_current_span() + + def set_disabled(self, disabled: bool) -> None: + """ + Set whether tracing is disabled. + """ + self._disabled = disabled + + def create_trace( + self, + name: str, + trace_id: str | None = None, + group_id: str | None = None, + metadata: dict[str, Any] | None = None, + disabled: bool = False, + ) -> Trace: + """ + Create a new trace. + """ + if self._disabled or disabled: + logger.debug(f"Tracing is disabled. Not creating trace {name}") + return NoOpTrace() + + trace_id = trace_id or util.gen_trace_id() + + logger.debug(f"Creating trace {name} with id {trace_id}") + + return TraceImpl( + name=name, + trace_id=trace_id, + group_id=group_id, + metadata=metadata, + processor=self._multi_processor, + ) + + def create_span( + self, + span_data: TSpanData, + span_id: str | None = None, + parent: Trace | Span[Any] | None = None, + disabled: bool = False, + ) -> Span[TSpanData]: + """ + Create a new span. + """ + if self._disabled or disabled: + logger.debug(f"Tracing is disabled. Not creating span {span_data}") + return NoOpSpan(span_data) + + if not parent: + current_span = Scope.get_current_span() + current_trace = Scope.get_current_trace() + if current_trace is None: + logger.error( + "No active trace. Make sure to start a trace with `trace()` first" + "Returning NoOpSpan." + ) + return NoOpSpan(span_data) + elif isinstance(current_trace, NoOpTrace) or isinstance(current_span, NoOpSpan): + logger.debug( + f"Parent {current_span} or {current_trace} is no-op, returning NoOpSpan" + ) + return NoOpSpan(span_data) + + parent_id = current_span.span_id if current_span else None + trace_id = current_trace.trace_id + + elif isinstance(parent, Trace): + if isinstance(parent, NoOpTrace): + logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + return NoOpSpan(span_data) + trace_id = parent.trace_id + parent_id = None + elif isinstance(parent, Span): + if isinstance(parent, NoOpSpan): + logger.debug(f"Parent {parent} is no-op, returning NoOpSpan") + return NoOpSpan(span_data) + parent_id = parent.span_id + trace_id = parent.trace_id + + logger.debug(f"Creating span {span_data} with id {span_id}") + + return SpanImpl( + trace_id=trace_id, + span_id=span_id, + parent_id=parent_id, + processor=self._multi_processor, + span_data=span_data, + ) + + def shutdown(self) -> None: + try: + logger.debug("Shutting down trace provider") + self._multi_processor.shutdown() + except Exception as e: + logger.error(f"Error shutting down trace provider: {e}") + + +GLOBAL_TRACE_PROVIDER = TraceProvider() diff --git a/tests/src/agents/tracing/span_data.py b/tests/src/agents/tracing/span_data.py new file mode 100644 index 0000000..5e5d38c --- /dev/null +++ b/tests/src/agents/tracing/span_data.py @@ -0,0 +1,188 @@ +from __future__ import annotations + +import abc +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from openai.types.responses import Response, ResponseInputItemParam + + +class SpanData(abc.ABC): + @abc.abstractmethod + def export(self) -> dict[str, Any]: + pass + + @property + @abc.abstractmethod + def type(self) -> str: + pass + + +class AgentSpanData(SpanData): + __slots__ = ("name", "handoffs", "tools", "output_type") + + def __init__( + self, + name: str, + handoffs: list[str] | None = None, + tools: list[str] | None = None, + output_type: str | None = None, + ): + self.name = name + self.handoffs: list[str] | None = handoffs + self.tools: list[str] | None = tools + self.output_type: str | None = output_type + + @property + def type(self) -> str: + return "agent" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "handoffs": self.handoffs, + "tools": self.tools, + "output_type": self.output_type, + } + + +class FunctionSpanData(SpanData): + __slots__ = ("name", "input", "output") + + def __init__(self, name: str, input: str | None, output: str | None): + self.name = name + self.input = input + self.output = output + + @property + def type(self) -> str: + return "function" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "input": self.input, + "output": self.output, + } + + +class GenerationSpanData(SpanData): + __slots__ = ( + "input", + "output", + "model", + "model_config", + "usage", + ) + + def __init__( + self, + input: Sequence[Mapping[str, Any]] | None = None, + output: Sequence[Mapping[str, Any]] | None = None, + model: str | None = None, + model_config: Mapping[str, Any] | None = None, + usage: dict[str, Any] | None = None, + ): + self.input = input + self.output = output + self.model = model + self.model_config = model_config + self.usage = usage + + @property + def type(self) -> str: + return "generation" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "input": self.input, + "output": self.output, + "model": self.model, + "model_config": self.model_config, + "usage": self.usage, + } + + +class ResponseSpanData(SpanData): + __slots__ = ("response", "input") + + def __init__( + self, + response: Response | None = None, + input: str | list[ResponseInputItemParam] | None = None, + ) -> None: + self.response = response + # This is not used by the OpenAI trace processors, but is useful for other tracing + # processor implementations + self.input = input + + @property + def type(self) -> str: + return "response" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "response_id": self.response.id if self.response else None, + } + + +class HandoffSpanData(SpanData): + __slots__ = ("from_agent", "to_agent") + + def __init__(self, from_agent: str | None, to_agent: str | None): + self.from_agent = from_agent + self.to_agent = to_agent + + @property + def type(self) -> str: + return "handoff" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "from_agent": self.from_agent, + "to_agent": self.to_agent, + } + + +class CustomSpanData(SpanData): + __slots__ = ("name", "data") + + def __init__(self, name: str, data: dict[str, Any]): + self.name = name + self.data = data + + @property + def type(self) -> str: + return "custom" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "data": self.data, + } + + +class GuardrailSpanData(SpanData): + __slots__ = ("name", "triggered") + + def __init__(self, name: str, triggered: bool = False): + self.name = name + self.triggered = triggered + + @property + def type(self) -> str: + return "guardrail" + + def export(self) -> dict[str, Any]: + return { + "type": self.type, + "name": self.name, + "triggered": self.triggered, + } diff --git a/tests/src/agents/tracing/spans.py b/tests/src/agents/tracing/spans.py new file mode 100644 index 0000000..d682a9a --- /dev/null +++ b/tests/src/agents/tracing/spans.py @@ -0,0 +1,264 @@ +from __future__ import annotations + +import abc +import contextvars +from typing import Any, Generic, TypeVar + +from typing_extensions import TypedDict + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope +from .span_data import SpanData + +TSpanData = TypeVar("TSpanData", bound=SpanData) + + +class SpanError(TypedDict): + message: str + data: dict[str, Any] | None + + +class Span(abc.ABC, Generic[TSpanData]): + @property + @abc.abstractmethod + def trace_id(self) -> str: + pass + + @property + @abc.abstractmethod + def span_id(self) -> str: + pass + + @property + @abc.abstractmethod + def span_data(self) -> TSpanData: + pass + + @abc.abstractmethod + def start(self, mark_as_current: bool = False): + """ + Start the span. + + Args: + mark_as_current: If true, the span will be marked as the current span. + """ + pass + + @abc.abstractmethod + def finish(self, reset_current: bool = False) -> None: + """ + Finish the span. + + Args: + reset_current: If true, the span will be reset as the current span. + """ + pass + + @abc.abstractmethod + def __enter__(self) -> Span[TSpanData]: + pass + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + @property + @abc.abstractmethod + def parent_id(self) -> str | None: + pass + + @abc.abstractmethod + def set_error(self, error: SpanError) -> None: + pass + + @property + @abc.abstractmethod + def error(self) -> SpanError | None: + pass + + @abc.abstractmethod + def export(self) -> dict[str, Any] | None: + pass + + @property + @abc.abstractmethod + def started_at(self) -> str | None: + pass + + @property + @abc.abstractmethod + def ended_at(self) -> str | None: + pass + + +class NoOpSpan(Span[TSpanData]): + __slots__ = ("_span_data", "_prev_span_token") + + def __init__(self, span_data: TSpanData): + self._span_data = span_data + self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None + + @property + def trace_id(self) -> str: + return "no-op" + + @property + def span_id(self) -> str: + return "no-op" + + @property + def span_data(self) -> TSpanData: + return self._span_data + + @property + def parent_id(self) -> str | None: + return None + + def start(self, mark_as_current: bool = False): + if mark_as_current: + self._prev_span_token = Scope.set_current_span(self) + + def finish(self, reset_current: bool = False) -> None: + if reset_current and self._prev_span_token is not None: + Scope.reset_current_span(self._prev_span_token) + self._prev_span_token = None + + def __enter__(self) -> Span[TSpanData]: + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + reset_current = True + if exc_type is GeneratorExit: + logger.debug("GeneratorExit, skipping span reset") + reset_current = False + + self.finish(reset_current=reset_current) + + def set_error(self, error: SpanError) -> None: + pass + + @property + def error(self) -> SpanError | None: + return None + + def export(self) -> dict[str, Any] | None: + return None + + @property + def started_at(self) -> str | None: + return None + + @property + def ended_at(self) -> str | None: + return None + + +class SpanImpl(Span[TSpanData]): + __slots__ = ( + "_trace_id", + "_span_id", + "_parent_id", + "_started_at", + "_ended_at", + "_error", + "_prev_span_token", + "_processor", + "_span_data", + ) + + def __init__( + self, + trace_id: str, + span_id: str | None, + parent_id: str | None, + processor: TracingProcessor, + span_data: TSpanData, + ): + self._trace_id = trace_id + self._span_id = span_id or util.gen_span_id() + self._parent_id = parent_id + self._started_at: str | None = None + self._ended_at: str | None = None + self._processor = processor + self._error: SpanError | None = None + self._prev_span_token: contextvars.Token[Span[TSpanData] | None] | None = None + self._span_data = span_data + + @property + def trace_id(self) -> str: + return self._trace_id + + @property + def span_id(self) -> str: + return self._span_id + + @property + def span_data(self) -> TSpanData: + return self._span_data + + @property + def parent_id(self) -> str | None: + return self._parent_id + + def start(self, mark_as_current: bool = False): + if self.started_at is not None: + logger.warning("Span already started") + return + + self._started_at = util.time_iso() + self._processor.on_span_start(self) + if mark_as_current: + self._prev_span_token = Scope.set_current_span(self) + + def finish(self, reset_current: bool = False) -> None: + if self.ended_at is not None: + logger.warning("Span already finished") + return + + self._ended_at = util.time_iso() + self._processor.on_span_end(self) + if reset_current and self._prev_span_token is not None: + Scope.reset_current_span(self._prev_span_token) + self._prev_span_token = None + + def __enter__(self) -> Span[TSpanData]: + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + reset_current = True + if exc_type is GeneratorExit: + logger.debug("GeneratorExit, skipping span reset") + reset_current = False + + self.finish(reset_current=reset_current) + + def set_error(self, error: SpanError) -> None: + self._error = error + + @property + def error(self) -> SpanError | None: + return self._error + + @property + def started_at(self) -> str | None: + return self._started_at + + @property + def ended_at(self) -> str | None: + return self._ended_at + + def export(self) -> dict[str, Any] | None: + return { + "object": "trace.span", + "id": self.span_id, + "trace_id": self.trace_id, + "parent_id": self._parent_id, + "started_at": self._started_at, + "ended_at": self._ended_at, + "span_data": self.span_data.export(), + "error": self._error, + } diff --git a/tests/src/agents/tracing/traces.py b/tests/src/agents/tracing/traces.py new file mode 100644 index 0000000..bf3b43d --- /dev/null +++ b/tests/src/agents/tracing/traces.py @@ -0,0 +1,195 @@ +from __future__ import annotations + +import abc +import contextvars +from typing import Any + +from . import util +from .logger import logger +from .processor_interface import TracingProcessor +from .scope import Scope + + +class Trace: + """ + A trace is the root level object that tracing creates. It represents a logical "workflow". + """ + + @abc.abstractmethod + def __enter__(self) -> Trace: + pass + + @abc.abstractmethod + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + @abc.abstractmethod + def start(self, mark_as_current: bool = False): + """ + Start the trace. + + Args: + mark_as_current: If true, the trace will be marked as the current trace. + """ + pass + + @abc.abstractmethod + def finish(self, reset_current: bool = False): + """ + Finish the trace. + + Args: + reset_current: If true, the trace will be reset as the current trace. + """ + pass + + @property + @abc.abstractmethod + def trace_id(self) -> str: + """ + The trace ID. + """ + pass + + @property + @abc.abstractmethod + def name(self) -> str: + """ + The name of the workflow being traced. + """ + pass + + @abc.abstractmethod + def export(self) -> dict[str, Any] | None: + """ + Export the trace as a dictionary. + """ + pass + + +class NoOpTrace(Trace): + """ + A no-op trace that will not be recorded. + """ + + def __init__(self): + self._started = False + self._prev_context_token: contextvars.Token[Trace | None] | None = None + + def __enter__(self) -> Trace: + if self._started: + if not self._prev_context_token: + logger.error("Trace already started but no context token set") + return self + + self._started = True + self.start(mark_as_current=True) + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish(reset_current=True) + + def start(self, mark_as_current: bool = False): + if mark_as_current: + self._prev_context_token = Scope.set_current_trace(self) + + def finish(self, reset_current: bool = False): + if reset_current and self._prev_context_token is not None: + Scope.reset_current_trace(self._prev_context_token) + self._prev_context_token = None + + @property + def trace_id(self) -> str: + return "no-op" + + @property + def name(self) -> str: + return "no-op" + + def export(self) -> dict[str, Any] | None: + return None + + +NO_OP_TRACE = NoOpTrace() + + +class TraceImpl(Trace): + """ + A trace that will be recorded by the tracing library. + """ + + __slots__ = ( + "_name", + "_trace_id", + "group_id", + "metadata", + "_prev_context_token", + "_processor", + "_started", + ) + + def __init__( + self, + name: str, + trace_id: str | None, + group_id: str | None, + metadata: dict[str, Any] | None, + processor: TracingProcessor, + ): + self._name = name + self._trace_id = trace_id or util.gen_trace_id() + self.group_id = group_id + self.metadata = metadata + self._prev_context_token: contextvars.Token[Trace | None] | None = None + self._processor = processor + self._started = False + + @property + def trace_id(self) -> str: + return self._trace_id + + @property + def name(self) -> str: + return self._name + + def start(self, mark_as_current: bool = False): + if self._started: + return + + self._started = True + self._processor.on_trace_start(self) + + if mark_as_current: + self._prev_context_token = Scope.set_current_trace(self) + + def finish(self, reset_current: bool = False): + if not self._started: + return + + self._processor.on_trace_end(self) + + if reset_current and self._prev_context_token is not None: + Scope.reset_current_trace(self._prev_context_token) + self._prev_context_token = None + + def __enter__(self) -> Trace: + if self._started: + if not self._prev_context_token: + logger.error("Trace already started but no context token set") + return self + + self.start(mark_as_current=True) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.finish(reset_current=exc_type is not GeneratorExit) + + def export(self) -> dict[str, Any] | None: + return { + "object": "trace", + "id": self.trace_id, + "workflow_name": self.name, + "group_id": self.group_id, + "metadata": self.metadata, + } diff --git a/tests/src/agents/tracing/util.py b/tests/src/agents/tracing/util.py new file mode 100644 index 0000000..3e5cad9 --- /dev/null +++ b/tests/src/agents/tracing/util.py @@ -0,0 +1,17 @@ +import uuid +from datetime import datetime, timezone + + +def time_iso() -> str: + """Returns the current time in ISO 8601 format.""" + return datetime.now(timezone.utc).isoformat() + + +def gen_trace_id() -> str: + """Generates a new trace ID.""" + return f"trace_{uuid.uuid4().hex}" + + +def gen_span_id() -> str: + """Generates a new span ID.""" + return f"span_{uuid.uuid4().hex[:24]}" diff --git a/tests/src/agents/usage.py b/tests/src/agents/usage.py new file mode 100644 index 0000000..23d989b --- /dev/null +++ b/tests/src/agents/usage.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass + + +@dataclass +class Usage: + requests: int = 0 + """Total requests made to the LLM API.""" + + input_tokens: int = 0 + """Total input tokens sent, across all requests.""" + + output_tokens: int = 0 + """Total output tokens received, across all requests.""" + + total_tokens: int = 0 + """Total tokens sent and received, across all requests.""" + + def add(self, other: "Usage") -> None: + self.requests += other.requests if other.requests else 0 + self.input_tokens += other.input_tokens if other.input_tokens else 0 + self.output_tokens += other.output_tokens if other.output_tokens else 0 + self.total_tokens += other.total_tokens if other.total_tokens else 0 diff --git a/tests/src/agents/version.py b/tests/src/agents/version.py new file mode 100644 index 0000000..a0b7e9b --- /dev/null +++ b/tests/src/agents/version.py @@ -0,0 +1,7 @@ +import importlib.metadata + +try: + __version__ = importlib.metadata.version("agents") +except importlib.metadata.PackageNotFoundError: + # Fallback if running from source without being installed + __version__ = "0.0.0" diff --git a/tests/src/openai_agents.egg-info/PKG-INFO b/tests/src/openai_agents.egg-info/PKG-INFO new file mode 100644 index 0000000..ebf2d7c --- /dev/null +++ b/tests/src/openai_agents.egg-info/PKG-INFO @@ -0,0 +1,217 @@ +Metadata-Version: 2.2 +Name: openai-agents +Version: 0.0.1 +Summary: OpenAI Agents SDK +Author-email: OpenAI +Project-URL: Homepage, https://github.com/openai/openai-agents-python +Project-URL: Repository, https://github.com/openai/openai-agents-python +Classifier: Typing :: Typed +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Requires-Dist: openai@ {root:parent:uri}/openai-1.30.1-py3-none-any.whl +Requires-Dist: pydantic<3,>=2.10 +Requires-Dist: griffe<2,>=1.5.6 +Requires-Dist: typing-extensions<5,>=4.12.2 +Requires-Dist: requests<3,>=2.0 +Requires-Dist: types-requests<3,>=2.0 + +# OpenAI Agents SDK + +The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows. + +### Core concepts: +1. [**Agents,**](docs/agents.md) which are LLMs configured with instructions, tools, guardrails, and handoffs +2. [**Handoffs,**](docs/handoffs.md) which allow agents to transfer control to other agents for specific tasks +3. [**Guardrails,**](docs/guardrails.md) which makes it easy to watch an agent execution and validate inputs/outputs +4. [**Tracing,**](docs/tracing.md) which automatically captures the entire agentic run, allowing you to view, debug and optimize your workflows + +Explore examples of the SDK in action in the [examples](examples) directory. + +## Using the SDK + +1. Set up python env + +``` +python -m venv env +source env/bin/activate +``` + +2. Install Agents SDK + +``` +pip install git+ssh://git@github.com/openai/agentsdk_prototype.git#subdirectory=agents +``` + +## Development (only needed if you need to edit the SDK/examples) + +0. Ensure you have [`uv`](https://docs.astral.sh/uv/) installed. + +```bash +uv --version +``` + +1. Install dependencies/setup virtual environment + +```bash +uv sync +``` + +2. Install the dependencies + +```bash +uv sync --all-extras --all-packages +``` + +3. Activate the virtual environment + +```bash +source .venv/bin/activate +``` + +## Tests + +Make sure the virtual environment is activated first. + +```bash +pytest +``` + +## Hello world example + +```py +from agents.agent import Agent +from agents.run import Runner +import asyncio + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent." +) + +async def main(): + out = await Runner.run(agent, input="Hola, ¿cómo estás?") + print(out) + + +if __name__ == "__main__": + asyncio.run(main()) + +# The capital of the United States is Washington, D.C. +``` + +## Handoffs example + +```py +from agents.agent import Agent +from agents.run import Runner +import asyncio + +spanish_agent = Agent( + name="spanish_agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="english_agent", + instructions="You only speak English", +) + +triage_agent = Agent( + name="triage_agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], +) + + +async def main(): + out = await Runner.run(triage_agent, input="Hola, ¿cómo estás?") + print(out) + + +if __name__ == "__main__": + asyncio.run(main()) + +# ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás? +``` + +## Functions example + +```python +from agents.agent import Agent +from agents.run import Runner +import asyncio +from agents.tool import function_tool + + +@function_tool +def get_weather(city: str) -> str: + print(f"Getting weather for {city}") + return f"The weather in {city} is sunny." + + +agent = Agent( + name="Hello world", + instructions="You are a helpful agent.", + tools=[get_weather], +) + + +async def main(): + out = await Runner.run(agent, input="What's the weather in Tokyo?") + print(out.final_output) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +For more complex systems, we recommend including detailed instructions about handoffs. We have a recommendation in `handoff.RECOMMENDED_PROMPT_PREFIX` that can be used to add these instructions to an agent. + +```py +agent = Agent( + ..., + instructions=f"{handoff.RECOMMENDED_PROMPT_PREFIX}\n\n{instructions}" +) +``` + +## The agent loop + +When you call `Runner.run()`, we run a loop until we get a final output. + +1. We call the LLM, using the model and settings on the agent, and the message history. +2. The LLM returns a response, which may include tool calls. +3. If the response has a final output (see below for the more on this), we return it and end the loop. +4. If the response has a handoff, we set the agent to the new agent and go back to step 1. +5. We process the tool calls (if any) and append the tool responses messsages. Then we go to step 1. + +There is a `max_turns` parameter that you can use to limit the number of times the loop executes. + +### Final output + +There are two ways to get a **final output**: + +1. If you set an `output_type` on the agent, the LLM is given a special tool called `final_output`. If it uses this tool, the output of the tool is the final output. +2. If there's no `output_type`, then we assume the final output is a string. As soon as the LLM produces a message without any tool calls, that is considered the final output. + +As a result, the mental model for the agent loop is: + +1. If the current agent has an `output_type`, the loop runs until the agent uses that tool to return the final output. +2. If the current agent does not have an `output_type`, the loop runs until the current agent produces a message without any tool calls. + +## Common agent patterns + +There are a number of useful patterns in agentic apps. There are a number of examples in [`examples/agent_patterns`](examples/agent_patterns), and we recommend reading them. diff --git a/tests/src/openai_agents.egg-info/SOURCES.txt b/tests/src/openai_agents.egg-info/SOURCES.txt new file mode 100644 index 0000000..695ad1f --- /dev/null +++ b/tests/src/openai_agents.egg-info/SOURCES.txt @@ -0,0 +1,81 @@ +README.md +pyproject.toml +src/agents/__init__.py +src/agents/_config.py +src/agents/_debug.py +src/agents/_run_impl.py +src/agents/_utils.py +src/agents/agent.py +src/agents/agent_output.py +src/agents/call_agent_tool.py +src/agents/computer.py +src/agents/exceptions.py +src/agents/function_schema.py +src/agents/guardrail.py +src/agents/handoffs.py +src/agents/items.py +src/agents/lifecycle.py +src/agents/logger.py +src/agents/model_settings.py +src/agents/result.py +src/agents/run.py +src/agents/run_context.py +src/agents/strict_schema.py +src/agents/tool.py +src/agents/usage.py +src/agents/version.py +src/agents/extensions/__init__.py +src/agents/extensions/handoff_filters.py +src/agents/extensions/handoff_prompt.py +src/agents/models/__init__.py +src/agents/models/_openai_shared.py +src/agents/models/fake_id.py +src/agents/models/interface.py +src/agents/models/map.py +src/agents/models/openai_chatcompletions.py +src/agents/models/openai_responses.py +src/agents/tracing/__init__.py +src/agents/tracing/create.py +src/agents/tracing/logger.py +src/agents/tracing/processor_interface.py +src/agents/tracing/processors.py +src/agents/tracing/scope.py +src/agents/tracing/setup.py +src/agents/tracing/span_data.py +src/agents/tracing/spans.py +src/agents/tracing/traces.py +src/agents/tracing/util.py +src/openai_agents.egg-info/PKG-INFO +src/openai_agents.egg-info/SOURCES.txt +src/openai_agents.egg-info/dependency_links.txt +src/openai_agents.egg-info/requires.txt +src/openai_agents.egg-info/top_level.txt +tests/test_agent_config.py +tests/test_agent_hooks.py +tests/test_agent_runner.py +tests/test_agent_runner_streamed.py +tests/test_agent_tracing.py +tests/test_config.py +tests/test_doc_parsing.py +tests/test_function_schema.py +tests/test_function_tool.py +tests/test_function_tool_decorator.py +tests/test_global_hooks.py +tests/test_guardrails.py +tests/test_handoff_tool.py +tests/test_items_helpers.py +tests/test_max_turns.py +tests/test_model_mapper.py +tests/test_openai_chatcompletions_converter.py +tests/test_openai_responses_converter.py +tests/test_output_tool.py +tests/test_responses.py +tests/test_run_config.py +tests/test_run_step_execution.py +tests/test_run_step_processing.py +tests/test_tool_converter.py +tests/test_trace_processor.py +tests/test_tracing.py +tests/test_tracing_errors.py +tests/test_tracing_errors_streamed.py +tests/testing_processor.py \ No newline at end of file diff --git a/tests/src/openai_agents.egg-info/dependency_links.txt b/tests/src/openai_agents.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/src/openai_agents.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/tests/src/openai_agents.egg-info/requires.txt b/tests/src/openai_agents.egg-info/requires.txt new file mode 100644 index 0000000..3dbad2b --- /dev/null +++ b/tests/src/openai_agents.egg-info/requires.txt @@ -0,0 +1,6 @@ +openai@ {root:parent:uri}/openai-1.30.1-py3-none-any.whl +pydantic<3,>=2.10 +griffe<2,>=1.5.6 +typing-extensions<5,>=4.12.2 +requests<3,>=2.0 +types-requests<3,>=2.0 diff --git a/tests/src/openai_agents.egg-info/top_level.txt b/tests/src/openai_agents.egg-info/top_level.txt new file mode 100644 index 0000000..4a33ff6 --- /dev/null +++ b/tests/src/openai_agents.egg-info/top_level.txt @@ -0,0 +1 @@ +agents diff --git a/tests/test_agent_config.py b/tests/test_agent_config.py new file mode 100644 index 0000000..44339da --- /dev/null +++ b/tests/test_agent_config.py @@ -0,0 +1,167 @@ +import pytest +from pydantic import BaseModel + +from agents import Agent, Handoff, RunContextWrapper, Runner, handoff + + +@pytest.mark.asyncio +async def test_system_instructions(): + agent = Agent[None]( + name="test", + instructions="abc123", + ) + context = RunContextWrapper(None) + + assert await agent.get_system_prompt(context) == "abc123" + + def sync_instructions(agent: Agent[None], context: RunContextWrapper[None]) -> str: + return "sync_123" + + agent = agent.clone(instructions=sync_instructions) + assert await agent.get_system_prompt(context) == "sync_123" + + async def async_instructions(agent: Agent[None], context: RunContextWrapper[None]) -> str: + return "async_123" + + agent = agent.clone(instructions=async_instructions) + assert await agent.get_system_prompt(context) == "async_123" + + +@pytest.mark.asyncio +async def test_handoff_with_agents(): + agent_1 = Agent( + name="agent_1", + ) + + agent_2 = Agent( + name="agent_2", + ) + + agent_3 = Agent( + name="agent_3", + handoffs=[agent_1, agent_2], + ) + + handoffs = Runner._get_handoffs(agent_3) + assert len(handoffs) == 2 + + assert handoffs[0].agent_name == "agent_1" + assert handoffs[1].agent_name == "agent_2" + + first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") + assert first_return == agent_1 + + second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") + assert second_return == agent_2 + + +@pytest.mark.asyncio +async def test_handoff_with_handoff_obj(): + agent_1 = Agent( + name="agent_1", + ) + + agent_2 = Agent( + name="agent_2", + ) + + agent_3 = Agent( + name="agent_3", + handoffs=[ + handoff(agent_1), + handoff( + agent_2, + tool_name_override="transfer_to_2", + tool_description_override="description_2", + ), + ], + ) + + handoffs = Runner._get_handoffs(agent_3) + assert len(handoffs) == 2 + + assert handoffs[0].agent_name == "agent_1" + assert handoffs[1].agent_name == "agent_2" + + assert handoffs[0].tool_name == Handoff.default_tool_name(agent_1) + assert handoffs[1].tool_name == "transfer_to_2" + + assert handoffs[0].tool_description == Handoff.default_tool_description(agent_1) + assert handoffs[1].tool_description == "description_2" + + first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") + assert first_return == agent_1 + + second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") + assert second_return == agent_2 + + +@pytest.mark.asyncio +async def test_handoff_with_handoff_obj_and_agent(): + agent_1 = Agent( + name="agent_1", + ) + + agent_2 = Agent( + name="agent_2", + ) + + agent_3 = Agent( + name="agent_3", + handoffs=[handoff(agent_1), agent_2], + ) + + handoffs = Runner._get_handoffs(agent_3) + assert len(handoffs) == 2 + + assert handoffs[0].agent_name == "agent_1" + assert handoffs[1].agent_name == "agent_2" + + assert handoffs[0].tool_name == Handoff.default_tool_name(agent_1) + assert handoffs[1].tool_name == Handoff.default_tool_name(agent_2) + + assert handoffs[0].tool_description == Handoff.default_tool_description(agent_1) + assert handoffs[1].tool_description == Handoff.default_tool_description(agent_2) + + first_return = await handoffs[0].on_invoke_handoff(RunContextWrapper(None), "") + assert first_return == agent_1 + + second_return = await handoffs[1].on_invoke_handoff(RunContextWrapper(None), "") + assert second_return == agent_2 + + +@pytest.mark.asyncio +async def test_agent_cloning(): + agent = Agent( + name="test", + handoff_description="test_description", + model="o3-mini", + ) + + cloned = agent.clone( + handoff_description="new_description", + model="o1", + ) + + assert cloned.name == "test" + assert cloned.handoff_description == "new_description" + assert cloned.model == "o1" + + +class Foo(BaseModel): + bar: str + + +@pytest.mark.asyncio +async def test_agent_final_output(): + agent = Agent( + name="test", + output_type=Foo, + ) + + schema = Runner._get_output_schema(agent) + assert schema is not None + assert schema.output_type == Foo + assert schema.strict_json_schema is True + assert schema.json_schema() is not None + assert not schema.is_plain_text() diff --git a/tests/test_agent_hooks.py b/tests/test_agent_hooks.py new file mode 100644 index 0000000..33107cb --- /dev/null +++ b/tests/test_agent_hooks.py @@ -0,0 +1,426 @@ +from __future__ import annotations + +import json +from collections import defaultdict +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents.agent import Agent +from agents.lifecycle import AgentHooks +from agents.run import Runner +from agents.run_context import RunContextWrapper, TContext +from agents.tool import Tool + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_message, +) + + +class AgentHooksForTests(AgentHooks): + def __init__(self): + self.events: dict[str, int] = defaultdict(int) + + def reset(self): + self.events.clear() + + async def on_start(self, context: RunContextWrapper[TContext], agent: Agent[TContext]) -> None: + self.events["on_start"] += 1 + + async def on_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + self.events["on_end"] += 1 + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + source: Agent[TContext], + ) -> None: + self.events["on_handoff"] += 1 + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + self.events["on_tool_start"] += 1 + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + self.events["on_tool_end"] += 1 + + +@pytest.mark.asyncio +async def test_non_streamed_agent_hooks(): + hooks = AgentHooksForTests() + model = FakeModel() + agent_1 = Agent( + name="test_1", + model=model, + ) + agent_2 = Agent( + name="test_2", + model=model, + ) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + hooks=hooks, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_text_message("user_message")]) + output = await Runner.run(agent_3, input="user_message") + assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message") + + # Shouldn't have on_end because it's not the last agent + assert hooks.events == { + "on_start": 1, # Agent runs once + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: text message + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message") + + assert hooks.events == { + "on_start": 2, # Agent runs twice + "on_tool_start": 2, # Only one tool call + "on_tool_end": 2, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_end": 1, # Agent 3 is the last agent + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +@pytest.mark.asyncio +async def test_streamed_agent_hooks(): + hooks = AgentHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + hooks=hooks, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_text_message("user_message")]) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + + # Shouldn't have on_end because it's not the last agent + assert hooks.events == { + "on_start": 1, # Agent runs twice + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: text message + [get_text_message("done")], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + + assert hooks.events == { + "on_start": 2, # Agent runs twice + "on_tool_start": 2, # Only one tool call + "on_tool_end": 2, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_end": 1, # Agent 3 is the last agent + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +class Foo(TypedDict): + a: str + + +@pytest.mark.asyncio +async def test_structed_output_non_streamed_agent_hooks(): + hooks = AgentHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + hooks=hooks, + output_type=Foo, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) + output = await Runner.run(agent_3, input="user_message") + assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: end message (for agent 1) + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message") + + # Shouldn't have on_end because it's not the last agent + assert hooks.events == { + "on_start": 1, # Agent runs twice + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: end message (for agent 3) + [get_final_output_message(json.dumps({"a": "b"}))], + ] + ) + await Runner.run(agent_3, input="user_message") + + assert hooks.events == { + "on_start": 2, # Agent runs twice + "on_tool_start": 2, # Only one tool call + "on_tool_end": 2, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_end": 1, # Agent 3 is the last agent + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +@pytest.mark.asyncio +async def test_structed_output_streamed_agent_hooks(): + hooks = AgentHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + hooks=hooks, + output_type=Foo, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + assert hooks.events == {"on_start": 1, "on_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: end message (for agent 1) + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message") + # Shouldn't have on_end because it's not the last agent + assert hooks.events == { + "on_start": 1, # Agent runs twice + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: end message (for agent 3) + [get_final_output_message(json.dumps({"a": "b"}))], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + + assert hooks.events == { + "on_start": 2, # Agent runs twice + "on_tool_start": 2, # 2 tool calls + "on_tool_end": 2, # 2 tool calls + "on_handoff": 1, # 1 handoff + "on_end": 1, # Agent 3 is the last agent + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +class EmptyAgentHooks(AgentHooks): + pass + + +@pytest.mark.asyncio +async def test_base_agent_hooks_dont_crash(): + hooks = EmptyAgentHooks() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + hooks=hooks, + output_type=Foo, + ) + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: end message (for agent 1) + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message") + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: end message (for agent 3) + [get_final_output_message(json.dumps({"a": "b"}))], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message") + async for _ in output.stream_events(): + pass diff --git a/tests/test_agent_runner.py b/tests/test_agent_runner.py new file mode 100644 index 0000000..c124915 --- /dev/null +++ b/tests/test_agent_runner.py @@ -0,0 +1,554 @@ +from __future__ import annotations + +import json +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents import ( + Agent, + GuardrailFunctionOutput, + Handoff, + HandoffInputData, + InputGuardrail, + InputGuardrailTripwireTriggered, + ModelBehaviorError, + OutputGuardrail, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + UserError, + handoff, +) + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_input_item, + get_text_message, +) + + +@pytest.mark.asyncio +async def test_simple_first_run(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + ) + model.set_next_output([get_text_message("first")]) + + result = await Runner.run(agent, input="test") + assert result.input == "test" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "first" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert result.raw_responses[0].output == [get_text_message("first")] + assert result.last_agent == agent + + assert len(result.to_input_list()) == 2, "should have original input and generated item" + + model.set_next_output([get_text_message("second")]) + + result = await Runner.run( + agent, input=[get_text_input_item("message"), get_text_input_item("another_message")] + ) + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "second" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert len(result.to_input_list()) == 3, "should have original input and generated item" + + +@pytest.mark.asyncio +async def test_subsequent_runs(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + ) + model.set_next_output([get_text_message("third")]) + + result = await Runner.run(agent, input="test") + assert result.input == "test" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert len(result.to_input_list()) == 2, "should have original input and generated item" + + model.set_next_output([get_text_message("fourth")]) + + result = await Runner.run(agent, input=result.to_input_list()) + assert len(result.input) == 2, f"should have previous input but got {result.input}" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "fourth" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert result.raw_responses[0].output == [get_text_message("fourth")] + assert result.last_agent == agent + assert len(result.to_input_list()) == 3, "should have original input and generated items" + + +@pytest.mark.asyncio +async def test_tool_call_runs(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + tools=[get_function_tool("foo", "tool_result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a message and tool call + [get_text_message("a_message"), get_function_tool_call("foo", json.dumps({"a": "b"}))], + # Second turn: text message + [get_text_message("done")], + ] + ) + + result = await Runner.run(agent, input="user_message") + + assert result.final_output == "done" + assert len(result.raw_responses) == 2, ( + "should have two responses: the first which produces a tool call, and the second which" + "handles the tool result" + ) + + assert len(result.to_input_list()) == 5, ( + "should have five inputs: the original input, the message, the tool call, the tool result " + "and the done message" + ) + + +@pytest.mark.asyncio +async def test_handoffs(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + ) + agent_3 = Agent( + name="test", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + + result = await Runner.run(agent_3, input="user_message") + + assert result.final_output == "done" + assert len(result.raw_responses) == 3, "should have three model responses" + assert len(result.to_input_list()) == 7, ( + "should have 7 inputs: orig input, tool call, tool result, message, handoff, handoff" + "result, and done message" + ) + assert result.last_agent == agent_1, "should have handed off to agent_1" + + +class Foo(TypedDict): + bar: str + + +@pytest.mark.asyncio +async def test_structured_output(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + tools=[get_function_tool("bar", "bar_result")], + output_type=Foo, + ) + + agent_2 = Agent( + name="test", + model=model, + tools=[get_function_tool("foo", "foo_result")], + handoffs=[agent_1], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: tool call and structured output + [ + get_function_tool_call("bar", json.dumps({"bar": "baz"})), + get_final_output_message(json.dumps(Foo(bar="baz"))), + ], + ] + ) + + result = await Runner.run( + agent_2, + input=[ + get_text_input_item("user_message"), + get_text_input_item("another_message"), + ], + ) + + assert result.final_output == Foo(bar="baz") + assert len(result.raw_responses) == 3, "should have three model responses" + assert len(result.to_input_list()) == 10, ( + "should have input: 2 orig inputs, function call, function call result, message, handoff, " + "handoff output, tool call, tool call result, final output message" + ) + + assert result.last_agent == agent_1, "should have handed off to agent_1" + assert result.final_output == Foo(bar="baz"), "should have structured output" + + +def remove_new_items(handoff_input_data: HandoffInputData) -> HandoffInputData: + return HandoffInputData( + input_history=handoff_input_data.input_history, + pre_handoff_items=(), + new_items=(), + ) + + +@pytest.mark.asyncio +async def test_handoff_filters(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + input_filter=remove_new_items, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + result = await Runner.run(agent_2, input="user_message") + + assert result.final_output == "last" + assert len(result.raw_responses) == 2, "should have two model responses" + assert len(result.to_input_list()) == 2, ( + "should only have 2 inputs: orig input and last message" + ) + + +@pytest.mark.asyncio +async def test_async_input_filter_fails(): + # DO NOT rename this without updating pyproject.toml + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + async def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: + return data # pragma: no cover + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + # Purposely ignoring the type error here to simulate invalid input + input_filter=invalid_input_filter, # type: ignore + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + await Runner.run(agent_2, input="user_message") + + +@pytest.mark.asyncio +async def test_invalid_input_filter_fails(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: + # Purposely returning a string to simulate invalid output + return "foo" # type: ignore + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + input_filter=invalid_input_filter, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + await Runner.run(agent_2, input="user_message") + + +@pytest.mark.asyncio +async def test_non_callable_input_filter_causes_error(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + # Purposely ignoring the type error here to simulate invalid input + input_filter="foo", # type: ignore + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + await Runner.run(agent_2, input="user_message") + + +@pytest.mark.asyncio +async def test_handoff_on_input(): + call_output: str | None = None + + def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: + nonlocal call_output + call_output = data["bar"] + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + on_handoff=on_input, + input_type=Foo, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [ + get_text_message("1"), + get_text_message("2"), + get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), + ], + [get_text_message("last")], + ] + ) + + result = await Runner.run(agent_2, input="user_message") + + assert result.final_output == "last" + + assert call_output == "test_input", "should have called the handoff with the correct input" + + +@pytest.mark.asyncio +async def test_async_handoff_on_input(): + call_output: str | None = None + + async def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: + nonlocal call_output + call_output = data["bar"] + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + on_handoff=on_input, + input_type=Foo, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [ + get_text_message("1"), + get_text_message("2"), + get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), + ], + [get_text_message("last")], + ] + ) + + result = await Runner.run(agent_2, input="user_message") + + assert result.final_output == "last" + + assert call_output == "test_input", "should have called the handoff with the correct input" + + +@pytest.mark.asyncio +async def test_wrong_params_on_input_causes_error(): + agent_1 = Agent( + name="test", + ) + + def _on_handoff_too_many_params(ctx: RunContextWrapper[Any], foo: Foo, bar: str) -> None: + pass + + with pytest.raises(UserError): + handoff( + agent_1, + input_type=Foo, + # Purposely ignoring the type error here to simulate invalid input + on_handoff=_on_handoff_too_many_params, # type: ignore + ) + + def on_handoff_too_few_params(ctx: RunContextWrapper[Any]) -> None: + pass + + with pytest.raises(UserError): + handoff( + agent_1, + input_type=Foo, + # Purposely ignoring the type error here to simulate invalid input + on_handoff=on_handoff_too_few_params, # type: ignore + ) + + +@pytest.mark.asyncio +async def test_invalid_handoff_input_json_causes_error(): + agent = Agent(name="test") + h = handoff(agent, input_type=Foo, on_handoff=lambda _ctx, _input: None) + + with pytest.raises(ModelBehaviorError): + await h.on_invoke_handoff( + RunContextWrapper(None), + # Purposely ignoring the type error here to simulate invalid input + None, # type: ignore + ) + + with pytest.raises(ModelBehaviorError): + await h.on_invoke_handoff(RunContextWrapper(None), "invalid") + + +@pytest.mark.asyncio +async def test_input_guardrail_tripwire_triggered_causes_exception(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], input: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + agent = Agent( + name="test", input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)] + ) + model = FakeModel() + model.set_next_output([get_text_message("user_message")]) + + with pytest.raises(InputGuardrailTripwireTriggered): + await Runner.run(agent, input="user_message") + + +@pytest.mark.asyncio +async def test_output_guardrail_tripwire_triggered_causes_exception(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + model = FakeModel() + agent = Agent( + name="test", + output_guardrails=[OutputGuardrail(guardrail_function=guardrail_function)], + model=model, + ) + model.set_next_output([get_text_message("user_message")]) + + with pytest.raises(OutputGuardrailTripwireTriggered): + await Runner.run(agent, input="user_message") diff --git a/tests/test_agent_runner_streamed.py b/tests/test_agent_runner_streamed.py new file mode 100644 index 0000000..4c7c7ef --- /dev/null +++ b/tests/test_agent_runner_streamed.py @@ -0,0 +1,686 @@ +from __future__ import annotations + +import json +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents import ( + Agent, + GuardrailFunctionOutput, + Handoff, + HandoffInputData, + InputGuardrail, + InputGuardrailTripwireTriggered, + OutputGuardrail, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + UserError, + handoff, +) +from agents.items import RunItem +from agents.run import RunConfig +from agents.stream_events import AgentUpdatedStreamEvent + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_input_item, + get_text_message, +) + + +@pytest.mark.asyncio +async def test_simple_first_run(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + ) + model.set_next_output([get_text_message("first")]) + + result = Runner.run_streamed(agent, input="test") + async for _ in result.stream_events(): + pass + + assert result.input == "test" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "first" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert result.raw_responses[0].output == [get_text_message("first")] + assert result.last_agent == agent + + assert len(result.to_input_list()) == 2, "should have original input and generated item" + + model.set_next_output([get_text_message("second")]) + + result = Runner.run_streamed( + agent, input=[get_text_input_item("message"), get_text_input_item("another_message")] + ) + async for _ in result.stream_events(): + pass + + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "second" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert len(result.to_input_list()) == 3, "should have original input and generated item" + + +@pytest.mark.asyncio +async def test_subsequent_runs(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + ) + model.set_next_output([get_text_message("third")]) + + result = Runner.run_streamed(agent, input="test") + async for _ in result.stream_events(): + pass + + assert result.input == "test" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert len(result.to_input_list()) == 2, "should have original input and generated item" + + model.set_next_output([get_text_message("fourth")]) + + result = Runner.run_streamed(agent, input=result.to_input_list()) + async for _ in result.stream_events(): + pass + + assert len(result.input) == 2, f"should have previous input but got {result.input}" + assert len(result.new_items) == 1, "exactly one item should be generated" + assert result.final_output == "fourth" + assert len(result.raw_responses) == 1, "exactly one model response should be generated" + assert result.raw_responses[0].output == [get_text_message("fourth")] + assert result.last_agent == agent + assert len(result.to_input_list()) == 3, "should have original input and generated items" + + +@pytest.mark.asyncio +async def test_tool_call_runs(): + model = FakeModel() + agent = Agent( + name="test", + model=model, + tools=[get_function_tool("foo", "tool_result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a message and tool call + [get_text_message("a_message"), get_function_tool_call("foo", json.dumps({"a": "b"}))], + # Second turn: text message + [get_text_message("done")], + ] + ) + + result = Runner.run_streamed(agent, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.final_output == "done" + assert len(result.raw_responses) == 2, ( + "should have two responses: the first which produces a tool call, and the second which" + "handles the tool result" + ) + + assert len(result.to_input_list()) == 5, ( + "should have five inputs: the original input, the message, the tool call, the tool result " + "and the done message" + ) + + +@pytest.mark.asyncio +async def test_handoffs(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + ) + agent_3 = Agent( + name="test", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + + result = Runner.run_streamed(agent_3, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.final_output == "done" + assert len(result.raw_responses) == 3, "should have three model responses" + assert len(result.to_input_list()) == 7, ( + "should have 7 inputs: orig input, tool call, tool result, message, handoff, handoff" + "result, and done message" + ) + assert result.last_agent == agent_1, "should have handed off to agent_1" + + +class Foo(TypedDict): + bar: str + + +@pytest.mark.asyncio +async def test_structured_output(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + tools=[get_function_tool("bar", "bar_result")], + output_type=Foo, + ) + + agent_2 = Agent( + name="test", + model=model, + tools=[get_function_tool("foo", "foo_result")], + handoffs=[agent_1], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: tool call and structured output + [ + get_function_tool_call("bar", json.dumps({"bar": "baz"})), + get_final_output_message(json.dumps(Foo(bar="baz"))), + ], + ] + ) + + result = Runner.run_streamed( + agent_2, + input=[ + get_text_input_item("user_message"), + get_text_input_item("another_message"), + ], + ) + async for _ in result.stream_events(): + pass + + assert result.final_output == Foo(bar="baz") + assert len(result.raw_responses) == 3, "should have three model responses" + assert len(result.to_input_list()) == 10, ( + "should have input: 2 orig inputs, function call, function call result, message, handoff, " + "handoff output, tool call, tool call result, final output" + ) + + assert result.last_agent == agent_1, "should have handed off to agent_1" + assert result.final_output == Foo(bar="baz"), "should have structured output" + + +def remove_new_items(handoff_input_data: HandoffInputData) -> HandoffInputData: + return HandoffInputData( + input_history=handoff_input_data.input_history, + pre_handoff_items=(), + new_items=(), + ) + + +@pytest.mark.asyncio +async def test_handoff_filters(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + input_filter=remove_new_items, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.final_output == "last" + assert len(result.raw_responses) == 2, "should have two model responses" + assert len(result.to_input_list()) == 2, ( + "should only have 2 inputs: orig input and last message" + ) + + +@pytest.mark.asyncio +async def test_async_input_filter_fails(): + # DO NOT rename this without updating pyproject.toml + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + async def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: + return data # pragma: no cover + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + # Purposely ignoring the type error here to simulate invalid input + input_filter=invalid_input_filter, # type: ignore + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_invalid_input_filter_fails(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + def invalid_input_filter(data: HandoffInputData) -> HandoffInputData: + # Purposely returning a string to simulate invalid output + return "foo" # type: ignore + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + input_filter=invalid_input_filter, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_non_callable_input_filter_causes_error(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + async def on_invoke_handoff(_ctx: RunContextWrapper[Any], _input: str) -> Agent[Any]: + return agent_1 + + agent_2 = Agent[None]( + name="test", + model=model, + handoffs=[ + Handoff( + tool_name=Handoff.default_tool_name(agent_1), + tool_description=Handoff.default_tool_description(agent_1), + input_json_schema={}, + on_invoke_handoff=on_invoke_handoff, + agent_name=agent_1.name, + # Purposely ignoring the type error here to simulate invalid input + input_filter="foo", # type: ignore + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_text_message("2"), get_handoff_tool_call(agent_1)], + [get_text_message("last")], + ] + ) + + with pytest.raises(UserError): + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_handoff_on_input(): + call_output: str | None = None + + def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: + nonlocal call_output + call_output = data["bar"] + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + on_handoff=on_input, + input_type=Foo, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [ + get_text_message("1"), + get_text_message("2"), + get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), + ], + [get_text_message("last")], + ] + ) + + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.final_output == "last" + + assert call_output == "test_input", "should have called the handoff with the correct input" + + +@pytest.mark.asyncio +async def test_async_handoff_on_input(): + call_output: str | None = None + + async def on_input(_ctx: RunContextWrapper[Any], data: Foo) -> None: + nonlocal call_output + call_output = data["bar"] + + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + ) + + agent_2 = Agent( + name="test", + model=model, + handoffs=[ + handoff( + agent=agent_1, + on_handoff=on_input, + input_type=Foo, + ) + ], + ) + + model.add_multiple_turn_outputs( + [ + [ + get_text_message("1"), + get_text_message("2"), + get_handoff_tool_call(agent_1, args=json.dumps(Foo(bar="test_input"))), + ], + [get_text_message("last")], + ] + ) + + result = Runner.run_streamed(agent_2, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.final_output == "last" + + assert call_output == "test_input", "should have called the handoff with the correct input" + + +@pytest.mark.asyncio +async def test_input_guardrail_tripwire_triggered_causes_exception_streamed(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], input: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + agent = Agent( + name="test", + input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)], + model=FakeModel(), + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + result = Runner.run_streamed(agent, input="user_message") + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_output_guardrail_tripwire_triggered_causes_exception_streamed(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + model = FakeModel(initial_output=[get_text_message("first_test")]) + + agent = Agent( + name="test", + output_guardrails=[OutputGuardrail(guardrail_function=guardrail_function)], + model=model, + ) + + with pytest.raises(OutputGuardrailTripwireTriggered): + result = Runner.run_streamed(agent, input="user_message") + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_run_input_guardrail_tripwire_triggered_causes_exception_streamed(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], input: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + agent = Agent( + name="test", + model=FakeModel(), + ) + + with pytest.raises(InputGuardrailTripwireTriggered): + result = Runner.run_streamed( + agent, + input="user_message", + run_config=RunConfig( + input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)] + ), + ) + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_run_output_guardrail_tripwire_triggered_causes_exception_streamed(): + def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any + ) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + model = FakeModel(initial_output=[get_text_message("first_test")]) + + agent = Agent( + name="test", + model=model, + ) + + with pytest.raises(OutputGuardrailTripwireTriggered): + result = Runner.run_streamed( + agent, + input="user_message", + run_config=RunConfig( + output_guardrails=[OutputGuardrail(guardrail_function=guardrail_function)] + ), + ) + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_streaming_events(): + model = FakeModel() + agent_1 = Agent( + name="test", + model=model, + tools=[get_function_tool("bar", "bar_result")], + output_type=Foo, + ) + + agent_2 = Agent( + name="test", + model=model, + tools=[get_function_tool("foo", "foo_result")], + handoffs=[agent_1], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("foo", json.dumps({"bar": "baz"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: tool call and structured output + [ + get_function_tool_call("bar", json.dumps({"bar": "baz"})), + get_final_output_message(json.dumps(Foo(bar="baz"))), + ], + ] + ) + + # event_type: (count, event) + event_counts: dict[str, int] = {} + item_data: list[RunItem] = [] + agent_data: list[AgentUpdatedStreamEvent] = [] + + result = Runner.run_streamed( + agent_2, + input=[ + get_text_input_item("user_message"), + get_text_input_item("another_message"), + ], + ) + async for event in result.stream_events(): + event_counts[event.type] = event_counts.get(event.type, 0) + 1 + if event.type == "run_item_stream_event": + item_data.append(event.item) + elif event.type == "agent_updated_stream_event": + agent_data.append(event) + + assert result.final_output == Foo(bar="baz") + assert len(result.raw_responses) == 3, "should have three model responses" + assert len(result.to_input_list()) == 10, ( + "should have input: 2 orig inputs, function call, function call result, message, handoff, " + "handoff output, tool call, tool call result, final output" + ) + + assert result.last_agent == agent_1, "should have handed off to agent_1" + assert result.final_output == Foo(bar="baz"), "should have structured output" + + # Now lets check the events + + expected_item_type_map = { + "tool_call": 2, + "tool_call_output": 2, + "message": 2, + "handoff": 1, + "handoff_output": 1, + } + + total_expected_item_count = sum(expected_item_type_map.values()) + + assert event_counts["run_item_stream_event"] == total_expected_item_count, ( + f"Expectd {total_expected_item_count} events, got {event_counts['run_item_stream_event']}" + f"Expected events were: {expected_item_type_map}, got {event_counts}" + ) + + assert len(item_data) == total_expected_item_count, ( + f"should have {total_expected_item_count} run items" + ) + assert len(agent_data) == 2, "should have 2 agent updated events" + assert agent_data[0].new_agent == agent_2, "should have started with agent_2" + assert agent_data[1].new_agent == agent_1, "should have handed off to agent_1" diff --git a/tests/test_agent_tracing.py b/tests/test_agent_tracing.py new file mode 100644 index 0000000..24bd72f --- /dev/null +++ b/tests/test_agent_tracing.py @@ -0,0 +1,322 @@ +from __future__ import annotations + +import asyncio + +import pytest + +from agents import Agent, RunConfig, Runner, trace + +from .fake_model import FakeModel +from .test_responses import get_text_message +from .testing_processor import fetch_ordered_spans, fetch_traces + + +@pytest.mark.asyncio +async def test_single_run_is_single_trace(): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + await Runner.run(agent, input="first_test") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 1, ( + f"Got {len(spans)}, but expected 1: the agent span. data:" + f"{[span.span_data for span in spans]}" + ) + + +@pytest.mark.asyncio +async def test_multiple_runs_are_multiple_traces(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + ] + ) + agent = Agent( + name="test_agent_1", + model=model, + ) + + await Runner.run(agent, input="first_test") + await Runner.run(agent, input="second_test") + + traces = fetch_traces() + assert len(traces) == 2, f"Expected 2 traces, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, f"Got {len(spans)}, but expected 2: agent span per run" + + +@pytest.mark.asyncio +async def test_wrapped_trace_is_single_trace(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + [get_text_message("third_test")], + ] + ) + with trace(workflow_name="test_workflow"): + agent = Agent( + name="test_agent_1", + model=model, + ) + + await Runner.run(agent, input="first_test") + await Runner.run(agent, input="second_test") + await Runner.run(agent, input="third_test") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 3, f"Got {len(spans)}, but expected 3: the agent span per run" + + +@pytest.mark.asyncio +async def test_parent_disabled_trace_disabled_agent_trace(): + with trace(workflow_name="test_workflow", disabled=True): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + await Runner.run(agent, input="first_test") + + traces = fetch_traces() + assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" + spans = fetch_ordered_spans() + assert len(spans) == 0, ( + f"Expected no spans, got {len(spans)}, with {[x.span_data for x in spans]}" + ) + + +@pytest.mark.asyncio +async def test_manual_disabling_works(): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + await Runner.run(agent, input="first_test", run_config=RunConfig(tracing_disabled=True)) + + traces = fetch_traces() + assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" + spans = fetch_ordered_spans() + assert len(spans) == 0, f"Got {len(spans)}, but expected no spans" + + +@pytest.mark.asyncio +async def test_trace_config_works(): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + await Runner.run( + agent, + input="first_test", + run_config=RunConfig(workflow_name="Foo bar", group_id="123", trace_id="456"), + ) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + export = traces[0].export() + assert export is not None, "Trace export should not be None" + assert export["workflow_name"] == "Foo bar" + assert export["group_id"] == "123" + assert export["id"] == "456" + + +@pytest.mark.asyncio +async def test_not_starting_streaming_creates_trace(): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + result = Runner.run_streamed(agent, input="first_test") + + # Purposely don't await the stream + while True: + if result.is_complete: + break + await asyncio.sleep(0.1) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 1, f"Got {len(spans)}, but expected 1: the agent span" + + # Await the stream to avoid warnings about it not being awaited + async for _ in result.stream_events(): + pass + + +@pytest.mark.asyncio +async def test_streaming_single_run_is_single_trace(): + agent = Agent( + name="test_agent", + model=FakeModel( + initial_output=[get_text_message("first_test")], + ), + ) + + x = Runner.run_streamed(agent, input="first_test") + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + +@pytest.mark.asyncio +async def test_multiple_streamed_runs_are_multiple_traces(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + ] + ) + agent = Agent( + name="test_agent_1", + model=model, + ) + + x = Runner.run_streamed(agent, input="first_test") + async for _ in x.stream_events(): + pass + + x = Runner.run_streamed(agent, input="second_test") + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 2, f"Expected 2 traces, got {len(traces)}" + + +@pytest.mark.asyncio +async def test_wrapped_streaming_trace_is_single_trace(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + [get_text_message("third_test")], + ] + ) + with trace(workflow_name="test_workflow"): + agent = Agent( + name="test_agent_1", + model=model, + ) + + x = Runner.run_streamed(agent, input="first_test") + async for _ in x.stream_events(): + pass + + x = Runner.run_streamed(agent, input="second_test") + async for _ in x.stream_events(): + pass + + x = Runner.run_streamed(agent, input="third_test") + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + +@pytest.mark.asyncio +async def test_wrapped_mixed_trace_is_single_trace(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + [get_text_message("third_test")], + ] + ) + with trace(workflow_name="test_workflow"): + agent = Agent( + name="test_agent_1", + model=model, + ) + + x = Runner.run_streamed(agent, input="first_test") + async for _ in x.stream_events(): + pass + + await Runner.run(agent, input="second_test") + + x = Runner.run_streamed(agent, input="third_test") + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + +@pytest.mark.asyncio +async def test_parent_disabled_trace_disables_streaming_agent_trace(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + ] + ) + with trace(workflow_name="test_workflow", disabled=True): + agent = Agent( + name="test_agent", + model=model, + ) + + x = Runner.run_streamed(agent, input="first_test") + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" + + +@pytest.mark.asyncio +async def test_manual_streaming_disabling_works(): + model = FakeModel() + model.add_multiple_turn_outputs( + [ + [get_text_message("first_test")], + [get_text_message("second_test")], + ] + ) + agent = Agent( + name="test_agent", + model=model, + ) + + x = Runner.run_streamed(agent, input="first_test", run_config=RunConfig(tracing_disabled=True)) + async for _ in x.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 0, f"Expected 0 traces, got {len(traces)}" diff --git a/tests/test_computer_action.py b/tests/test_computer_action.py new file mode 100644 index 0000000..70dcabd --- /dev/null +++ b/tests/test_computer_action.py @@ -0,0 +1,311 @@ +"""Unit tests for the ComputerAction methods in `agents._run_impl`. + +These confirm that the correct computer action method is invoked for each action type and +that screenshots are taken and wrapped appropriately, and that the execute function invokes +hooks and returns the expected ToolCallOutputItem.""" + +from typing import Any + +import pytest +from openai.types.responses.response_computer_tool_call import ( + ActionClick, + ActionDoubleClick, + ActionDrag, + ActionDragPath, + ActionKeypress, + ActionMove, + ActionScreenshot, + ActionScroll, + ActionType, + ActionWait, + ResponseComputerToolCall, +) + +from agents import ( + Agent, + AgentHooks, + AsyncComputer, + Computer, + ComputerTool, + RunConfig, + RunContextWrapper, + RunHooks, +) +from agents._run_impl import ComputerAction, ToolRunComputerAction +from agents.items import ToolCallOutputItem + + +class LoggingComputer(Computer): + """A `Computer` implementation that logs calls to its methods for verification in tests.""" + + def __init__(self, screenshot_return: str = "screenshot"): + self.calls: list[tuple[str, tuple[Any, ...]]] = [] + self._screenshot_return = screenshot_return + + @property + def environment(self): + return "mac" + + @property + def dimensions(self) -> tuple[int, int]: + return (800, 600) + + def screenshot(self) -> str: + self.calls.append(("screenshot", ())) + return self._screenshot_return + + def click(self, x: int, y: int, button: str) -> None: + self.calls.append(("click", (x, y, button))) + + def double_click(self, x: int, y: int) -> None: + self.calls.append(("double_click", (x, y))) + + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + self.calls.append(("scroll", (x, y, scroll_x, scroll_y))) + + def type(self, text: str) -> None: + self.calls.append(("type", (text,))) + + def wait(self) -> None: + self.calls.append(("wait", ())) + + def move(self, x: int, y: int) -> None: + self.calls.append(("move", (x, y))) + + def keypress(self, keys: list[str]) -> None: + self.calls.append(("keypress", (keys,))) + + def drag(self, path: list[tuple[int, int]]) -> None: + self.calls.append(("drag", (tuple(path),))) + + +class LoggingAsyncComputer(AsyncComputer): + """An `AsyncComputer` implementation that logs calls to its methods for verification.""" + + def __init__(self, screenshot_return: str = "async_screenshot"): + self.calls: list[tuple[str, tuple[Any, ...]]] = [] + self._screenshot_return = screenshot_return + + @property + def environment(self): + return "mac" + + @property + def dimensions(self) -> tuple[int, int]: + return (800, 600) + + async def screenshot(self) -> str: + self.calls.append(("screenshot", ())) + return self._screenshot_return + + async def click(self, x: int, y: int, button: str) -> None: + self.calls.append(("click", (x, y, button))) + + async def double_click(self, x: int, y: int) -> None: + self.calls.append(("double_click", (x, y))) + + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + self.calls.append(("scroll", (x, y, scroll_x, scroll_y))) + + async def type(self, text: str) -> None: + self.calls.append(("type", (text,))) + + async def wait(self) -> None: + self.calls.append(("wait", ())) + + async def move(self, x: int, y: int) -> None: + self.calls.append(("move", (x, y))) + + async def keypress(self, keys: list[str]) -> None: + self.calls.append(("keypress", (keys,))) + + async def drag(self, path: list[tuple[int, int]]) -> None: + self.calls.append(("drag", (tuple(path),))) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "action,expected_call", + [ + (ActionClick(type="click", x=10, y=21, button="left"), ("click", (10, 21, "left"))), + (ActionDoubleClick(type="double_click", x=42, y=47), ("double_click", (42, 47))), + ( + ActionDrag(type="drag", path=[ActionDragPath(x=1, y=2), ActionDragPath(x=3, y=4)]), + ("drag", (((1, 2), (3, 4)),)), + ), + (ActionKeypress(type="keypress", keys=["a", "b"]), ("keypress", (["a", "b"],))), + (ActionMove(type="move", x=100, y=200), ("move", (100, 200))), + (ActionScreenshot(type="screenshot"), ("screenshot", ())), + ( + ActionScroll(type="scroll", x=1, y=2, scroll_x=3, scroll_y=4), + ("scroll", (1, 2, 3, 4)), + ), + (ActionType(type="type", text="hello"), ("type", ("hello",))), + (ActionWait(type="wait"), ("wait", ())), + ], +) +async def test_get_screenshot_sync_executes_action_and_takes_screenshot( + action: Any, expected_call: tuple[str, tuple[Any, ...]] +) -> None: + """For each action type, assert that the corresponding computer method is invoked + and that a screenshot is taken and returned.""" + computer = LoggingComputer(screenshot_return="synthetic") + tool_call = ResponseComputerToolCall( + id="c1", + type="computer_call", + action=action, + call_id="c1", + pending_safety_checks=[], + status="completed", + ) + screenshot_output = await ComputerAction._get_screenshot_sync(computer, tool_call) + # The last call is always to screenshot() + if isinstance(action, ActionScreenshot): + # Screenshot is taken twice: initial explicit call plus final capture. + assert computer.calls == [("screenshot", ()), ("screenshot", ())] + else: + assert computer.calls == [expected_call, ("screenshot", ())] + assert screenshot_output == "synthetic" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "action,expected_call", + [ + (ActionClick(type="click", x=2, y=3, button="right"), ("click", (2, 3, "right"))), + (ActionDoubleClick(type="double_click", x=12, y=13), ("double_click", (12, 13))), + ( + ActionDrag(type="drag", path=[ActionDragPath(x=5, y=6), ActionDragPath(x=6, y=7)]), + ("drag", (((5, 6), (6, 7)),)), + ), + (ActionKeypress(type="keypress", keys=["ctrl", "c"]), ("keypress", (["ctrl", "c"],))), + (ActionMove(type="move", x=8, y=9), ("move", (8, 9))), + (ActionScreenshot(type="screenshot"), ("screenshot", ())), + ( + ActionScroll(type="scroll", x=9, y=8, scroll_x=7, scroll_y=6), + ("scroll", (9, 8, 7, 6)), + ), + (ActionType(type="type", text="world"), ("type", ("world",))), + (ActionWait(type="wait"), ("wait", ())), + ], +) +async def test_get_screenshot_async_executes_action_and_takes_screenshot( + action: Any, expected_call: tuple[str, tuple[Any, ...]] +) -> None: + """For each action type on an `AsyncComputer`, the corresponding coroutine should be awaited + and a screenshot taken.""" + computer = LoggingAsyncComputer(screenshot_return="async_return") + assert computer.environment == "mac" + assert computer.dimensions == (800, 600) + tool_call = ResponseComputerToolCall( + id="c2", + type="computer_call", + action=action, + call_id="c2", + pending_safety_checks=[], + status="completed", + ) + screenshot_output = await ComputerAction._get_screenshot_async(computer, tool_call) + if isinstance(action, ActionScreenshot): + assert computer.calls == [("screenshot", ()), ("screenshot", ())] + else: + assert computer.calls == [expected_call, ("screenshot", ())] + assert screenshot_output == "async_return" + + +class LoggingRunHooks(RunHooks[Any]): + """Capture on_tool_start and on_tool_end invocations.""" + + def __init__(self) -> None: + super().__init__() + self.started: list[tuple[Agent[Any], Any]] = [] + self.ended: list[tuple[Agent[Any], Any, str]] = [] + + async def on_tool_start( + self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any + ) -> None: + self.started.append((agent, tool)) + + async def on_tool_end( + self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any, result: str + ) -> None: + self.ended.append((agent, tool, result)) + + +class LoggingAgentHooks(AgentHooks[Any]): + """Minimal override to capture agent's tool hook invocations.""" + + def __init__(self) -> None: + super().__init__() + self.started: list[tuple[Agent[Any], Any]] = [] + self.ended: list[tuple[Agent[Any], Any, str]] = [] + + async def on_tool_start( + self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any + ) -> None: + self.started.append((agent, tool)) + + async def on_tool_end( + self, context: RunContextWrapper[Any], agent: Agent[Any], tool: Any, result: str + ) -> None: + self.ended.append((agent, tool, result)) + + +@pytest.mark.asyncio +async def test_execute_invokes_hooks_and_returns_tool_call_output() -> None: + # ComputerAction.execute should invoke lifecycle hooks and return a proper ToolCallOutputItem. + computer = LoggingComputer(screenshot_return="xyz") + comptool = ComputerTool(computer=computer) + # Create a dummy click action to trigger a click and screenshot. + action = ActionClick(type="click", x=1, y=2, button="left") + tool_call = ResponseComputerToolCall( + id="tool123", + type="computer_call", + action=action, + call_id="tool123", + pending_safety_checks=[], + status="completed", + ) + tool_call.call_id = "tool123" + + # Wrap tool call in ToolRunComputerAction + tool_run = ToolRunComputerAction(tool_call=tool_call, computer_tool=comptool) + # Setup agent and hooks. + agent = Agent(name="test_agent", tools=[comptool]) + # Attach per-agent hooks as well as global run hooks. + agent_hooks = LoggingAgentHooks() + agent.hooks = agent_hooks + run_hooks = LoggingRunHooks() + context_wrapper: RunContextWrapper[Any] = RunContextWrapper(context=None) + # Execute the computer action. + output_item = await ComputerAction.execute( + agent=agent, + action=tool_run, + hooks=run_hooks, + context_wrapper=context_wrapper, + config=RunConfig(), + ) + # Both global and per-agent hooks should have been called once. + assert len(run_hooks.started) == 1 and len(agent_hooks.started) == 1 + assert len(run_hooks.ended) == 1 and len(agent_hooks.ended) == 1 + # The hook invocations should refer to our agent and tool. + assert run_hooks.started[0][0] is agent + assert run_hooks.ended[0][0] is agent + assert run_hooks.started[0][1] is comptool + assert run_hooks.ended[0][1] is comptool + # The result passed to on_tool_end should be the raw screenshot string. + assert run_hooks.ended[0][2] == "xyz" + assert agent_hooks.ended[0][2] == "xyz" + # The computer should have performed a click then a screenshot. + assert computer.calls == [("click", (1, 2, "left")), ("screenshot", ())] + # The returned item should include the agent, output string, and a ComputerCallOutput. + assert output_item.agent is agent + assert isinstance(output_item, ToolCallOutputItem) + assert output_item.output == "data:image/png;base64,xyz" + raw = output_item.raw_item + # Raw item is a dict-like mapping with expected output fields. + assert isinstance(raw, dict) + assert raw["type"] == "computer_call_output" + assert raw["output"]["type"] == "computer_screenshot" + assert "image_url" in raw["output"] + assert raw["output"]["image_url"].endswith("xyz") diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..8f37200 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,61 @@ +import os + +import openai +import pytest + +from agents import set_default_openai_api, set_default_openai_client, set_default_openai_key +from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel +from agents.models.openai_provider import OpenAIProvider +from agents.models.openai_responses import OpenAIResponsesModel + + +def test_cc_no_default_key_errors(monkeypatch): + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + with pytest.raises(openai.OpenAIError): + OpenAIProvider(use_responses=False).get_model("gpt-4") + + +def test_cc_set_default_openai_key(): + set_default_openai_key("test_key") + chat_model = OpenAIProvider(use_responses=False).get_model("gpt-4") + assert chat_model._client.api_key == "test_key" # type: ignore + + +def test_cc_set_default_openai_client(): + client = openai.AsyncOpenAI(api_key="test_key") + set_default_openai_client(client) + chat_model = OpenAIProvider(use_responses=False).get_model("gpt-4") + assert chat_model._client.api_key == "test_key" # type: ignore + + +def test_resp_no_default_key_errors(monkeypatch): + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + assert os.getenv("OPENAI_API_KEY") is None + with pytest.raises(openai.OpenAIError): + OpenAIProvider(use_responses=True).get_model("gpt-4") + + +def test_resp_set_default_openai_key(): + set_default_openai_key("test_key") + resp_model = OpenAIProvider(use_responses=True).get_model("gpt-4") + assert resp_model._client.api_key == "test_key" # type: ignore + + +def test_resp_set_default_openai_client(): + client = openai.AsyncOpenAI(api_key="test_key") + set_default_openai_client(client) + resp_model = OpenAIProvider(use_responses=True).get_model("gpt-4") + assert resp_model._client.api_key == "test_key" # type: ignore + + +def test_set_default_openai_api(): + assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel), \ + "Default should be responses" + + set_default_openai_api("chat_completions") + assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIChatCompletionsModel), \ + "Should be chat completions model" + + set_default_openai_api("responses") + assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel), \ + "Should be responses model" diff --git a/tests/test_doc_parsing.py b/tests/test_doc_parsing.py new file mode 100644 index 0000000..6c7a95d --- /dev/null +++ b/tests/test_doc_parsing.py @@ -0,0 +1,115 @@ +from agents.function_schema import generate_func_documentation + + +def func_foo_google(a: int, b: float) -> str: + """ + This is func_foo. + + Args: + a: The first argument. + b: The second argument. + + Returns: + A result + """ + + return "ok" + + +def func_foo_numpy(a: int, b: float) -> str: + """ + This is func_foo. + + Parameters + ---------- + a: int + The first argument. + b: float + The second argument. + + Returns + ------- + str + A result + """ + return "ok" + + +def func_foo_sphinx(a: int, b: float) -> str: + """ + This is func_foo. + + :param a: The first argument. + :param b: The second argument. + :return: A result + """ + return "ok" + + +class Bar: + def func_bar(self, a: int, b: float) -> str: + """ + This is func_bar. + + Args: + a: The first argument. + b: The second argument. + + Returns: + A result + """ + return "ok" + + @classmethod + def func_baz(cls, a: int, b: float) -> str: + """ + This is func_baz. + + Args: + a: The first argument. + b: The second argument. + + Returns: + A result + """ + return "ok" + + +def test_functions_are_ok(): + func_foo_google(1, 2.0) + func_foo_numpy(1, 2.0) + func_foo_sphinx(1, 2.0) + Bar().func_bar(1, 2.0) + Bar.func_baz(1, 2.0) + + +def test_auto_detection() -> None: + doc = generate_func_documentation(func_foo_google) + assert doc.name == "func_foo_google" + assert doc.description == "This is func_foo." + assert doc.param_descriptions == {"a": "The first argument.", "b": "The second argument."} + + doc = generate_func_documentation(func_foo_numpy) + assert doc.name == "func_foo_numpy" + assert doc.description == "This is func_foo." + assert doc.param_descriptions == {"a": "The first argument.", "b": "The second argument."} + + doc = generate_func_documentation(func_foo_sphinx) + assert doc.name == "func_foo_sphinx" + assert doc.description == "This is func_foo." + assert doc.param_descriptions == {"a": "The first argument.", "b": "The second argument."} + + +def test_instance_method() -> None: + bar = Bar() + doc = generate_func_documentation(bar.func_bar) + assert doc.name == "func_bar" + assert doc.description == "This is func_bar." + assert doc.param_descriptions == {"a": "The first argument.", "b": "The second argument."} + + +def test_classmethod() -> None: + doc = generate_func_documentation(Bar.func_baz) + assert doc.name == "func_baz" + assert doc.description == "This is func_baz." + assert doc.param_descriptions == {"a": "The first argument.", "b": "The second argument."} diff --git a/tests/test_extension_filters.py b/tests/test_extension_filters.py new file mode 100644 index 0000000..4cb017a --- /dev/null +++ b/tests/test_extension_filters.py @@ -0,0 +1,188 @@ +from openai.types.responses import ResponseOutputMessage, ResponseOutputText + +from agents import Agent, HandoffInputData +from agents.extensions.handoff_filters import remove_all_tools +from agents.items import ( + HandoffOutputItem, + MessageOutputItem, + ToolCallOutputItem, + TResponseInputItem, +) + + +def fake_agent(): + return Agent( + name="fake_agent", + ) + + +def _get_message_input_item(content: str) -> TResponseInputItem: + return { + "role": "assistant", + "content": content, + } + + +def _get_function_result_input_item(content: str) -> TResponseInputItem: + return { + "call_id": "1", + "output": content, + "type": "function_call_output", + } + + +def _get_message_output_run_item(content: str) -> MessageOutputItem: + return MessageOutputItem( + agent=fake_agent(), + raw_item=ResponseOutputMessage( + id="1", + content=[ResponseOutputText(text=content, annotations=[], type="output_text")], + role="assistant", + status="completed", + type="message", + ), + ) + + +def _get_tool_output_run_item(content: str) -> ToolCallOutputItem: + return ToolCallOutputItem( + agent=fake_agent(), + raw_item={ + "call_id": "1", + "output": content, + "type": "function_call_output", + }, + output=content, + ) + + +def _get_handoff_input_item(content: str) -> TResponseInputItem: + return { + "call_id": "1", + "output": content, + "type": "function_call_output", + } + + +def _get_handoff_output_run_item(content: str) -> HandoffOutputItem: + return HandoffOutputItem( + agent=fake_agent(), + raw_item={ + "call_id": "1", + "output": content, + "type": "function_call_output", + }, + source_agent=fake_agent(), + target_agent=fake_agent(), + ) + + +def test_empty_data(): + handoff_input_data = HandoffInputData(input_history=(), pre_handoff_items=(), new_items=()) + filtered_data = remove_all_tools(handoff_input_data) + assert filtered_data == handoff_input_data + + +def test_str_historyonly(): + handoff_input_data = HandoffInputData(input_history="Hello", pre_handoff_items=(), new_items=()) + filtered_data = remove_all_tools(handoff_input_data) + assert filtered_data == handoff_input_data + + +def test_str_history_and_list(): + handoff_input_data = HandoffInputData( + input_history="Hello", + pre_handoff_items=(), + new_items=(_get_message_output_run_item("Hello"),), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert filtered_data == handoff_input_data + + +def test_list_history_and_list(): + handoff_input_data = HandoffInputData( + input_history=(_get_message_input_item("Hello"),), + pre_handoff_items=(_get_message_output_run_item("123"),), + new_items=(_get_message_output_run_item("World"),), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert filtered_data == handoff_input_data + + +def test_removes_tools_from_history(): + handoff_input_data = HandoffInputData( + input_history=( + _get_message_input_item("Hello1"), + _get_function_result_input_item("World"), + _get_message_input_item("Hello2"), + ), + pre_handoff_items=( + _get_tool_output_run_item("abc"), + _get_message_output_run_item("123"), + ), + new_items=(_get_message_output_run_item("World"),), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert len(filtered_data.input_history) == 2 + assert len(filtered_data.pre_handoff_items) == 1 + assert len(filtered_data.new_items) == 1 + + +def test_removes_tools_from_new_items(): + handoff_input_data = HandoffInputData( + input_history=(), + pre_handoff_items=(), + new_items=( + _get_message_output_run_item("Hello"), + _get_tool_output_run_item("World"), + ), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert len(filtered_data.input_history) == 0 + assert len(filtered_data.pre_handoff_items) == 0 + assert len(filtered_data.new_items) == 1 + + +def test_removes_tools_from_new_items_and_history(): + handoff_input_data = HandoffInputData( + input_history=( + _get_message_input_item("Hello1"), + _get_function_result_input_item("World"), + _get_message_input_item("Hello2"), + ), + pre_handoff_items=( + _get_message_output_run_item("123"), + _get_tool_output_run_item("456"), + ), + new_items=( + _get_message_output_run_item("Hello"), + _get_tool_output_run_item("World"), + ), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert len(filtered_data.input_history) == 2 + assert len(filtered_data.pre_handoff_items) == 1 + assert len(filtered_data.new_items) == 1 + + +def test_removes_handoffs_from_history(): + handoff_input_data = HandoffInputData( + input_history=( + _get_message_input_item("Hello1"), + _get_handoff_input_item("World"), + ), + pre_handoff_items=( + _get_message_output_run_item("Hello"), + _get_tool_output_run_item("World"), + _get_handoff_output_run_item("World"), + ), + new_items=( + _get_message_output_run_item("Hello"), + _get_tool_output_run_item("World"), + _get_handoff_output_run_item("World"), + ), + ) + filtered_data = remove_all_tools(handoff_input_data) + assert len(filtered_data.input_history) == 1 + assert len(filtered_data.pre_handoff_items) == 1 + assert len(filtered_data.new_items) == 1 diff --git a/tests/test_function_schema.py b/tests/test_function_schema.py new file mode 100644 index 0000000..2407ab0 --- /dev/null +++ b/tests/test_function_schema.py @@ -0,0 +1,430 @@ +from enum import Enum +from typing import Any, Literal + +import pytest +from pydantic import BaseModel, ValidationError +from typing_extensions import TypedDict + +from agents import RunContextWrapper +from agents.exceptions import UserError +from agents.function_schema import function_schema + + +def no_args_function(): + """This function has no args.""" + + return "ok" + + +def test_no_args_function(): + func_schema = function_schema(no_args_function) + assert func_schema.params_json_schema.get("title") == "no_args_function_args" + assert func_schema.description == "This function has no args." + assert not func_schema.takes_context + + parsed = func_schema.params_pydantic_model() + args, kwargs_dict = func_schema.to_call_args(parsed) + result = no_args_function(*args, **kwargs_dict) + assert result == "ok" + + +def no_args_function_with_context(ctx: RunContextWrapper[str]): + return "ok" + + +def test_no_args_function_with_context() -> None: + func_schema = function_schema(no_args_function_with_context) + assert func_schema.takes_context + + context = RunContextWrapper(context="test") + parsed = func_schema.params_pydantic_model() + args, kwargs_dict = func_schema.to_call_args(parsed) + result = no_args_function_with_context(context, *args, **kwargs_dict) + assert result == "ok" + + +def simple_function(a: int, b: int = 5): + """ + Args: + a: The first argument + b: The second argument + + Returns: + The sum of a and b + """ + return a + b + + +def test_simple_function(): + """Test a function that has simple typed parameters and defaults.""" + + func_schema = function_schema(simple_function) + # Check that the JSON schema is a dictionary with title, type, etc. + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "simple_function_args" + assert ( + func_schema.params_json_schema.get("properties", {}).get("a").get("description") + == "The first argument" + ) + assert ( + func_schema.params_json_schema.get("properties", {}).get("b").get("description") + == "The second argument" + ) + assert not func_schema.takes_context + + # Valid input + valid_input = {"a": 3} + parsed = func_schema.params_pydantic_model(**valid_input) + args_tuple, kwargs_dict = func_schema.to_call_args(parsed) + result = simple_function(*args_tuple, **kwargs_dict) + assert result == 8 # 3 + 5 + + # Another valid input + valid_input2 = {"a": 3, "b": 10} + parsed2 = func_schema.params_pydantic_model(**valid_input2) + args_tuple2, kwargs_dict2 = func_schema.to_call_args(parsed2) + result2 = simple_function(*args_tuple2, **kwargs_dict2) + assert result2 == 13 # 3 + 10 + + # Invalid input: 'a' must be int + with pytest.raises(ValidationError): + func_schema.params_pydantic_model(**{"a": "not an integer"}) + + +def varargs_function(x: int, *numbers: float, flag: bool = False, **kwargs: Any): + return x, numbers, flag, kwargs + + +def test_varargs_function(): + """Test a function that uses *args and **kwargs.""" + + func_schema = function_schema(varargs_function) + # Check JSON schema structure + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "varargs_function_args" + + # Valid input including *args in 'numbers' and **kwargs in 'kwargs' + valid_input = { + "x": 10, + "numbers": [1.1, 2.2, 3.3], + "flag": True, + "kwargs": {"extra1": "hello", "extra2": 42}, + } + parsed = func_schema.params_pydantic_model(**valid_input) + args, kwargs_dict = func_schema.to_call_args(parsed) + + result = varargs_function(*args, **kwargs_dict) + # result should be (10, (1.1, 2.2, 3.3), True, {"extra1": "hello", "extra2": 42}) + assert result[0] == 10 + assert result[1] == (1.1, 2.2, 3.3) + assert result[2] is True + assert result[3] == {"extra1": "hello", "extra2": 42} + + # Missing 'x' should raise error + with pytest.raises(ValidationError): + func_schema.params_pydantic_model(**{"numbers": [1.1, 2.2]}) + + # 'flag' can be omitted because it has a default + valid_input_no_flag = {"x": 7, "numbers": [9.9], "kwargs": {"some_key": "some_value"}} + parsed2 = func_schema.params_pydantic_model(**valid_input_no_flag) + args2, kwargs_dict2 = func_schema.to_call_args(parsed2) + result2 = varargs_function(*args2, **kwargs_dict2) + # result2 should be (7, (9.9,), False, {'some_key': 'some_value'}) + assert result2 == (7, (9.9,), False, {"some_key": "some_value"}) + + +class Foo(TypedDict): + a: int + b: str + + +class InnerModel(BaseModel): + a: int + b: str + + +class OuterModel(BaseModel): + inner: InnerModel + foo: Foo + + +def complex_args_function(model: OuterModel) -> str: + return f"{model.inner.a}, {model.inner.b}, {model.foo['a']}, {model.foo['b']}" + + +def test_nested_data_function(): + func_schema = function_schema(complex_args_function) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "complex_args_function_args" + + # Valid input + model = OuterModel(inner=InnerModel(a=1, b="hello"), foo=Foo(a=2, b="world")) + valid_input = { + "model": model.model_dump(), + } + + parsed = func_schema.params_pydantic_model(**valid_input) + args, kwargs_dict = func_schema.to_call_args(parsed) + + result = complex_args_function(*args, **kwargs_dict) + assert result == "1, hello, 2, world" + + +def complex_args_and_docs_function(model: OuterModel, some_flag: int = 0) -> str: + """ + This function takes a model and a flag, and returns a string. + + Args: + model: A model with an inner and foo field + some_flag: An optional flag with a default of 0 + + Returns: + A string with the values of the model and flag + """ + return f"{model.inner.a}, {model.inner.b}, {model.foo['a']}, {model.foo['b']}, {some_flag or 0}" + + +def test_complex_args_and_docs_function(): + func_schema = function_schema(complex_args_and_docs_function) + + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "complex_args_and_docs_function_args" + + # Check docstring is parsed correctly + properties = func_schema.params_json_schema.get("properties", {}) + assert properties.get("model").get("description") == "A model with an inner and foo field" + assert properties.get("some_flag").get("description") == "An optional flag with a default of 0" + + # Valid input + model = OuterModel(inner=InnerModel(a=1, b="hello"), foo=Foo(a=2, b="world")) + valid_input = { + "model": model.model_dump(), + } + + parsed = func_schema.params_pydantic_model(**valid_input) + args, kwargs_dict = func_schema.to_call_args(parsed) + + result = complex_args_and_docs_function(*args, **kwargs_dict) + assert result == "1, hello, 2, world, 0" + + # Invalid input: 'some_flag' must be int + with pytest.raises(ValidationError): + func_schema.params_pydantic_model( + **{"model": model.model_dump(), "some_flag": "not an int"} + ) + + # Valid input: 'some_flag' can be omitted because it has a default + valid_input_no_flag = {"model": model.model_dump()} + parsed2 = func_schema.params_pydantic_model(**valid_input_no_flag) + args2, kwargs_dict2 = func_schema.to_call_args(parsed2) + result2 = complex_args_and_docs_function(*args2, **kwargs_dict2) + assert result2 == "1, hello, 2, world, 0" + + +def function_with_context(ctx: RunContextWrapper[str], a: int, b: int = 5): + return a + b + + +def test_function_with_context(): + func_schema = function_schema(function_with_context) + assert func_schema.takes_context + + context = RunContextWrapper(context="test") + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + + result = function_with_context(context, *args, **kwargs_dict) + assert result == 3 + + +class MyClass: + def foo(self, a: int, b: int = 5): + return a + b + + def foo_ctx(self, ctx: RunContextWrapper[str], a: int, b: int = 5): + return a + b + + @classmethod + def bar(cls, a: int, b: int = 5): + return a + b + + @classmethod + def bar_ctx(cls, ctx: RunContextWrapper[str], a: int, b: int = 5): + return a + b + + @staticmethod + def baz(a: int, b: int = 5): + return a + b + + @staticmethod + def baz_ctx(ctx: RunContextWrapper[str], a: int, b: int = 5): + return a + b + + +def test_class_based_functions(): + context = RunContextWrapper(context="test") + + # Instance method + instance = MyClass() + func_schema = function_schema(instance.foo) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "foo_args" + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = instance.foo(*args, **kwargs_dict) + assert result == 3 + + # Instance method with context + func_schema = function_schema(instance.foo_ctx) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "foo_ctx_args" + assert func_schema.takes_context + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = instance.foo_ctx(context, *args, **kwargs_dict) + assert result == 3 + + # Class method + func_schema = function_schema(MyClass.bar) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "bar_args" + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = MyClass.bar(*args, **kwargs_dict) + assert result == 3 + + # Class method with context + func_schema = function_schema(MyClass.bar_ctx) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "bar_ctx_args" + assert func_schema.takes_context + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = MyClass.bar_ctx(context, *args, **kwargs_dict) + assert result == 3 + + # Static method + func_schema = function_schema(MyClass.baz) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "baz_args" + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = MyClass.baz(*args, **kwargs_dict) + assert result == 3 + + # Static method with context + func_schema = function_schema(MyClass.baz_ctx) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "baz_ctx_args" + assert func_schema.takes_context + + input = {"a": 1, "b": 2} + parsed = func_schema.params_pydantic_model(**input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = MyClass.baz_ctx(context, *args, **kwargs_dict) + assert result == 3 + + +class MyEnum(str, Enum): + FOO = "foo" + BAR = "bar" + BAZ = "baz" + + +def enum_and_literal_function(a: MyEnum, b: Literal["a", "b", "c"]) -> str: + return f"{a.value} {b}" + + +def test_enum_and_literal_function(): + func_schema = function_schema(enum_and_literal_function) + assert isinstance(func_schema.params_json_schema, dict) + assert func_schema.params_json_schema.get("title") == "enum_and_literal_function_args" + + # Check that the enum values are included in the JSON schema + assert func_schema.params_json_schema.get("$defs", {}).get("MyEnum", {}).get("enum") == [ + "foo", + "bar", + "baz", + ] + + # Check that the enum is expressed as a def + assert ( + func_schema.params_json_schema.get("properties", {}).get("a", {}).get("$ref") + == "#/$defs/MyEnum" + ) + + # Check that the literal values are included in the JSON schema + assert func_schema.params_json_schema.get("properties", {}).get("b", {}).get("enum") == [ + "a", + "b", + "c", + ] + + # Valid input + valid_input = {"a": "foo", "b": "a"} + parsed = func_schema.params_pydantic_model(**valid_input) + args, kwargs_dict = func_schema.to_call_args(parsed) + result = enum_and_literal_function(*args, **kwargs_dict) + assert result == "foo a" + + # Invalid input: 'a' must be a valid enum value + with pytest.raises(ValidationError): + func_schema.params_pydantic_model(**{"a": "not an enum value", "b": "a"}) + + # Invalid input: 'b' must be a valid literal value + with pytest.raises(ValidationError): + func_schema.params_pydantic_model(**{"a": "foo", "b": "not a literal value"}) + + +def test_run_context_in_non_first_position_raises_value_error(): + # When a parameter (after the first) is annotated as RunContextWrapper, + # function_schema() should raise a UserError. + def func(a: int, context: RunContextWrapper) -> None: + pass + + with pytest.raises(UserError): + function_schema(func, use_docstring_info=False) + + +def test_var_positional_tuple_annotation(): + # When a function has a var-positional parameter annotated with a tuple type, + # function_schema() should convert it into a field with type List[]. + def func(*args: tuple[int, ...]) -> int: + total = 0 + for arg in args: + total += sum(arg) + return total + + fs = function_schema(func, use_docstring_info=False) + + properties = fs.params_json_schema.get("properties", {}) + assert properties.get("args").get("type") == "array" + assert properties.get("args").get("items").get("type") == "integer" + + +def test_var_keyword_dict_annotation(): + # Case 3: + # When a function has a var-keyword parameter annotated with a dict type, + # function_schema() should convert it into a field with type Dict[, ]. + def func(**kwargs: dict[str, int]): + return kwargs + + fs = function_schema(func, use_docstring_info=False) + + properties = fs.params_json_schema.get("properties", {}) + # The name of the field is "kwargs", and it's a JSON object i.e. a dict. + assert properties.get("kwargs").get("type") == "object" + # The values in the dict are integers. + assert properties.get("kwargs").get("additionalProperties").get("type") == "integer" diff --git a/tests/test_function_tool.py b/tests/test_function_tool.py new file mode 100644 index 0000000..6a78309 --- /dev/null +++ b/tests/test_function_tool.py @@ -0,0 +1,257 @@ +import json +from typing import Any + +import pytest +from pydantic import BaseModel +from typing_extensions import TypedDict + +from agents import FunctionTool, ModelBehaviorError, RunContextWrapper, function_tool +from agents.tool import default_tool_error_function + + +def argless_function() -> str: + return "ok" + + +@pytest.mark.asyncio +async def test_argless_function(): + tool = function_tool(argless_function) + assert tool.name == "argless_function" + + result = await tool.on_invoke_tool(RunContextWrapper(None), "") + assert result == "ok" + + +def argless_with_context(ctx: RunContextWrapper[str]) -> str: + return "ok" + + +@pytest.mark.asyncio +async def test_argless_with_context(): + tool = function_tool(argless_with_context) + assert tool.name == "argless_with_context" + + result = await tool.on_invoke_tool(RunContextWrapper(None), "") + assert result == "ok" + + # Extra JSON should not raise an error + result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1}') + assert result == "ok" + + +def simple_function(a: int, b: int = 5): + return a + b + + +@pytest.mark.asyncio +async def test_simple_function(): + tool = function_tool(simple_function, failure_error_function=None) + assert tool.name == "simple_function" + + result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1}') + assert result == "6" + + result = await tool.on_invoke_tool(RunContextWrapper(None), '{"a": 1, "b": 2}') + assert result == "3" + + # Missing required argument should raise an error + with pytest.raises(ModelBehaviorError): + await tool.on_invoke_tool(RunContextWrapper(None), "") + + +class Foo(BaseModel): + a: int + b: int = 5 + + +class Bar(TypedDict): + x: str + y: int + + +def complex_args_function(foo: Foo, bar: Bar, baz: str = "hello"): + return f"{foo.a + foo.b} {bar['x']}{bar['y']} {baz}" + + +@pytest.mark.asyncio +async def test_complex_args_function(): + tool = function_tool(complex_args_function, failure_error_function=None) + assert tool.name == "complex_args_function" + + valid_json = json.dumps( + { + "foo": Foo(a=1).model_dump(), + "bar": Bar(x="hello", y=10), + } + ) + result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) + assert result == "6 hello10 hello" + + valid_json = json.dumps( + { + "foo": Foo(a=1, b=2).model_dump(), + "bar": Bar(x="hello", y=10), + } + ) + result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) + assert result == "3 hello10 hello" + + valid_json = json.dumps( + { + "foo": Foo(a=1, b=2).model_dump(), + "bar": Bar(x="hello", y=10), + "baz": "world", + } + ) + result = await tool.on_invoke_tool(RunContextWrapper(None), valid_json) + assert result == "3 hello10 world" + + # Missing required argument should raise an error + with pytest.raises(ModelBehaviorError): + await tool.on_invoke_tool(RunContextWrapper(None), '{"foo": {"a": 1}}') + + +def test_function_config_overrides(): + tool = function_tool(simple_function, name_override="custom_name") + assert tool.name == "custom_name" + + tool = function_tool(simple_function, description_override="custom description") + assert tool.description == "custom description" + + tool = function_tool( + simple_function, + name_override="custom_name", + description_override="custom description", + ) + assert tool.name == "custom_name" + assert tool.description == "custom description" + + +def test_func_schema_is_strict(): + tool = function_tool(simple_function) + assert tool.strict_json_schema, "Should be strict by default" + assert ( + "additionalProperties" in tool.params_json_schema + and not tool.params_json_schema["additionalProperties"] + ) + + tool = function_tool(complex_args_function) + assert tool.strict_json_schema, "Should be strict by default" + assert ( + "additionalProperties" in tool.params_json_schema + and not tool.params_json_schema["additionalProperties"] + ) + + +@pytest.mark.asyncio +async def test_manual_function_tool_creation_works(): + def do_some_work(data: str) -> str: + return f"{data}_done" + + class FunctionArgs(BaseModel): + data: str + + async def run_function(ctx: RunContextWrapper[Any], args: str) -> str: + parsed = FunctionArgs.model_validate_json(args) + return do_some_work(data=parsed.data) + + tool = FunctionTool( + name="test", + description="Processes extracted user data", + params_json_schema=FunctionArgs.model_json_schema(), + on_invoke_tool=run_function, + ) + + assert tool.name == "test" + assert tool.description == "Processes extracted user data" + for key, value in FunctionArgs.model_json_schema().items(): + assert tool.params_json_schema[key] == value + assert tool.strict_json_schema + + result = await tool.on_invoke_tool(RunContextWrapper(None), '{"data": "hello"}') + assert result == "hello_done" + + tool_not_strict = FunctionTool( + name="test", + description="Processes extracted user data", + params_json_schema=FunctionArgs.model_json_schema(), + on_invoke_tool=run_function, + strict_json_schema=False, + ) + + assert not tool_not_strict.strict_json_schema + assert "additionalProperties" not in tool_not_strict.params_json_schema + + result = await tool_not_strict.on_invoke_tool( + RunContextWrapper(None), '{"data": "hello", "bar": "baz"}' + ) + assert result == "hello_done" + + +@pytest.mark.asyncio +async def test_function_tool_default_error_works(): + def my_func(a: int, b: int = 5): + raise ValueError("test") + + tool = function_tool(my_func) + ctx = RunContextWrapper(None) + + result = await tool.on_invoke_tool(ctx, "") + assert "Invalid JSON" in str(result) + + result = await tool.on_invoke_tool(ctx, "{}") + assert "Invalid JSON" in str(result) + + result = await tool.on_invoke_tool(ctx, '{"a": 1}') + assert result == default_tool_error_function(ctx, ValueError("test")) + + result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') + assert result == default_tool_error_function(ctx, ValueError("test")) + + +@pytest.mark.asyncio +async def test_sync_custom_error_function_works(): + def my_func(a: int, b: int = 5): + raise ValueError("test") + + def custom_sync_error_function(ctx: RunContextWrapper[Any], error: Exception) -> str: + return f"error_{error.__class__.__name__}" + + tool = function_tool(my_func, failure_error_function=custom_sync_error_function) + ctx = RunContextWrapper(None) + + result = await tool.on_invoke_tool(ctx, "") + assert result == "error_ModelBehaviorError" + + result = await tool.on_invoke_tool(ctx, "{}") + assert result == "error_ModelBehaviorError" + + result = await tool.on_invoke_tool(ctx, '{"a": 1}') + assert result == "error_ValueError" + + result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') + assert result == "error_ValueError" + + +@pytest.mark.asyncio +async def test_async_custom_error_function_works(): + async def my_func(a: int, b: int = 5): + raise ValueError("test") + + def custom_sync_error_function(ctx: RunContextWrapper[Any], error: Exception) -> str: + return f"error_{error.__class__.__name__}" + + tool = function_tool(my_func, failure_error_function=custom_sync_error_function) + ctx = RunContextWrapper(None) + + result = await tool.on_invoke_tool(ctx, "") + assert result == "error_ModelBehaviorError" + + result = await tool.on_invoke_tool(ctx, "{}") + assert result == "error_ModelBehaviorError" + + result = await tool.on_invoke_tool(ctx, '{"a": 1}') + assert result == "error_ValueError" + + result = await tool.on_invoke_tool(ctx, '{"a": 1, "b": 2}') + assert result == "error_ValueError" diff --git a/tests/test_function_tool_decorator.py b/tests/test_function_tool_decorator.py new file mode 100644 index 0000000..3a47deb --- /dev/null +++ b/tests/test_function_tool_decorator.py @@ -0,0 +1,144 @@ +import asyncio +import json +from typing import Any + +import pytest + +from agents import function_tool +from agents.run_context import RunContextWrapper + + +class DummyContext: + def __init__(self): + self.data = "something" + + +def ctx_wrapper() -> RunContextWrapper[DummyContext]: + return RunContextWrapper(DummyContext()) + + +@function_tool +def sync_no_context_no_args() -> str: + return "test_1" + + +@pytest.mark.asyncio +async def test_sync_no_context_no_args_invocation(): + tool = sync_no_context_no_args + output = await tool.on_invoke_tool(ctx_wrapper(), "") + assert output == "test_1" + + +@function_tool +def sync_no_context_with_args(a: int, b: int) -> int: + return a + b + + +@pytest.mark.asyncio +async def test_sync_no_context_with_args_invocation(): + tool = sync_no_context_with_args + input_data = {"a": 5, "b": 7} + output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) + assert int(output) == 12 + + +@function_tool +def sync_with_context(ctx: RunContextWrapper[DummyContext], name: str) -> str: + return f"{name}_{ctx.context.data}" + + +@pytest.mark.asyncio +async def test_sync_with_context_invocation(): + tool = sync_with_context + input_data = {"name": "Alice"} + output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) + assert output == "Alice_something" + + +@function_tool +async def async_no_context(a: int, b: int) -> int: + await asyncio.sleep(0) # Just to illustrate async + return a * b + + +@pytest.mark.asyncio +async def test_async_no_context_invocation(): + tool = async_no_context + input_data = {"a": 3, "b": 4} + output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) + assert int(output) == 12 + + +@function_tool +async def async_with_context(ctx: RunContextWrapper[DummyContext], prefix: str, num: int) -> str: + await asyncio.sleep(0) + return f"{prefix}-{num}-{ctx.context.data}" + + +@pytest.mark.asyncio +async def test_async_with_context_invocation(): + tool = async_with_context + input_data = {"prefix": "Value", "num": 42} + output = await tool.on_invoke_tool(ctx_wrapper(), json.dumps(input_data)) + assert output == "Value-42-something" + + +@function_tool(name_override="my_custom_tool", description_override="custom desc") +def sync_no_context_override() -> str: + return "override_result" + + +@pytest.mark.asyncio +async def test_sync_no_context_override_invocation(): + tool = sync_no_context_override + assert tool.name == "my_custom_tool" + assert tool.description == "custom desc" + output = await tool.on_invoke_tool(ctx_wrapper(), "") + assert output == "override_result" + + +@function_tool(failure_error_function=None) +def will_fail_on_bad_json(x: int) -> int: + return x * 2 # pragma: no cover + + +@pytest.mark.asyncio +async def test_error_on_invalid_json(): + tool = will_fail_on_bad_json + # Passing an invalid JSON string + with pytest.raises(Exception) as exc_info: + await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") + assert "Invalid JSON input for tool" in str(exc_info.value) + + +def sync_error_handler(ctx: RunContextWrapper[Any], error: Exception) -> str: + return f"error_{error.__class__.__name__}" + + +@function_tool(failure_error_function=sync_error_handler) +def will_not_fail_on_bad_json(x: int) -> int: + return x * 2 # pragma: no cover + + +@pytest.mark.asyncio +async def test_no_error_on_invalid_json(): + tool = will_not_fail_on_bad_json + # Passing an invalid JSON string + result = await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") + assert result == "error_ModelBehaviorError" + + +def async_error_handler(ctx: RunContextWrapper[Any], error: Exception) -> str: + return f"error_{error.__class__.__name__}" + + +@function_tool(failure_error_function=sync_error_handler) +def will_not_fail_on_bad_json_async(x: int) -> int: + return x * 2 # pragma: no cover + + +@pytest.mark.asyncio +async def test_no_error_on_invalid_json_async(): + tool = will_not_fail_on_bad_json_async + result = await tool.on_invoke_tool(ctx_wrapper(), "{not valid json}") + assert result == "error_ModelBehaviorError" diff --git a/tests/test_global_hooks.py b/tests/test_global_hooks.py new file mode 100644 index 0000000..6ac35b9 --- /dev/null +++ b/tests/test_global_hooks.py @@ -0,0 +1,373 @@ +from __future__ import annotations + +import json +from collections import defaultdict +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents import Agent, RunContextWrapper, RunHooks, Runner, TContext, Tool + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_message, +) + + +class RunHooksForTests(RunHooks): + def __init__(self): + self.events: dict[str, int] = defaultdict(int) + + def reset(self): + self.events.clear() + + async def on_agent_start( + self, context: RunContextWrapper[TContext], agent: Agent[TContext] + ) -> None: + self.events["on_agent_start"] += 1 + + async def on_agent_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + output: Any, + ) -> None: + self.events["on_agent_end"] += 1 + + async def on_handoff( + self, + context: RunContextWrapper[TContext], + from_agent: Agent[TContext], + to_agent: Agent[TContext], + ) -> None: + self.events["on_handoff"] += 1 + + async def on_tool_start( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + ) -> None: + self.events["on_tool_start"] += 1 + + async def on_tool_end( + self, + context: RunContextWrapper[TContext], + agent: Agent[TContext], + tool: Tool, + result: str, + ) -> None: + self.events["on_tool_end"] += 1 + + +@pytest.mark.asyncio +async def test_non_streamed_agent_hooks(): + hooks = RunHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_text_message("user_message")]) + output = await Runner.run(agent_3, input="user_message", hooks=hooks) + assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message", hooks=hooks) + assert hooks.events == { + # We only invoke on_agent_start when we begin executing a new agent. + # Although agent_3 runs two turns internally before handing off, + # that's one logical agent segment, so on_agent_start fires once. + # Then we hand off to agent_1, so on_agent_start fires for that agent. + "on_agent_start": 2, + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: text message + [get_text_message("done")], + ] + ) + await Runner.run(agent_3, input="user_message", hooks=hooks) + + assert hooks.events == { + # agent_3 starts (fires on_agent_start), runs two turns and hands off. + # agent_1 starts (fires on_agent_start), then hands back to agent_3. + # agent_3 starts again (fires on_agent_start) to complete execution. + "on_agent_start": 3, + "on_tool_start": 2, # 2 tool calls + "on_tool_end": 2, # 2 tool calls + "on_handoff": 2, # 2 handoffs + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +@pytest.mark.asyncio +async def test_streamed_agent_hooks(): + hooks = RunHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_text_message("user_message")]) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: text message + [get_text_message("done")], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + assert hooks.events == { + # As in the non-streamed case above, two logical agent segments: + # starting agent_3, then handoff to agent_1. + "on_agent_start": 2, + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: text message + [get_text_message("done")], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + + assert hooks.events == { + # Same three logical agent segments as in the non-streamed case, + # so on_agent_start fires three times. + "on_agent_start": 3, + "on_tool_start": 2, # 2 tool calls + "on_tool_end": 2, # 2 tool calls + "on_handoff": 2, # 2 handoffs + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +class Foo(TypedDict): + a: str + + +@pytest.mark.asyncio +async def test_structed_output_non_streamed_agent_hooks(): + hooks = RunHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + output_type=Foo, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) + output = await Runner.run(agent_3, input="user_message", hooks=hooks) + assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: end message (for agent 1) + [get_text_message("done")], + ] + ) + output = await Runner.run(agent_3, input="user_message", hooks=hooks) + + assert hooks.events == { + # As with unstructured output, we expect on_agent_start once for + # agent_3 and once for agent_1. + "on_agent_start": 2, + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: end message (for agent 3) + [get_final_output_message(json.dumps({"a": "b"}))], + ] + ) + await Runner.run(agent_3, input="user_message", hooks=hooks) + + assert hooks.events == { + # We still expect three logical agent segments, as before. + "on_agent_start": 3, + "on_tool_start": 2, # 2 tool calls + "on_tool_end": 2, # 2 tool calls + "on_handoff": 2, # 2 handoffs + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + +@pytest.mark.asyncio +async def test_structed_output_streamed_agent_hooks(): + hooks = RunHooksForTests() + model = FakeModel() + agent_1 = Agent(name="test_1", model=model) + agent_2 = Agent(name="test_2", model=model) + agent_3 = Agent( + name="test_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + output_type=Foo, + ) + + agent_1.handoffs.append(agent_3) + + model.set_next_output([get_final_output_message(json.dumps({"a": "b"}))]) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + assert hooks.events == {"on_agent_start": 1, "on_agent_end": 1}, f"{output}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and a handoff + [get_text_message("a_message"), get_handoff_tool_call(agent_1)], + # Third turn: end message (for agent 1) + [get_text_message("done")], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + + assert hooks.events == { + # Two agent segments: agent_3 and then agent_1. + "on_agent_start": 2, + "on_tool_start": 1, # Only one tool call + "on_tool_end": 1, # Only one tool call + "on_handoff": 1, # Only one handoff + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message, another tool call, and a handoff + [ + get_text_message("a_message"), + get_function_tool_call("some_function", json.dumps({"a": "b"})), + get_handoff_tool_call(agent_1), + ], + # Third turn: a message and a handoff back to the orig agent + [get_text_message("a_message"), get_handoff_tool_call(agent_3)], + # Fourth turn: end message (for agent 3) + [get_final_output_message(json.dumps({"a": "b"}))], + ] + ) + output = Runner.run_streamed(agent_3, input="user_message", hooks=hooks) + async for _ in output.stream_events(): + pass + + assert hooks.events == { + # Three agent segments: agent_3, agent_1, agent_3 again. + "on_agent_start": 3, + "on_tool_start": 2, # 2 tool calls + "on_tool_end": 2, # 2 tool calls + "on_handoff": 2, # 2 handoffs + "on_agent_end": 1, # Should always have one end + }, f"got unexpected event count: {hooks.events}" + hooks.reset() diff --git a/tests/test_guardrails.py b/tests/test_guardrails.py new file mode 100644 index 0000000..c9f318c --- /dev/null +++ b/tests/test_guardrails.py @@ -0,0 +1,262 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrail, + OutputGuardrail, + RunContextWrapper, + TResponseInputItem, + UserError, +) +from agents.guardrail import input_guardrail, output_guardrail + + +def get_sync_guardrail(triggers: bool, output_info: Any | None = None): + def sync_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] + ): + return GuardrailFunctionOutput( + output_info=output_info, + tripwire_triggered=triggers, + ) + + return sync_guardrail + + +@pytest.mark.asyncio +async def test_sync_input_guardrail(): + guardrail = InputGuardrail(guardrail_function=get_sync_guardrail(triggers=False)) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = InputGuardrail(guardrail_function=get_sync_guardrail(triggers=True)) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = InputGuardrail( + guardrail_function=get_sync_guardrail(triggers=True, output_info="test") + ) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info == "test" + + +def get_async_input_guardrail(triggers: bool, output_info: Any | None = None): + async def async_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] + ): + return GuardrailFunctionOutput( + output_info=output_info, + tripwire_triggered=triggers, + ) + + return async_guardrail + + +@pytest.mark.asyncio +async def test_async_input_guardrail(): + guardrail = InputGuardrail(guardrail_function=get_async_input_guardrail(triggers=False)) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = InputGuardrail(guardrail_function=get_async_input_guardrail(triggers=True)) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = InputGuardrail( + guardrail_function=get_async_input_guardrail(triggers=True, output_info="test") + ) + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info == "test" + + +@pytest.mark.asyncio +async def test_invalid_input_guardrail_raises_user_error(): + with pytest.raises(UserError): + # Purposely ignoring type error + guardrail = InputGuardrail(guardrail_function="foo") # type: ignore + await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + + +def get_sync_output_guardrail(triggers: bool, output_info: Any | None = None): + def sync_guardrail(context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any): + return GuardrailFunctionOutput( + output_info=output_info, + tripwire_triggered=triggers, + ) + + return sync_guardrail + + +@pytest.mark.asyncio +async def test_sync_output_guardrail(): + guardrail = OutputGuardrail(guardrail_function=get_sync_output_guardrail(triggers=False)) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = OutputGuardrail(guardrail_function=get_sync_output_guardrail(triggers=True)) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = OutputGuardrail( + guardrail_function=get_sync_output_guardrail(triggers=True, output_info="test") + ) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info == "test" + + +def get_async_output_guardrail(triggers: bool, output_info: Any | None = None): + async def async_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any + ): + return GuardrailFunctionOutput( + output_info=output_info, + tripwire_triggered=triggers, + ) + + return async_guardrail + + +@pytest.mark.asyncio +async def test_async_output_guardrail(): + guardrail = OutputGuardrail(guardrail_function=get_async_output_guardrail(triggers=False)) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = OutputGuardrail(guardrail_function=get_async_output_guardrail(triggers=True)) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info is None + + guardrail = OutputGuardrail( + guardrail_function=get_async_output_guardrail(triggers=True, output_info="test") + ) + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert result.output.tripwire_triggered + assert result.output.output_info == "test" + + +@pytest.mark.asyncio +async def test_invalid_output_guardrail_raises_user_error(): + with pytest.raises(UserError): + # Purposely ignoring type error + guardrail = OutputGuardrail(guardrail_function="foo") # type: ignore + await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + + +@input_guardrail +def decorated_input_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info="test_1", + tripwire_triggered=False, + ) + + +@input_guardrail(name="Custom name") +def decorated_named_input_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info="test_2", + tripwire_triggered=False, + ) + + +@pytest.mark.asyncio +async def test_input_guardrail_decorators(): + guardrail = decorated_input_guardrail + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info == "test_1" + + guardrail = decorated_named_input_guardrail + result = await guardrail.run( + agent=Agent(name="test"), input="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info == "test_2" + assert guardrail.get_name() == "Custom name" + + +@output_guardrail +def decorated_output_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info="test_3", + tripwire_triggered=False, + ) + + +@output_guardrail(name="Custom name") +def decorated_named_output_guardrail( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info="test_4", + tripwire_triggered=False, + ) + + +@pytest.mark.asyncio +async def test_output_guardrail_decorators(): + guardrail = decorated_output_guardrail + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info == "test_3" + + guardrail = decorated_named_output_guardrail + result = await guardrail.run( + agent=Agent(name="test"), agent_output="test", context=RunContextWrapper(context=None) + ) + assert not result.output.tripwire_triggered + assert result.output.output_info == "test_4" + assert guardrail.get_name() == "Custom name" diff --git a/tests/test_handoff_tool.py b/tests/test_handoff_tool.py new file mode 100644 index 0000000..a2a0620 --- /dev/null +++ b/tests/test_handoff_tool.py @@ -0,0 +1,278 @@ +from typing import Any + +import pytest +from openai.types.responses import ResponseOutputMessage, ResponseOutputText +from pydantic import BaseModel + +from agents import ( + Agent, + Handoff, + HandoffInputData, + MessageOutputItem, + ModelBehaviorError, + RunContextWrapper, + Runner, + UserError, + handoff, +) + + +def message_item(content: str, agent: Agent[Any]) -> MessageOutputItem: + return MessageOutputItem( + agent=agent, + raw_item=ResponseOutputMessage( + id="123", + status="completed", + role="assistant", + type="message", + content=[ResponseOutputText(text=content, type="output_text", annotations=[])], + ), + ) + + +def get_len(data: HandoffInputData) -> int: + input_len = len(data.input_history) if isinstance(data.input_history, tuple) else 1 + pre_handoff_len = len(data.pre_handoff_items) + new_items_len = len(data.new_items) + return input_len + pre_handoff_len + new_items_len + + +def test_single_handoff_setup(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2", handoffs=[agent_1]) + + assert not agent_1.handoffs + assert agent_2.handoffs == [agent_1] + + assert not Runner._get_handoffs(agent_1) + + handoff_objects = Runner._get_handoffs(agent_2) + assert len(handoff_objects) == 1 + obj = handoff_objects[0] + assert obj.tool_name == Handoff.default_tool_name(agent_1) + assert obj.tool_description == Handoff.default_tool_description(agent_1) + assert obj.agent_name == agent_1.name + + +def test_multiple_handoffs_setup(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) + + assert agent_3.handoffs == [agent_1, agent_2] + assert not agent_1.handoffs + assert not agent_2.handoffs + + handoff_objects = Runner._get_handoffs(agent_3) + assert len(handoff_objects) == 2 + assert handoff_objects[0].tool_name == Handoff.default_tool_name(agent_1) + assert handoff_objects[1].tool_name == Handoff.default_tool_name(agent_2) + + assert handoff_objects[0].tool_description == Handoff.default_tool_description(agent_1) + assert handoff_objects[1].tool_description == Handoff.default_tool_description(agent_2) + + assert handoff_objects[0].agent_name == agent_1.name + assert handoff_objects[1].agent_name == agent_2.name + + +def test_custom_handoff_setup(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent( + name="test_3", + handoffs=[ + agent_1, + handoff( + agent_2, + tool_name_override="custom_tool_name", + tool_description_override="custom tool description", + ), + ], + ) + + assert len(agent_3.handoffs) == 2 + assert not agent_1.handoffs + assert not agent_2.handoffs + + handoff_objects = Runner._get_handoffs(agent_3) + assert len(handoff_objects) == 2 + + first_handoff = handoff_objects[0] + assert isinstance(first_handoff, Handoff) + assert first_handoff.tool_name == Handoff.default_tool_name(agent_1) + assert first_handoff.tool_description == Handoff.default_tool_description(agent_1) + assert first_handoff.agent_name == agent_1.name + + second_handoff = handoff_objects[1] + assert isinstance(second_handoff, Handoff) + assert second_handoff.tool_name == "custom_tool_name" + assert second_handoff.tool_description == "custom tool description" + assert second_handoff.agent_name == agent_2.name + + +class Foo(BaseModel): + bar: str + + +@pytest.mark.asyncio +async def test_handoff_input_type(): + async def _on_handoff(ctx: RunContextWrapper[Any], input: Foo): + pass + + agent = Agent(name="test") + obj = handoff(agent, input_type=Foo, on_handoff=_on_handoff) + for key, value in Foo.model_json_schema().items(): + assert obj.input_json_schema[key] == value + + # Invalid JSON should raise an error + with pytest.raises(ModelBehaviorError): + await obj.on_invoke_handoff(RunContextWrapper(agent), "not json") + + # Empty JSON should raise an error + with pytest.raises(ModelBehaviorError): + await obj.on_invoke_handoff(RunContextWrapper(agent), "") + + # Valid JSON should call the on_handoff function + invoked = await obj.on_invoke_handoff( + RunContextWrapper(agent), Foo(bar="baz").model_dump_json() + ) + assert invoked == agent + + +@pytest.mark.asyncio +async def test_on_handoff_called(): + was_called = False + + async def _on_handoff(ctx: RunContextWrapper[Any], input: Foo): + nonlocal was_called + was_called = True + + agent = Agent(name="test") + obj = handoff(agent, input_type=Foo, on_handoff=_on_handoff) + for key, value in Foo.model_json_schema().items(): + assert obj.input_json_schema[key] == value + + invoked = await obj.on_invoke_handoff( + RunContextWrapper(agent), Foo(bar="baz").model_dump_json() + ) + assert invoked == agent + + assert was_called, "on_handoff should have been called" + + +@pytest.mark.asyncio +async def test_on_handoff_without_input_called(): + was_called = False + + def _on_handoff(ctx: RunContextWrapper[Any]): + nonlocal was_called + was_called = True + + agent = Agent(name="test") + obj = handoff(agent, on_handoff=_on_handoff) + + invoked = await obj.on_invoke_handoff(RunContextWrapper(agent), "") + assert invoked == agent + + assert was_called, "on_handoff should have been called" + + +@pytest.mark.asyncio +async def test_async_on_handoff_without_input_called(): + was_called = False + + async def _on_handoff(ctx: RunContextWrapper[Any]): + nonlocal was_called + was_called = True + + agent = Agent(name="test") + obj = handoff(agent, on_handoff=_on_handoff) + + invoked = await obj.on_invoke_handoff(RunContextWrapper(agent), "") + assert invoked == agent + + assert was_called, "on_handoff should have been called" + + +@pytest.mark.asyncio +async def test_invalid_on_handoff_raises_error(): + was_called = False + + async def _on_handoff(ctx: RunContextWrapper[Any], blah: str): + nonlocal was_called + was_called = True # pragma: no cover + + agent = Agent(name="test") + + with pytest.raises(UserError): + # Purposely ignoring the type error here to simulate invalid input + handoff(agent, on_handoff=_on_handoff) # type: ignore + + +def test_handoff_input_data(): + agent = Agent(name="test") + + data = HandoffInputData( + input_history="", + pre_handoff_items=(), + new_items=(), + ) + assert get_len(data) == 1 + + data = HandoffInputData( + input_history=({"role": "user", "content": "foo"},), + pre_handoff_items=(), + new_items=(), + ) + assert get_len(data) == 1 + + data = HandoffInputData( + input_history=( + {"role": "user", "content": "foo"}, + {"role": "assistant", "content": "bar"}, + ), + pre_handoff_items=(), + new_items=(), + ) + assert get_len(data) == 2 + + data = HandoffInputData( + input_history=({"role": "user", "content": "foo"},), + pre_handoff_items=( + message_item("foo", agent), + message_item("foo2", agent), + ), + new_items=( + message_item("bar", agent), + message_item("baz", agent), + ), + ) + assert get_len(data) == 5 + + data = HandoffInputData( + input_history=( + {"role": "user", "content": "foo"}, + {"role": "assistant", "content": "bar"}, + ), + pre_handoff_items=(message_item("baz", agent),), + new_items=( + message_item("baz", agent), + message_item("qux", agent), + ), + ) + + assert get_len(data) == 5 + + +def test_handoff_input_schema_is_strict(): + agent = Agent(name="test") + obj = handoff(agent, input_type=Foo, on_handoff=lambda ctx, input: None) + for key, value in Foo.model_json_schema().items(): + assert obj.input_json_schema[key] == value + + assert obj.strict_json_schema, "Input schema should be strict" + + assert ( + "additionalProperties" in obj.input_json_schema + and not obj.input_json_schema["additionalProperties"] + ), "Input schema should be strict and have additionalProperties=False" diff --git a/tests/test_items_helpers.py b/tests/test_items_helpers.py new file mode 100644 index 0000000..64e2dcd --- /dev/null +++ b/tests/test_items_helpers.py @@ -0,0 +1,281 @@ +from __future__ import annotations + +from openai.types.responses.response_computer_tool_call import ( + ActionScreenshot, + ResponseComputerToolCall, +) +from openai.types.responses.response_computer_tool_call_param import ResponseComputerToolCallParam +from openai.types.responses.response_file_search_tool_call import ResponseFileSearchToolCall +from openai.types.responses.response_file_search_tool_call_param import ( + ResponseFileSearchToolCallParam, +) +from openai.types.responses.response_function_tool_call import ResponseFunctionToolCall +from openai.types.responses.response_function_tool_call_param import ResponseFunctionToolCallParam +from openai.types.responses.response_function_web_search import ResponseFunctionWebSearch +from openai.types.responses.response_function_web_search_param import ResponseFunctionWebSearchParam +from openai.types.responses.response_input_item_param import Reasoning as ReasoningInputParam +from openai.types.responses.response_output_item import Reasoning, ReasoningContent +from openai.types.responses.response_output_message import ResponseOutputMessage +from openai.types.responses.response_output_message_param import ResponseOutputMessageParam +from openai.types.responses.response_output_refusal import ResponseOutputRefusal +from openai.types.responses.response_output_text import ResponseOutputText + +from agents import ( + Agent, + ItemHelpers, + MessageOutputItem, + ModelResponse, + ReasoningItem, + RunItem, + TResponseInputItem, + Usage, +) + + +def make_message( + content_items: list[ResponseOutputText | ResponseOutputRefusal], +) -> ResponseOutputMessage: + """ + Helper to construct a ResponseOutputMessage with a single batch of content + items, using a fixed id/status. + """ + return ResponseOutputMessage( + id="msg123", + content=content_items, + role="assistant", + status="completed", + type="message", + ) + + +def test_extract_last_content_of_text_message() -> None: + # Build a message containing two text segments. + content1 = ResponseOutputText(annotations=[], text="Hello ", type="output_text") + content2 = ResponseOutputText(annotations=[], text="world!", type="output_text") + message = make_message([content1, content2]) + # Helpers should yield the last segment's text. + assert ItemHelpers.extract_last_content(message) == "world!" + + +def test_extract_last_content_of_refusal_message() -> None: + # Build a message whose last content entry is a refusal. + content1 = ResponseOutputText(annotations=[], text="Before refusal", type="output_text") + refusal = ResponseOutputRefusal(refusal="I cannot do that", type="refusal") + message = make_message([content1, refusal]) + # Helpers should extract the refusal string when last content is a refusal. + assert ItemHelpers.extract_last_content(message) == "I cannot do that" + + +def test_extract_last_content_non_message_returns_empty() -> None: + # Construct some other type of output item, e.g. a tool call, to verify non-message returns "". + tool_call = ResponseFunctionToolCall( + id="tool123", + arguments="{}", + call_id="call123", + name="func", + type="function_call", + ) + assert ItemHelpers.extract_last_content(tool_call) == "" + + +def test_extract_last_text_returns_text_only() -> None: + # A message whose last segment is text yields the text. + first_text = ResponseOutputText(annotations=[], text="part1", type="output_text") + second_text = ResponseOutputText(annotations=[], text="part2", type="output_text") + message = make_message([first_text, second_text]) + assert ItemHelpers.extract_last_text(message) == "part2" + # Whereas when last content is a refusal, extract_last_text returns None. + message2 = make_message([first_text, ResponseOutputRefusal(refusal="no", type="refusal")]) + assert ItemHelpers.extract_last_text(message2) is None + + +def test_input_to_new_input_list_from_string() -> None: + result = ItemHelpers.input_to_new_input_list("hi") + # Should wrap the string into a list with a single dict containing content and user role. + assert isinstance(result, list) + assert result == [{"content": "hi", "role": "user"}] + + +def test_input_to_new_input_list_deep_copies_lists() -> None: + # Given a list of message dictionaries, ensure the returned list is a deep copy. + original: list[TResponseInputItem] = [{"content": "abc", "role": "developer"}] + new_list = ItemHelpers.input_to_new_input_list(original) + assert new_list == original + # Mutating the returned list should not mutate the original. + new_list.pop() + assert "content" in original[0] and original[0].get("content") == "abc" + + +def test_text_message_output_concatenates_text_segments() -> None: + # Build a message with both text and refusal segments, only text segments are concatenated. + pieces: list[ResponseOutputText | ResponseOutputRefusal] = [] + pieces.append(ResponseOutputText(annotations=[], text="a", type="output_text")) + pieces.append(ResponseOutputRefusal(refusal="denied", type="refusal")) + pieces.append(ResponseOutputText(annotations=[], text="b", type="output_text")) + message = make_message(pieces) + # Wrap into MessageOutputItem to feed into text_message_output. + item = MessageOutputItem(agent=Agent(name="test"), raw_item=message) + assert ItemHelpers.text_message_output(item) == "ab" + + +def test_text_message_outputs_across_list_of_runitems() -> None: + """ + Compose several RunItem instances, including a non-message run item, and ensure + that only MessageOutputItem instances contribute any text. The non-message + (ReasoningItem) should be ignored by Helpers.text_message_outputs. + """ + message1 = make_message([ResponseOutputText(annotations=[], text="foo", type="output_text")]) + message2 = make_message([ResponseOutputText(annotations=[], text="bar", type="output_text")]) + item1: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message1) + item2: RunItem = MessageOutputItem(agent=Agent(name="test"), raw_item=message2) + # Create a non-message run item of a different type, e.g., a reasoning trace. + reasoning = Reasoning(id="rid", content=[], type="reasoning") + non_message_item: RunItem = ReasoningItem(agent=Agent(name="test"), raw_item=reasoning) + # Confirm only the message outputs are concatenated. + assert ItemHelpers.text_message_outputs([item1, non_message_item, item2]) == "foobar" + + +def test_tool_call_output_item_constructs_function_call_output_dict(): + # Build a simple ResponseFunctionToolCall. + call = ResponseFunctionToolCall( + id="call-abc", + arguments='{"x": 1}', + call_id="call-abc", + name="do_something", + type="function_call", + ) + payload = ItemHelpers.tool_call_output_item(call, "result-string") + + assert isinstance(payload, dict) + assert payload["type"] == "function_call_output" + assert payload["call_id"] == call.id + assert payload["output"] == "result-string" + + +# The following tests ensure that every possible output item type defined by +# OpenAI's API can be converted back into an input item dict via +# ModelResponse.to_input_items. The output and input schema for each item are +# intended to be symmetric, so given any ResponseOutputItem, its model_dump +# should produce a dict that can satisfy the corresponding TypedDict input +# type. These tests construct minimal valid instances of each output type, +# invoke to_input_items, and then verify that the resulting dict can be used +# to round-trip back into a Pydantic output model without errors. + + +def test_to_input_items_for_message() -> None: + """An output message should convert into an input dict matching the message's own structure.""" + content = ResponseOutputText(annotations=[], text="hello world", type="output_text") + message = ResponseOutputMessage( + id="m1", content=[content], role="assistant", status="completed", type="message" + ) + resp = ModelResponse(output=[message], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + # The dict should contain exactly the primitive values of the message + expected: ResponseOutputMessageParam = { + "id": "m1", + "content": [ + { + "annotations": [], + "text": "hello world", + "type": "output_text", + } + ], + "role": "assistant", + "status": "completed", + "type": "message", + } + assert input_items[0] == expected + + +def test_to_input_items_for_function_call() -> None: + """A function tool call output should produce the same dict as a function tool call input.""" + tool_call = ResponseFunctionToolCall( + id="f1", arguments="{}", call_id="c1", name="func", type="function_call" + ) + resp = ModelResponse(output=[tool_call], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + expected: ResponseFunctionToolCallParam = { + "id": "f1", + "arguments": "{}", + "call_id": "c1", + "name": "func", + "type": "function_call", + } + assert input_items[0] == expected + + +def test_to_input_items_for_file_search_call() -> None: + """A file search tool call output should produce the same dict as a file search input.""" + fs_call = ResponseFileSearchToolCall( + id="fs1", queries=["query"], status="completed", type="file_search_call" + ) + resp = ModelResponse(output=[fs_call], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + expected: ResponseFileSearchToolCallParam = { + "id": "fs1", + "queries": ["query"], + "status": "completed", + "type": "file_search_call", + } + assert input_items[0] == expected + + +def test_to_input_items_for_web_search_call() -> None: + """A web search tool call output should produce the same dict as a web search input.""" + ws_call = ResponseFunctionWebSearch(id="w1", status="completed", type="web_search_call") + resp = ModelResponse(output=[ws_call], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + expected: ResponseFunctionWebSearchParam = { + "id": "w1", + "status": "completed", + "type": "web_search_call", + } + assert input_items[0] == expected + + +def test_to_input_items_for_computer_call_click() -> None: + """A computer call output should yield a dict whose shape matches the computer call input.""" + action = ActionScreenshot(type="screenshot") + comp_call = ResponseComputerToolCall( + id="comp1", + action=action, + type="computer_call", + call_id="comp1", + pending_safety_checks=[], + status="completed", + ) + resp = ModelResponse(output=[comp_call], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + converted_dict = input_items[0] + # Top-level keys should match what we expect for a computer call input + expected: ResponseComputerToolCallParam = { + "id": "comp1", + "type": "computer_call", + "action": {"type": "screenshot"}, + "call_id": "comp1", + "pending_safety_checks": [], + "status": "completed", + } + assert converted_dict == expected + + +def test_to_input_items_for_reasoning() -> None: + """A reasoning output should produce the same dict as a reasoning input item.""" + rc = ReasoningContent(text="why", type="reasoning_summary") + reasoning = Reasoning(id="rid1", content=[rc], type="reasoning") + resp = ModelResponse(output=[reasoning], usage=Usage(), referenceable_id=None) + input_items = resp.to_input_items() + assert isinstance(input_items, list) and len(input_items) == 1 + converted_dict = input_items[0] + + expected: ReasoningInputParam = { + "id": "rid1", + "content": [{"text": "why", "type": "reasoning_summary"}], + "type": "reasoning", + } + assert converted_dict == expected diff --git a/tests/test_max_turns.py b/tests/test_max_turns.py new file mode 100644 index 0000000..f01bb18 --- /dev/null +++ b/tests/test_max_turns.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import json + +import pytest +from typing_extensions import TypedDict + +from agents import Agent, MaxTurnsExceeded, Runner + +from .fake_model import FakeModel +from .test_responses import get_function_tool, get_function_tool_call, get_text_message + + +@pytest.mark.asyncio +async def test_non_streamed_max_turns(): + model = FakeModel() + agent = Agent( + name="test_1", + model=model, + tools=[get_function_tool("some_function", "result")], + ) + + func_output = json.dumps({"a": "b"}) + + model.add_multiple_turn_outputs( + [ + [get_text_message("1"), get_function_tool_call("some_function", func_output)], + [get_text_message("2"), get_function_tool_call("some_function", func_output)], + [get_text_message("3"), get_function_tool_call("some_function", func_output)], + [get_text_message("4"), get_function_tool_call("some_function", func_output)], + [get_text_message("5"), get_function_tool_call("some_function", func_output)], + ] + ) + with pytest.raises(MaxTurnsExceeded): + await Runner.run(agent, input="user_message", max_turns=3) + + +@pytest.mark.asyncio +async def test_streamed_max_turns(): + model = FakeModel() + agent = Agent( + name="test_1", + model=model, + tools=[get_function_tool("some_function", "result")], + ) + func_output = json.dumps({"a": "b"}) + + model.add_multiple_turn_outputs( + [ + [ + get_text_message("1"), + get_function_tool_call("some_function", func_output), + ], + [ + get_text_message("2"), + get_function_tool_call("some_function", func_output), + ], + [ + get_text_message("3"), + get_function_tool_call("some_function", func_output), + ], + [ + get_text_message("4"), + get_function_tool_call("some_function", func_output), + ], + [ + get_text_message("5"), + get_function_tool_call("some_function", func_output), + ], + ] + ) + with pytest.raises(MaxTurnsExceeded): + output = Runner.run_streamed(agent, input="user_message", max_turns=3) + async for _ in output.stream_events(): + pass + + +class Foo(TypedDict): + a: str + + +@pytest.mark.asyncio +async def test_structured_output_non_streamed_max_turns(): + model = FakeModel() + agent = Agent( + name="test_1", + model=model, + output_type=Foo, + tools=[get_function_tool("tool_1", "result")], + ) + + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + ] + ) + with pytest.raises(MaxTurnsExceeded): + await Runner.run(agent, input="user_message", max_turns=3) + + +@pytest.mark.asyncio +async def test_structured_output_streamed_max_turns(): + model = FakeModel() + agent = Agent( + name="test_1", + model=model, + output_type=Foo, + tools=[get_function_tool("tool_1", "result")], + ) + + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + [get_function_tool_call("tool_1")], + ] + ) + with pytest.raises(MaxTurnsExceeded): + output = Runner.run_streamed(agent, input="user_message", max_turns=3) + async for _ in output.stream_events(): + pass diff --git a/tests/test_openai_chatcompletions.py b/tests/test_openai_chatcompletions.py new file mode 100644 index 0000000..9521647 --- /dev/null +++ b/tests/test_openai_chatcompletions.py @@ -0,0 +1,290 @@ +from __future__ import annotations + +from collections.abc import AsyncIterator +from typing import Any + +import httpx +import pytest +from openai import NOT_GIVEN +from openai.types.chat.chat_completion import ChatCompletion, Choice +from openai.types.chat.chat_completion_chunk import ChatCompletionChunk +from openai.types.chat.chat_completion_message import ChatCompletionMessage +from openai.types.chat.chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall, + Function, +) +from openai.types.completion_usage import CompletionUsage +from openai.types.responses import ( + Response, + ResponseFunctionToolCall, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, +) + +from agents import ( + ModelResponse, + ModelSettings, + ModelTracing, + OpenAIChatCompletionsModel, + OpenAIProvider, + generation_span, +) +from agents.models.fake_id import FAKE_RESPONSES_ID + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_get_response_with_text_message(monkeypatch) -> None: + """ + When the model returns a ChatCompletionMessage with plain text content, + `get_response` should produce a single `ResponseOutputMessage` containing + a `ResponseOutputText` with that content, and a `Usage` populated from + the completion's usage. + """ + msg = ChatCompletionMessage(role="assistant", content="Hello") + choice = Choice(index=0, finish_reason="stop", message=msg) + chat = ChatCompletion( + id="resp-id", + created=0, + model="fake", + object="chat.completion", + choices=[choice], + usage=CompletionUsage(completion_tokens=5, prompt_tokens=7, total_tokens=12), + ) + + async def patched_fetch_response(self, *args, **kwargs): + return chat + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + resp: ModelResponse = await model.get_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ) + # Should have produced exactly one output message with one text part + assert isinstance(resp, ModelResponse) + assert len(resp.output) == 1 + assert isinstance(resp.output[0], ResponseOutputMessage) + msg_item = resp.output[0] + assert len(msg_item.content) == 1 + assert isinstance(msg_item.content[0], ResponseOutputText) + assert msg_item.content[0].text == "Hello" + # Usage should be preserved from underlying ChatCompletion.usage + assert resp.usage.input_tokens == 7 + assert resp.usage.output_tokens == 5 + assert resp.usage.total_tokens == 12 + assert resp.referenceable_id is None + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_get_response_with_refusal(monkeypatch) -> None: + """ + When the model returns a ChatCompletionMessage with a `refusal` instead + of normal `content`, `get_response` should produce a single + `ResponseOutputMessage` containing a `ResponseOutputRefusal` part. + """ + msg = ChatCompletionMessage(role="assistant", refusal="No thanks") + choice = Choice(index=0, finish_reason="stop", message=msg) + chat = ChatCompletion( + id="resp-id", + created=0, + model="fake", + object="chat.completion", + choices=[choice], + usage=None, + ) + + async def patched_fetch_response(self, *args, **kwargs): + return chat + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + resp: ModelResponse = await model.get_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ) + assert len(resp.output) == 1 + assert isinstance(resp.output[0], ResponseOutputMessage) + refusal_part = resp.output[0].content[0] + assert isinstance(refusal_part, ResponseOutputRefusal) + assert refusal_part.refusal == "No thanks" + # With no usage from the completion, usage defaults to zeros. + assert resp.usage.requests == 0 + assert resp.usage.input_tokens == 0 + assert resp.usage.output_tokens == 0 + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_get_response_with_tool_call(monkeypatch) -> None: + """ + If the ChatCompletionMessage includes one or more tool_calls, `get_response` + should append corresponding `ResponseFunctionToolCall` items after the + assistant message item with matching name/arguments. + """ + tool_call = ChatCompletionMessageToolCall( + id="call-id", + type="function", + function=Function(name="do_thing", arguments="{'x':1}"), + ) + msg = ChatCompletionMessage(role="assistant", content="Hi", tool_calls=[tool_call]) + choice = Choice(index=0, finish_reason="stop", message=msg) + chat = ChatCompletion( + id="resp-id", + created=0, + model="fake", + object="chat.completion", + choices=[choice], + usage=None, + ) + + async def patched_fetch_response(self, *args, **kwargs): + return chat + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + resp: ModelResponse = await model.get_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ) + # Expect a message item followed by a function tool call item. + assert len(resp.output) == 2 + assert isinstance(resp.output[0], ResponseOutputMessage) + fn_call_item = resp.output[1] + assert isinstance(fn_call_item, ResponseFunctionToolCall) + assert fn_call_item.call_id == "call-id" + assert fn_call_item.name == "do_thing" + assert fn_call_item.arguments == "{'x':1}" + + +@pytest.mark.asyncio +async def test_fetch_response_non_stream(monkeypatch) -> None: + """ + Verify that `_fetch_response` builds the correct OpenAI API call when not + streaming and returns the ChatCompletion object directly. We supply a + dummy ChatCompletion through a stubbed OpenAI client and inspect the + captured kwargs. + """ + + # Dummy completions to record kwargs + class DummyCompletions: + def __init__(self) -> None: + self.kwargs: dict[str, Any] = {} + + async def create(self, **kwargs: Any) -> Any: + self.kwargs = kwargs + return chat + + class DummyClient: + def __init__(self, completions: DummyCompletions) -> None: + self.chat = type("_Chat", (), {"completions": completions})() + self.base_url = httpx.URL("http://fake") + + msg = ChatCompletionMessage(role="assistant", content="ignored") + choice = Choice(index=0, finish_reason="stop", message=msg) + chat = ChatCompletion( + id="resp-id", + created=0, + model="fake", + object="chat.completion", + choices=[choice], + ) + completions = DummyCompletions() + dummy_client = DummyClient(completions) + model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore + # Execute the private fetch with a system instruction and simple string input. + with generation_span(disabled=True) as span: + result = await model._fetch_response( + system_instructions="sys", + input="hi", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + span=span, + tracing=ModelTracing.DISABLED, + stream=False, + ) + assert result is chat + # Ensure expected args were passed through to OpenAI client. + kwargs = completions.kwargs + assert kwargs["stream"] is False + assert kwargs["model"] == "gpt-4" + assert kwargs["messages"][0]["role"] == "system" + assert kwargs["messages"][0]["content"] == "sys" + assert kwargs["messages"][1]["role"] == "user" + # Defaults for optional fields become the NOT_GIVEN sentinel + assert kwargs["tools"] is NOT_GIVEN + assert kwargs["tool_choice"] is NOT_GIVEN + assert kwargs["response_format"] is NOT_GIVEN + assert kwargs["stream_options"] is NOT_GIVEN + + +@pytest.mark.asyncio +async def test_fetch_response_stream(monkeypatch) -> None: + """ + When `stream=True`, `_fetch_response` should return a bare `Response` + object along with the underlying async stream. The OpenAI client call + should include `stream_options` to request usage-delimited chunks. + """ + + async def event_stream() -> AsyncIterator[ChatCompletionChunk]: + if False: # pragma: no cover + yield # pragma: no cover + + class DummyCompletions: + def __init__(self) -> None: + self.kwargs: dict[str, Any] = {} + + async def create(self, **kwargs: Any) -> Any: + self.kwargs = kwargs + return event_stream() + + class DummyClient: + def __init__(self, completions: DummyCompletions) -> None: + self.chat = type("_Chat", (), {"completions": completions})() + self.base_url = httpx.URL("http://fake") + + completions = DummyCompletions() + dummy_client = DummyClient(completions) + model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore + with generation_span(disabled=True) as span: + response, stream = await model._fetch_response( + system_instructions=None, + input="hi", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + span=span, + tracing=ModelTracing.DISABLED, + stream=True, + ) + # Check OpenAI client was called for streaming + assert completions.kwargs["stream"] is True + assert completions.kwargs["stream_options"] == {"include_usage": True} + # Response is a proper openai Response + assert isinstance(response, Response) + assert response.id == FAKE_RESPONSES_ID + assert response.model == "gpt-4" + assert response.object == "response" + assert response.output == [] + # We returned the async iterator produced by our dummy. + assert hasattr(stream, "__aiter__") diff --git a/tests/test_openai_chatcompletions_converter.py b/tests/test_openai_chatcompletions_converter.py new file mode 100644 index 0000000..8cf07d7 --- /dev/null +++ b/tests/test_openai_chatcompletions_converter.py @@ -0,0 +1,395 @@ +# Copyright (c) OpenAI +# +# Licensed under the MIT License. +# See LICENSE file in the project root for full license information. + +""" +Unit tests for the internal `_Converter` class defined in +`agents.models.openai_chatcompletions`. The converter is responsible for +translating between internal "item" structures (e.g., `ResponseOutputMessage` +and related types from `openai.types.responses`) and the ChatCompletion message +structures defined by the OpenAI client library. + +These tests exercise both conversion directions: + +- `_Converter.message_to_output_items` turns a `ChatCompletionMessage` (as + returned by the OpenAI API) into a list of `ResponseOutputItem` instances. + +- `_Converter.items_to_messages` takes in either a simple string prompt, or a + list of input/output items such as `ResponseOutputMessage` and + `ResponseFunctionToolCallParam` dicts, and constructs a list of + `ChatCompletionMessageParam` dicts suitable for sending back to the API. +""" + +from __future__ import annotations + +from typing import Literal, cast + +import pytest +from openai.types.chat import ChatCompletionMessage, ChatCompletionMessageToolCall +from openai.types.chat.chat_completion_message_tool_call import Function +from openai.types.responses import ( + ResponseFunctionToolCall, + ResponseFunctionToolCallParam, + ResponseInputTextParam, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, +) +from openai.types.responses.response_input_item_param import FunctionCallOutput + +from agents.agent_output import AgentOutputSchema +from agents.exceptions import UserError +from agents.items import TResponseInputItem +from agents.models.fake_id import FAKE_RESPONSES_ID +from agents.models.openai_chatcompletions import _Converter + + +def test_message_to_output_items_with_text_only(): + """ + Make sure a simple ChatCompletionMessage with string content is converted + into a single ResponseOutputMessage containing one ResponseOutputText. + """ + msg = ChatCompletionMessage(role="assistant", content="Hello") + items = _Converter.message_to_output_items(msg) + # Expect exactly one output item (the message) + assert len(items) == 1 + message_item = cast(ResponseOutputMessage, items[0]) + assert message_item.id == FAKE_RESPONSES_ID + assert message_item.role == "assistant" + assert message_item.type == "message" + assert message_item.status == "completed" + # Message content should have exactly one text part with the same text. + assert len(message_item.content) == 1 + text_part = cast(ResponseOutputText, message_item.content[0]) + assert text_part.type == "output_text" + assert text_part.text == "Hello" + + +def test_message_to_output_items_with_refusal(): + """ + Make sure a message with a refusal string produces a ResponseOutputMessage + with a ResponseOutputRefusal content part. + """ + msg = ChatCompletionMessage(role="assistant", refusal="I'm sorry") + items = _Converter.message_to_output_items(msg) + assert len(items) == 1 + message_item = cast(ResponseOutputMessage, items[0]) + assert len(message_item.content) == 1 + refusal_part = cast(ResponseOutputRefusal, message_item.content[0]) + assert refusal_part.type == "refusal" + assert refusal_part.refusal == "I'm sorry" + + +def test_message_to_output_items_with_tool_call(): + """ + If the ChatCompletionMessage contains one or more tool_calls, they should + be reflected as separate `ResponseFunctionToolCall` items appended after + the message item. + """ + tool_call = ChatCompletionMessageToolCall( + id="tool1", + type="function", + function=Function(name="myfn", arguments='{"x":1}'), + ) + msg = ChatCompletionMessage(role="assistant", content="Hi", tool_calls=[tool_call]) + items = _Converter.message_to_output_items(msg) + # Should produce a message item followed by one function tool call item + assert len(items) == 2 + message_item = cast(ResponseOutputMessage, items[0]) + assert isinstance(message_item, ResponseOutputMessage) + fn_call_item = cast(ResponseFunctionToolCall, items[1]) + assert fn_call_item.id == FAKE_RESPONSES_ID + assert fn_call_item.call_id == tool_call.id + assert fn_call_item.name == tool_call.function.name + assert fn_call_item.arguments == tool_call.function.arguments + assert fn_call_item.type == "function_call" + + +def test_items_to_messages_with_string_user_content(): + """ + A simple string as the items argument should be converted into a user + message param dict with the same content. + """ + result = _Converter.items_to_messages("Ask me anything") + assert isinstance(result, list) + assert len(result) == 1 + msg = result[0] + assert msg["role"] == "user" + assert msg["content"] == "Ask me anything" + + +def test_items_to_messages_with_easy_input_message(): + """ + Given an easy input message dict (just role/content), the converter should + produce the appropriate ChatCompletionMessageParam with the same content. + """ + items: list[TResponseInputItem] = [ + { + "role": "user", + "content": "How are you?", + } + ] + messages = _Converter.items_to_messages(items) + assert len(messages) == 1 + out = messages[0] + assert out["role"] == "user" + # For simple string inputs, the converter returns the content as a bare string + assert out["content"] == "How are you?" + + +def test_items_to_messages_with_output_message_and_function_call(): + """ + Given a sequence of one ResponseOutputMessageParam followed by a + ResponseFunctionToolCallParam, the converter should produce a single + ChatCompletionAssistantMessageParam that includes both the assistant's + textual content and a populated `tool_calls` reflecting the function call. + """ + # Construct output message param dict with two content parts. + output_text: ResponseOutputText = ResponseOutputText( + text="Part 1", + type="output_text", + annotations=[], + ) + refusal: ResponseOutputRefusal = ResponseOutputRefusal( + refusal="won't do that", + type="refusal", + ) + resp_msg: ResponseOutputMessage = ResponseOutputMessage( + id="42", + type="message", + role="assistant", + status="completed", + content=[output_text, refusal], + ) + # Construct a function call item dict (as if returned from model) + func_item: ResponseFunctionToolCallParam = { + "id": "99", + "call_id": "abc", + "name": "math", + "arguments": "{}", + "type": "function_call", + } + items: list[TResponseInputItem] = [ + resp_msg.model_dump(), # type:ignore + func_item, + ] + messages = _Converter.items_to_messages(items) + # Should return a single assistant message + assert len(messages) == 1 + assistant = messages[0] + assert assistant["role"] == "assistant" + # Content combines text portions of the output message + assert "content" in assistant + assert assistant["content"] == "Part 1" + # Refusal in output message should be represented in assistant message + assert "refusal" in assistant + assert assistant["refusal"] == refusal.refusal + # Tool calls list should contain one ChatCompletionMessageToolCall dict + tool_calls = assistant.get("tool_calls") + assert isinstance(tool_calls, list) + assert len(tool_calls) == 1 + tool_call = tool_calls[0] + assert tool_call["type"] == "function" + assert tool_call["function"]["name"] == "math" + assert tool_call["function"]["arguments"] == "{}" + + +def test_convert_tool_choice_handles_standard_and_named_options() -> None: + """ + The `_Converter.convert_tool_choice` method should return NOT_GIVEN + if no choice is provided, pass through values like "auto", "required", + or "none" unchanged, and translate any other string into a function + selection dict. + """ + assert _Converter.convert_tool_choice(None).__class__.__name__ == "NotGiven" + assert _Converter.convert_tool_choice("auto") == "auto" + assert _Converter.convert_tool_choice("required") == "required" + assert _Converter.convert_tool_choice("none") == "none" + tool_choice_dict = _Converter.convert_tool_choice("mytool") + assert isinstance(tool_choice_dict, dict) + assert tool_choice_dict["type"] == "function" + assert tool_choice_dict["function"]["name"] == "mytool" + + +def test_convert_response_format_returns_not_given_for_plain_text_and_dict_for_schemas() -> None: + """ + The `_Converter.convert_response_format` method should return NOT_GIVEN + when no output schema is provided or if the output schema indicates + plain text. For structured output schemas, it should return a dict + with type `json_schema` and include the generated JSON schema and + strict flag from the provided `AgentOutputSchema`. + """ + # when output is plain text (schema None or output_type str), do not include response_format + assert _Converter.convert_response_format(None).__class__.__name__ == "NotGiven" + assert ( + _Converter.convert_response_format(AgentOutputSchema(str)).__class__.__name__ == "NotGiven" + ) + # For e.g. integer output, we expect a response_format dict + schema = AgentOutputSchema(int) + resp_format = _Converter.convert_response_format(schema) + assert isinstance(resp_format, dict) + assert resp_format["type"] == "json_schema" + assert resp_format["json_schema"]["name"] == "final_output" + assert "strict" in resp_format["json_schema"] + assert resp_format["json_schema"]["strict"] == schema.strict_json_schema + assert "schema" in resp_format["json_schema"] + assert resp_format["json_schema"]["schema"] == schema.json_schema() + + +def test_items_to_messages_with_function_output_item(): + """ + A function call output item should be converted into a tool role message + dict with the appropriate tool_call_id and content. + """ + func_output_item: FunctionCallOutput = { + "type": "function_call_output", + "call_id": "somecall", + "output": '{"foo": "bar"}', + } + messages = _Converter.items_to_messages([func_output_item]) + assert len(messages) == 1 + tool_msg = messages[0] + assert tool_msg["role"] == "tool" + assert tool_msg["tool_call_id"] == func_output_item["call_id"] + assert tool_msg["content"] == func_output_item["output"] + + +def test_extract_all_and_text_content_for_strings_and_lists(): + """ + The converter provides helpers for extracting user-supplied message content + either as a simple string or as a list of `input_text` dictionaries. + When passed a bare string, both `extract_all_content` and + `extract_text_content` should return the string unchanged. + When passed a list of input dictionaries, `extract_all_content` should + produce a list of `ChatCompletionContentPart` dicts, and `extract_text_content` + should filter to only the textual parts. + """ + prompt = "just text" + assert _Converter.extract_all_content(prompt) == prompt + assert _Converter.extract_text_content(prompt) == prompt + text1: ResponseInputTextParam = {"type": "input_text", "text": "one"} + text2: ResponseInputTextParam = {"type": "input_text", "text": "two"} + all_parts = _Converter.extract_all_content([text1, text2]) + assert isinstance(all_parts, list) + assert len(all_parts) == 2 + assert all_parts[0]["type"] == "text" and all_parts[0]["text"] == "one" + assert all_parts[1]["type"] == "text" and all_parts[1]["text"] == "two" + text_parts = _Converter.extract_text_content([text1, text2]) + assert isinstance(text_parts, list) + assert all(p["type"] == "text" for p in text_parts) + assert [p["text"] for p in text_parts] == ["one", "two"] + + +def test_items_to_messages_handles_system_and_developer_roles(): + """ + Roles other than `user` (e.g. `system` and `developer`) need to be + converted appropriately whether provided as simple dicts or as full + `message` typed dicts. + """ + sys_items: list[TResponseInputItem] = [{"role": "system", "content": "setup"}] + sys_msgs = _Converter.items_to_messages(sys_items) + assert len(sys_msgs) == 1 + assert sys_msgs[0]["role"] == "system" + assert sys_msgs[0]["content"] == "setup" + dev_items: list[TResponseInputItem] = [{"role": "developer", "content": "debug"}] + dev_msgs = _Converter.items_to_messages(dev_items) + assert len(dev_msgs) == 1 + assert dev_msgs[0]["role"] == "developer" + assert dev_msgs[0]["content"] == "debug" + + +def test_maybe_input_message_allows_message_typed_dict(): + """ + The `_Converter.maybe_input_message` should recognize a dict with + "type": "message" and a supported role as an input message. Ensure + that such dicts are passed through by `items_to_messages`. + """ + # Construct a dict with the proper required keys for a ResponseInputParam.Message + message_dict: TResponseInputItem = { + "type": "message", + "role": "user", + "content": "hi", + } + assert _Converter.maybe_input_message(message_dict) is not None + # items_to_messages should process this correctly + msgs = _Converter.items_to_messages([message_dict]) + assert len(msgs) == 1 + assert msgs[0]["role"] == "user" + assert msgs[0]["content"] == "hi" + + +def test_tool_call_conversion(): + """ + Test that tool calls are converted correctly. + """ + function_call = ResponseFunctionToolCallParam( + id="tool1", + call_id="abc", + name="math", + arguments="{}", + type="function_call", + ) + + messages = _Converter.items_to_messages([function_call]) + assert len(messages) == 1 + tool_msg = messages[0] + assert tool_msg["role"] == "assistant" + assert tool_msg.get("content") is None + tool_calls = list(tool_msg.get("tool_calls", [])) + assert len(tool_calls) == 1 + + tool_call = tool_calls[0] + assert tool_call["id"] == function_call["call_id"] + assert tool_call["function"]["name"] == function_call["name"] + assert tool_call["function"]["arguments"] == function_call["arguments"] + + +@pytest.mark.parametrize("role", ["user", "system", "developer"]) +def test_input_message_with_all_roles(role: str): + """ + The `_Converter.maybe_input_message` should recognize a dict with + "type": "message" and a supported role as an input message. Ensure + that such dicts are passed through by `items_to_messages`. + """ + # Construct a dict with the proper required keys for a ResponseInputParam.Message + casted_role = cast(Literal["user", "system", "developer"], role) + message_dict: TResponseInputItem = { + "type": "message", + "role": casted_role, + "content": "hi", + } + assert _Converter.maybe_input_message(message_dict) is not None + # items_to_messages should process this correctly + msgs = _Converter.items_to_messages([message_dict]) + assert len(msgs) == 1 + assert msgs[0]["role"] == casted_role + assert msgs[0]["content"] == "hi" + + +def test_item_reference_errors(): + """ + Test that item references are converted correctly. + """ + with pytest.raises(UserError): + _Converter.items_to_messages( + [ + { + "type": "item_reference", + "id": "item1", + } + ] + ) + + +class TestObject: + pass + + +def test_unknown_object_errors(): + """ + Test that unknown objects are converted correctly. + """ + with pytest.raises(UserError, match="Unhandled item type or structure"): + # Purposely ignore the type error + _Converter.items_to_messages([TestObject()]) # type: ignore diff --git a/tests/test_openai_chatcompletions_stream.py b/tests/test_openai_chatcompletions_stream.py new file mode 100644 index 0000000..2a15f7f --- /dev/null +++ b/tests/test_openai_chatcompletions_stream.py @@ -0,0 +1,278 @@ +from collections.abc import AsyncIterator + +import pytest +from openai.types.chat.chat_completion_chunk import ( + ChatCompletionChunk, + Choice, + ChoiceDelta, + ChoiceDeltaToolCall, + ChoiceDeltaToolCallFunction, +) +from openai.types.completion_usage import CompletionUsage +from openai.types.responses import ( + Response, + ResponseFunctionToolCall, + ResponseOutputMessage, + ResponseOutputRefusal, + ResponseOutputText, +) + +from agents.model_settings import ModelSettings +from agents.models.interface import ModelTracing +from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel +from agents.models.openai_provider import OpenAIProvider + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_response_yields_events_for_text_content(monkeypatch) -> None: + """ + Validate that `stream_response` emits the correct sequence of events when + streaming a simple assistant message consisting of plain text content. + We simulate two chunks of text returned from the chat completion stream. + """ + # Create two chunks that will be emitted by the fake stream. + chunk1 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(content="He"))], + ) + # Mark last chunk with usage so stream_response knows this is final. + chunk2 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(content="llo"))], + usage=CompletionUsage(completion_tokens=5, prompt_tokens=7, total_tokens=12), + ) + + async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: + for c in (chunk1, chunk2): + yield c + + # Patch _fetch_response to inject our fake stream + async def patched_fetch_response(self, *args, **kwargs): + # `_fetch_response` is expected to return a Response skeleton and the async stream + resp = Response( + id="resp-id", + created_at=0, + model="fake-model", + object="response", + output=[], + tool_choice="none", + tools=[], + parallel_tool_calls=False, + ) + return resp, fake_stream() + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + output_events = [] + async for event in model.stream_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ): + output_events.append(event) + # We expect a response.created, then a response.output_item.added, content part added, + # two content delta events (for "He" and "llo"), a content part done, the assistant message + # output_item.done, and finally response.completed. + # There should be 8 events in total. + assert len(output_events) == 8 + # First event indicates creation. + assert output_events[0].type == "response.created" + # The output item added and content part added events should mark the assistant message. + assert output_events[1].type == "response.output_item.added" + assert output_events[2].type == "response.content_part.added" + # Two text delta events. + assert output_events[3].type == "response.output_text.delta" + assert output_events[3].delta == "He" + assert output_events[4].type == "response.output_text.delta" + assert output_events[4].delta == "llo" + # After streaming, the content part and item should be marked done. + assert output_events[5].type == "response.content_part.done" + assert output_events[6].type == "response.output_item.done" + # Last event indicates completion of the stream. + assert output_events[7].type == "response.completed" + # The completed response should have one output message with full text. + completed_resp = output_events[7].response + assert isinstance(completed_resp.output[0], ResponseOutputMessage) + assert isinstance(completed_resp.output[0].content[0], ResponseOutputText) + assert completed_resp.output[0].content[0].text == "Hello" + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_response_yields_events_for_refusal_content(monkeypatch) -> None: + """ + Validate that when the model streams a refusal string instead of normal content, + `stream_response` emits the appropriate sequence of events including + `response.refusal.delta` events for each chunk of the refusal message and + constructs a completed assistant message with a `ResponseOutputRefusal` part. + """ + # Simulate refusal text coming in two pieces, like content but using the `refusal` + # field on the delta rather than `content`. + chunk1 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(refusal="No"))], + ) + chunk2 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(refusal="Thanks"))], + usage=CompletionUsage(completion_tokens=2, prompt_tokens=2, total_tokens=4), + ) + + async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: + for c in (chunk1, chunk2): + yield c + + async def patched_fetch_response(self, *args, **kwargs): + resp = Response( + id="resp-id", + created_at=0, + model="fake-model", + object="response", + output=[], + tool_choice="none", + tools=[], + parallel_tool_calls=False, + ) + return resp, fake_stream() + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + output_events = [] + async for event in model.stream_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ): + output_events.append(event) + # Expect sequence similar to text: created, output_item.added, content part added, + # two refusal delta events, content part done, output_item.done, completed. + assert len(output_events) == 8 + assert output_events[0].type == "response.created" + assert output_events[1].type == "response.output_item.added" + assert output_events[2].type == "response.content_part.added" + assert output_events[3].type == "response.refusal.delta" + assert output_events[3].delta == "No" + assert output_events[4].type == "response.refusal.delta" + assert output_events[4].delta == "Thanks" + assert output_events[5].type == "response.content_part.done" + assert output_events[6].type == "response.output_item.done" + assert output_events[7].type == "response.completed" + completed_resp = output_events[7].response + assert isinstance(completed_resp.output[0], ResponseOutputMessage) + refusal_part = completed_resp.output[0].content[0] + assert isinstance(refusal_part, ResponseOutputRefusal) + assert refusal_part.refusal == "NoThanks" + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_response_yields_events_for_tool_call(monkeypatch) -> None: + """ + Validate that `stream_response` emits the correct sequence of events when + the model is streaming a function/tool call instead of plain text. + The function call will be split across two chunks. + """ + # Simulate a single tool call whose ID stays constant and function name/args built over chunks. + tool_call_delta1 = ChoiceDeltaToolCall( + index=0, + id="tool-id", + function=ChoiceDeltaToolCallFunction(name="my_", arguments="arg1"), + type="function", + ) + tool_call_delta2 = ChoiceDeltaToolCall( + index=0, + id="tool-id", + function=ChoiceDeltaToolCallFunction(name="func", arguments="arg2"), + type="function", + ) + chunk1 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(tool_calls=[tool_call_delta1]))], + ) + chunk2 = ChatCompletionChunk( + id="chunk-id", + created=1, + model="fake", + object="chat.completion.chunk", + choices=[Choice(index=0, delta=ChoiceDelta(tool_calls=[tool_call_delta2]))], + usage=CompletionUsage(completion_tokens=1, prompt_tokens=1, total_tokens=2), + ) + + async def fake_stream() -> AsyncIterator[ChatCompletionChunk]: + for c in (chunk1, chunk2): + yield c + + async def patched_fetch_response(self, *args, **kwargs): + resp = Response( + id="resp-id", + created_at=0, + model="fake-model", + object="response", + output=[], + tool_choice="none", + tools=[], + parallel_tool_calls=False, + ) + return resp, fake_stream() + + monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) + model = OpenAIProvider(use_responses=False).get_model("gpt-4") + output_events = [] + async for event in model.stream_response( + system_instructions=None, + input="", + model_settings=ModelSettings(), + tools=[], + output_schema=None, + handoffs=[], + tracing=ModelTracing.DISABLED, + ): + output_events.append(event) + # Sequence should be: response.created, then after loop we expect function call-related events: + # one response.output_item.added for function call, a response.function_call_arguments.delta, + # a response.output_item.done, and finally response.completed. + assert output_events[0].type == "response.created" + # The next three events are about the tool call. + assert output_events[1].type == "response.output_item.added" + # The added item should be a ResponseFunctionToolCall. + added_fn = output_events[1].item + assert isinstance(added_fn, ResponseFunctionToolCall) + assert added_fn.name == "my_func" # Name should be concatenation of both chunks. + assert added_fn.arguments == "arg1arg2" + assert output_events[2].type == "response.function_call_arguments.delta" + assert output_events[2].delta == "arg1arg2" + assert output_events[3].type == "response.output_item.done" + assert output_events[4].type == "response.completed" + assert output_events[2].delta == "arg1arg2" + assert output_events[3].type == "response.output_item.done" + assert output_events[4].type == "response.completed" + assert added_fn.name == "my_func" # Name should be concatenation of both chunks. + assert added_fn.arguments == "arg1arg2" + assert output_events[2].type == "response.function_call_arguments.delta" + assert output_events[2].delta == "arg1arg2" + assert output_events[3].type == "response.output_item.done" + assert output_events[4].type == "response.completed" diff --git a/tests/test_openai_responses_converter.py b/tests/test_openai_responses_converter.py new file mode 100644 index 0000000..5820426 --- /dev/null +++ b/tests/test_openai_responses_converter.py @@ -0,0 +1,205 @@ +# Copyright (c) OpenAI +# +# Licensed under the MIT License. +# See LICENSE file in the project root for full license information. + +""" +Unit tests for the `Converter` class defined in +`agents.models.openai_responses`. The converter is responsible for +translating various agent tool types and output schemas into the parameter +structures expected by the OpenAI Responses API. + +We test the following aspects: + +- `convert_tool_choice` correctly maps high-level tool choice strings into + the tool choice values accepted by the Responses API, including special types + like `file_search` and `web_search`, and falling back to function names + for arbitrary string values. +- `get_response_format` returns `openai.NOT_GIVEN` for plain-text response + formats and an appropriate format dict when a JSON-structured output schema + is provided. +- `convert_tools` maps our internal `Tool` dataclasses into the appropriate + request payloads and includes list, and enforces constraints like at most + one `ComputerTool`. +""" + +import pytest +from openai import NOT_GIVEN +from pydantic import BaseModel + +from agents import ( + Agent, + AgentOutputSchema, + Computer, + ComputerTool, + FileSearchTool, + Handoff, + Tool, + UserError, + WebSearchTool, + function_tool, + handoff, +) +from agents.models.openai_responses import Converter + + +def test_convert_tool_choice_standard_values(): + """ + Make sure that the standard tool_choice values map to themselves or + to "auto"/"required"/"none" as appropriate, and that special string + values map to the appropriate dicts. + """ + assert Converter.convert_tool_choice(None) is NOT_GIVEN + assert Converter.convert_tool_choice("auto") == "auto" + assert Converter.convert_tool_choice("required") == "required" + assert Converter.convert_tool_choice("none") == "none" + # Special tool types are represented as dicts of type only. + assert Converter.convert_tool_choice("file_search") == {"type": "file_search"} + assert Converter.convert_tool_choice("web_search_preview") == {"type": "web_search_preview"} + assert Converter.convert_tool_choice("computer_use_preview") == {"type": "computer_use_preview"} + # Arbitrary string should be interpreted as a function name. + assert Converter.convert_tool_choice("my_function") == { + "type": "function", + "name": "my_function", + } + + +def test_get_response_format_plain_text_and_json_schema(): + """ + For plain text output (default, or output type of `str`), the converter + should return NOT_GIVEN, indicating no special response format constraint. + If an output schema is provided for a structured type, the converter + should return a `format` dict with the schema and strictness. The exact + JSON schema depends on the output type; we just assert that required + keys are present and that we get back the original schema. + """ + # Default output (None) should be considered plain text. + assert Converter.get_response_format(None) is NOT_GIVEN + # An explicit plain-text schema (str) should also yield NOT_GIVEN. + assert Converter.get_response_format(AgentOutputSchema(str)) is NOT_GIVEN + + # A model-based schema should produce a format dict. + class OutModel(BaseModel): + foo: int + bar: str + + out_schema = AgentOutputSchema(OutModel) + fmt = Converter.get_response_format(out_schema) + assert isinstance(fmt, dict) + assert "format" in fmt + inner = fmt["format"] + assert inner.get("type") == "json_schema" + assert inner.get("name") == "final_output" + assert isinstance(inner.get("schema"), dict) + # Should include a strict flag matching the schema's strictness setting. + assert inner.get("strict") == out_schema.strict_json_schema + + +def test_convert_tools_basic_types_and_includes(): + """ + Construct a variety of tool types and make sure `convert_tools` returns + a matching list of tool param dicts and the expected includes. Also + check that only a single computer tool is allowed. + """ + # Simple function tool + tool_fn = function_tool(lambda a: "x", name_override="fn") + # File search tool with include_search_results set + file_tool = FileSearchTool( + max_num_results=3, vector_store_ids=["vs1"], include_search_results=True + ) + # Web search tool with custom params + web_tool = WebSearchTool(user_location=None, search_context_size="high") + + # Dummy computer tool subclassing the Computer ABC with minimal methods. + class DummyComputer(Computer): + @property + def environment(self): + return "mac" + + @property + def dimensions(self): + return (800, 600) + + def screenshot(self) -> str: + raise NotImplementedError + + def click(self, x: int, y: int, button: str) -> None: + raise NotImplementedError + + def double_click(self, x: int, y: int) -> None: + raise NotImplementedError + + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + raise NotImplementedError + + def type(self, text: str) -> None: + raise NotImplementedError + + def wait(self) -> None: + raise NotImplementedError + + def move(self, x: int, y: int) -> None: + raise NotImplementedError + + def keypress(self, keys: list[str]) -> None: + raise NotImplementedError + + def drag(self, path: list[tuple[int, int]]) -> None: + raise NotImplementedError + + # Wrap our concrete computer in a ComputerTool for conversion. + comp_tool = ComputerTool(computer=DummyComputer()) + tools: list[Tool] = [tool_fn, file_tool, web_tool, comp_tool] + converted = Converter.convert_tools(tools, handoffs=[]) + assert isinstance(converted.tools, list) + assert isinstance(converted.includes, list) + # The includes list should have exactly the include for file search when include_search_results + # is True. + assert converted.includes == ["file_search_call.results"] + # There should be exactly four converted tool dicts. + assert len(converted.tools) == 4 + # Extract types and verify. + types = [ct["type"] for ct in converted.tools] + assert "function" in types + assert "file_search" in types + assert "web_search_preview" in types + assert "computer-preview" in types + # Verify file search tool contains max_num_results and vector_store_ids + file_params = next(ct for ct in converted.tools if ct["type"] == "file_search") + assert file_params.get("max_num_results") == file_tool.max_num_results + assert file_params.get("vector_store_ids") == file_tool.vector_store_ids + # Verify web search tool contains user_location and search_context_size + web_params = next(ct for ct in converted.tools if ct["type"] == "web_search_preview") + assert web_params.get("user_location") == web_tool.user_location + assert web_params.get("search_context_size") == web_tool.search_context_size + # Verify computer tool contains environment and computed dimensions + comp_params = next(ct for ct in converted.tools if ct["type"] == "computer-preview") + assert comp_params.get("environment") == "mac" + assert comp_params.get("display_width") == 800 + assert comp_params.get("display_height") == 600 + # The function tool dict should have name and description fields. + fn_params = next(ct for ct in converted.tools if ct["type"] == "function") + assert fn_params.get("name") == tool_fn.name + assert fn_params.get("description") == tool_fn.description + + # Only one computer tool should be allowed. + with pytest.raises(UserError): + Converter.convert_tools(tools=[comp_tool, comp_tool], handoffs=[]) + + +def test_convert_tools_includes_handoffs(): + """ + When handoff objects are included, `convert_tools` should append their + tool param dicts after tools and include appropriate descriptions. + """ + agent = Agent(name="support", handoff_description="Handles support") + handoff_obj = handoff(agent) + converted = Converter.convert_tools(tools=[], handoffs=[handoff_obj]) + assert isinstance(converted.tools, list) + assert len(converted.tools) == 1 + handoff_tool = converted.tools[0] + assert handoff_tool.get("type") == "function" + assert handoff_tool.get("name") == Handoff.default_tool_name(agent) + assert handoff_tool.get("description") == Handoff.default_tool_description(agent) + # No includes for handoffs by default. + assert converted.includes == [] diff --git a/tests/test_output_tool.py b/tests/test_output_tool.py new file mode 100644 index 0000000..31ac984 --- /dev/null +++ b/tests/test_output_tool.py @@ -0,0 +1,113 @@ +import json + +import pytest +from pydantic import BaseModel +from typing_extensions import TypedDict + +from agents import Agent, AgentOutputSchema, ModelBehaviorError, Runner, UserError, _utils +from agents.agent_output import _WRAPPER_DICT_KEY + + +def test_plain_text_output(): + agent = Agent(name="test") + output_schema = Runner._get_output_schema(agent) + assert not output_schema, "Shouldn't have an output tool config without an output type" + + agent = Agent(name="test", output_type=str) + assert not output_schema, "Shouldn't have an output tool config with str output type" + + +class Foo(BaseModel): + bar: str + + +def test_structured_output_pydantic(): + agent = Agent(name="test", output_type=Foo) + output_schema = Runner._get_output_schema(agent) + assert output_schema, "Should have an output tool config with a structured output type" + + assert output_schema.output_type == Foo, "Should have the correct output type" + assert not output_schema._is_wrapped, "Pydantic objects should not be wrapped" + for key, value in Foo.model_json_schema().items(): + assert output_schema.json_schema()[key] == value + + json_str = Foo(bar="baz").model_dump_json() + validated = output_schema.validate_json(json_str) + assert validated == Foo(bar="baz") + + +class Bar(TypedDict): + bar: str + + +def test_structured_output_typed_dict(): + agent = Agent(name="test", output_type=Bar) + output_schema = Runner._get_output_schema(agent) + assert output_schema, "Should have an output tool config with a structured output type" + assert output_schema.output_type == Bar, "Should have the correct output type" + assert not output_schema._is_wrapped, "TypedDicts should not be wrapped" + + json_str = json.dumps(Bar(bar="baz")) + validated = output_schema.validate_json(json_str) + assert validated == Bar(bar="baz") + + +def test_structured_output_list(): + agent = Agent(name="test", output_type=list[str]) + output_schema = Runner._get_output_schema(agent) + assert output_schema, "Should have an output tool config with a structured output type" + assert output_schema.output_type == list[str], "Should have the correct output type" + assert output_schema._is_wrapped, "Lists should be wrapped" + + # This is testing implementation details, but it's useful to make sure this doesn't break + json_str = json.dumps({_WRAPPER_DICT_KEY: ["foo", "bar"]}) + validated = output_schema.validate_json(json_str) + assert validated == ["foo", "bar"] + + +def test_bad_json_raises_error(mocker): + agent = Agent(name="test", output_type=Foo) + output_schema = Runner._get_output_schema(agent) + assert output_schema, "Should have an output tool config with a structured output type" + + with pytest.raises(ModelBehaviorError): + output_schema.validate_json("not valid json") + + agent = Agent(name="test", output_type=list[str]) + output_schema = Runner._get_output_schema(agent) + assert output_schema, "Should have an output tool config with a structured output type" + + mock_validate_json = mocker.patch.object(_utils, "validate_json") + mock_validate_json.return_value = ["foo"] + + with pytest.raises(ModelBehaviorError): + output_schema.validate_json(json.dumps(["foo"])) + + mock_validate_json.return_value = {"value": "foo"} + + with pytest.raises(ModelBehaviorError): + output_schema.validate_json(json.dumps(["foo"])) + + +def test_plain_text_obj_doesnt_produce_schema(): + output_wrapper = AgentOutputSchema(output_type=str) + with pytest.raises(UserError): + output_wrapper.json_schema() + + +def test_structured_output_is_strict(): + output_wrapper = AgentOutputSchema(output_type=Foo) + assert output_wrapper.strict_json_schema + for key, value in Foo.model_json_schema().items(): + assert output_wrapper.json_schema()[key] == value + + assert ( + "additionalProperties" in output_wrapper.json_schema() + and not output_wrapper.json_schema()["additionalProperties"] + ) + + +def test_setting_strict_false_works(): + output_wrapper = AgentOutputSchema(output_type=Foo, strict_json_schema=False) + assert not output_wrapper.strict_json_schema + assert output_wrapper.json_schema() == Foo.model_json_schema() diff --git a/tests/test_responses.py b/tests/test_responses.py new file mode 100644 index 0000000..6b91bf8 --- /dev/null +++ b/tests/test_responses.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from typing import Any + +from openai.types.responses import ( + ResponseFunctionToolCall, + ResponseOutputItem, + ResponseOutputMessage, + ResponseOutputText, +) + +from agents import ( + Agent, + FunctionTool, + Handoff, + TResponseInputItem, + default_tool_error_function, + function_tool, +) + + +def get_text_input_item(content: str) -> TResponseInputItem: + return { + "content": content, + "role": "user", + } + + +def get_text_message(content: str) -> ResponseOutputItem: + return ResponseOutputMessage( + id="1", + type="message", + role="assistant", + content=[ResponseOutputText(text=content, type="output_text", annotations=[])], + status="completed", + ) + + +def get_function_tool( + name: str | None = None, return_value: str | None = None, hide_errors: bool = False +) -> FunctionTool: + def _foo() -> str: + return return_value or "result_ok" + + return function_tool( + _foo, + name_override=name, + failure_error_function=None if hide_errors else default_tool_error_function, + ) + + +def get_function_tool_call(name: str, arguments: str | None = None) -> ResponseOutputItem: + return ResponseFunctionToolCall( + id="1", + call_id="2", + type="function_call", + name=name, + arguments=arguments or "", + ) + + +def get_handoff_tool_call( + to_agent: Agent[Any], override_name: str | None = None, args: str | None = None +) -> ResponseOutputItem: + name = override_name or Handoff.default_tool_name(to_agent) + return get_function_tool_call(name, args) + + +def get_final_output_message(args: str) -> ResponseOutputItem: + return ResponseOutputMessage( + id="1", + type="message", + role="assistant", + content=[ResponseOutputText(text=args, type="output_text", annotations=[])], + status="completed", + ) diff --git a/tests/test_responses_tracing.py b/tests/test_responses_tracing.py new file mode 100644 index 0000000..82b8e75 --- /dev/null +++ b/tests/test_responses_tracing.py @@ -0,0 +1,212 @@ +import pytest +from openai import AsyncOpenAI +from openai.types.responses import ResponseCompletedEvent + +from agents import ModelSettings, ModelTracing, OpenAIResponsesModel, trace +from agents.tracing.span_data import ResponseSpanData +from tests import fake_model + +from .testing_processor import fetch_ordered_spans + + +class DummyTracing: + def is_disabled(self): + return False + + +class DummyUsage: + def __init__(self, input_tokens=1, output_tokens=1, total_tokens=2): + self.input_tokens = input_tokens + self.output_tokens = output_tokens + self.total_tokens = total_tokens + + +class DummyResponse: + def __init__(self): + self.id = "dummy-id" + self.output = [] + self.usage = DummyUsage() + + def __aiter__(self): + yield ResponseCompletedEvent( + type="response.completed", + response=fake_model.get_response_obj(self.output), + ) + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_get_response_creates_trace(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Mock _fetch_response to return a dummy response with a known id + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + return DummyResponse() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Call get_response + await model.get_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED + ) + + spans = fetch_ordered_spans() + assert len(spans) == 1 + + assert isinstance(spans[0].span_data, ResponseSpanData) + assert spans[0].span_data.response is not None + assert spans[0].span_data.response.id == "dummy-id" + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_non_data_tracing_doesnt_set_response_id(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Mock _fetch_response to return a dummy response with a known id + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + return DummyResponse() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Call get_response + await model.get_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED_WITHOUT_DATA + ) + + spans = fetch_ordered_spans() + assert len(spans) == 1 + assert spans[0].span_data.response is None + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_disable_tracing_does_not_create_span(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Mock _fetch_response to return a dummy response with a known id + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + return DummyResponse() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Call get_response + await model.get_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.DISABLED + ) + + spans = fetch_ordered_spans() + assert len(spans) == 0 + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_response_creates_trace(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Define a dummy fetch function that returns an async stream with a dummy response + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + class DummyStream: + async def __aiter__(self): + yield ResponseCompletedEvent( + type="response.completed", + response=fake_model.get_response_obj([], "dummy-id-123"), + ) + + return DummyStream() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Consume the stream to trigger processing of the final response + async for _ in model.stream_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED + ): + pass + + spans = fetch_ordered_spans() + assert len(spans) == 1 + assert isinstance(spans[0].span_data, ResponseSpanData) + assert spans[0].span_data.response is not None + assert spans[0].span_data.response.id == "dummy-id-123" + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_non_data_tracing_doesnt_set_response_id(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Define a dummy fetch function that returns an async stream with a dummy response + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + class DummyStream: + async def __aiter__(self): + yield ResponseCompletedEvent( + type="response.completed", + response=fake_model.get_response_obj([], "dummy-id-123"), + ) + + return DummyStream() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Consume the stream to trigger processing of the final response + async for _ in model.stream_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.ENABLED_WITHOUT_DATA + ): + pass + + spans = fetch_ordered_spans() + assert len(spans) == 1 + assert isinstance(spans[0].span_data, ResponseSpanData) + assert spans[0].span_data.response is None + + +@pytest.mark.allow_call_model_methods +@pytest.mark.asyncio +async def test_stream_disabled_tracing_doesnt_create_span(monkeypatch): + with trace(workflow_name="test"): + # Create an instance of the model + model = OpenAIResponsesModel(model="test-model", openai_client=AsyncOpenAI(api_key="test")) + + # Define a dummy fetch function that returns an async stream with a dummy response + async def dummy_fetch_response( + system_instructions, input, model_settings, tools, output_schema, handoffs, stream + ): + class DummyStream: + async def __aiter__(self): + yield ResponseCompletedEvent( + type="response.completed", + response=fake_model.get_response_obj([], "dummy-id-123"), + ) + + return DummyStream() + + monkeypatch.setattr(model, "_fetch_response", dummy_fetch_response) + + # Consume the stream to trigger processing of the final response + async for _ in model.stream_response( + "instr", "input", ModelSettings(), [], None, [], ModelTracing.DISABLED + ): + pass + + spans = fetch_ordered_spans() + assert len(spans) == 0 diff --git a/tests/test_result_cast.py b/tests/test_result_cast.py new file mode 100644 index 0000000..ec17e32 --- /dev/null +++ b/tests/test_result_cast.py @@ -0,0 +1,58 @@ +from typing import Any + +import pytest +from pydantic import BaseModel + +from agents import Agent, RunResult + + +def create_run_result(final_output: Any) -> RunResult: + return RunResult( + input="test", + new_items=[], + raw_responses=[], + final_output=final_output, + input_guardrail_results=[], + output_guardrail_results=[], + _last_agent=Agent(name="test"), + ) + + +class Foo(BaseModel): + bar: int + + +def test_result_cast_typechecks(): + """Correct casts should work fine.""" + result = create_run_result(1) + assert result.final_output_as(int) == 1 + + result = create_run_result("test") + assert result.final_output_as(str) == "test" + + result = create_run_result(Foo(bar=1)) + assert result.final_output_as(Foo) == Foo(bar=1) + + +def test_bad_cast_doesnt_raise(): + """Bad casts shouldn't error unless we ask for it.""" + result = create_run_result(1) + result.final_output_as(str) + + result = create_run_result("test") + result.final_output_as(Foo) + + +def test_bad_cast_with_param_raises(): + """Bad casts should raise a TypeError when we ask for it.""" + result = create_run_result(1) + with pytest.raises(TypeError): + result.final_output_as(str, raise_if_incorrect_type=True) + + result = create_run_result("test") + with pytest.raises(TypeError): + result.final_output_as(Foo, raise_if_incorrect_type=True) + + result = create_run_result(Foo(bar=1)) + with pytest.raises(TypeError): + result.final_output_as(int, raise_if_incorrect_type=True) diff --git a/tests/test_run_config.py b/tests/test_run_config.py new file mode 100644 index 0000000..51835ab --- /dev/null +++ b/tests/test_run_config.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +import pytest + +from agents import Agent, RunConfig, Runner +from agents.models.interface import Model, ModelProvider + +from .fake_model import FakeModel +from .test_responses import get_text_message + + +class DummyProvider(ModelProvider): + """A simple model provider that always returns the same model, and + records the model name it was asked to provide.""" + + def __init__(self, model_to_return: Model | None = None) -> None: + self.last_requested: str | None = None + self.model_to_return: Model = model_to_return or FakeModel() + + def get_model(self, model_name: str | None) -> Model: + # record the requested model name and return our test model + self.last_requested = model_name + return self.model_to_return + + +@pytest.mark.asyncio +async def test_model_provider_on_run_config_is_used_for_agent_model_name() -> None: + """ + When the agent's ``model`` attribute is a string and no explicit model override is + provided in the ``RunConfig``, the ``Runner`` should resolve the model using the + ``model_provider`` on the ``RunConfig``. + """ + fake_model = FakeModel(initial_output=[get_text_message("from-provider")]) + provider = DummyProvider(model_to_return=fake_model) + agent = Agent(name="test", model="test-model") + run_config = RunConfig(model_provider=provider) + result = await Runner.run(agent, input="any", run_config=run_config) + # We picked up the model from our dummy provider + assert provider.last_requested == "test-model" + assert result.final_output == "from-provider" + + +@pytest.mark.asyncio +async def test_run_config_model_name_override_takes_precedence() -> None: + """ + When a model name string is set on the RunConfig, then that name should be looked up + using the RunConfig's model_provider, and should override any model on the agent. + """ + fake_model = FakeModel(initial_output=[get_text_message("override-name")]) + provider = DummyProvider(model_to_return=fake_model) + agent = Agent(name="test", model="agent-model") + run_config = RunConfig(model="override-name", model_provider=provider) + result = await Runner.run(agent, input="any", run_config=run_config) + # We should have requested the override name, not the agent.model + assert provider.last_requested == "override-name" + assert result.final_output == "override-name" + + +@pytest.mark.asyncio +async def test_run_config_model_override_object_takes_precedence() -> None: + """ + When a concrete Model instance is set on the RunConfig, then that instance should be + returned by Runner._get_model regardless of the agent's model. + """ + fake_model = FakeModel(initial_output=[get_text_message("override-object")]) + agent = Agent(name="test", model="agent-model") + run_config = RunConfig(model=fake_model) + result = await Runner.run(agent, input="any", run_config=run_config) + # Our FakeModel on the RunConfig should have been used. + assert result.final_output == "override-object" + + +@pytest.mark.asyncio +async def test_agent_model_object_is_used_when_present() -> None: + """ + If the agent has a concrete Model object set as its model, and the RunConfig does + not specify a model override, then that object should be used directly without + consulting the RunConfig's model_provider. + """ + fake_model = FakeModel(initial_output=[get_text_message("from-agent-object")]) + provider = DummyProvider() + agent = Agent(name="test", model=fake_model) + run_config = RunConfig(model_provider=provider) + result = await Runner.run(agent, input="any", run_config=run_config) + # The dummy provider should never have been called, and the output should come from + # the FakeModel on the agent. + assert provider.last_requested is None + assert result.final_output == "from-agent-object" diff --git a/tests/test_run_step_execution.py b/tests/test_run_step_execution.py new file mode 100644 index 0000000..2d581bf --- /dev/null +++ b/tests/test_run_step_execution.py @@ -0,0 +1,307 @@ +from __future__ import annotations + +from typing import Any + +import pytest +from pydantic import BaseModel + +from agents import ( + Agent, + MessageOutputItem, + ModelResponse, + RunConfig, + RunContextWrapper, + RunHooks, + RunItem, + Runner, + ToolCallItem, + ToolCallOutputItem, + TResponseInputItem, + Usage, +) +from agents._run_impl import ( + NextStepFinalOutput, + NextStepHandoff, + NextStepRunAgain, + RunImpl, + SingleStepResult, +) + +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_input_item, + get_text_message, +) + + +@pytest.mark.asyncio +async def test_empty_response_is_final_output(): + agent = Agent[None](name="test") + response = ModelResponse( + output=[], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent, response) + + assert result.original_input == "hello" + assert result.generated_items == [] + assert isinstance(result.next_step, NextStepFinalOutput) + assert result.next_step.output == "" + + +@pytest.mark.asyncio +async def test_plaintext_agent_no_tool_calls_is_final_output(): + agent = Agent(name="test") + response = ModelResponse( + output=[get_text_message("hello_world")], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent, response) + + assert result.original_input == "hello" + assert len(result.generated_items) == 1 + assert_item_is_message(result.generated_items[0], "hello_world") + assert isinstance(result.next_step, NextStepFinalOutput) + assert result.next_step.output == "hello_world" + + +@pytest.mark.asyncio +async def test_plaintext_agent_no_tool_calls_multiple_messages_is_final_output(): + agent = Agent(name="test") + response = ModelResponse( + output=[ + get_text_message("hello_world"), + get_text_message("bye"), + ], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result( + agent, + response, + original_input=[ + get_text_input_item("test"), + get_text_input_item("test2"), + ], + ) + + assert len(result.original_input) == 2 + assert len(result.generated_items) == 2 + assert_item_is_message(result.generated_items[0], "hello_world") + assert_item_is_message(result.generated_items[1], "bye") + + assert isinstance(result.next_step, NextStepFinalOutput) + assert result.next_step.output == "bye" + + +@pytest.mark.asyncio +async def test_plaintext_agent_with_tool_call_is_run_again(): + agent = Agent(name="test", tools=[get_function_tool(name="test", return_value="123")]) + response = ModelResponse( + output=[get_text_message("hello_world"), get_function_tool_call("test", "")], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent, response) + + assert result.original_input == "hello" + + # 3 items: new message, tool call, tool result + assert len(result.generated_items) == 3 + assert isinstance(result.next_step, NextStepRunAgain) + + items = result.generated_items + assert_item_is_message(items[0], "hello_world") + assert_item_is_function_tool_call(items[1], "test", None) + assert_item_is_function_tool_call_output(items[2], "123") + + assert isinstance(result.next_step, NextStepRunAgain) + + +@pytest.mark.asyncio +async def test_multiple_tool_calls(): + agent = Agent( + name="test", + tools=[ + get_function_tool(name="test_1", return_value="123"), + get_function_tool(name="test_2", return_value="456"), + get_function_tool(name="test_3", return_value="789"), + ], + ) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_function_tool_call("test_1"), + get_function_tool_call("test_2"), + ], + usage=Usage(), + referenceable_id=None, + ) + + result = await get_execute_result(agent, response) + assert result.original_input == "hello" + + # 5 items: new message, 2 tool calls, 2 tool call outputs + assert len(result.generated_items) == 5 + assert isinstance(result.next_step, NextStepRunAgain) + + items = result.generated_items + assert_item_is_message(items[0], "Hello, world!") + assert_item_is_function_tool_call(items[1], "test_1", None) + assert_item_is_function_tool_call(items[2], "test_2", None) + + assert isinstance(result.next_step, NextStepRunAgain) + + +@pytest.mark.asyncio +async def test_handoff_output_leads_to_handoff_next_step(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) + response = ModelResponse( + output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_1)], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent_3, response) + + assert isinstance(result.next_step, NextStepHandoff) + assert result.next_step.new_agent == agent_1 + + assert len(result.generated_items) == 3 + + +class Foo(BaseModel): + bar: str + + +@pytest.mark.asyncio +async def test_final_output_without_tool_runs_again(): + agent = Agent(name="test", output_type=Foo, tools=[get_function_tool("tool_1", "result")]) + response = ModelResponse( + output=[get_function_tool_call("tool_1")], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent, response) + + assert isinstance(result.next_step, NextStepRunAgain) + assert len(result.generated_items) == 2, "expected 2 items: tool call, tool call output" + + +@pytest.mark.asyncio +async def test_final_output_leads_to_final_output_next_step(): + agent = Agent(name="test", output_type=Foo) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_final_output_message(Foo(bar="123").model_dump_json()), + ], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent, response) + + assert isinstance(result.next_step, NextStepFinalOutput) + assert result.next_step.output == Foo(bar="123") + + +@pytest.mark.asyncio +async def test_handoff_and_final_output_leads_to_handoff_next_step(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2], output_type=Foo) + response = ModelResponse( + output=[ + get_final_output_message(Foo(bar="123").model_dump_json()), + get_handoff_tool_call(agent_1), + ], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent_3, response) + + assert isinstance(result.next_step, NextStepHandoff) + assert result.next_step.new_agent == agent_1 + + +@pytest.mark.asyncio +async def test_multiple_final_output_leads_to_final_output_next_step(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2], output_type=Foo) + response = ModelResponse( + output=[ + get_final_output_message(Foo(bar="123").model_dump_json()), + get_final_output_message(Foo(bar="456").model_dump_json()), + ], + usage=Usage(), + referenceable_id=None, + ) + result = await get_execute_result(agent_3, response) + + assert isinstance(result.next_step, NextStepFinalOutput) + assert result.next_step.output == Foo(bar="456") + + +# === Helpers === + + +def assert_item_is_message(item: RunItem, text: str) -> None: + assert isinstance(item, MessageOutputItem) + assert item.raw_item.type == "message" + assert item.raw_item.role == "assistant" + assert item.raw_item.content[0].type == "output_text" + assert item.raw_item.content[0].text == text + + +def assert_item_is_function_tool_call( + item: RunItem, name: str, arguments: str | None = None +) -> None: + assert isinstance(item, ToolCallItem) + assert item.raw_item.type == "function_call" + assert item.raw_item.name == name + assert not arguments or item.raw_item.arguments == arguments + + +def assert_item_is_function_tool_call_output(item: RunItem, output: str) -> None: + assert isinstance(item, ToolCallOutputItem) + assert item.raw_item["type"] == "function_call_output" + assert item.raw_item["output"] == output + + +async def get_execute_result( + agent: Agent[Any], + response: ModelResponse, + *, + original_input: str | list[TResponseInputItem] | None = None, + generated_items: list[RunItem] | None = None, + hooks: RunHooks[Any] | None = None, + context_wrapper: RunContextWrapper[Any] | None = None, + run_config: RunConfig | None = None, +) -> SingleStepResult: + output_schema = Runner._get_output_schema(agent) + handoffs = Runner._get_handoffs(agent) + + processed_response = RunImpl.process_model_response( + agent=agent, + response=response, + output_schema=output_schema, + handoffs=handoffs, + ) + return await RunImpl.execute_tools_and_side_effects( + agent=agent, + original_input=original_input or "hello", + new_response=response, + pre_step_items=generated_items or [], + processed_response=processed_response, + output_schema=output_schema, + hooks=hooks or RunHooks(), + context_wrapper=context_wrapper or RunContextWrapper(None), + run_config=run_config or RunConfig(), + ) diff --git a/tests/test_run_step_processing.py b/tests/test_run_step_processing.py new file mode 100644 index 0000000..41f65c4 --- /dev/null +++ b/tests/test_run_step_processing.py @@ -0,0 +1,422 @@ +from __future__ import annotations + +import pytest +from openai.types.responses import ( + ResponseComputerToolCall, + ResponseFileSearchToolCall, + ResponseFunctionWebSearch, +) +from openai.types.responses.response_computer_tool_call import ActionClick +from openai.types.responses.response_output_item import Reasoning, ReasoningContent +from pydantic import BaseModel + +from agents import ( + Agent, + Computer, + ComputerTool, + Handoff, + ModelBehaviorError, + ModelResponse, + ReasoningItem, + RunContextWrapper, + Runner, + ToolCallItem, + Usage, +) +from agents._run_impl import RunImpl + +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_message, +) + + +def test_empty_response(): + agent = Agent(name="test") + response = ModelResponse( + output=[], + usage=Usage(), + referenceable_id=None, + ) + + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert not result.handoffs + assert not result.functions + + +def test_no_tool_calls(): + agent = Agent(name="test") + response = ModelResponse( + output=[get_text_message("Hello, world!")], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert not result.handoffs + assert not result.functions + + +def test_single_tool_call(): + agent = Agent(name="test", tools=[get_function_tool(name="test")]) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_function_tool_call("test", ""), + ], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert not result.handoffs + assert result.functions and len(result.functions) == 1 + + func = result.functions[0] + assert func.tool_call.name == "test" + assert func.tool_call.arguments == "" + + +def test_missing_tool_call_raises_error(): + agent = Agent(name="test", tools=[get_function_tool(name="test")]) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_function_tool_call("missing", ""), + ], + usage=Usage(), + referenceable_id=None, + ) + + with pytest.raises(ModelBehaviorError): + RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + + +def test_multiple_tool_calls(): + agent = Agent( + name="test", + tools=[ + get_function_tool(name="test_1"), + get_function_tool(name="test_2"), + get_function_tool(name="test_3"), + ], + ) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_function_tool_call("test_1", "abc"), + get_function_tool_call("test_2", "xyz"), + ], + usage=Usage(), + referenceable_id=None, + ) + + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert not result.handoffs + assert result.functions and len(result.functions) == 2 + + func_1 = result.functions[0] + assert func_1.tool_call.name == "test_1" + assert func_1.tool_call.arguments == "abc" + + func_2 = result.functions[1] + assert func_2.tool_call.name == "test_2" + assert func_2.tool_call.arguments == "xyz" + + +@pytest.mark.asyncio +async def test_handoffs_parsed_correctly(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) + response = ModelResponse( + output=[get_text_message("Hello, world!")], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent_3, response=response, output_schema=None, handoffs=[] + ) + assert not result.handoffs, "Shouldn't have a handoff here" + + response = ModelResponse( + output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_1)], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent_3, + response=response, + output_schema=None, + handoffs=Runner._get_handoffs(agent_3), + ) + assert len(result.handoffs) == 1, "Should have a handoff here" + handoff = result.handoffs[0] + assert handoff.handoff.tool_name == Handoff.default_tool_name(agent_1) + assert handoff.handoff.tool_description == Handoff.default_tool_description(agent_1) + assert handoff.handoff.agent_name == agent_1.name + + handoff_agent = await handoff.handoff.on_invoke_handoff( + RunContextWrapper(None), handoff.tool_call.arguments + ) + assert handoff_agent == agent_1 + + +@pytest.mark.asyncio +async def test_missing_handoff_fails(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1]) + response = ModelResponse( + output=[get_text_message("Hello, world!"), get_handoff_tool_call(agent_2)], + usage=Usage(), + referenceable_id=None, + ) + with pytest.raises(ModelBehaviorError): + RunImpl.process_model_response( + agent=agent_3, + response=response, + output_schema=None, + handoffs=Runner._get_handoffs(agent_3), + ) + + +def test_multiple_handoffs_doesnt_error(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent(name="test_3", handoffs=[agent_1, agent_2]) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_handoff_tool_call(agent_1), + get_handoff_tool_call(agent_2), + ], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent_3, + response=response, + output_schema=None, + handoffs=Runner._get_handoffs(agent_3), + ) + assert len(result.handoffs) == 2, "Should have multiple handoffs here" + + +class Foo(BaseModel): + bar: str + + +def test_final_output_parsed_correctly(): + agent = Agent(name="test", output_type=Foo) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_final_output_message(Foo(bar="123").model_dump_json()), + ], + usage=Usage(), + referenceable_id=None, + ) + + RunImpl.process_model_response( + agent=agent, + response=response, + output_schema=Runner._get_output_schema(agent), + handoffs=[], + ) + + +def test_file_search_tool_call_parsed_correctly(): + # Ensure that a ResponseFileSearchToolCall output is parsed into a ToolCallItem and that no tool + # runs are scheduled. + + agent = Agent(name="test") + file_search_call = ResponseFileSearchToolCall( + id="fs1", + queries=["query"], + status="completed", + type="file_search_call", + ) + response = ModelResponse( + output=[get_text_message("hello"), file_search_call], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + # The final item should be a ToolCallItem for the file search call + assert any( + isinstance(item, ToolCallItem) and item.raw_item is file_search_call + for item in result.new_items + ) + assert not result.functions + assert not result.handoffs + + +def test_function_web_search_tool_call_parsed_correctly(): + agent = Agent(name="test") + web_search_call = ResponseFunctionWebSearch(id="w1", status="completed", type="web_search_call") + response = ModelResponse( + output=[get_text_message("hello"), web_search_call], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert any( + isinstance(item, ToolCallItem) and item.raw_item is web_search_call + for item in result.new_items + ) + assert not result.functions + assert not result.handoffs + + +def test_reasoning_item_parsed_correctly(): + # Verify that a Reasoning output item is converted into a ReasoningItem. + + reasoning = Reasoning( + id="r1", type="reasoning", content=[ReasoningContent(text="why", type="reasoning_summary")] + ) + response = ModelResponse( + output=[reasoning], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=Agent(name="test"), response=response, output_schema=None, handoffs=[] + ) + assert any( + isinstance(item, ReasoningItem) and item.raw_item is reasoning for item in result.new_items + ) + + +class DummyComputer(Computer): + """Minimal computer implementation for testing.""" + + @property + def environment(self): + return "mac" # pragma: no cover + + @property + def dimensions(self): + return (0, 0) # pragma: no cover + + def screenshot(self) -> str: + return "" # pragma: no cover + + def click(self, x: int, y: int, button: str) -> None: + return None # pragma: no cover + + def double_click(self, x: int, y: int) -> None: + return None # pragma: no cover + + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: + return None # pragma: no cover + + def type(self, text: str) -> None: + return None # pragma: no cover + + def wait(self) -> None: + return None # pragma: no cover + + def move(self, x: int, y: int) -> None: + return None # pragma: no cover + + def keypress(self, keys: list[str]) -> None: + return None # pragma: no cover + + def drag(self, path: list[tuple[int, int]]) -> None: + return None # pragma: no cover + + +def test_computer_tool_call_without_computer_tool_raises_error(): + # If the agent has no ComputerTool in its tools, process_model_response should raise a + # ModelBehaviorError when encountering a ResponseComputerToolCall. + computer_call = ResponseComputerToolCall( + id="c1", + type="computer_call", + action=ActionClick(type="click", x=1, y=2, button="left"), + call_id="c1", + pending_safety_checks=[], + status="completed", + ) + response = ModelResponse( + output=[computer_call], + usage=Usage(), + referenceable_id=None, + ) + with pytest.raises(ModelBehaviorError): + RunImpl.process_model_response( + agent=Agent(name="test"), response=response, output_schema=None, handoffs=[] + ) + + +def test_computer_tool_call_with_computer_tool_parsed_correctly(): + # If the agent contains a ComputerTool, ensure that a ResponseComputerToolCall is parsed into a + # ToolCallItem and scheduled to run in computer_actions. + dummy_computer = DummyComputer() + agent = Agent(name="test", tools=[ComputerTool(computer=dummy_computer)]) + computer_call = ResponseComputerToolCall( + id="c1", + type="computer_call", + action=ActionClick(type="click", x=1, y=2, button="left"), + call_id="c1", + pending_safety_checks=[], + status="completed", + ) + response = ModelResponse( + output=[computer_call], + usage=Usage(), + referenceable_id=None, + ) + result = RunImpl.process_model_response( + agent=agent, response=response, output_schema=None, handoffs=[] + ) + assert any( + isinstance(item, ToolCallItem) and item.raw_item is computer_call + for item in result.new_items + ) + assert result.computer_actions and result.computer_actions[0].tool_call == computer_call + + +def test_tool_and_handoff_parsed_correctly(): + agent_1 = Agent(name="test_1") + agent_2 = Agent(name="test_2") + agent_3 = Agent( + name="test_3", tools=[get_function_tool(name="test")], handoffs=[agent_1, agent_2] + ) + response = ModelResponse( + output=[ + get_text_message("Hello, world!"), + get_function_tool_call("test", "abc"), + get_handoff_tool_call(agent_1), + ], + usage=Usage(), + referenceable_id=None, + ) + + result = RunImpl.process_model_response( + agent=agent_3, + response=response, + output_schema=None, + handoffs=Runner._get_handoffs(agent_3), + ) + assert result.functions and len(result.functions) == 1 + assert len(result.handoffs) == 1, "Should have a handoff here" + handoff = result.handoffs[0] + assert handoff.handoff.tool_name == Handoff.default_tool_name(agent_1) + assert handoff.handoff.tool_description == Handoff.default_tool_description(agent_1) + assert handoff.handoff.agent_name == agent_1.name diff --git a/tests/test_strict_schema.py b/tests/test_strict_schema.py new file mode 100644 index 0000000..c35e9ad --- /dev/null +++ b/tests/test_strict_schema.py @@ -0,0 +1,126 @@ +import pytest + +from agents.exceptions import UserError +from agents.strict_schema import ensure_strict_json_schema + + +def test_empty_schema_has_additional_properties_false(): + strict_schema = ensure_strict_json_schema({}) + assert strict_schema["additionalProperties"] is False + + +def test_non_dict_schema_errors(): + with pytest.raises(TypeError): + ensure_strict_json_schema([]) # type: ignore + + +def test_object_without_additional_properties(): + # When an object type schema has properties but no additionalProperties, + # it should be added and the "required" list set from the property keys. + schema = {"type": "object", "properties": {"a": {"type": "string"}}} + result = ensure_strict_json_schema(schema) + assert result["type"] == "object" + assert result["additionalProperties"] is False + assert result["required"] == ["a"] + # The inner property remains unchanged (no additionalProperties is added for non-object types) + assert result["properties"]["a"] == {"type": "string"} + + +def test_object_with_true_additional_properties(): + # If additionalProperties is explicitly set to True for an object, a UserError should be raised. + schema = { + "type": "object", + "properties": {"a": {"type": "number"}}, + "additionalProperties": True, + } + with pytest.raises(UserError): + ensure_strict_json_schema(schema) + + +def test_array_items_processing_and_default_removal(): + # When processing an array, the items schema is processed recursively. + # Also, any "default": None should be removed. + schema = { + "type": "array", + "items": {"type": "number", "default": None}, + } + result = ensure_strict_json_schema(schema) + # "default" should be stripped from the items schema. + assert "default" not in result["items"] + assert result["items"]["type"] == "number" + + +def test_anyOf_processing(): + # Test that anyOf schemas are processed. + schema = { + "anyOf": [ + {"type": "object", "properties": {"a": {"type": "string"}}}, + {"type": "number", "default": None}, + ] + } + result = ensure_strict_json_schema(schema) + # For the first variant: object type should get additionalProperties and required keys set. + variant0 = result["anyOf"][0] + assert variant0["type"] == "object" + assert variant0["additionalProperties"] is False + assert variant0["required"] == ["a"] + + # For the second variant: the "default": None should be removed. + variant1 = result["anyOf"][1] + assert variant1["type"] == "number" + assert "default" not in variant1 + + +def test_allOf_single_entry_merging(): + # When an allOf list has a single entry, its content should be merged into the parent. + schema = { + "type": "object", + "allOf": [{"properties": {"a": {"type": "boolean"}}}], + } + result = ensure_strict_json_schema(schema) + # allOf should be removed and merged. + assert "allOf" not in result + # The object should now have additionalProperties set and required set. + assert result["additionalProperties"] is False + assert result["required"] == ["a"] + assert "a" in result["properties"] + assert result["properties"]["a"]["type"] == "boolean" + + +def test_default_removal_on_non_object(): + # Test that "default": None is stripped from schemas that are not objects. + schema = {"type": "string", "default": None} + result = ensure_strict_json_schema(schema) + assert result["type"] == "string" + assert "default" not in result + + +def test_ref_expansion(): + # Construct a schema with a definitions section and a property with a $ref. + schema = { + "definitions": {"refObj": {"type": "string", "default": None}}, + "type": "object", + "properties": {"a": {"$ref": "#/definitions/refObj", "description": "desc"}}, + } + result = ensure_strict_json_schema(schema) + a_schema = result["properties"]["a"] + # The $ref should be expanded so that the type is from the referenced definition, + # the description from the original takes precedence, and default is removed. + assert a_schema["type"] == "string" + assert a_schema["description"] == "desc" + assert "default" not in a_schema + + +def test_ref_no_expansion_when_alone(): + # If the schema only contains a $ref key, it should not be expanded. + schema = {"$ref": "#/definitions/refObj"} + result = ensure_strict_json_schema(schema) + # Because there is only one key, the $ref remains unchanged. + assert result == {"$ref": "#/definitions/refObj"} + + +def test_invalid_ref_format(): + # A $ref that does not start with "#/" should trigger a ValueError when resolved. + schema = {"type": "object", "properties": {"a": {"$ref": "invalid", "description": "desc"}}} + with pytest.raises(ValueError): + ensure_strict_json_schema(schema) diff --git a/tests/test_tool_converter.py b/tests/test_tool_converter.py new file mode 100644 index 0000000..1b6ebcf --- /dev/null +++ b/tests/test_tool_converter.py @@ -0,0 +1,54 @@ +import pytest +from pydantic import BaseModel + +from agents import Agent, Handoff, function_tool, handoff +from agents.exceptions import UserError +from agents.models.openai_chatcompletions import ToolConverter +from agents.tool import FileSearchTool, WebSearchTool + + +def some_function(a: str, b: list[int]) -> str: + return "hello" + + +def test_to_openai_with_function_tool(): + some_function(a="foo", b=[1, 2, 3]) + + tool = function_tool(some_function) + result = ToolConverter.to_openai(tool) + + assert result["type"] == "function" + assert result["function"]["name"] == "some_function" + params = result.get("function", {}).get("parameters") + assert params is not None + properties = params.get("properties", {}) + assert isinstance(properties, dict) + assert properties.keys() == {"a", "b"} + + +class Foo(BaseModel): + a: str + b: list[int] + + +def test_convert_handoff_tool(): + agent = Agent(name="test_1", handoff_description="test_2") + handoff_obj = handoff(agent=agent) + result = ToolConverter.convert_handoff_tool(handoff_obj) + + assert result["type"] == "function" + assert result["function"]["name"] == Handoff.default_tool_name(agent) + assert result["function"].get("description") == Handoff.default_tool_description(agent) + params = result.get("function", {}).get("parameters") + assert params is not None + + for key, value in handoff_obj.input_json_schema.items(): + assert params[key] == value + + +def test_tool_converter_hosted_tools_errors(): + with pytest.raises(UserError): + ToolConverter.to_openai(WebSearchTool()) + + with pytest.raises(UserError): + ToolConverter.to_openai(FileSearchTool(vector_store_ids=["abc"], max_num_results=1)) diff --git a/tests/test_trace_processor.py b/tests/test_trace_processor.py new file mode 100644 index 0000000..72318ca --- /dev/null +++ b/tests/test_trace_processor.py @@ -0,0 +1,276 @@ +import os +import time +from unittest.mock import MagicMock, patch + +import httpx +import pytest + +from agents.tracing.processor_interface import TracingProcessor +from agents.tracing.processors import BackendSpanExporter, BatchTraceProcessor +from agents.tracing.span_data import AgentSpanData +from agents.tracing.spans import SpanImpl +from agents.tracing.traces import TraceImpl + + +def get_span(processor: TracingProcessor) -> SpanImpl[AgentSpanData]: + """Create a minimal agent span for testing processors.""" + return SpanImpl( + trace_id="test_trace_id", + span_id="test_span_id", + parent_id=None, + processor=processor, + span_data=AgentSpanData(name="test_agent"), + ) + + +def get_trace(processor: TracingProcessor) -> TraceImpl: + """Create a minimal trace.""" + return TraceImpl( + name="test_trace", + trace_id="test_trace_id", + group_id="test_session_id", + metadata={}, + processor=processor, + ) + + +@pytest.fixture +def mocked_exporter(): + exporter = MagicMock() + exporter.export = MagicMock() + return exporter + + +def test_batch_trace_processor_on_trace_start(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=0.1) + test_trace = get_trace(processor) + + processor.on_trace_start(test_trace) + assert processor._queue.qsize() == 1, "Trace should be added to the queue" + + # Shutdown to clean up the worker thread + processor.shutdown() + + +def test_batch_trace_processor_on_span_end(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=0.1) + test_span = get_span(processor) + + processor.on_span_end(test_span) + assert processor._queue.qsize() == 1, "Span should be added to the queue" + + # Shutdown to clean up the worker thread + processor.shutdown() + + +def test_batch_trace_processor_queue_full(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter, max_queue_size=2, schedule_delay=0.1) + # Fill the queue + processor.on_trace_start(get_trace(processor)) + processor.on_trace_start(get_trace(processor)) + assert processor._queue.full() is True + + # Next item should not be queued + processor.on_trace_start(get_trace(processor)) + assert processor._queue.qsize() == 2, "Queue should not exceed max_queue_size" + + processor.on_span_end(get_span(processor)) + assert processor._queue.qsize() == 2, "Queue should not exceed max_queue_size" + + processor.shutdown() + + +def test_batch_processor_doesnt_enqueue_on_trace_end_or_span_start(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter) + + processor.on_trace_start(get_trace(processor)) + assert processor._queue.qsize() == 1, "Trace should be queued" + + processor.on_span_start(get_span(processor)) + assert processor._queue.qsize() == 1, "Span should not be queued" + + processor.on_span_end(get_span(processor)) + assert processor._queue.qsize() == 2, "Span should be queued" + + processor.on_trace_end(get_trace(processor)) + assert processor._queue.qsize() == 2, "Nothing new should be queued" + + processor.shutdown() + + +def test_batch_trace_processor_force_flush(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter, max_batch_size=2, schedule_delay=5.0) + + processor.on_trace_start(get_trace(processor)) + processor.on_span_end(get_span(processor)) + processor.on_span_end(get_span(processor)) + + processor.force_flush() + + # Ensure exporter.export was called with all items + # Because max_batch_size=2, it may have been called multiple times + total_exported = 0 + for call_args in mocked_exporter.export.call_args_list: + batch = call_args[0][0] # first positional arg to export() is the items list + total_exported += len(batch) + + # We pushed 3 items; ensure they all got exported + assert total_exported == 3 + + processor.shutdown() + + +def test_batch_trace_processor_shutdown_flushes(mocked_exporter): + processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=5.0) + processor.on_trace_start(get_trace(processor)) + processor.on_span_end(get_span(processor)) + qsize_before = processor._queue.qsize() + assert qsize_before == 2 + + processor.shutdown() + + # Ensure everything was exported after shutdown + total_exported = 0 + for call_args in mocked_exporter.export.call_args_list: + batch = call_args[0][0] + total_exported += len(batch) + + assert total_exported == 2, "All items in the queue should be exported upon shutdown" + + +def test_batch_trace_processor_scheduled_export(mocked_exporter): + """ + Tests that items are automatically exported when the schedule_delay expires. + We mock time.time() so we can trigger the condition without waiting in real time. + """ + with patch("time.time") as mock_time: + base_time = 1000.0 + mock_time.return_value = base_time + + processor = BatchTraceProcessor(exporter=mocked_exporter, schedule_delay=1.0) + + processor.on_span_end(get_span(processor)) # queue size = 1 + + # Now artificially advance time beyond the next export time + mock_time.return_value = base_time + 2.0 # > base_time + schedule_delay + # Let the background thread run a bit + time.sleep(0.3) + + # Check that exporter.export was eventually called + # Because the background thread runs, we might need a small sleep + processor.shutdown() + + total_exported = 0 + for call_args in mocked_exporter.export.call_args_list: + batch = call_args[0][0] + total_exported += len(batch) + + assert total_exported == 1, "Item should be exported after scheduled delay" + + +@pytest.fixture +def patched_time_sleep(): + """ + Fixture to replace time.sleep with a no-op to speed up tests + that rely on retry/backoff logic. + """ + with patch("time.sleep") as mock_sleep: + yield mock_sleep + + +def mock_processor(): + processor = MagicMock() + processor.on_trace_start = MagicMock() + processor.on_span_end = MagicMock() + return processor + + +@patch("httpx.Client") +def test_backend_span_exporter_no_items(mock_client): + exporter = BackendSpanExporter(api_key="test_key") + exporter.export([]) + # No calls should be made if there are no items + mock_client.return_value.post.assert_not_called() + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_no_api_key(mock_client): + # Ensure that os.environ is empty (sometimes devs have the openai api key set in their env) + + with patch.dict(os.environ, {}, clear=True): + exporter = BackendSpanExporter(api_key=None) + exporter.export([get_span(mock_processor())]) + + # Should log an error and return without calling post + mock_client.return_value.post.assert_not_called() + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_2xx_success(mock_client): + mock_response = MagicMock() + mock_response.status_code = 200 + mock_client.return_value.post.return_value = mock_response + + exporter = BackendSpanExporter(api_key="test_key") + exporter.export([get_span(mock_processor()), get_trace(mock_processor())]) + + # Should have called post exactly once + mock_client.return_value.post.assert_called_once() + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_4xx_client_error(mock_client): + mock_response = MagicMock() + mock_response.status_code = 400 + mock_response.text = "Bad Request" + mock_client.return_value.post.return_value = mock_response + + exporter = BackendSpanExporter(api_key="test_key") + exporter.export([get_span(mock_processor())]) + + # 4xx should not be retried + mock_client.return_value.post.assert_called_once() + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_5xx_retry(mock_client, patched_time_sleep): + mock_response = MagicMock() + mock_response.status_code = 500 + + # Make post() return 500 every time + mock_client.return_value.post.return_value = mock_response + + exporter = BackendSpanExporter(api_key="test_key", max_retries=3, base_delay=0.1, max_delay=0.2) + exporter.export([get_span(mock_processor())]) + + # Should retry up to max_retries times + assert mock_client.return_value.post.call_count == 3 + + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_request_error(mock_client, patched_time_sleep): + # Make post() raise a RequestError each time + mock_client.return_value.post.side_effect = httpx.RequestError("Network error") + + exporter = BackendSpanExporter(api_key="test_key", max_retries=2, base_delay=0.1, max_delay=0.2) + exporter.export([get_span(mock_processor())]) + + # Should retry up to max_retries times + assert mock_client.return_value.post.call_count == 2 + + exporter.close() + + +@patch("httpx.Client") +def test_backend_span_exporter_close(mock_client): + exporter = BackendSpanExporter(api_key="test_key") + exporter.close() + + # Ensure underlying http client is closed + mock_client.return_value.close.assert_called_once() diff --git a/tests/test_tracing.py b/tests/test_tracing.py new file mode 100644 index 0000000..c54c3d8 --- /dev/null +++ b/tests/test_tracing.py @@ -0,0 +1,402 @@ +from __future__ import annotations + +import asyncio +from typing import Any + +import pytest + +from agents.tracing import ( + Span, + Trace, + agent_span, + custom_span, + function_span, + generation_span, + handoff_span, + trace, +) +from agents.tracing.spans import SpanError + +from .testing_processor import fetch_events, fetch_ordered_spans, fetch_traces + +### HELPERS + + +def standard_span_checks( + span: Span[Any], trace_id: str, parent_id: str | None, span_type: str +) -> None: + assert span.span_id is not None + assert span.trace_id == trace_id + assert span.parent_id == parent_id + assert span.started_at is not None + assert span.ended_at is not None + assert span.span_data.type == span_type + + +def standard_trace_checks(trace: Trace, name_check: str | None = None) -> None: + assert trace.trace_id is not None + + if name_check: + assert trace.name == name_check + + +### TESTS + + +def simple_tracing(): + x = trace("test") + x.start() + + span_1 = agent_span(name="agent_1", parent=x) + span_1.start() + span_1.finish() + + span_2 = custom_span(name="custom_1", span_id="span_2", parent=x) + span_2.start() + + span_3 = custom_span(name="custom_2", span_id="span_3", parent=span_2) + span_3.start() + span_3.finish() + + span_2.finish() + + x.finish() + + +def test_simple_tracing() -> None: + simple_tracing() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 3 + assert len(traces) == 1 + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + first_span = spans[0] + standard_span_checks(first_span, trace_id=trace_id, parent_id=None, span_type="agent") + assert first_span.span_data.name == "agent_1" + + second_span = spans[1] + standard_span_checks(second_span, trace_id=trace_id, parent_id=None, span_type="custom") + assert second_span.span_id == "span_2" + assert second_span.span_data.name == "custom_1" + + third_span = spans[2] + standard_span_checks( + third_span, trace_id=trace_id, parent_id=second_span.span_id, span_type="custom" + ) + assert third_span.span_id == "span_3" + assert third_span.span_data.name == "custom_2" + + +def ctxmanager_spans(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + with custom_span(name="custom_1", span_id="span_1"): + with custom_span(name="custom_2", span_id="span_1_inner"): + pass + + with custom_span(name="custom_2", span_id="span_2"): + pass + + +def test_ctxmanager_spans() -> None: + ctxmanager_spans() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 3 + assert len(traces) == 1 + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + first_span = spans[0] + standard_span_checks(first_span, trace_id=trace_id, parent_id=None, span_type="custom") + assert first_span.span_id == "span_1" + + first_inner_span = spans[1] + standard_span_checks( + first_inner_span, trace_id=trace_id, parent_id=first_span.span_id, span_type="custom" + ) + assert first_inner_span.span_id == "span_1_inner" + + second_span = spans[2] + standard_span_checks(second_span, trace_id=trace_id, parent_id=None, span_type="custom") + assert second_span.span_id == "span_2" + + +async def run_subtask(span_id: str | None = None) -> None: + with generation_span(span_id=span_id): + await asyncio.sleep(0.01) + + +async def simple_async_tracing(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + await run_subtask(span_id="span_1") + await run_subtask(span_id="span_2") + + +@pytest.mark.asyncio +async def test_async_tracing() -> None: + await simple_async_tracing() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 2 + assert len(traces) == 1 + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + # We don't care about ordering here, just that they're there + for s in spans: + standard_span_checks(s, trace_id=trace_id, parent_id=None, span_type="generation") + + ids = [span.span_id for span in spans] + assert "span_1" in ids + assert "span_2" in ids + + +async def run_tasks_parallel(span_ids: list[str]) -> None: + await asyncio.gather( + *[run_subtask(span_id=span_id) for span_id in span_ids], + ) + + +async def run_tasks_as_children(first_span_id: str, second_span_id: str) -> None: + with generation_span(span_id=first_span_id): + await run_subtask(span_id=second_span_id) + + +async def complex_async_tracing(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + await asyncio.sleep(0.01) + await asyncio.gather( + run_tasks_parallel(["span_1", "span_2"]), + run_tasks_parallel(["span_3", "span_4"]), + ) + await asyncio.sleep(0.01) + await asyncio.gather( + run_tasks_as_children("span_5", "span_6"), + run_tasks_as_children("span_7", "span_8"), + ) + + +@pytest.mark.asyncio +async def test_complex_async_tracing() -> None: + await complex_async_tracing() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 8 + assert len(traces) == 1 + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + # First ensure 1,2,3,4 exist and are in parallel with the trace as parent + for span_id in ["span_1", "span_2", "span_3", "span_4"]: + span = next((s for s in spans if s.span_id == span_id), None) + assert span is not None + standard_span_checks(span, trace_id=trace_id, parent_id=None, span_type="generation") + + # Ensure 5 and 7 exist and have the trace as parent + for span_id in ["span_5", "span_7"]: + span = next((s for s in spans if s.span_id == span_id), None) + assert span is not None + standard_span_checks(span, trace_id=trace_id, parent_id=None, span_type="generation") + + # Ensure 6 and 8 exist and have 5 and 7 as parents + six = next((s for s in spans if s.span_id == "span_6"), None) + assert six is not None + standard_span_checks(six, trace_id=trace_id, parent_id="span_5", span_type="generation") + eight = next((s for s in spans if s.span_id == "span_8"), None) + assert eight is not None + standard_span_checks(eight, trace_id=trace_id, parent_id="span_7", span_type="generation") + + +def spans_with_setters(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + with agent_span(name="agent_1") as span_a: + span_a.span_data.name = "agent_2" + + with function_span(name="function_1") as span_b: + span_b.span_data.input = "i" + span_b.span_data.output = "o" + + with generation_span() as span_c: + span_c.span_data.input = [{"foo": "bar"}] + + with handoff_span(from_agent="agent_1", to_agent="agent_2"): + pass + + +def test_spans_with_setters() -> None: + spans_with_setters() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 4 + assert len(traces) == 1 + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + # Check the spans + first_span = spans[0] + standard_span_checks(first_span, trace_id=trace_id, parent_id=None, span_type="agent") + assert first_span.span_data.name == "agent_2" + + second_span = spans[1] + standard_span_checks( + second_span, trace_id=trace_id, parent_id=first_span.span_id, span_type="function" + ) + assert second_span.span_data.input == "i" + assert second_span.span_data.output == "o" + + third_span = spans[2] + standard_span_checks( + third_span, trace_id=trace_id, parent_id=first_span.span_id, span_type="generation" + ) + + fourth_span = spans[3] + standard_span_checks( + fourth_span, trace_id=trace_id, parent_id=first_span.span_id, span_type="handoff" + ) + + +def disabled_tracing(): + with trace(workflow_name="test", trace_id="123", group_id="456", disabled=True): + with agent_span(name="agent_1"): + with function_span(name="function_1"): + pass + + +def test_disabled_tracing(): + disabled_tracing() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 0 + assert len(traces) == 0 + + +def enabled_trace_disabled_span(): + with trace(workflow_name="test", trace_id="123"): + with agent_span(name="agent_1"): + with function_span(name="function_1", disabled=True): + with generation_span(): + pass + + +def test_enabled_trace_disabled_span(): + enabled_trace_disabled_span() + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 1 # Only the agent span is recorded + assert len(traces) == 1 # The trace is recorded + + trace = traces[0] + standard_trace_checks(trace, name_check="test") + trace_id = trace.trace_id + + first_span = spans[0] + standard_span_checks(first_span, trace_id=trace_id, parent_id=None, span_type="agent") + assert first_span.span_data.name == "agent_1" + + +def test_start_and_end_called_manual(): + simple_tracing() + + events = fetch_events() + + assert events == [ + "trace_start", + "span_start", # span_1 + "span_end", # span_1 + "span_start", # span_2 + "span_start", # span_3 + "span_end", # span_3 + "span_end", # span_2 + "trace_end", + ] + + +def test_start_and_end_called_ctxmanager(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + with custom_span(name="custom_1", span_id="span_1"): + with custom_span(name="custom_2", span_id="span_1_inner"): + pass + + with custom_span(name="custom_2", span_id="span_2"): + pass + + events = fetch_events() + + assert events == [ + "trace_start", + "span_start", # span_1 + "span_start", # span_1_inner + "span_end", # span_1_inner + "span_end", # span_1 + "span_start", # span_2 + "span_end", # span_2 + "trace_end", + ] + + +@pytest.mark.asyncio +async def test_start_and_end_called_async_ctxmanager(): + await simple_async_tracing() + + events = fetch_events() + + assert events == [ + "trace_start", + "span_start", # span_1 + "span_end", # span_1 + "span_start", # span_2 + "span_end", # span_2 + "trace_end", + ] + + +async def test_noop_span_doesnt_record(): + with trace(workflow_name="test", disabled=True) as t: + with custom_span(name="span_1") as span: + span.set_error(SpanError(message="test", data={})) + + spans, traces = fetch_ordered_spans(), fetch_traces() + assert len(spans) == 0 + assert len(traces) == 0 + + assert t.export() is None + assert span.export() is None + assert span.started_at is None + assert span.ended_at is None + assert span.error is None + + +async def test_multiple_span_start_finish_doesnt_crash(): + with trace(workflow_name="test", trace_id="123", group_id="456"): + with custom_span(name="span_1") as span: + span.start() + + span.finish() + + +async def test_noop_parent_is_noop_child(): + tr = trace(workflow_name="test", disabled=True) + + span = custom_span(name="span_1", parent=tr) + span.start() + span.finish() + + assert span.export() is None + + span_2 = custom_span(name="span_2", parent=span) + span_2.start() + span_2.finish() + + assert span_2.export() is None diff --git a/tests/test_tracing_errors.py b/tests/test_tracing_errors.py new file mode 100644 index 0000000..d57e1a8 --- /dev/null +++ b/tests/test_tracing_errors.py @@ -0,0 +1,328 @@ +from __future__ import annotations + +import json +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents import ( + Agent, + GuardrailFunctionOutput, + InputGuardrail, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + RunContextWrapper, + Runner, + TResponseInputItem, +) +from agents.tracing import AgentSpanData, FunctionSpanData, GenerationSpanData + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_message, +) +from .testing_processor import fetch_ordered_spans, fetch_traces + + +@pytest.mark.asyncio +async def test_single_turn_model_error(): + model = FakeModel(tracing_enabled=True) + model.set_next_output(ValueError("test error")) + + agent = Agent( + name="test_agent", + model=model, + ) + with pytest.raises(ValueError): + await Runner.run(agent, input="first_test") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, f"should have agent and generation spans, got {len(spans)}" + + generation_span = spans[1] + assert isinstance(generation_span.span_data, GenerationSpanData) + assert generation_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_multi_turn_no_handoffs(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test_agent", + model=model, + tools=[get_function_tool("foo", "tool_result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a message and tool call + [get_text_message("a_message"), get_function_tool_call("foo", json.dumps({"a": "b"}))], + # Second turn: error + ValueError("test error"), + # Third turn: text message + [get_text_message("done")], + ] + ) + + with pytest.raises(ValueError): + await Runner.run(agent, input="first_test") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 4, ( + f"should have agent, generation, tool, generation, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + last_generation_span = [x for x in spans if isinstance(x.span_data, GenerationSpanData)][-1] + assert last_generation_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_tool_call_error(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test_agent", + model=model, + tools=[get_function_tool("foo", "tool_result", hide_errors=True)], + ) + + model.set_next_output( + [get_text_message("a_message"), get_function_tool_call("foo", "bad_json")], + ) + + with pytest.raises(ModelBehaviorError): + await Runner.run(agent, input="first_test") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 3, ( + f"should have agent, generation, tool spans, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + function_span = [x for x in spans if isinstance(x.span_data, FunctionSpanData)][0] + assert function_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_multiple_handoff_doesnt_error(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + ) + agent_3 = Agent( + name="test", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and 2 handoff + [ + get_text_message("a_message"), + get_handoff_tool_call(agent_1), + get_handoff_tool_call(agent_2), + ], + # Third turn: text message + [get_text_message("done")], + ] + ) + result = await Runner.run(agent_3, input="user_message") + assert result.last_agent == agent_1, "should have picked first handoff" + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 7, ( + f"should have 2 agent, 1 function, 3 generation, 1 handoff, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +class Foo(TypedDict): + bar: str + + +@pytest.mark.asyncio +async def test_multiple_final_output_doesnt_error(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test", + model=model, + output_type=Foo, + ) + + model.set_next_output( + [ + get_final_output_message(json.dumps(Foo(bar="baz"))), + get_final_output_message(json.dumps(Foo(bar="abc"))), + ] + ) + + result = await Runner.run(agent_1, input="user_message") + assert result.final_output == Foo(bar="abc") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, ( + f"should have 1 agent, 1 generation, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +@pytest.mark.asyncio +async def test_handoffs_lead_to_correct_agent_spans(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test_agent_1", + model=model, + tools=[get_function_tool("some_function", "result")], + ) + agent_2 = Agent( + name="test_agent_2", + model=model, + handoffs=[agent_1], + tools=[get_function_tool("some_function", "result")], + ) + agent_3 = Agent( + name="test_agent_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + agent_1.handoffs.append(agent_3) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and 2 handoff + [ + get_text_message("a_message"), + get_handoff_tool_call(agent_1), + get_handoff_tool_call(agent_2), + ], + # Third turn: tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Fourth turn: handoff + [get_handoff_tool_call(agent_3)], + # Fifth turn: text message + [get_text_message("done")], + ] + ) + result = await Runner.run(agent_3, input="user_message") + + assert result.last_agent == agent_3, ( + f"should have ended on the third agent, got {result.last_agent.name}" + ) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 12, ( + f"should have 3 agents, 2 function, 5 generation, 2 handoff, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +@pytest.mark.asyncio +async def test_max_turns_exceeded(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test", + model=model, + output_type=Foo, + tools=[get_function_tool("foo", "result")], + ) + + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + ] + ) + + with pytest.raises(MaxTurnsExceeded): + await Runner.run(agent, input="user_message", max_turns=2) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 5, ( + f"should have 1 agent span, 2 generations, 2 function calls, got " + f"{len(spans)} with data: {[x.span_data for x in spans]}" + ) + + agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] + assert agent_span.error, "last agent should have error" + + +def guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + +@pytest.mark.asyncio +async def test_guardrail_error(): + agent = Agent( + name="test", input_guardrails=[InputGuardrail(guardrail_function=guardrail_function)] + ) + model = FakeModel() + model.set_next_output([get_text_message("some_message")]) + + with pytest.raises(InputGuardrailTripwireTriggered): + await Runner.run(agent, input="user_message") + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, ( + f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] + assert agent_span.error, "last agent should have error" diff --git a/tests/test_tracing_errors_streamed.py b/tests/test_tracing_errors_streamed.py new file mode 100644 index 0000000..00f440e --- /dev/null +++ b/tests/test_tracing_errors_streamed.py @@ -0,0 +1,397 @@ +from __future__ import annotations + +import asyncio +import json +from typing import Any + +import pytest +from typing_extensions import TypedDict + +from agents import ( + Agent, + AgentSpanData, + FunctionSpanData, + GenerationSpanData, + GuardrailFunctionOutput, + InputGuardrail, + InputGuardrailTripwireTriggered, + MaxTurnsExceeded, + ModelBehaviorError, + OutputGuardrail, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + TResponseInputItem, +) + +from .fake_model import FakeModel +from .test_responses import ( + get_final_output_message, + get_function_tool, + get_function_tool_call, + get_handoff_tool_call, + get_text_message, +) +from .testing_processor import fetch_ordered_spans, fetch_traces + + +@pytest.mark.asyncio +async def test_single_turn_model_error(): + model = FakeModel(tracing_enabled=True) + model.set_next_output(ValueError("test error")) + + agent = Agent( + name="test_agent", + model=model, + ) + with pytest.raises(ValueError): + result = Runner.run_streamed(agent, input="first_test") + async for _ in result.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, f"should have agent and generation spans, got {len(spans)}" + + generation_span = spans[1] + assert isinstance(generation_span.span_data, GenerationSpanData) + assert generation_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_multi_turn_no_handoffs(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test_agent", + model=model, + tools=[get_function_tool("foo", "tool_result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a message and tool call + [get_text_message("a_message"), get_function_tool_call("foo", json.dumps({"a": "b"}))], + # Second turn: error + ValueError("test error"), + # Third turn: text message + [get_text_message("done")], + ] + ) + + with pytest.raises(ValueError): + result = Runner.run_streamed(agent, input="first_test") + async for _ in result.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 4, ( + f"should have agent, generation, tool, generation, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + last_generation_span = [x for x in spans if isinstance(x.span_data, GenerationSpanData)][-1] + assert last_generation_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_tool_call_error(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test_agent", + model=model, + tools=[get_function_tool("foo", "tool_result", hide_errors=True)], + ) + + model.set_next_output( + [get_text_message("a_message"), get_function_tool_call("foo", "bad_json")], + ) + + with pytest.raises(ModelBehaviorError): + result = Runner.run_streamed(agent, input="first_test") + async for _ in result.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 3, ( + f"should have agent, generation, tool spans, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + function_span = [x for x in spans if isinstance(x.span_data, FunctionSpanData)][0] + assert function_span.error, "should have error" + + +@pytest.mark.asyncio +async def test_multiple_handoff_doesnt_error(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test", + model=model, + ) + agent_2 = Agent( + name="test", + model=model, + ) + agent_3 = Agent( + name="test", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and 2 handoff + [ + get_text_message("a_message"), + get_handoff_tool_call(agent_1), + get_handoff_tool_call(agent_2), + ], + # Third turn: text message + [get_text_message("done")], + ] + ) + result = Runner.run_streamed(agent_3, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.last_agent == agent_1, "should have picked first handoff" + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 7, ( + f"should have 2 agent, 1 function, 3 generation, 1 handoff, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +class Foo(TypedDict): + bar: str + + +@pytest.mark.asyncio +async def test_multiple_final_output_no_error(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test", + model=model, + output_type=Foo, + ) + + model.set_next_output( + [ + get_final_output_message(json.dumps(Foo(bar="baz"))), + get_final_output_message(json.dumps(Foo(bar="abc"))), + ] + ) + + result = Runner.run_streamed(agent_1, input="user_message") + async for _ in result.stream_events(): + pass + + assert isinstance(result.final_output, dict) + assert result.final_output["bar"] == "abc" + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, ( + f"should have 1 agent, 1 generation, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +@pytest.mark.asyncio +async def test_handoffs_lead_to_correct_agent_spans(): + model = FakeModel(tracing_enabled=True) + + agent_1 = Agent( + name="test_agent_1", + model=model, + tools=[get_function_tool("some_function", "result")], + ) + agent_2 = Agent( + name="test_agent_2", + model=model, + handoffs=[agent_1], + tools=[get_function_tool("some_function", "result")], + ) + agent_3 = Agent( + name="test_agent_3", + model=model, + handoffs=[agent_1, agent_2], + tools=[get_function_tool("some_function", "result")], + ) + + agent_1.handoffs.append(agent_3) + + model.add_multiple_turn_outputs( + [ + # First turn: a tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Second turn: a message and 2 handoff + [ + get_text_message("a_message"), + get_handoff_tool_call(agent_1), + get_handoff_tool_call(agent_2), + ], + # Third turn: tool call + [get_function_tool_call("some_function", json.dumps({"a": "b"}))], + # Fourth turn: handoff + [get_handoff_tool_call(agent_3)], + # Fifth turn: text message + [get_text_message("done")], + ] + ) + result = Runner.run_streamed(agent_3, input="user_message") + async for _ in result.stream_events(): + pass + + assert result.last_agent == agent_3, ( + f"should have ended on the third agent, got {result.last_agent.name}" + ) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 12, ( + f"should have 3 agents, 2 function, 5 generation, 2 handoff, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + +@pytest.mark.asyncio +async def test_max_turns_exceeded(): + model = FakeModel(tracing_enabled=True) + + agent = Agent( + name="test", + model=model, + output_type=Foo, + tools=[get_function_tool("foo", "result")], + ) + + model.add_multiple_turn_outputs( + [ + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + [get_function_tool_call("foo")], + ] + ) + + with pytest.raises(MaxTurnsExceeded): + result = Runner.run_streamed(agent, input="user_message", max_turns=2) + async for _ in result.stream_events(): + pass + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 5, ( + f"should have 1 agent, 2 generations, 2 function calls, got " + f"{len(spans)} with data: {[x.span_data for x in spans]}" + ) + + agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] + assert agent_span.error, "last agent should have error" + + +def input_guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], input: str | list[TResponseInputItem] +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + +@pytest.mark.asyncio +async def test_input_guardrail_error(): + model = FakeModel() + + agent = Agent( + name="test", + model=model, + input_guardrails=[InputGuardrail(guardrail_function=input_guardrail_function)], + ) + model.set_next_output([get_text_message("some_message")]) + + with pytest.raises(InputGuardrailTripwireTriggered): + result = Runner.run_streamed(agent, input="user_message") + async for _ in result.stream_events(): + pass + + await asyncio.sleep(1) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, ( + f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] + assert agent_span.error, "last agent should have error" + + +def output_guardrail_function( + context: RunContextWrapper[Any], agent: Agent[Any], agent_output: Any +) -> GuardrailFunctionOutput: + return GuardrailFunctionOutput( + output_info=None, + tripwire_triggered=True, + ) + + +@pytest.mark.asyncio +async def test_output_guardrail_error(): + model = FakeModel() + + agent = Agent( + name="test", + model=model, + output_guardrails=[OutputGuardrail(guardrail_function=output_guardrail_function)], + ) + model.set_next_output([get_text_message("some_message")]) + + with pytest.raises(OutputGuardrailTripwireTriggered): + result = Runner.run_streamed(agent, input="user_message") + async for _ in result.stream_events(): + pass + + await asyncio.sleep(1) + + traces = fetch_traces() + assert len(traces) == 1, f"Expected 1 trace, got {len(traces)}" + + spans = fetch_ordered_spans() + assert len(spans) == 2, ( + f"should have 1 agent, 1 guardrail, got {len(spans)} with data: " + f"{[x.span_data for x in spans]}" + ) + + agent_span = [x for x in spans if isinstance(x.span_data, AgentSpanData)][-1] + assert agent_span.error, "last agent should have error" diff --git a/tests/testing_processor.py b/tests/testing_processor.py new file mode 100644 index 0000000..258a08d --- /dev/null +++ b/tests/testing_processor.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import threading +from typing import Any, Literal + +from agents.tracing import Span, Trace, TracingProcessor + +TestSpanProcessorEvent = Literal["trace_start", "trace_end", "span_start", "span_end"] + + +class SpanProcessorForTests(TracingProcessor): + """ + A simple processor that stores finished spans in memory. + This is thread-safe and suitable for tests or basic usage. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + # Dictionary of trace_id -> list of spans + self._spans: list[Span[Any]] = [] + self._traces: list[Trace] = [] + self._events: list[TestSpanProcessorEvent] = [] + + def on_trace_start(self, trace: Trace) -> None: + with self._lock: + self._traces.append(trace) + self._events.append("trace_start") + + def on_trace_end(self, trace: Trace) -> None: + with self._lock: + # We don't append the trace here, we want to do that in on_trace_start + self._events.append("trace_end") + + def on_span_start(self, span: Span[Any]) -> None: + with self._lock: + # Purposely not appending the span here, we want to do that in on_span_end + self._events.append("span_start") + + def on_span_end(self, span: Span[Any]) -> None: + with self._lock: + self._events.append("span_end") + self._spans.append(span) + + def get_ordered_spans(self, including_empty: bool = False) -> list[Span[Any]]: + with self._lock: + spans = [x for x in self._spans if including_empty or x.export()] + return sorted(spans, key=lambda x: x.started_at or 0) + + def get_traces(self, including_empty: bool = False) -> list[Trace]: + with self._lock: + traces = [x for x in self._traces if including_empty or x.export()] + return traces + + def clear(self) -> None: + with self._lock: + self._spans.clear() + self._traces.clear() + self._events.clear() + + def shutdown(self) -> None: + pass + + def force_flush(self) -> None: + pass + + +SPAN_PROCESSOR_TESTING = SpanProcessorForTests() + + +def fetch_ordered_spans() -> list[Span[Any]]: + return SPAN_PROCESSOR_TESTING.get_ordered_spans() + + +def fetch_traces() -> list[Trace]: + return SPAN_PROCESSOR_TESTING.get_traces() + + +def fetch_events() -> list[TestSpanProcessorEvent]: + return SPAN_PROCESSOR_TESTING._events diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..141e15b --- /dev/null +++ b/uv.lock @@ -0,0 +1,1360 @@ +version = 1 +revision = 1 +requires-python = ">=3.9" + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] + +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, +] + +[[package]] +name = "backrefs" +version = "5.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/46/caba1eb32fa5784428ab401a5487f73db4104590ecd939ed9daaf18b47e0/backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd", size = 6773994 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/cb/d019ab87fe70e0fe3946196d50d6a4428623dc0c38a6669c8cae0320fbf3/backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d", size = 380337 }, + { url = "https://files.pythonhosted.org/packages/a9/86/abd17f50ee21b2248075cb6924c6e7f9d23b4925ca64ec660e869c2633f1/backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b", size = 392142 }, + { url = "https://files.pythonhosted.org/packages/b3/04/7b415bd75c8ab3268cc138c76fa648c19495fcc7d155508a0e62f3f82308/backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486", size = 398021 }, + { url = "https://files.pythonhosted.org/packages/04/b8/60dcfb90eb03a06e883a92abbc2ab95c71f0d8c9dd0af76ab1d5ce0b1402/backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585", size = 399915 }, + { url = "https://files.pythonhosted.org/packages/0c/37/fb6973edeb700f6e3d6ff222400602ab1830446c25c7b4676d8de93e65b8/backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc", size = 380336 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/58/5580c1716040bc89206c77d8f74418caf82ce519aae06450393ca73475d1/charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", size = 198013 }, + { url = "https://files.pythonhosted.org/packages/d0/11/00341177ae71c6f5159a08168bcb98c6e6d196d372c94511f9f6c9afe0c6/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", size = 141285 }, + { url = "https://files.pythonhosted.org/packages/01/09/11d684ea5819e5a8f5100fb0b38cf8d02b514746607934134d31233e02c8/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", size = 151449 }, + { url = "https://files.pythonhosted.org/packages/08/06/9f5a12939db324d905dc1f70591ae7d7898d030d7662f0d426e2286f68c9/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", size = 143892 }, + { url = "https://files.pythonhosted.org/packages/93/62/5e89cdfe04584cb7f4d36003ffa2936681b03ecc0754f8e969c2becb7e24/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", size = 146123 }, + { url = "https://files.pythonhosted.org/packages/a9/ac/ab729a15c516da2ab70a05f8722ecfccc3f04ed7a18e45c75bbbaa347d61/charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", size = 147943 }, + { url = "https://files.pythonhosted.org/packages/03/d2/3f392f23f042615689456e9a274640c1d2e5dd1d52de36ab8f7955f8f050/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", size = 142063 }, + { url = "https://files.pythonhosted.org/packages/f2/e3/e20aae5e1039a2cd9b08d9205f52142329f887f8cf70da3650326670bddf/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", size = 150578 }, + { url = "https://files.pythonhosted.org/packages/8d/af/779ad72a4da0aed925e1139d458adc486e61076d7ecdcc09e610ea8678db/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", size = 153629 }, + { url = "https://files.pythonhosted.org/packages/c2/b6/7aa450b278e7aa92cf7732140bfd8be21f5f29d5bf334ae987c945276639/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", size = 150778 }, + { url = "https://files.pythonhosted.org/packages/39/f4/d9f4f712d0951dcbfd42920d3db81b00dd23b6ab520419626f4023334056/charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", size = 146453 }, + { url = "https://files.pythonhosted.org/packages/49/2b/999d0314e4ee0cff3cb83e6bc9aeddd397eeed693edb4facb901eb8fbb69/charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", size = 95479 }, + { url = "https://files.pythonhosted.org/packages/2d/ce/3cbed41cff67e455a386fb5e5dd8906cdda2ed92fbc6297921f2e4419309/charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", size = 102790 }, + { url = "https://files.pythonhosted.org/packages/72/80/41ef5d5a7935d2d3a773e3eaebf0a9350542f2cab4eac59a7a4741fbbbbe/charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", size = 194995 }, + { url = "https://files.pythonhosted.org/packages/7a/28/0b9fefa7b8b080ec492110af6d88aa3dea91c464b17d53474b6e9ba5d2c5/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", size = 139471 }, + { url = "https://files.pythonhosted.org/packages/71/64/d24ab1a997efb06402e3fc07317e94da358e2585165930d9d59ad45fcae2/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", size = 149831 }, + { url = "https://files.pythonhosted.org/packages/37/ed/be39e5258e198655240db5e19e0b11379163ad7070962d6b0c87ed2c4d39/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", size = 142335 }, + { url = "https://files.pythonhosted.org/packages/88/83/489e9504711fa05d8dde1574996408026bdbdbd938f23be67deebb5eca92/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", size = 143862 }, + { url = "https://files.pythonhosted.org/packages/c6/c7/32da20821cf387b759ad24627a9aca289d2822de929b8a41b6241767b461/charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", size = 145673 }, + { url = "https://files.pythonhosted.org/packages/68/85/f4288e96039abdd5aeb5c546fa20a37b50da71b5cf01e75e87f16cd43304/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", size = 140211 }, + { url = "https://files.pythonhosted.org/packages/28/a3/a42e70d03cbdabc18997baf4f0227c73591a08041c149e710045c281f97b/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", size = 148039 }, + { url = "https://files.pythonhosted.org/packages/85/e4/65699e8ab3014ecbe6f5c71d1a55d810fb716bbfd74f6283d5c2aa87febf/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", size = 151939 }, + { url = "https://files.pythonhosted.org/packages/b1/82/8e9fe624cc5374193de6860aba3ea8070f584c8565ee77c168ec13274bd2/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", size = 149075 }, + { url = "https://files.pythonhosted.org/packages/3d/7b/82865ba54c765560c8433f65e8acb9217cb839a9e32b42af4aa8e945870f/charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", size = 144340 }, + { url = "https://files.pythonhosted.org/packages/b5/b6/9674a4b7d4d99a0d2df9b215da766ee682718f88055751e1e5e753c82db0/charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", size = 95205 }, + { url = "https://files.pythonhosted.org/packages/1e/ab/45b180e175de4402dcf7547e4fb617283bae54ce35c27930a6f35b6bef15/charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", size = 102441 }, + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/7f/c0/b913f8f02836ed9ab32ea643c6fe4d3325c3d8627cf6e78098671cafff86/charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", size = 197867 }, + { url = "https://files.pythonhosted.org/packages/0f/6c/2bee440303d705b6fb1e2ec789543edec83d32d258299b16eed28aad48e0/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", size = 141385 }, + { url = "https://files.pythonhosted.org/packages/3d/04/cb42585f07f6f9fd3219ffb6f37d5a39b4fd2db2355b23683060029c35f7/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", size = 151367 }, + { url = "https://files.pythonhosted.org/packages/54/54/2412a5b093acb17f0222de007cc129ec0e0df198b5ad2ce5699355269dfe/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", size = 143928 }, + { url = "https://files.pythonhosted.org/packages/5a/6d/e2773862b043dcf8a221342954f375392bb2ce6487bcd9f2c1b34e1d6781/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", size = 146203 }, + { url = "https://files.pythonhosted.org/packages/b9/f8/ca440ef60d8f8916022859885f231abb07ada3c347c03d63f283bec32ef5/charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", size = 148082 }, + { url = "https://files.pythonhosted.org/packages/04/d2/42fd330901aaa4b805a1097856c2edf5095e260a597f65def493f4b8c833/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", size = 142053 }, + { url = "https://files.pythonhosted.org/packages/9e/af/3a97a4fa3c53586f1910dadfc916e9c4f35eeada36de4108f5096cb7215f/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", size = 150625 }, + { url = "https://files.pythonhosted.org/packages/26/ae/23d6041322a3556e4da139663d02fb1b3c59a23ab2e2b56432bd2ad63ded/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", size = 153549 }, + { url = "https://files.pythonhosted.org/packages/94/22/b8f2081c6a77cb20d97e57e0b385b481887aa08019d2459dc2858ed64871/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", size = 150945 }, + { url = "https://files.pythonhosted.org/packages/c7/0b/c5ec5092747f801b8b093cdf5610e732b809d6cb11f4c51e35fc28d1d389/charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", size = 146595 }, + { url = "https://files.pythonhosted.org/packages/0c/5a/0b59704c38470df6768aa154cc87b1ac7c9bb687990a1559dc8765e8627e/charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", size = 95453 }, + { url = "https://files.pythonhosted.org/packages/85/2d/a9790237cb4d01a6d57afadc8573c8b73c609ade20b80f4cda30802009ee/charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", size = 102811 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "coverage" +version = "7.6.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/d6/2b53ab3ee99f2262e6f0b8369a43f6d66658eab45510331c0b3d5c8c4272/coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", size = 805941 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/67/81dc41ec8f548c365d04a29f1afd492d3176b372c33e47fa2a45a01dc13a/coverage-7.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", size = 208345 }, + { url = "https://files.pythonhosted.org/packages/33/43/17f71676016c8829bde69e24c852fef6bd9ed39f774a245d9ec98f689fa0/coverage-7.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", size = 208775 }, + { url = "https://files.pythonhosted.org/packages/86/25/c6ff0775f8960e8c0840845b723eed978d22a3cd9babd2b996e4a7c502c6/coverage-7.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", size = 237925 }, + { url = "https://files.pythonhosted.org/packages/b0/3d/5f5bd37046243cb9d15fff2c69e498c2f4fe4f9b42a96018d4579ed3506f/coverage-7.6.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", size = 235835 }, + { url = "https://files.pythonhosted.org/packages/b5/f1/9e6b75531fe33490b910d251b0bf709142e73a40e4e38a3899e6986fe088/coverage-7.6.12-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", size = 236966 }, + { url = "https://files.pythonhosted.org/packages/4f/bc/aef5a98f9133851bd1aacf130e754063719345d2fb776a117d5a8d516971/coverage-7.6.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", size = 236080 }, + { url = "https://files.pythonhosted.org/packages/eb/d0/56b4ab77f9b12aea4d4c11dc11cdcaa7c29130b837eb610639cf3400c9c3/coverage-7.6.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", size = 234393 }, + { url = "https://files.pythonhosted.org/packages/0d/77/28ef95c5d23fe3dd191a0b7d89c82fea2c2d904aef9315daf7c890e96557/coverage-7.6.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", size = 235536 }, + { url = "https://files.pythonhosted.org/packages/29/62/18791d3632ee3ff3f95bc8599115707d05229c72db9539f208bb878a3d88/coverage-7.6.12-cp310-cp310-win32.whl", hash = "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", size = 211063 }, + { url = "https://files.pythonhosted.org/packages/fc/57/b3878006cedfd573c963e5c751b8587154eb10a61cc0f47a84f85c88a355/coverage-7.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", size = 211955 }, + { url = "https://files.pythonhosted.org/packages/64/2d/da78abbfff98468c91fd63a73cccdfa0e99051676ded8dd36123e3a2d4d5/coverage-7.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", size = 208464 }, + { url = "https://files.pythonhosted.org/packages/31/f2/c269f46c470bdabe83a69e860c80a82e5e76840e9f4bbd7f38f8cebbee2f/coverage-7.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", size = 208893 }, + { url = "https://files.pythonhosted.org/packages/47/63/5682bf14d2ce20819998a49c0deadb81e608a59eed64d6bc2191bc8046b9/coverage-7.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", size = 241545 }, + { url = "https://files.pythonhosted.org/packages/6a/b6/6b6631f1172d437e11067e1c2edfdb7238b65dff965a12bce3b6d1bf2be2/coverage-7.6.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", size = 239230 }, + { url = "https://files.pythonhosted.org/packages/c7/01/9cd06cbb1be53e837e16f1b4309f6357e2dfcbdab0dd7cd3b1a50589e4e1/coverage-7.6.12-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", size = 241013 }, + { url = "https://files.pythonhosted.org/packages/4b/26/56afefc03c30871326e3d99709a70d327ac1f33da383cba108c79bd71563/coverage-7.6.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", size = 239750 }, + { url = "https://files.pythonhosted.org/packages/dd/ea/88a1ff951ed288f56aa561558ebe380107cf9132facd0b50bced63ba7238/coverage-7.6.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", size = 238462 }, + { url = "https://files.pythonhosted.org/packages/6e/d4/1d9404566f553728889409eff82151d515fbb46dc92cbd13b5337fa0de8c/coverage-7.6.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", size = 239307 }, + { url = "https://files.pythonhosted.org/packages/12/c1/e453d3b794cde1e232ee8ac1d194fde8e2ba329c18bbf1b93f6f5eef606b/coverage-7.6.12-cp311-cp311-win32.whl", hash = "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", size = 211117 }, + { url = "https://files.pythonhosted.org/packages/d5/db/829185120c1686fa297294f8fcd23e0422f71070bf85ef1cc1a72ecb2930/coverage-7.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", size = 212019 }, + { url = "https://files.pythonhosted.org/packages/e2/7f/4af2ed1d06ce6bee7eafc03b2ef748b14132b0bdae04388e451e4b2c529b/coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", size = 208645 }, + { url = "https://files.pythonhosted.org/packages/dc/60/d19df912989117caa95123524d26fc973f56dc14aecdec5ccd7d0084e131/coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", size = 208898 }, + { url = "https://files.pythonhosted.org/packages/bd/10/fecabcf438ba676f706bf90186ccf6ff9f6158cc494286965c76e58742fa/coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", size = 242987 }, + { url = "https://files.pythonhosted.org/packages/4c/53/4e208440389e8ea936f5f2b0762dcd4cb03281a7722def8e2bf9dc9c3d68/coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", size = 239881 }, + { url = "https://files.pythonhosted.org/packages/c4/47/2ba744af8d2f0caa1f17e7746147e34dfc5f811fb65fc153153722d58835/coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", size = 242142 }, + { url = "https://files.pythonhosted.org/packages/e9/90/df726af8ee74d92ee7e3bf113bf101ea4315d71508952bd21abc3fae471e/coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", size = 241437 }, + { url = "https://files.pythonhosted.org/packages/f6/af/995263fd04ae5f9cf12521150295bf03b6ba940d0aea97953bb4a6db3e2b/coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", size = 239724 }, + { url = "https://files.pythonhosted.org/packages/1c/8e/5bb04f0318805e190984c6ce106b4c3968a9562a400180e549855d8211bd/coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", size = 241329 }, + { url = "https://files.pythonhosted.org/packages/9e/9d/fa04d9e6c3f6459f4e0b231925277cfc33d72dfab7fa19c312c03e59da99/coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", size = 211289 }, + { url = "https://files.pythonhosted.org/packages/53/40/53c7ffe3c0c3fff4d708bc99e65f3d78c129110d6629736faf2dbd60ad57/coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", size = 212079 }, + { url = "https://files.pythonhosted.org/packages/76/89/1adf3e634753c0de3dad2f02aac1e73dba58bc5a3a914ac94a25b2ef418f/coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", size = 208673 }, + { url = "https://files.pythonhosted.org/packages/ce/64/92a4e239d64d798535c5b45baac6b891c205a8a2e7c9cc8590ad386693dc/coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", size = 208945 }, + { url = "https://files.pythonhosted.org/packages/b4/d0/4596a3ef3bca20a94539c9b1e10fd250225d1dec57ea78b0867a1cf9742e/coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", size = 242484 }, + { url = "https://files.pythonhosted.org/packages/1c/ef/6fd0d344695af6718a38d0861408af48a709327335486a7ad7e85936dc6e/coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", size = 239525 }, + { url = "https://files.pythonhosted.org/packages/0c/4b/373be2be7dd42f2bcd6964059fd8fa307d265a29d2b9bcf1d044bcc156ed/coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", size = 241545 }, + { url = "https://files.pythonhosted.org/packages/a6/7d/0e83cc2673a7790650851ee92f72a343827ecaaea07960587c8f442b5cd3/coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", size = 241179 }, + { url = "https://files.pythonhosted.org/packages/ff/8c/566ea92ce2bb7627b0900124e24a99f9244b6c8c92d09ff9f7633eb7c3c8/coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", size = 239288 }, + { url = "https://files.pythonhosted.org/packages/7d/e4/869a138e50b622f796782d642c15fb5f25a5870c6d0059a663667a201638/coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", size = 241032 }, + { url = "https://files.pythonhosted.org/packages/ae/28/a52ff5d62a9f9e9fe9c4f17759b98632edd3a3489fce70154c7d66054dd3/coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", size = 211315 }, + { url = "https://files.pythonhosted.org/packages/bc/17/ab849b7429a639f9722fa5628364c28d675c7ff37ebc3268fe9840dda13c/coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", size = 212099 }, + { url = "https://files.pythonhosted.org/packages/d2/1c/b9965bf23e171d98505eb5eb4fb4d05c44efd256f2e0f19ad1ba8c3f54b0/coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", size = 209511 }, + { url = "https://files.pythonhosted.org/packages/57/b3/119c201d3b692d5e17784fee876a9a78e1b3051327de2709392962877ca8/coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", size = 209729 }, + { url = "https://files.pythonhosted.org/packages/52/4e/a7feb5a56b266304bc59f872ea07b728e14d5a64f1ad3a2cc01a3259c965/coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", size = 253988 }, + { url = "https://files.pythonhosted.org/packages/65/19/069fec4d6908d0dae98126aa7ad08ce5130a6decc8509da7740d36e8e8d2/coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", size = 249697 }, + { url = "https://files.pythonhosted.org/packages/1c/da/5b19f09ba39df7c55f77820736bf17bbe2416bbf5216a3100ac019e15839/coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", size = 252033 }, + { url = "https://files.pythonhosted.org/packages/1e/89/4c2750df7f80a7872267f7c5fe497c69d45f688f7b3afe1297e52e33f791/coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", size = 251535 }, + { url = "https://files.pythonhosted.org/packages/78/3b/6d3ae3c1cc05f1b0460c51e6f6dcf567598cbd7c6121e5ad06643974703c/coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", size = 249192 }, + { url = "https://files.pythonhosted.org/packages/6e/8e/c14a79f535ce41af7d436bbad0d3d90c43d9e38ec409b4770c894031422e/coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", size = 250627 }, + { url = "https://files.pythonhosted.org/packages/cb/79/b7cee656cfb17a7f2c1b9c3cee03dd5d8000ca299ad4038ba64b61a9b044/coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", size = 212033 }, + { url = "https://files.pythonhosted.org/packages/b6/c3/f7aaa3813f1fa9a4228175a7bd368199659d392897e184435a3b66408dd3/coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", size = 213240 }, + { url = "https://files.pythonhosted.org/packages/6c/eb/cf062b1c3dbdcafd64a2a154beea2e4aa8e9886c34e41f53fa04925c8b35/coverage-7.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", size = 208343 }, + { url = "https://files.pythonhosted.org/packages/95/42/4ebad0ab065228e29869a060644712ab1b0821d8c29bfefa20c2118c9e19/coverage-7.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", size = 208769 }, + { url = "https://files.pythonhosted.org/packages/44/9f/421e84f7f9455eca85ff85546f26cbc144034bb2587e08bfc214dd6e9c8f/coverage-7.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", size = 237553 }, + { url = "https://files.pythonhosted.org/packages/c9/c4/a2c4f274bcb711ed5db2ccc1b851ca1c45f35ed6077aec9d6c61845d80e3/coverage-7.6.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", size = 235473 }, + { url = "https://files.pythonhosted.org/packages/e0/10/a3d317e38e5627b06debe861d6c511b1611dd9dc0e2a47afbe6257ffd341/coverage-7.6.12-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", size = 236575 }, + { url = "https://files.pythonhosted.org/packages/4d/49/51cd991b56257d2e07e3d5cb053411e9de5b0f4e98047167ec05e4e19b55/coverage-7.6.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", size = 235690 }, + { url = "https://files.pythonhosted.org/packages/f7/87/631e5883fe0a80683a1f20dadbd0f99b79e17a9d8ea9aff3a9b4cfe50b93/coverage-7.6.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73", size = 234040 }, + { url = "https://files.pythonhosted.org/packages/7c/34/edd03f6933f766ec97dddd178a7295855f8207bb708dbac03777107ace5b/coverage-7.6.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", size = 235048 }, + { url = "https://files.pythonhosted.org/packages/ee/1e/d45045b7d3012fe518c617a57b9f9396cdaebe6455f1b404858b32c38cdd/coverage-7.6.12-cp39-cp39-win32.whl", hash = "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", size = 211085 }, + { url = "https://files.pythonhosted.org/packages/df/ea/086cb06af14a84fe773b86aa140892006a906c5ec947e609ceb6a93f6257/coverage-7.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", size = 211965 }, + { url = "https://files.pythonhosted.org/packages/7a/7f/05818c62c7afe75df11e0233bd670948d68b36cdbf2a339a095bc02624a8/coverage-7.6.12-pp39.pp310-none-any.whl", hash = "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", size = 200558 }, + { url = "https://files.pythonhosted.org/packages/fb/b2/f655700e1024dec98b10ebaafd0cedbc25e40e4abe62a3c8e2ceef4f8f0a/coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", size = 200552 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, +] + +[[package]] +name = "greenlet" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, + { url = "https://files.pythonhosted.org/packages/8c/82/8051e82af6d6b5150aacb6789a657a8afd48f0a44d8e91cb72aaaf28553a/greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", size = 270027 }, + { url = "https://files.pythonhosted.org/packages/f9/74/f66de2785880293780eebd18a2958aeea7cbe7814af1ccef634f4701f846/greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", size = 634822 }, + { url = "https://files.pythonhosted.org/packages/68/23/acd9ca6bc412b02b8aa755e47b16aafbe642dde0ad2f929f836e57a7949c/greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f", size = 646866 }, + { url = "https://files.pythonhosted.org/packages/a9/ab/562beaf8a53dc9f6b2459f200e7bc226bb07e51862a66351d8b7817e3efd/greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", size = 641985 }, + { url = "https://files.pythonhosted.org/packages/03/d3/1006543621f16689f6dc75f6bcf06e3c23e044c26fe391c16c253623313e/greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", size = 641268 }, + { url = "https://files.pythonhosted.org/packages/2f/c1/ad71ce1b5f61f900593377b3f77b39408bce5dc96754790311b49869e146/greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", size = 597376 }, + { url = "https://files.pythonhosted.org/packages/f7/ff/183226685b478544d61d74804445589e069d00deb8ddef042699733950c7/greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", size = 1123359 }, + { url = "https://files.pythonhosted.org/packages/c0/8b/9b3b85a89c22f55f315908b94cd75ab5fed5973f7393bbef000ca8b2c5c1/greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", size = 1147458 }, + { url = "https://files.pythonhosted.org/packages/b8/1c/248fadcecd1790b0ba793ff81fa2375c9ad6442f4c748bf2cc2e6563346a/greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", size = 281131 }, + { url = "https://files.pythonhosted.org/packages/ae/02/e7d0aef2354a38709b764df50b2b83608f0621493e47f47694eb80922822/greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", size = 298306 }, +] + +[[package]] +name = "griffe" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/1a/d467b93f5e0ea4edf3c1caef44cfdd53a4a498cb3a6bb722df4dd0fdd66a/griffe-1.6.0.tar.gz", hash = "sha256:eb5758088b9c73ad61c7ac014f3cdfb4c57b5c2fcbfca69996584b702aefa354", size = 391819 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/02/5a22bc98d0aebb68c15ba70d2da1c84a5ef56048d79634e5f96cd2ba96e9/griffe-1.6.0-py3-none-any.whl", hash = "sha256:9f1dfe035d4715a244ed2050dfbceb05b1f470809ed4f6bb10ece5a7302f8dd1", size = 128470 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/08/c1395a292bb23fd03bdf572a1357c5a733d3eecbab877641ceacab23db6e/importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580", size = 55767 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/9d/0fb148dc4d6fa4a7dd1d8378168d9b4cd8d4560a6fbf6f0121c5fc34eb68/importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e", size = 26971 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/82/39f7c9e67b3b0121f02a0b90d433626caa95a565c3d2449fea6bcfa3f5f5/jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad", size = 314540 }, + { url = "https://files.pythonhosted.org/packages/01/07/7bf6022c5a152fca767cf5c086bb41f7c28f70cf33ad259d023b53c0b858/jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea", size = 321065 }, + { url = "https://files.pythonhosted.org/packages/6c/b2/de3f3446ecba7c48f317568e111cc112613da36c7b29a6de45a1df365556/jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51", size = 341664 }, + { url = "https://files.pythonhosted.org/packages/13/cf/6485a4012af5d407689c91296105fcdb080a3538e0658d2abf679619c72f/jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538", size = 364635 }, + { url = "https://files.pythonhosted.org/packages/0d/f7/4a491c568f005553240b486f8e05c82547340572d5018ef79414b4449327/jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d", size = 406288 }, + { url = "https://files.pythonhosted.org/packages/d3/ca/f4263ecbce7f5e6bded8f52a9f1a66540b270c300b5c9f5353d163f9ac61/jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12", size = 397499 }, + { url = "https://files.pythonhosted.org/packages/ac/a2/522039e522a10bac2f2194f50e183a49a360d5f63ebf46f6d890ef8aa3f9/jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51", size = 352926 }, + { url = "https://files.pythonhosted.org/packages/b1/67/306a5c5abc82f2e32bd47333a1c9799499c1c3a415f8dde19dbf876f00cb/jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708", size = 384506 }, + { url = "https://files.pythonhosted.org/packages/0f/89/c12fe7b65a4fb74f6c0d7b5119576f1f16c79fc2953641f31b288fad8a04/jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5", size = 520621 }, + { url = "https://files.pythonhosted.org/packages/c4/2b/d57900c5c06e6273fbaa76a19efa74dbc6e70c7427ab421bf0095dfe5d4a/jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678", size = 512613 }, + { url = "https://files.pythonhosted.org/packages/89/05/d8b90bfb21e58097d5a4e0224f2940568366f68488a079ae77d4b2653500/jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4", size = 206613 }, + { url = "https://files.pythonhosted.org/packages/2c/1d/5767f23f88e4f885090d74bbd2755518050a63040c0f59aa059947035711/jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322", size = 208371 }, + { url = "https://files.pythonhosted.org/packages/23/44/e241a043f114299254e44d7e777ead311da400517f179665e59611ab0ee4/jiter-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6c4d99c71508912a7e556d631768dcdef43648a93660670986916b297f1c54af", size = 314654 }, + { url = "https://files.pythonhosted.org/packages/fb/1b/a7e5e42db9fa262baaa9489d8d14ca93f8663e7f164ed5e9acc9f467fc00/jiter-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f60fb8ce7df529812bf6c625635a19d27f30806885139e367af93f6e734ef58", size = 320909 }, + { url = "https://files.pythonhosted.org/packages/60/bf/8ebdfce77bc04b81abf2ea316e9c03b4a866a7d739cf355eae4d6fd9f6fe/jiter-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c4e1a4f8ea84d98b7b98912aa4290ac3d1eabfde8e3c34541fae30e9d1f08b", size = 341733 }, + { url = "https://files.pythonhosted.org/packages/a8/4e/754ebce77cff9ab34d1d0fa0fe98f5d42590fd33622509a3ba6ec37ff466/jiter-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f4c677c424dc76684fea3e7285a7a2a7493424bea89ac441045e6a1fb1d7b3b", size = 365097 }, + { url = "https://files.pythonhosted.org/packages/32/2c/6019587e6f5844c612ae18ca892f4cd7b3d8bbf49461ed29e384a0f13d98/jiter-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2221176dfec87f3470b21e6abca056e6b04ce9bff72315cb0b243ca9e835a4b5", size = 406603 }, + { url = "https://files.pythonhosted.org/packages/da/e9/c9e6546c817ab75a1a7dab6dcc698e62e375e1017113e8e983fccbd56115/jiter-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c7adb66f899ffa25e3c92bfcb593391ee1947dbdd6a9a970e0d7e713237d572", size = 396625 }, + { url = "https://files.pythonhosted.org/packages/be/bd/976b458add04271ebb5a255e992bd008546ea04bb4dcadc042a16279b4b4/jiter-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98d27330fdfb77913c1097a7aab07f38ff2259048949f499c9901700789ac15", size = 351832 }, + { url = "https://files.pythonhosted.org/packages/07/51/fe59e307aaebec9265dbad44d9d4381d030947e47b0f23531579b9a7c2df/jiter-0.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eda3f8cc74df66892b1d06b5d41a71670c22d95a1ca2cbab73654745ce9d0419", size = 384590 }, + { url = "https://files.pythonhosted.org/packages/db/55/5dcd2693794d8e6f4889389ff66ef3be557a77f8aeeca8973a97a7c00557/jiter-0.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd5ab5ddc11418dce28343123644a100f487eaccf1de27a459ab36d6cca31043", size = 520690 }, + { url = "https://files.pythonhosted.org/packages/54/d5/9f51dc90985e9eb251fbbb747ab2b13b26601f16c595a7b8baba964043bd/jiter-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42f8a68a69f047b310319ef8e2f52fdb2e7976fb3313ef27df495cf77bcad965", size = 512649 }, + { url = "https://files.pythonhosted.org/packages/a6/e5/4e385945179bcf128fa10ad8dca9053d717cbe09e258110e39045c881fe5/jiter-0.9.0-cp311-cp311-win32.whl", hash = "sha256:a25519efb78a42254d59326ee417d6f5161b06f5da827d94cf521fed961b1ff2", size = 206920 }, + { url = "https://files.pythonhosted.org/packages/4c/47/5e0b94c603d8e54dd1faab439b40b832c277d3b90743e7835879ab663757/jiter-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:923b54afdd697dfd00d368b7ccad008cccfeb1efb4e621f32860c75e9f25edbd", size = 210119 }, + { url = "https://files.pythonhosted.org/packages/af/d7/c55086103d6f29b694ec79156242304adf521577530d9031317ce5338c59/jiter-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7b46249cfd6c48da28f89eb0be3f52d6fdb40ab88e2c66804f546674e539ec11", size = 309203 }, + { url = "https://files.pythonhosted.org/packages/b0/01/f775dfee50beb420adfd6baf58d1c4d437de41c9b666ddf127c065e5a488/jiter-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:609cf3c78852f1189894383cf0b0b977665f54cb38788e3e6b941fa6d982c00e", size = 319678 }, + { url = "https://files.pythonhosted.org/packages/ab/b8/09b73a793714726893e5d46d5c534a63709261af3d24444ad07885ce87cb/jiter-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d726a3890a54561e55a9c5faea1f7655eda7f105bd165067575ace6e65f80bb2", size = 341816 }, + { url = "https://files.pythonhosted.org/packages/35/6f/b8f89ec5398b2b0d344257138182cc090302854ed63ed9c9051e9c673441/jiter-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e89dc075c1fef8fa9be219e249f14040270dbc507df4215c324a1839522ea75", size = 364152 }, + { url = "https://files.pythonhosted.org/packages/9b/ca/978cc3183113b8e4484cc7e210a9ad3c6614396e7abd5407ea8aa1458eef/jiter-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e8ffa3c353b1bc4134f96f167a2082494351e42888dfcf06e944f2729cbe1d", size = 406991 }, + { url = "https://files.pythonhosted.org/packages/13/3a/72861883e11a36d6aa314b4922125f6ae90bdccc225cd96d24cc78a66385/jiter-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203f28a72a05ae0e129b3ed1f75f56bc419d5f91dfacd057519a8bd137b00c42", size = 395824 }, + { url = "https://files.pythonhosted.org/packages/87/67/22728a86ef53589c3720225778f7c5fdb617080e3deaed58b04789418212/jiter-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fca1a02ad60ec30bb230f65bc01f611c8608b02d269f998bc29cca8619a919dc", size = 351318 }, + { url = "https://files.pythonhosted.org/packages/69/b9/f39728e2e2007276806d7a6609cda7fac44ffa28ca0d02c49a4f397cc0d9/jiter-0.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:237e5cee4d5d2659aaf91bbf8ec45052cc217d9446070699441a91b386ae27dc", size = 384591 }, + { url = "https://files.pythonhosted.org/packages/eb/8f/8a708bc7fd87b8a5d861f1c118a995eccbe6d672fe10c9753e67362d0dd0/jiter-0.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:528b6b71745e7326eed73c53d4aa57e2a522242320b6f7d65b9c5af83cf49b6e", size = 520746 }, + { url = "https://files.pythonhosted.org/packages/95/1e/65680c7488bd2365dbd2980adaf63c562d3d41d3faac192ebc7ef5b4ae25/jiter-0.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9f48e86b57bc711eb5acdfd12b6cb580a59cc9a993f6e7dcb6d8b50522dcd50d", size = 512754 }, + { url = "https://files.pythonhosted.org/packages/78/f3/fdc43547a9ee6e93c837685da704fb6da7dba311fc022e2766d5277dfde5/jiter-0.9.0-cp312-cp312-win32.whl", hash = "sha256:699edfde481e191d81f9cf6d2211debbfe4bd92f06410e7637dffb8dd5dfde06", size = 207075 }, + { url = "https://files.pythonhosted.org/packages/cd/9d/742b289016d155f49028fe1bfbeb935c9bf0ffeefdf77daf4a63a42bb72b/jiter-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:099500d07b43f61d8bd780466d429c45a7b25411b334c60ca875fa775f68ccb0", size = 207999 }, + { url = "https://files.pythonhosted.org/packages/e7/1b/4cd165c362e8f2f520fdb43245e2b414f42a255921248b4f8b9c8d871ff1/jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7", size = 308197 }, + { url = "https://files.pythonhosted.org/packages/13/aa/7a890dfe29c84c9a82064a9fe36079c7c0309c91b70c380dc138f9bea44a/jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b", size = 318160 }, + { url = "https://files.pythonhosted.org/packages/6a/38/5888b43fc01102f733f085673c4f0be5a298f69808ec63de55051754e390/jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69", size = 341259 }, + { url = "https://files.pythonhosted.org/packages/3d/5e/bbdbb63305bcc01006de683b6228cd061458b9b7bb9b8d9bc348a58e5dc2/jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103", size = 363730 }, + { url = "https://files.pythonhosted.org/packages/75/85/53a3edc616992fe4af6814c25f91ee3b1e22f7678e979b6ea82d3bc0667e/jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635", size = 405126 }, + { url = "https://files.pythonhosted.org/packages/ae/b3/1ee26b12b2693bd3f0b71d3188e4e5d817b12e3c630a09e099e0a89e28fa/jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4", size = 393668 }, + { url = "https://files.pythonhosted.org/packages/11/87/e084ce261950c1861773ab534d49127d1517b629478304d328493f980791/jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d", size = 352350 }, + { url = "https://files.pythonhosted.org/packages/f0/06/7dca84b04987e9df563610aa0bc154ea176e50358af532ab40ffb87434df/jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3", size = 384204 }, + { url = "https://files.pythonhosted.org/packages/16/2f/82e1c6020db72f397dd070eec0c85ebc4df7c88967bc86d3ce9864148f28/jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5", size = 520322 }, + { url = "https://files.pythonhosted.org/packages/36/fd/4f0cd3abe83ce208991ca61e7e5df915aa35b67f1c0633eb7cf2f2e88ec7/jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d", size = 512184 }, + { url = "https://files.pythonhosted.org/packages/a0/3c/8a56f6d547731a0b4410a2d9d16bf39c861046f91f57c98f7cab3d2aa9ce/jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53", size = 206504 }, + { url = "https://files.pythonhosted.org/packages/f4/1c/0c996fd90639acda75ed7fa698ee5fd7d80243057185dc2f63d4c1c9f6b9/jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7", size = 204943 }, + { url = "https://files.pythonhosted.org/packages/78/0f/77a63ca7aa5fed9a1b9135af57e190d905bcd3702b36aca46a01090d39ad/jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001", size = 317281 }, + { url = "https://files.pythonhosted.org/packages/f9/39/a3a1571712c2bf6ec4c657f0d66da114a63a2e32b7e4eb8e0b83295ee034/jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a", size = 350273 }, + { url = "https://files.pythonhosted.org/packages/ee/47/3729f00f35a696e68da15d64eb9283c330e776f3b5789bac7f2c0c4df209/jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf", size = 206867 }, + { url = "https://files.pythonhosted.org/packages/aa/2c/9bee940db68d8cefb84178f8b15220c836276db8c6e09cbd422071c01c33/jiter-0.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:9ef340fae98065071ccd5805fe81c99c8f80484e820e40043689cf97fb66b3e2", size = 315246 }, + { url = "https://files.pythonhosted.org/packages/d0/9b/42d5d59585d9af4fe207e96c6edac2a62bca26d76e2471e78c2f5da28bb8/jiter-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:efb767d92c63b2cd9ec9f24feeb48f49574a713870ec87e9ba0c2c6e9329c3e2", size = 312621 }, + { url = "https://files.pythonhosted.org/packages/2e/a5/a64de757516e5531f8d147a32251905f0e23641738d3520a0a0724fe9651/jiter-0.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:113f30f87fb1f412510c6d7ed13e91422cfd329436364a690c34c8b8bd880c42", size = 343006 }, + { url = "https://files.pythonhosted.org/packages/89/be/08d2bae711200d558ab8c5771f05f47cd09b82b2258a8d6fad0ee2c6a1f3/jiter-0.9.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8793b6df019b988526f5a633fdc7456ea75e4a79bd8396a3373c371fc59f5c9b", size = 365099 }, + { url = "https://files.pythonhosted.org/packages/03/9e/d137a0088be90ba5081f7d5d2383374bd77a1447158e44c3ec4e142f902c/jiter-0.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a9aaa5102dba4e079bb728076fadd5a2dca94c05c04ce68004cfd96f128ea34", size = 407834 }, + { url = "https://files.pythonhosted.org/packages/04/4c/b6bee52a5b327830abea13eba4222f33f88895a1055eff8870ab3ebbde41/jiter-0.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d838650f6ebaf4ccadfb04522463e74a4c378d7e667e0eb1865cfe3990bfac49", size = 399255 }, + { url = "https://files.pythonhosted.org/packages/12/b7/364b615a35f99d01cc27d3caea8c3a3ac5451bd5cadf8e5dc4355b102aba/jiter-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0194f813efdf4b8865ad5f5c5f50f8566df7d770a82c51ef593d09e0b347020", size = 354142 }, + { url = "https://files.pythonhosted.org/packages/65/cc/5156f75c496aac65080e2995910104d0e46644df1452c20d963cb904b4b1/jiter-0.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a7954a401d0a8a0b8bc669199db78af435aae1e3569187c2939c477c53cb6a0a", size = 385142 }, + { url = "https://files.pythonhosted.org/packages/46/cf/370be59c38e56a6fed0308ca266b12d8178b8d6630284cc88ae5af110764/jiter-0.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4feafe787eb8a8d98168ab15637ca2577f6ddf77ac6c8c66242c2d028aa5420e", size = 522035 }, + { url = "https://files.pythonhosted.org/packages/ff/f5/c462d994dcbff43de8a3c953548d609c73a5db8138182408944fce2b68c1/jiter-0.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:27cd1f2e8bb377f31d3190b34e4328d280325ad7ef55c6ac9abde72f79e84d2e", size = 513844 }, + { url = "https://files.pythonhosted.org/packages/15/39/60d8f17de27586fa1e7c8215ead8222556d40a6b96b20f1ad70528961f99/jiter-0.9.0-cp39-cp39-win32.whl", hash = "sha256:161d461dcbe658cf0bd0aa375b30a968b087cdddc624fc585f3867c63c6eca95", size = 207147 }, + { url = "https://files.pythonhosted.org/packages/4b/13/c10f17dcddd1b4c1313418e64ace5e77cc4f7313246140fb09044516a62c/jiter-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:e8b36d8a16a61993be33e75126ad3d8aa29cf450b09576f3c427d27647fcb4aa", size = 208879 }, +] + +[[package]] +name = "markdown" +version = "3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354 }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451 }, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/44/140469d87379c02f1e1870315f3143718036a983dd0416650827b8883192/mkdocs_autorefs-1.4.1.tar.gz", hash = "sha256:4b5b6235a4becb2b10425c2fa191737e415b37aa3418919db33e5d774c9db079", size = 4131355 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/29/1125f7b11db63e8e32bcfa0752a4eea30abff3ebd0796f808e14571ddaa2/mkdocs_autorefs-1.4.1-py3-none-any.whl", hash = "sha256:9793c5ac06a6ebbe52ec0f8439256e66187badf4b5334b5fde0b128ec134df4f", size = 5782047 }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521 }, +] + +[[package]] +name = "mkdocs-material" +version = "9.6.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/d7/93e19c9587e5f4ed25647890555d58cf484a4d412be7037dc17b9c9179d9/mkdocs_material-9.6.7.tar.gz", hash = "sha256:3e2c1fceb9410056c2d91f334a00cdea3215c28750e00c691c1e46b2a33309b4", size = 3947458 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/d3/12f22de41bdd9e576ddc459b38c651d68edfb840b32acaa1f46ae36845e3/mkdocs_material-9.6.7-py3-none-any.whl", hash = "sha256:8a159e45e80fcaadd9fbeef62cbf928569b93df954d4dc5ba76d46820caf7b47", size = 8696755 }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728 }, +] + +[[package]] +name = "mkdocstrings" +version = "0.29.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "pymdown-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/4d/a9484dc5d926295bdf308f1f6c4f07fcc99735b970591edc414d401fcc91/mkdocstrings-0.29.0.tar.gz", hash = "sha256:3657be1384543ce0ee82112c3e521bbf48e41303aa0c229b9ffcccba057d922e", size = 1212185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/47/eb876dfd84e48f31ff60897d161b309cf6a04ca270155b0662aae562b3fb/mkdocstrings-0.29.0-py3-none-any.whl", hash = "sha256:8ea98358d2006f60befa940fdebbbc88a26b37ecbcded10be726ba359284f73d", size = 1630824 }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.16.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffe" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocstrings" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/81/3575e451682e0ed3c39e9b57d1fd30590cd28a965131ead14bf2efe34a1b/mkdocstrings_python-1.16.5.tar.gz", hash = "sha256:706b28dd0f59249a7c22cc5d517c9521e06c030b57e2a5478e1928a58f900abb", size = 426979 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/27/42f8a520111a4dde9722f08ca75d761b68722158b2232b63def061de12a8/mkdocstrings_python-1.16.5-py3-none-any.whl", hash = "sha256:0899a12e356eab8e83720c63e15d0ff51cd96603216c837618de346e086b39ba", size = 451550 }, +] + +[[package]] +name = "mypy" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/f8/65a7ce8d0e09b6329ad0c8d40330d100ea343bd4dd04c4f8ae26462d0a17/mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", size = 10738433 }, + { url = "https://files.pythonhosted.org/packages/b4/95/9c0ecb8eacfe048583706249439ff52105b3f552ea9c4024166c03224270/mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", size = 9861472 }, + { url = "https://files.pythonhosted.org/packages/84/09/9ec95e982e282e20c0d5407bc65031dfd0f0f8ecc66b69538296e06fcbee/mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", size = 11611424 }, + { url = "https://files.pythonhosted.org/packages/78/13/f7d14e55865036a1e6a0a69580c240f43bc1f37407fe9235c0d4ef25ffb0/mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", size = 12365450 }, + { url = "https://files.pythonhosted.org/packages/48/e1/301a73852d40c241e915ac6d7bcd7fedd47d519246db2d7b86b9d7e7a0cb/mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", size = 12551765 }, + { url = "https://files.pythonhosted.org/packages/77/ba/c37bc323ae5fe7f3f15a28e06ab012cd0b7552886118943e90b15af31195/mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", size = 9274701 }, + { url = "https://files.pythonhosted.org/packages/03/bc/f6339726c627bd7ca1ce0fa56c9ae2d0144604a319e0e339bdadafbbb599/mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", size = 10662338 }, + { url = "https://files.pythonhosted.org/packages/e2/90/8dcf506ca1a09b0d17555cc00cd69aee402c203911410136cd716559efe7/mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", size = 9787540 }, + { url = "https://files.pythonhosted.org/packages/05/05/a10f9479681e5da09ef2f9426f650d7b550d4bafbef683b69aad1ba87457/mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", size = 11538051 }, + { url = "https://files.pythonhosted.org/packages/e9/9a/1f7d18b30edd57441a6411fcbc0c6869448d1a4bacbaee60656ac0fc29c8/mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", size = 12286751 }, + { url = "https://files.pythonhosted.org/packages/72/af/19ff499b6f1dafcaf56f9881f7a965ac2f474f69f6f618b5175b044299f5/mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", size = 12421783 }, + { url = "https://files.pythonhosted.org/packages/96/39/11b57431a1f686c1aed54bf794870efe0f6aeca11aca281a0bd87a5ad42c/mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", size = 9265618 }, + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, + { url = "https://files.pythonhosted.org/packages/5a/fa/79cf41a55b682794abe71372151dbbf856e3008f6767057229e6649d294a/mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078", size = 10737129 }, + { url = "https://files.pythonhosted.org/packages/d3/33/dd8feb2597d648de29e3da0a8bf4e1afbda472964d2a4a0052203a6f3594/mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba", size = 9856335 }, + { url = "https://files.pythonhosted.org/packages/e4/b5/74508959c1b06b96674b364ffeb7ae5802646b32929b7701fc6b18447592/mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5", size = 11611935 }, + { url = "https://files.pythonhosted.org/packages/6c/53/da61b9d9973efcd6507183fdad96606996191657fe79701b2c818714d573/mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b", size = 12365827 }, + { url = "https://files.pythonhosted.org/packages/c1/72/965bd9ee89540c79a25778cc080c7e6ef40aa1eeac4d52cec7eae6eb5228/mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2", size = 12541924 }, + { url = "https://files.pythonhosted.org/packages/46/d0/f41645c2eb263e6c77ada7d76f894c580c9ddb20d77f0c24d34273a4dab2/mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980", size = 9271176 }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "openai" +version = "1.66.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/84/c5/3c422ca3ccc81c063955e7c20739d7f8f37fea0af865c4a60c81e6225e14/openai-1.66.0.tar.gz", hash = "sha256:8a9e672bc6eadec60a962f0b40d7d1c09050010179c919ed65322e433e2d1025", size = 396819 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/f1/d52960dac9519c9de64593460826a0fe2e19159389ec97ecf3e931d2e6a3/openai-1.66.0-py3-none-any.whl", hash = "sha256:43e4a3c0c066cc5809be4e6aac456a3ebc4ec1848226ef9d1340859ac130d45a", size = 566389 }, +] + +[[package]] +name = "openai-agents" +version = "0.0.1" +source = { editable = "." } +dependencies = [ + { name = "griffe" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "types-requests" }, + { name = "typing-extensions" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage" }, + { name = "mkdocs" }, + { name = "mkdocs-material" }, + { name = "mkdocstrings", extra = ["python"] }, + { name = "mypy" }, + { name = "playwright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-mock" }, + { name = "rich" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "griffe", specifier = ">=1.5.6,<2" }, + { name = "openai", specifier = ">=1.66.0" }, + { name = "pydantic", specifier = ">=2.10,<3" }, + { name = "requests", specifier = ">=2.0,<3" }, + { name = "types-requests", specifier = ">=2.0,<3" }, + { name = "typing-extensions", specifier = ">=4.12.2,<5" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7.6.12" }, + { name = "mkdocs", specifier = ">=1.6.0" }, + { name = "mkdocs-material", specifier = ">=9.6.0" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.28.0" }, + { name = "mypy" }, + { name = "playwright", specifier = "==1.50.0" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "rich" }, + { name = "ruff", specifier = "==0.9.2" }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746 }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "playwright" +version = "1.50.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/5e/068dea3c96e9c09929b45c92cf7e573403b52a89aa463f89b9da9b87b7a4/playwright-1.50.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f36d754a6c5bd9bf7f14e8f57a2aea6fd08f39ca4c8476481b9c83e299531148", size = 40277564 }, + { url = "https://files.pythonhosted.org/packages/78/85/b3deb3d2add00d2a6ee74bf6f57ccefb30efc400fd1b7b330ba9a3626330/playwright-1.50.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:40f274384591dfd27f2b014596250b2250c843ed1f7f4ef5d2960ecb91b4961e", size = 39521844 }, + { url = "https://files.pythonhosted.org/packages/f3/f6/002b3d98df9c84296fea84f070dc0d87c2270b37f423cf076a913370d162/playwright-1.50.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:9922ef9bcd316995f01e220acffd2d37a463b4ad10fd73e388add03841dfa230", size = 40277563 }, + { url = "https://files.pythonhosted.org/packages/b9/63/c9a73736e434df894e484278dddc0bf154312ff8d0f16d516edb790a7d42/playwright-1.50.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:8fc628c492d12b13d1f347137b2ac6c04f98197ff0985ef0403a9a9ee0d39131", size = 45076712 }, + { url = "https://files.pythonhosted.org/packages/bd/2c/a54b5a64cc7d1a62f2d944c5977fb3c88e74d76f5cdc7966e717426bce66/playwright-1.50.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcff35f72db2689a79007aee78f1b0621a22e6e3d6c1f58aaa9ac805bf4497c", size = 44493111 }, + { url = "https://files.pythonhosted.org/packages/2b/4a/047cbb2ffe1249bd7a56441fc3366fb4a8a1f44bc36a9061d10edfda2c86/playwright-1.50.0-py3-none-win32.whl", hash = "sha256:3b906f4d351260016a8c5cc1e003bb341651ae682f62213b50168ed581c7558a", size = 34784543 }, + { url = "https://files.pythonhosted.org/packages/bc/2b/e944e10c9b18e77e43d3bb4d6faa323f6cc27597db37b75bc3fd796adfd5/playwright-1.50.0-py3-none-win_amd64.whl", hash = "sha256:1859423da82de631704d5e3d88602d755462b0906824c1debe140979397d2e8d", size = 34784546 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "pydantic" +version = "2.10.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/bc/fed5f74b5d802cf9a03e83f60f18864e90e3aed7223adaca5ffb7a8d8d64/pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", size = 1895938 }, + { url = "https://files.pythonhosted.org/packages/71/2a/185aff24ce844e39abb8dd680f4e959f0006944f4a8a0ea372d9f9ae2e53/pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", size = 1815684 }, + { url = "https://files.pythonhosted.org/packages/c3/43/fafabd3d94d159d4f1ed62e383e264f146a17dd4d48453319fd782e7979e/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", size = 1829169 }, + { url = "https://files.pythonhosted.org/packages/a2/d1/f2dfe1a2a637ce6800b799aa086d079998959f6f1215eb4497966efd2274/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", size = 1867227 }, + { url = "https://files.pythonhosted.org/packages/7d/39/e06fcbcc1c785daa3160ccf6c1c38fea31f5754b756e34b65f74e99780b5/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", size = 2037695 }, + { url = "https://files.pythonhosted.org/packages/7a/67/61291ee98e07f0650eb756d44998214231f50751ba7e13f4f325d95249ab/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", size = 2741662 }, + { url = "https://files.pythonhosted.org/packages/32/90/3b15e31b88ca39e9e626630b4c4a1f5a0dfd09076366f4219429e6786076/pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", size = 1993370 }, + { url = "https://files.pythonhosted.org/packages/ff/83/c06d333ee3a67e2e13e07794995c1535565132940715931c1c43bfc85b11/pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", size = 1996813 }, + { url = "https://files.pythonhosted.org/packages/7c/f7/89be1c8deb6e22618a74f0ca0d933fdcb8baa254753b26b25ad3acff8f74/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", size = 2005287 }, + { url = "https://files.pythonhosted.org/packages/b7/7d/8eb3e23206c00ef7feee17b83a4ffa0a623eb1a9d382e56e4aa46fd15ff2/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", size = 2128414 }, + { url = "https://files.pythonhosted.org/packages/4e/99/fe80f3ff8dd71a3ea15763878d464476e6cb0a2db95ff1c5c554133b6b83/pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", size = 2155301 }, + { url = "https://files.pythonhosted.org/packages/2b/a3/e50460b9a5789ca1451b70d4f52546fa9e2b420ba3bfa6100105c0559238/pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", size = 1816685 }, + { url = "https://files.pythonhosted.org/packages/57/4c/a8838731cb0f2c2a39d3535376466de6049034d7b239c0202a64aaa05533/pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", size = 1982876 }, + { url = "https://files.pythonhosted.org/packages/c2/89/f3450af9d09d44eea1f2c369f49e8f181d742f28220f88cc4dfaae91ea6e/pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", size = 1893421 }, + { url = "https://files.pythonhosted.org/packages/9e/e3/71fe85af2021f3f386da42d291412e5baf6ce7716bd7101ea49c810eda90/pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", size = 1814998 }, + { url = "https://files.pythonhosted.org/packages/a6/3c/724039e0d848fd69dbf5806894e26479577316c6f0f112bacaf67aa889ac/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", size = 1826167 }, + { url = "https://files.pythonhosted.org/packages/2b/5b/1b29e8c1fb5f3199a9a57c1452004ff39f494bbe9bdbe9a81e18172e40d3/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", size = 1865071 }, + { url = "https://files.pythonhosted.org/packages/89/6c/3985203863d76bb7d7266e36970d7e3b6385148c18a68cc8915fd8c84d57/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", size = 2036244 }, + { url = "https://files.pythonhosted.org/packages/0e/41/f15316858a246b5d723f7d7f599f79e37493b2e84bfc789e58d88c209f8a/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", size = 2737470 }, + { url = "https://files.pythonhosted.org/packages/a8/7c/b860618c25678bbd6d1d99dbdfdf0510ccb50790099b963ff78a124b754f/pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", size = 1992291 }, + { url = "https://files.pythonhosted.org/packages/bf/73/42c3742a391eccbeab39f15213ecda3104ae8682ba3c0c28069fbcb8c10d/pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", size = 1994613 }, + { url = "https://files.pythonhosted.org/packages/94/7a/941e89096d1175d56f59340f3a8ebaf20762fef222c298ea96d36a6328c5/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", size = 2002355 }, + { url = "https://files.pythonhosted.org/packages/6e/95/2359937a73d49e336a5a19848713555605d4d8d6940c3ec6c6c0ca4dcf25/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", size = 2126661 }, + { url = "https://files.pythonhosted.org/packages/2b/4c/ca02b7bdb6012a1adef21a50625b14f43ed4d11f1fc237f9d7490aa5078c/pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", size = 2153261 }, + { url = "https://files.pythonhosted.org/packages/72/9d/a241db83f973049a1092a079272ffe2e3e82e98561ef6214ab53fe53b1c7/pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", size = 1812361 }, + { url = "https://files.pythonhosted.org/packages/e8/ef/013f07248041b74abd48a385e2110aa3a9bbfef0fbd97d4e6d07d2f5b89a/pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", size = 1982484 }, + { url = "https://files.pythonhosted.org/packages/10/1c/16b3a3e3398fd29dca77cea0a1d998d6bde3902fa2706985191e2313cc76/pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", size = 1867102 }, + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, + { url = "https://files.pythonhosted.org/packages/27/97/3aef1ddb65c5ccd6eda9050036c956ff6ecbfe66cb7eb40f280f121a5bb0/pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993", size = 1896475 }, + { url = "https://files.pythonhosted.org/packages/ad/d3/5668da70e373c9904ed2f372cb52c0b996426f302e0dee2e65634c92007d/pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308", size = 1772279 }, + { url = "https://files.pythonhosted.org/packages/8a/9e/e44b8cb0edf04a2f0a1f6425a65ee089c1d6f9c4c2dcab0209127b6fdfc2/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4", size = 1829112 }, + { url = "https://files.pythonhosted.org/packages/1c/90/1160d7ac700102effe11616e8119e268770f2a2aa5afb935f3ee6832987d/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf", size = 1866780 }, + { url = "https://files.pythonhosted.org/packages/ee/33/13983426df09a36d22c15980008f8d9c77674fc319351813b5a2739b70f3/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76", size = 2037943 }, + { url = "https://files.pythonhosted.org/packages/01/d7/ced164e376f6747e9158c89988c293cd524ab8d215ae4e185e9929655d5c/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118", size = 2740492 }, + { url = "https://files.pythonhosted.org/packages/8b/1f/3dc6e769d5b7461040778816aab2b00422427bcaa4b56cc89e9c653b2605/pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630", size = 1995714 }, + { url = "https://files.pythonhosted.org/packages/07/d7/a0bd09bc39283530b3f7c27033a814ef254ba3bd0b5cfd040b7abf1fe5da/pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54", size = 1997163 }, + { url = "https://files.pythonhosted.org/packages/2d/bb/2db4ad1762e1c5699d9b857eeb41959191980de6feb054e70f93085e1bcd/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f", size = 2005217 }, + { url = "https://files.pythonhosted.org/packages/53/5f/23a5a3e7b8403f8dd8fc8a6f8b49f6b55c7d715b77dcf1f8ae919eeb5628/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362", size = 2127899 }, + { url = "https://files.pythonhosted.org/packages/c2/ae/aa38bb8dd3d89c2f1d8362dd890ee8f3b967330821d03bbe08fa01ce3766/pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96", size = 2155726 }, + { url = "https://files.pythonhosted.org/packages/98/61/4f784608cc9e98f70839187117ce840480f768fed5d386f924074bf6213c/pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e", size = 1817219 }, + { url = "https://files.pythonhosted.org/packages/57/82/bb16a68e4a1a858bb3768c2c8f1ff8d8978014e16598f001ea29a25bf1d1/pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67", size = 1985382 }, + { url = "https://files.pythonhosted.org/packages/46/72/af70981a341500419e67d5cb45abe552a7c74b66326ac8877588488da1ac/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", size = 1891159 }, + { url = "https://files.pythonhosted.org/packages/ad/3d/c5913cccdef93e0a6a95c2d057d2c2cba347815c845cda79ddd3c0f5e17d/pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", size = 1768331 }, + { url = "https://files.pythonhosted.org/packages/f6/f0/a3ae8fbee269e4934f14e2e0e00928f9346c5943174f2811193113e58252/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", size = 1822467 }, + { url = "https://files.pythonhosted.org/packages/d7/7a/7bbf241a04e9f9ea24cd5874354a83526d639b02674648af3f350554276c/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", size = 1979797 }, + { url = "https://files.pythonhosted.org/packages/4f/5f/4784c6107731f89e0005a92ecb8a2efeafdb55eb992b8e9d0a2be5199335/pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", size = 1987839 }, + { url = "https://files.pythonhosted.org/packages/6d/a7/61246562b651dff00de86a5f01b6e4befb518df314c54dec187a78d81c84/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", size = 1998861 }, + { url = "https://files.pythonhosted.org/packages/86/aa/837821ecf0c022bbb74ca132e117c358321e72e7f9702d1b6a03758545e2/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", size = 2116582 }, + { url = "https://files.pythonhosted.org/packages/81/b0/5e74656e95623cbaa0a6278d16cf15e10a51f6002e3ec126541e95c29ea3/pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", size = 2151985 }, + { url = "https://files.pythonhosted.org/packages/63/37/3e32eeb2a451fddaa3898e2163746b0cffbbdbb4740d38372db0490d67f3/pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", size = 2004715 }, + { url = "https://files.pythonhosted.org/packages/29/0e/dcaea00c9dbd0348b723cae82b0e0c122e0fa2b43fa933e1622fd237a3ee/pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656", size = 1891733 }, + { url = "https://files.pythonhosted.org/packages/86/d3/e797bba8860ce650272bda6383a9d8cad1d1c9a75a640c9d0e848076f85e/pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278", size = 1768375 }, + { url = "https://files.pythonhosted.org/packages/41/f7/f847b15fb14978ca2b30262548f5fc4872b2724e90f116393eb69008299d/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb", size = 1822307 }, + { url = "https://files.pythonhosted.org/packages/9c/63/ed80ec8255b587b2f108e514dc03eed1546cd00f0af281e699797f373f38/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd", size = 1979971 }, + { url = "https://files.pythonhosted.org/packages/a9/6d/6d18308a45454a0de0e975d70171cadaf454bc7a0bf86b9c7688e313f0bb/pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc", size = 1987616 }, + { url = "https://files.pythonhosted.org/packages/82/8a/05f8780f2c1081b800a7ca54c1971e291c2d07d1a50fb23c7e4aef4ed403/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b", size = 1998943 }, + { url = "https://files.pythonhosted.org/packages/5e/3e/fe5b6613d9e4c0038434396b46c5303f5ade871166900b357ada4766c5b7/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b", size = 2116654 }, + { url = "https://files.pythonhosted.org/packages/db/ad/28869f58938fad8cc84739c4e592989730bfb69b7c90a8fff138dff18e1e/pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2", size = 2152292 }, + { url = "https://files.pythonhosted.org/packages/a1/0c/c5c5cd3689c32ed1fe8c5d234b079c12c281c051759770c05b8bed6412b5/pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35", size = 2004961 }, +] + +[[package]] +name = "pyee" +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/37/8fb6e653597b2b67ef552ed49b438d5398ba3b85a9453f8ada0fd77d455c/pyee-12.1.1.tar.gz", hash = "sha256:bbc33c09e2ff827f74191e3e5bbc6be7da02f627b7ec30d86f5ce1a6fb2424a3", size = 30915 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/68/7e150cba9eeffdeb3c5cecdb6896d70c8edd46ce41c0491e12fb2b2256ff/pyee-12.1.1-py3-none-any.whl", hash = "sha256:18a19c650556bb6b32b406d7f017c8f513aceed1ef7ca618fb65de7bd2d347ef", size = 15527 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pymdown-extensions" +version = "10.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/44/e6de2fdc880ad0ec7547ca2e087212be815efbc9a425a8d5ba9ede602cbb/pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b", size = 846846 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/f5/b9e2a42aa8f9e34d52d66de87941ecd236570c7ed2e87775ed23bbe4e224/pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9", size = 264467 }, +] + +[[package]] +name = "pytest" +version = "8.3.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.25.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/a8/ecbc8ede70921dd2f544ab1cadd3ff3bf842af27f87bbdea774c7baa1d38/pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a", size = 54239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/17/3493c5624e48fd97156ebaec380dcaafee9506d7e2c46218ceebbb57d7de/pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3", size = 19467 }, +] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/8e/da1c6c58f751b70f8ceb1eb25bc25d524e8f14fe16edcce3f4e3ba08629c/pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb", size = 5631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rich" +version = "13.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, +] + +[[package]] +name = "ruff" +version = "0.9.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/63/77ecca9d21177600f551d1c58ab0e5a0b260940ea7312195bd2a4798f8a8/ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0", size = 3553799 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/b9/0e168e4e7fb3af851f739e8f07889b91d1a33a30fca8c29fa3149d6b03ec/ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347", size = 11652408 }, + { url = "https://files.pythonhosted.org/packages/2c/22/08ede5db17cf701372a461d1cb8fdde037da1d4fa622b69ac21960e6237e/ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00", size = 11587553 }, + { url = "https://files.pythonhosted.org/packages/42/05/dedfc70f0bf010230229e33dec6e7b2235b2a1b8cbb2a991c710743e343f/ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4", size = 11020755 }, + { url = "https://files.pythonhosted.org/packages/df/9b/65d87ad9b2e3def67342830bd1af98803af731243da1255537ddb8f22209/ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d", size = 11826502 }, + { url = "https://files.pythonhosted.org/packages/93/02/f2239f56786479e1a89c3da9bc9391120057fc6f4a8266a5b091314e72ce/ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c", size = 11390562 }, + { url = "https://files.pythonhosted.org/packages/c9/37/d3a854dba9931f8cb1b2a19509bfe59e00875f48ade632e95aefcb7a0aee/ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f", size = 12548968 }, + { url = "https://files.pythonhosted.org/packages/fa/c3/c7b812bb256c7a1d5553433e95980934ffa85396d332401f6b391d3c4569/ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684", size = 13187155 }, + { url = "https://files.pythonhosted.org/packages/bd/5a/3c7f9696a7875522b66aa9bba9e326e4e5894b4366bd1dc32aa6791cb1ff/ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d", size = 12704674 }, + { url = "https://files.pythonhosted.org/packages/be/d6/d908762257a96ce5912187ae9ae86792e677ca4f3dc973b71e7508ff6282/ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df", size = 14529328 }, + { url = "https://files.pythonhosted.org/packages/2d/c2/049f1e6755d12d9cd8823242fa105968f34ee4c669d04cac8cea51a50407/ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247", size = 12385955 }, + { url = "https://files.pythonhosted.org/packages/91/5a/a9bdb50e39810bd9627074e42743b00e6dc4009d42ae9f9351bc3dbc28e7/ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e", size = 11810149 }, + { url = "https://files.pythonhosted.org/packages/e5/fd/57df1a0543182f79a1236e82a79c68ce210efb00e97c30657d5bdb12b478/ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe", size = 11479141 }, + { url = "https://files.pythonhosted.org/packages/dc/16/bc3fd1d38974f6775fc152a0554f8c210ff80f2764b43777163c3c45d61b/ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb", size = 12014073 }, + { url = "https://files.pythonhosted.org/packages/47/6b/e4ca048a8f2047eb652e1e8c755f384d1b7944f69ed69066a37acd4118b0/ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a", size = 12435758 }, + { url = "https://files.pythonhosted.org/packages/c2/40/4d3d6c979c67ba24cf183d29f706051a53c36d78358036a9cd21421582ab/ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145", size = 9796916 }, + { url = "https://files.pythonhosted.org/packages/c3/ef/7f548752bdb6867e6939489c87fe4da489ab36191525fadc5cede2a6e8e2/ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5", size = 10773080 }, + { url = "https://files.pythonhosted.org/packages/0e/4e/33df635528292bd2d18404e4daabcd74ca8a9853b2e1df85ed3d32d24362/ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6", size = 10001738 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "types-requests" +version = "2.32.0.20250306" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/1a/beaeff79ef9efd186566ba5f0d95b44ae21f6d31e9413bcfbef3489b6ae3/types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1", size = 23012 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/26/645d89f56004aa0ba3b96fec27793e3c7e62b40982ee069e52568922b6db/types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b", size = 20673 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389 }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020 }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390 }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386 }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017 }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902 }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380 }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903 }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381 }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +]

AM8*kifXuLs;_Fl#BdKJ9QdR?| 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 J$TDZ5{{`S9FYf>V literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_global_hooks.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8ef217f09b463ef9452f4524bbaadb03c8f5655 GIT binary patch literal 20979 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_global_hooks.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_global_hooks.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a566457587e19b4018052ea1c4e81d446d2a6a06 GIT binary patch literal 8785 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8f1650043b17bfdb758efe35e997f825480e939 GIT binary patch literal 43922 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_handoff_tool.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..036bdfb9408c4edd027671e52a33fde7a04126ec GIT binary patch literal 44557 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 TrhDr<_>Bum9(IKCq+|U*DZ(Uk literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0dc8b124a15c797350d32c9935735d7c10b50f65 GIT binary patch literal 16832 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 J=qhF3{eMLlzwZD5 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_handoff_tool.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4130b0bed0c9c0b4274336bc11584d7baa2fee0 GIT binary patch literal 17350 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_items_helpers.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_items_helpers.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2db1092dd24706649a3f652fcc4cc6981bcc4162 GIT binary patch literal 17961 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 Pr?R^;Q5mmPDtrDHY4(ll literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_lifecycle.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_lifecycle.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c305861ff7444202cbb2ceaa9cf94a25c166a770 GIT binary patch literal 21404 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 V6h^lELUnG(bb%WVWSOnc{{fGH2kig= literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55703beccb9666010ee32662def403cccba69d5d GIT binary patch literal 5535 zcmeHLO>7&-6`tYjF1gEpNi;1xvM5WAMA;VQ*orGRX<|t>P!bkUZPcWtfdx&iRNB&# zon5BZscs!KL0TX|0i!ZfvJ z84ij98G>)#yf=Tl^X8jxb|W0t2&8{~`Nx^RM+o^83tp1z3f=e>CFB;-iB5AQOF2!U z?#uZ&b8{&x+rAXZD%{`j1bD#ps9c4d<+N;&2VI|(3uVJR?CNqZl8y4Ht1G!!HqPU& z?$5PkTX`$!0bR|tWfMH%>RRqVHp!FOgZyB&owujRWp(0=H`%Rx;>9i9)AZmWGSn#W z=l0Pqw5?soL2{Jn;X_1^Tn-MgdUyQDK6;C8>#cJi?PA;7rDlBT_(L2#O+~O^S!Ji- zl**Pp4rTSV`HDF;Rw_DBkk8MU7Amnzh3l_Xxn*CtUNp@qa|&$XOXVqZRyQ4|WX;%M zP$sIDW%7rx*4VRpv2e|F>!g{8&zO!eUA2lhkl~cev*N&in2cg!c2>mD?wHpdW6reg z!i-t0zo)tkLUxl7x-sY@q?@E5tnxUZo4f_GNWSrM34~m}5r%|JLeLG2~%cto6K|0hJ)ACJ{iSW)kSjtB|n;!7$=HpEpoAfou z^=+%G?xU`@kGjEq)D3-A-HhzjNRfPt_e>LQp0<@ACHVth$!p_-HOn9LtP_VkZ7V-Y zCaE6QBYJe0eHVb@N3fP}(pzj?4nbRh1NeDwFGXfZfQ*N`$O+Qd=wb@iX4;n`olqP7 zFSAZEP0zAvdM7q66vwn37W-~U0!cDt6>p;v(R$k21U)V289|3csA`+s3)_bnf_HpEE?JeTBhFqpRYp!_5ZgF$cq6U0MugM1cB{4#gr!g?rv zKb%-k_Ixn3l03B@O)YeO*tZ(}_HyKSf6d@^L@cwVKWdjS*g0T(}p;&~KJ{NcS=MAY4e zJRv}yCKLd+x}ac~Zvq6_Y1u_2-j7X=gAhs`)C4tbL_>h2REnQKPtwJeJuu}oc+>E= zk3cZ&4^y=Nf+?E{iFYluuEvh7$DUvK=|{oU*jPc{Z$XSbCCS9Y*iAY^ zS_w1}d)<}t2<~nIBH94A4BcjJQmq|UWzV|)J*`dpnV~y~8!`wtO~9>fcLL%T^K4$P zE^+$`x(CEf)ipi%%!ylQoa2NzvI!tI3Dl8VieMc<4CE>t$s|9F;yDzEqWlPoP82B= zT_DoI-8kn4$QHWo0c;#GC+*t;FdOd;9$KCB(5hMliU%C1F{{V$qETo%34i+o5Rb>O zwq%11)j~wpc5IZXsM(b;fHCdC4%jnb2j`*dbm%E|O$Hirq`+xuYwPe+ zXng^MErHlN^wW>oMtjhJ3()Wy_V9ZU|Klz63=V17R&B7YdPf;sJRyw?ON)baOzZ>?0p6pswKEVTDAziTA4SR zrFNr!aU3YvaQnkAG6UTF2`@ROP@5*<_iP$8b{D3=>s%1wQN#gp;lqysA8Jg?#?MC_ z7gL1lcgO)ZbMLq!-Y}W)@u=c{X>tjpNHpv_Z`kgYSZEC&(8>ChR-1;;Nk|mTSvL+v z#GMn|)+;bJ+je;>SfB-_6OQDc zLVSU@5C81&8f{ylZI66ybd+u|;!D(AwKlNJ-$UaKH)xyVFi!BD^d^C<7T8s_WveSx RYyG?YZH=yQ(75|h{|2ZhF5dtE literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_max_turns.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9029b7797b853a098b6837c5e119897a31f944bc GIT binary patch literal 5368 zcmeHLO>7&-6`t8Y?k;~sN}_2=R!#jl5*=smL*qbjVSv+{ziqXn~{%bfX{vx%ANYmRwR+Eg zfr;)@O3988Cn<&bYBefT-5QN)@DwNYl%M(CI%zVH3bJ4-#6qbi)&x4)P$t8v2#dI~ zZ?ZYn!dgtS-I+xRC7N(?h-3~?ANF(s)XT@-DaQL1G^(yAvlp0GFIFK6j+Z4_IWCTX0-fs_e) z3d85M%{0|@!n9BySJ>B9JwjXU2yOKvwDteHwn^D*5ht0j_fF|%Pij27Nv72+c{T#< zQs$VaO&|B9#HK%FP2R9m^d_h0I342jJf{~p9p-_uV=>Pyx6#EO2wcwF#d3*j3)$Od2?HV#WExa4 zbUKDu$E90N!RGQzd7Qi}RLiVHV0I=)z$Ev_RkDN^ucq4U^2~9m`?p zqRfXiA}iYok+q;KLv%nW`Z2J)PHgF4-T`_0ZU+I<}^ceJK(>v8MHZS`(q+Kb^Di*6)lrPc+e$*7osHab=K> zk0>ii3FY$|>PINbqxwX^x2pO;lLMQ2szX@n8wi6%F`fmD|F;lki_>DdqrTsD!8@M9 zr1=2I+0u3J3V=05KLqZc59%1kWFm9`dsr7}SvO`qnAIVM^RJ_fx+_%{GB#{?B~V~{cEse zPa%=6rIwBGsjcuUOTYNYzY(6O$YL3E=BnEhL_Gvtc#5NOJLgtaCH=>U!+eN3nq~3DqJWc6}NM?}#9+fkp~iWoBnOY{z4Ob=j8 z-$b*xa!Hy{rIpKcLQz+V2=b~Zf__z@D614@O@sEU&H6;#w|c?{@>8gT=K>_U8;ATJ zxj`5;2ons-#8VK1HzY&Eoi!vT&&dgei`NQ;?J3Z35%!TqL3%)@ph7iGJTFa?>!t~3 zW*JT~lLt)mXXWgCt)|H|XYDudxxY=Bby&bN4R3fo|_#?VCAL^j^L)R@I=W5 z9+}4e52Q?;3mSsQc#`yGX`fkvjT4u*N`VHL9fk)V13hc^UT8w6V4xGFB6lg37+8*)jqX9E>8Y4p(oM^Kfsp9Uk~W zanY=o+RW;8aWLz^oew{;3^MEIEMKy?R#$PKr_;gM-JmS1(nF+zJOa3zx93dw@?zNz ze)?g!sa&aE8LQXX236E&;qwlj|JJls+NdUl8&ua@YSE z2|gixUyz}7GV~A9^*8dyI(eg_5J7#n$mHdjuX8^+3~>Mpr0S&K#(Z*1FmeKyqK_Uo|BCxBvhE literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_max_turns.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_max_turns.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86c7081a7eb5106c46c6eb71d173362c8ab33c88 GIT binary patch literal 2965 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3f1c0ed85039eadbd93cd8db46548756218e18f GIT binary patch literal 1244 zcmZ7#O-~a+^i8+jeo-s|3rLg=#Dvw*Vw97HfW`#dfC)`cJ#3bCS&Q4<&CE91o;Y~m z50LcW!K;76Uw|RTWP1t^g?R#sR{gL0fEG$Dyq)N+z0C9PCa;o9OB< z;dWmqJY`#fVN#nRo!;}Bma}iREqvsJpe-1NcX-?wrXi7vP{D8DVzy^cM;M-Zlr3xy zc`npo7Ir@XxTKzl^lD@zkJVypL$-<5d-DKw;a{GER78#O3U;7Irm@@XtNd6yqOM!LB-O+QS;vZ8 zr9K;JT&r*{;*Bxdk1C6zTOlHNi%Ii2yt!?^R;Ay5N$JNVcwoJw> z8fbUd{O*R%xPpi$q*TvRF*+I-Kn#Ruw*78kF|h@F#yIk5+eBJAXfvBnq1ZU=w49*j zGOjva!*m#5Fv8ddwR~#Wj(2D}hKrlS(*`q-EWzf}JJL(bbDvY{Q9c`ax8C}W>6!sb z^f*tOPH4+`CR{k-h>F?stcV`w^ni(+H9Y)TsfP8j5^a|nxS=J}w%mXJen3ZDt4pg+H&YT7dEOWi)L~M z_{;>ah&_;NA=6t^9ySW=#Pf@U4Z+M>(&6#q9{MDSZ7gnS2h2VY?62dsXB`vX-a IPl+`B2SRBh1^@s6 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_model_mapper.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39b502b234cc7208da0b5285e588376ff117fbd3 GIT binary patch literal 1237 zcmZ7#O=}ZD^ldiTd^I(tHEpfhr6P2dCKc&PDUBl3gjQ(CDTig7Y}eGy?lQAMlT!~K z`UeU*c<|~!(Oad6FjPDg5l@Qw1HPHfB(1~deSg1~VJ?>ed>?;*a>@$81!v@ojfmkk z5+_iDnou|v}$4n;b>Z?c z)L|C3-vf9`BO&eT%2*$($5uyT3%z&d0UA8MczY>HP4Eg%psq|~w+~errKYZ-K`mZO zG{hvi_1@IPM^Y=WF(Mx!ZY^EOH7%_scm?V)nPXgK;fhy&VlAo5UN4kdVqICojy$z7 zD=jW{I45NjjQ-=!WOiH0f({NC+HPMHYts7#oxb$DZcFc2fgbw0rHgg-^O;SHQk#VO z-PNGCZcwHoVhN!&vV^aW#5v##VVSL<7uu9x0h=)oeA2Oy76CfcVN<9!_q%O3Y9#tpl5j*>qV%Y5U$YLVUtzWpw*Z;98y)qQ;1` zwB<&=lx3oWt#};0Vt13-1=s@^_A&Ulf*) zGw1nRgTu4Dah&*hZSJ`HHC4uMpbh#1_w?S@X@RrpyI*Ak6>( literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87a8e2cd8f5307ddd1607329fab6c49b34f910ac GIT binary patch literal 868 zcmY*Y&5qMB5Vn&hP1+Q(ELz}#asiP`3lc}PTC^aokmv!#B?4LA+8YAS54H=mYA-Br zfsnRGUW3Q*l@qUkI52j$uvnV$=b3o?%{K}ijRpwT@B812BaF}wd$_nfI2?efC*T-j zSfCuc-K%^LVN~_<0Nacw!m6M59ri_3#d+*-PYkMIJ_H=F@H5I2hM%LX{}bw=X@*Ty za80Nz6yW%2!?>8zio;=K6O|2+=J?Ibe!pEk03p~Rn5w~Pr12?UqZN9DUcFpn?XB$D zTX`zc{#lRV75)Mozxo@@{A-BySfGQmaD(0>bp&7ZSKbE8XMiG$YL5ja-a~6&#}FH= z;NB_zgx@6$0sC;e3?0>UxtFTf&0MgwqHU_1l%|gI^73GxD#fKvZ{2AZcQa*t*i7KE z*-%ID{iF0+Uw(XCo7BrI0ZM%nj0sH|_+3hQ^YAkfjD8{)SBd zJSJ^f69UDuF5DdQQOADlm?$@veCNN|7>Kz^ISNjK3vSm|j2^)Cx#0WoA0cIhyX_@7 G@&5o5RQQen literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_model_mapper.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16b5c5ff1593f6dd63b9dde12730f3748f0e5841 GIT binary patch literal 861 zcmYjQ&5qMB5Vn&hP1+Q(ELz}#asiP`yWG)gR|^tCq6ZL{2xNI{ZwNR)*e=kjy|BCm zLfRgA6LWUUjXA*e}kES1F;?pbZ{PS(0imF!;AjP+hF+&tjJom$AS{?p|!7Lhz(Zo z-5LIb-z5wI`f$1o9n^HWm#WvzT(GpFZK|7;rVjG*>fnGX#idT~+-n#6nKC|HCUDto zC@rzlx5^G<28ZoJb7h$@JLI&H6$RTBs8nGRNRyL!DRfyY6N+X=g)%ou*PBS*N>YgC zgbGpvdnP7|o^l&DSB_kjvZ)_Q*+{dytq2~PaK D&x-ck literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_chatcompletions.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_chatcompletions.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a035941511debe5f9866ec7b43c724baab5e21e3 GIT binary patch literal 38744 zcmeHwd2k%pnP1OLV=$N=+$ZnGK>|a90C-9~B}ybE0um^rIVQcZI2;0lLj-1^o`ED# z_AYJtpb~mb#id%Tty<#`Rf~}d zNqSw*bvldIhr=c2#y(=~c|^>#FXo(QBBwsH?WKPOs}+qpx9h zf7ja1b^1DHR=U=AZqPS$Zqzq+Zqhe(s=C@)uh%obKvzR&qu$urq&IbL);D)<(YJIq z>&=}ldJFR}?h1Bp)winBr;B?&$s54F_nfiK+;oqSzsLRUyH=@{td*XV_gGH+uD9JI zjSrezY0li*Kcw8sa^}`?kKA&%Pq{MmbvJU%oYy8!3vOy{Sgi9}>Dij+3w^f(odk31e388Ljv$76kz1=1+BGYqlV z1%|IDnixaDqedM!@dzS`YVLD#JUKR=w6Zp`oO_}Jv8qr~)kMvuQR+#l7A(!pqq8X6T0 zC&nUF%ktw#x{if=j%m-F?ADHH;S)#uT=sJ!6~p|BB)RTiWS@7mCRDTA zrO~tEi+Ms)PZ1Ai-+S5UNOr9(vd0eK|qQX^seFILIu5R zxp@ypZ@S_GEv7+w!qvYGHLBI>de>V{QqM+ivhNKNFVg`y_HUA;UX}ayG;ouBZ;*QH zxQ*AH=NUnH9I=~tIQyO}_DPpkr>$90Dc5D>le3hef<>i68|N#X9Sfyeqpe+0sg~1r zXd&}jRJ!G~-J;ki<=VV;^76d2b#3x?uO!6^dz*PV9I<&faxdH)q);J`&F@i{Ru%Jx z3VOCW0$RqHuXj6l4f%MCSdq3qJ{|J!e-a$}J)R9X6g}t2zm3PV?+uc+A*A35-gxHg zyWM-Nw-s&Eeeai6o+mFeFXgg$dQoij%chSePdnN>=ksazTHcUFysh$v^z)FK+E&0g zFrd|I4WU4Xi`j}-z>mc|Eq*6{JmiY`@LzPuHTK=yoTvznUJ^OvYwnpvGL?I<*kq4JZ-ZzqLr?IgGza8 z@Vm)DW4D&%oU%|^&rZ%S_WeT}@wR*nN4(}0ao2KY@iN{=f~%G@i;Gy`rslldB<88S zAqC&%4f)$0@p5uz&Jk}n=Unl|`1gIpYgrLbF6SN2Zo}1#c#C3#M_cmplizyR9f*B>i)8cpQour(A z6l!0@tl(o`|9@Fl3s<0px1YISx9`XFO;o(Kq{{ee_$Aezyt!dx!4f|2)^}}|lxCy-; z_<+5>^Fgok*0k@xlU{$+&YzoNC0bSdPeMgcIb?JF-(AKyzANO#IKKPLt$#q{xc!#1 z$C{H%wY}Oyp;Bvfu3Ew9T*XIaez!;GvEjigpY(*kd-@^IfGha$#drOuhoUhxITTe# zNlvcn(d4)uOQ;bwQwna$-qja}lSAs*NMsn1AYfPf;<02jmTdK(8;mByx+T*;rzVEt z<0Jj*m>%yR?~5Y21O)6OQS}^`#Ak@~&#`onC<6H-*?~xsf6mM~3GhPXsYYV`NckLt z6waw*@v-rdNHW^54(RbwKh>#E;2g~f6(GT2w9Ri6MG}eOL^2Xf8U;^AM@Hh)ZbR1N zBT=K!s*O%U5kpQS<73kvqhL7JAAQLv9zgLDL#SgU0o>x@{oX|Lf%>V$SVtM&`1$9eeMz`8{3c-(u=-^n4pfbA65>z>!(o?+IGpk{N!0t(uI_1RT1GXa zBLjvU(E*2eFTBX^GF2i$LPu3qFBwm^hZ0de(XNlS_kl~=_}p|{LC3dpZ-NtdLB(#p%qZ|+bXUh#MVL1krmd>Vk+cp@swka)jfa4flf zcef!;7aEm`i9|9w8Xk@%lKMEqvCzcBF~Cqp3G0Dyf`do}Nnl>+#5hB#!U z9||Tin&*?qgK~f9eP{0bsNcpW_96FYR+qcQ)#gZ*kv;lhl{IpXZy8DAp>z)?knqD) z*n6~#9vz74Q9KQi^CQvla6eKQPOt|C>^1FiunciaMsuLi^daivdY!;e-A9B5XPq!l zqY%$@B$?E8$l4^mmYj-cP%?+yfOCA(t0+h{1@Rp{p&fbbsbfd=XUL5nP9yN>*hKjJ z`0xmDW{j)m$YuE9%0{|~Zq$X%IT8cVm>wP+iJ!-iAB&7ejiPWOLNy%Ijglj5!~n>A zOxNSOQ5hbH>!XomIQkMM;TVITF%__ZNAIPK%flEWM_8gX_D1w5dy;g>cO=H$hD;2s zlQhN%I9W>!U}jhAgl8FL*^M2nHI&hK>_T*cjlzZpkB;FrCn23)^YtA>K0%QcY6SC9 z*kxMG((iHl>~vaoJ1q}6EqhIinZQ1?eND@b9nNr;#q`>%_h*%(!x`2phZ)w&&9pq6 zkwJ$}U#$t^2h4{#u1EDlluzR_W->r|FKJt-z=`2 ztiQJRJG;`wk6b!_Q(3d1R4tU1FO*a*l&TBmwO4&NOE!Gl_5JeHC$x0QsdtJC1KvwV ze^ucssJiWy3d-i?+O%9dTemZ{`&jCUXJ_TJHv?<0Ri^{(^MQToz`pM^&jcR3bZkN0 zJg@Fdt2-Cgs|zJ%H*1@|_6N8x)T~`7ue@2de!gsTx@`09QmKB|ZOK>fy({_FA)oiE zq(IHp{x_bUQMRV!tv8gd^K!;7EpNTDU$|x3Oy?W&)~o%_5DuFN@y6565ZpE=Z%yUO z5tIE_Uw-xFl&oIab@}BPWgSeD&oh!%)Op$Z&B$s>StqQfVU8jRw{(WhbiN_0my^yA z+=i%^Up76QNy9w(ya+E02#wbLqYJNHn5us*mXZ%%*?%=QyXC-)auAj|<>0)W@k>(( z;pVW-$p^2-gokOEl@B5*;fT<}ir}e5iC>owT8%cj%HerAvx?3!Oe7-HKQ=k8RlcU!_0$-m@wRs+kU2$es%w=`%`lBywZ?X8eo`;Fp^f9 z=i$nJGjelEX%JS^Fef)(-YwkHwv9Z{P88P}5$36g2rmpb|tfpa3ZlOY0LDII3JWvSoa7KiA3J;Z?s%gFS%7wnWR+c`C&DMTJ01 zWF)ZcLRb;05a+YffYmI-yrN>Tp^T53#L)!TsR{7VEIW4*nzB5a6%p21x#it$7?FRI zpx2F89xES}TyIsmMh?kuRX-PmCF4Vo-gRD~55Z^fR(-bW{wzJyu;2^}&8k9G5Z466 zLNl=-4S8BuFTG~5ODoP;XhvUarhAE2iiKuk;Td>exBD&t=c`7!|^Yk|*N zoa8zz-KZyXIgM~pcl&?%_|h>wHIcQ z5C~WNMV={J;ImF!zoI@Qc-Ss~&OYoF%i?(Xd5+8Cv_-Mer|TVg=H>taKx!NAdwuLS z%msJbt&y{2jvlaLqdpt*)<I z5(=BAaxEJBJ6;TH(e0*>2Ult8ig;x?e~axNi~BoP%9eZ--LuzoEdPmp@Lu2cpx3h=Fj(;p@KNdY{fkR?A9}s@ zL9Z`v*WZ4x5Bjl;ps;(AEhE@|@kgs#MgZs@=mJo#k#i~moY6?XKR%$w2;mq}&skvI zxmI;~YYY7O*A~!ffy@O6*5*O19*F44HouN{J?UamcN|M1BC!jJpj)T!aRVSfD=1qT zpsR4w>xpb8vV{okE9%ziRXRiRLSJ9sycYzo@1>Xz5uq_s0PYM;U|@=I_#TjgRi1p9 z_zJxLY)UJ@xZ0);FtP}d0T5+lG%(M}ZI^+{S#D|D29K0tg(j2+*jq+KSm7*Cn#Or$ zBZGz-r|MHO2(Y1f+4{}MjRY8Rt7({%8wnt>f~0L5d7$aY!x<6gsd^D!7;eao0wDQ^ z4HW@3dAHF4nfT6aalNIu&Na$!1ME%Vg>koBPc2BLOE)CX+~hz z9_tQpr3~N-&5i8pspBU5uGKMcr7Qzn@rJxTYwa-Hgnii?xY^!!jr}%j;*+LmApXy| zV9f$o^1vh{f=A1(JM*eTG_bz6f#=ebQ)tWUcL2edMgTh(ddbVW0FMaa5^qOdN+$BQ zV{>Om3b|rKz_uPj;+2$g9R%}sJ~>Mn62J#91`|%&vAAFii2l~cH$JG2lkIZBf| zq<~EFhO|FrH(KC|g5Bk6NU;PM4#>limJr+9)xign5U}u`f?%->f(rmEw1awVIyYx( z{sJTuEADOQsfCJj?4G{NtIEI??C@SuACf57?v0#%*eitey!<@JWpUb~*yz&+N1i+} zUP;`YaDvu&-|N%CvszVs7R5$=8uQjiaGus=3B*cl(997#HwOt}Juk7zL1PQ|;GE5j zb4neYQ`);z)Y@&DV%S~H8{Yy^wtPIe2m6cf_c$d)o4iGqHBO0G;DKiBC|yaZmh7jZ(Jcixm&_nPW<<7_(AY%!eK$5mDBK9`k&_9&3BhV;@?NJ)G5Du%gH057=Yv4|?pw z>9N5wh_b!iCz&X_|n{1 zRr{@daFH==nd--WG@bVE#AE%~=w^$evlZV#9FDyx*n~F#U4B#;KNiuOTh>%-GZYJ9 z?uo;8fW9G0k2VjqWB=CpDDBH?^It5atpKz&%{O4_;xl~x@oJoWGuSpG`ZiyVe4Op&U^`r_eONi=<7~ImJvPfw z*V+PbM29?m#}X2G!33F+Hs~aJ&`H+tyAYF8dts|1?Q9wD*Z+h{@lB9m6%&gaMWWX~ zxP<(lQsP+>awZoisecZ+xFr_HcG%oI82Q+sPa{4F@e?kGV7$DIws#cQ-Yj1;dGXty zN|!(KPN7`xy>#L|zbQ0t`q7CW9Z366e?$f6RSyK`tJ!*C3(keK{NRjoC?y}7SFrUU z5!JUPVGy{AZev_UbcQS zax0U6+b-~$lUqqzZn>px8$3j4#GHcQJ|iNmvvMoTa(!A^kDWzRen!%c?LITK`)9q_ z7i1c+C5W=L_8?)u5x23uLRM(Robro6!Z0guWm!TP521Y9)TbD6VghV02Bhj4qWz^Y z0V_0N0-wr?2rJ^gTkjA;Z|L3WdP{Znw##qTe+u$}Od&L_hi5`)83Q0+w_iu18CrAi zBC)XM<_fK1F7nYlYb8wvbcnT?5?Qx|&zZtp0&FOduCi6mauF;< z%+>8u$k$tfHegpz88_K?T8qA~2y6JOGsMhAS~dOgR#2tWMW?Euh(ZXF`)}gYy@IEc zYi|HrvW|(6*Mtz)@p-#;X|cE6PE3eQ;#oVN9HMWuq!4p*2?7&)*s+9=nU^TCg+)>KheIZ1Hc|!_e zl~!-HO+ahV8ihj8^4{gQO)fD{H3SEwhN%o|W{b5Zt1U{YE!YZ6hq$qSIjymo33#^r zy=aYQY7NcDXp7Y{!3Qk^K5@3pR;y(yAGC~UnQa+7)B2!g&@$VpP2#`o6+mj%So<%Q zJ=V5`O6*itGwN#XE1LaC=x@)iIp<`%JX|mwsI8Hc2Y}Ae#T_A!eRv41- z@d6RRb3WJ$G+Fl!76g69!%RAvsdOzIqoZqB3)r&qqg0p2h1$Htt?wbjjD>L|4#z?< z*BzP!Uza+t*mndb^BR=cn)YdzPG7&c?ZYjB4wY0>MUF2Fm1@JuaM(Dq+9I{_B>n=) z-l5fV(Dt^q_43iLblrhvacRFnrKO3QtH3)`W|#Ps+f;{VuEqaTgaX7trDioSS430m zLl2%=1Fxxw&v<0jNJq)|B9QSvJOp4!hSTZKTH~{#-SW5Yy`sL=f@)>mQQIoJTR;$k|V>h zXe=HM>;Ie*_;V1WIt*rjLaDwmX}}sjKaTo^!$FtX1k{CA1C(X+i2f~j5}zc9C4T)S z={>irX!~8SyQuD62}J)hIO}hLWbiaG5-!&r0+g~ixQaeZXk zZSv>8N3aA{PBot`)|bY?RkV|MqW&FHyF(lCFG)A+)=!?EI)ClZcebUr9-pmyVy5oI z<)c?l++1J(%@bcgF*Q1~em5Sur&;HsNdF13SfRVYpj0&0;UY!2C{pq$RvKb#<~bNz+~3L*{n z&~W$h=_36}ig|)aCy_29PZ4<<1R6GRKA7H3Mz1;E>coBk;wlJ;gQi5TzYZ84J?x3` zktAzNY(LT{nGi^ZQb8yY)IUi+KLwe#5`yYRo)G*|iBlAUf@SEygT_KVNoBye^I+KzpK2j|l3(%JD4B8Fe5i^DHH! znCKnTV``L|06AFQ)Wl|#m`w(yqU@l%U}ZM`Sc5clT7b;9H)U_md4W7WLnKLLoXCqr zULrC<lyDB|axx^UK{TZ$aIA63E>$Pr;sd zBoGcU2Gjnd%Yhf)QM{yT?wt~;vT>%O=~CB^D>qKQJX5(17*=4*wbS2!DjoR5rDMM+ zs+(%MUetVT_d@OZ`P$ZWZ7Y<`EmYLpT-WlAM;0oo7S?WDsIFZoufAEO&Q}G~Rl$YI zy4#gf-S*p(zs`SG@>lpTd+&NAf6MJUDNu3s)McpiT4VmM%WE$0y?RR6O~V|y3b%BI z&2+vYuW`ge2!~BXMB+ayuA493lrG+M>G*n(>A{` zoXXUHcKMMvhE1Jivrun_cD1#m!3;O<4I6Z!Icy>*0#VVoGxc8E-#n30w$IDfZ${n@ zU0rXUFdb%@O?V1N?hl8wj1;m$z>1j2BUJ&VaZM?C$G3`7%8q&2`pw8YQcBaeicE)D zW)q&mk^93TO(|rBfE6)WCQy?G&2$}aPNbBMdD;5S$Q@AA_U44?Fw1PhQ#f*eIHW0s ztPrry$sO;u;}K>L@QtG8zfI6Y^U9G8XC>DU)E&>ZdEP2_J-f~OR)tLFn%-)7yuHl@ z%iC@KXAc#={jd)%86O=22z|5^Kxl=z8wbh^2m+NyD;Ok7svzh)$ftQY@Ml4yG9b|c z;pWZ&Gd&E<^zgSfz@Xw?PxFTGMFlXkAQLd%=K`2X=K#_xc-BQbdu%w=0cJuOS;%WE zLhGUZMOhbtt_;k~P)Ih8SG=*8kS_js9aJgg=?P>-hgB=4BH`3QADub~z*C$#$iSW@ zp;WxPmar2COP%L$Ip9|z^tAJJf|q$i+9yMm3Fr&ZuzD4Bf`1MhRLlR39C6v)#f!d ztNUQhrC5%Of7pH&J9bX4TYM5L1njFhOV!RNXDRKyXrgrBM+bN2ES((-rCQBds(DJ~ z=rL=o5~U+>!j7G@bcRg6B0CjPD1GUziKVcY;bxwUfPUx%^j*ojAyEOssPRO3A5 zUkr_idMt;Vns{mWNDOYmVh?L)M1W3^zMmuf*x!I>w(vadJ5HPNa#+?Lzt|oYyZ%e@ z%O>X%hmSj# zNm*5IH9qJqoFe>T^cG3~vaVQ3yYzv2tLZ^+eHgu^ZH_7SspF@8IPHzZ!MNhKGQ|Id zuYxVut9id?jaD-!N#f%^f6SNl{VR)ei0RO3 zN+B>NyuwTRza;W!xytnoDi^)rsX)C2*_M=Bkgfh5$X|c}LuKWmk2MoRHL-3{>dd*QVTrm4 z7A!y=^%NycEU2eJy7lX1oFS4qG?*CB|A7ZJjXp;qShQI(9V0Ria!UU(Xt#co9Ns2E z&>RE$^#4Yt|4!uRM509g2E-_~hP*I4>dV|@_zUtqN#q#{0%dTtVH7|ceaG-UgIkx_ z!ROpANI2md#KW2f0VFa+5P;qGCDLFAUS;e6T&$I^ZHf5iypWLqa$GV7JHsj?1`fgs zM=;9k;qXQ0(Rlz4&EwKwu#Zd2JUY)jM9wH2iX><`L(n=%?i95lA(7@ma&CrCUu-x+ zv(yRX6iLSQ-psW>kJAkEY_QhU1b9!J@co|9NG2O#{32Pe61hU;%S66HT<2(MP@xq%nURb|nzJ71I ze(!?Xlm{=Y1zyk#*K#o}thplrX?~L8&oQO>JyTEA1({^V=uCe=4=B6DH8K(m5|%zw2}%%CIo!Wn2YZI19LHx#^H->jSySvV$_?KHqx7r zXzRmjFGj-lVlVTsN%~SSn)3$63A&hj^dbr!%pclXuEW;H7`v7o9w&=h>MQI${>FyP z=wd;r^`=62tWlE@hQFsI%y|wtt)(NloJ{oTYc{HM5FzWEp7=O`Jr@%tth+v#S+>fJnBQ%o_$Jr^1OcgELcFz{X zusJL1<<9UOG*?O<)@w1N;U^{PXlnDgT&}mJhQF4iFGzDz@NKE)=ThLmO2uzW>whX8 zeOr3sZK><7+v9To$|t$@-*tOol0fd36@o}0zbcblyKy3;+xO*lpIbNY+MIT6UT_tf zma4R?>W;hB6};n-+|@+3+?HXzeb%*9gyzf$u--nkWQ1+5c4rQ--u|>5!7RlA*A5ou Xwi~9~pUL*#;c8*t@3>)NZ@&K@_yM6K literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_chatcompletions.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_chatcompletions.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..165b9bf8a6cd8450e0c8f3a2bd55a9935fa8e5a1 GIT binary patch literal 17478 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 YN~vr#fw=0>exZAWjwh_*oeArI0Nf!~7ytkO literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_chatcompletions_converter.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_chatcompletions_converter.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d96f09421490c70b2334f51a3db1ccdbd805760d GIT binary patch literal 70801 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 b3e$yqtCQ6|g{i_`{LK{hPB;^|SIzuCTckS8 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_chatcompletions_get_response.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_chatcompletions_get_response.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c1a130fd2b31abe1fc6ecbf527b62a1d88f674d GIT binary patch literal 24226 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_chatcompletions_stream.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_chatcompletions_stream.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c57882eb5b0817747d0b51bbd6ca8852cda93b77 GIT binary patch literal 13750 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 NPv}EeB>gJU{{!y_Z?OOX literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_responses_converter.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_responses_converter.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ede5c418c34fca53daa70a4b8c8d2369e0a405fe GIT binary patch literal 44041 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_openai_responses_converter.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_openai_responses_converter.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de9f045c9dd692798f877d368e3959542cc7cb47 GIT binary patch literal 19318 zcmds9X^b4lb?$5KnLTC?-qeWJ!I8-2OfN2?9IL9{C3oGU z*p`wwi51fa635vXloBYWqZp3kI1XYPN`4rL5g3Ah1VO{dj|4EF{4x^%2=qhF_g;6; z^bChuidrf_W-&F@UDx+sz3P7N)vK-Ka%l~pd*3kJVHujPHDaZ>?w2G=36z-iWFu8d@ja`aZe&Us&d2K6My`}=43&l&!=>TI zNNJ=oS{iNSOZmoFX{<3`8gERLCK{8a$;PJACTE)tf^_~4>xO0!LgO23bQR)aOUm8%mc0FvMn83&J?Qkic$q( z&(@l@DAbzinaZ5qbktO%CG5JIYAxE$O06tywb*JZTg^-roOw@FrpUHYiOr?4j01LBA&8kytH49BJPN657>xw*6!(7Of05&Fr;7t(^=4_`k zsby4dRGb-1Bv{>4g&BX=r>0LmQ-0*+M;@A<;ro3~L|cAjssfr6`mnO)LsGV+mwVPe z6RDUBMS{iLOU(Q22+;%K;;ji)S3&pEX{#gyqC8#$VQ!HT5XU6OQ(>E}up&Nx7zha^zj)`Bx8q zjhA&1>$Lm~x5Pd9Ic|v@TE;|tEjFpOEqeC3R!v7f=A=*~?Z#dPy3goBx3so~FZ~*A zGr*tf;uooQ+%??zS(Liw1zpXcy(Q92MTSaxsX&c9qXFUzy4=oq)}e&zxN%UFWxNS` zCc6p7n+)(K2jK-~vmFmG4@WQ$_h2^E__~;X6v6z)`-XWWf_bD5^FOYG*-)z;%!YjN zeZxE&!JHSPHN6M7sXnz1Zc`oX;5Oy4_YL=05ALx(+?Kj?UEKc}!Trnk4fl8t?(xfT z|6(27mU^Xw+mf%nZ@4ESxF^Mg*tBN!NJ>n7V;$r%_3;k!n4G=-$b}9aaP#U9@vP?^p1quSaWl5<(wT80m%Xk$+tD}B!b5~`_Y3#?SEmU%R-HH1N^rY@M(Wa~~Kue(kr!VQAQBy^I^FRH1i-VjiiZttcBhl{G1Zj+)xS9cbb9L5_@l6rqB z=9!Bt`zbj{$ze*4QgY{8!_bqVGjpQ@7{Rj^R}Sp=EMmtc{Vn78@#_HcxKHCiPA|YsZ^+G!kKs(?i!zuDCnjtR}(JMWRv~VxqRV>Sk(q69F=e+T1pox^1 zlpWqJWg08xP^0jYVXkP%n+S$QYG*OSob<*9@SfT0rSDx_AycGWk=|&z3LO?Y=%Sm9}JTFyNm06p5@P_W=*?|V^ zD??*}PFsf6P_Hy$i?o`>l$W6q{E0jey*L!TI1;_M!@uw~6cw9`5WZ+TU|^ z@RBC)0h``>Vjrn%YM*TEt3o5(=d0O!*)~#p!JGM3b06DkoR<5IuX4ayTTU&mc)OXE z1DIV7*LFEj_%XI3ana(h>))Y2<9j;(jJ$5>#@q5ZN_td5s!PT&tAbC&|G%h$PR7Zy z8mqYmexU-gIvaQ6Q1pJx4{%Zeb&;ZycdrVHbg0ClzkB&{pJB!EF@D@54@EXDGLc&J zB1kw{uC>UukVmZybjDceOaw}2qEG2G ze1-E7O$>{X7d0{BdQDz9vRRzC(eb zb~BbCU@;^qLrDjDP>>g6ky_JK%T05wk5Vnlqt=)hkJNIwuVJqBJokk>YLUvDbYU)t zi8TxKWdfSOQD5teEdY(b^;K$ifIrrtJ}RVMluuhQ(-La+u0j zg?F;aaRZgGoE17;Fnv|n9RU|iUlpc2xKLp91B@rWKMJaDWRoK=c8L$TdB!^y;2n$L z?U+a`0iyxtn!`U&A9$-H36encgMJ`y4_D6y>VZw3YImt@im!m7Gom_MQdqQuu7)0EHI|PCwG;BYe82 zaLz6)EWy)7Wo@oTjy9hMfm$v{ zpdP$?g?E70aeb{^<}sGsOvx61)boMn?%M)T;osR7xbmn2?1@7~6)_VcmI5jL5^gal z-I$&a)LzXT1_~Pu2YYla)YVLU@5NW`L}F_tU1%la!8#a0;bVmRY4Pm%Vhk4A=h>JkVt?TP{DhRH>7)jVlw5P;a=5uA41~HELBZ%M*ai+HE8` zhI?Kz9O-GI%tDgZle#>K%<7SdF$H_fpGRc61{}`;Y?(cwk%yV^(lrIk0ETPT2Ktfiea?$>&CCNZRUuT)YJN_`tx`$kXiaOLoT4S=kZ7jf_5gjv6drKl=fR;u$aPn z$!Kd1_6|gRPN#i=m&~^A7_=)YGAJ`(++;-#b#smh*L*<}LoexVi}p02I9YQR z_7P$WaW^&xQwXh~KrSTO2`A~obb{HFbdxZ-;$lRMI_ZlU-rcz1WH06%`0?deksG2M zlp`{X!W!SE8nh1}zfUzNhZ=d#!xV!764v|~)pVd<$)8gxgds5v&@a%>I_1zdz7(BWbloXi4+KkH*|0_H>pCfu-_afg8mMjgyanmNKX z0$e+&gmLW*XA!twC0q^+E#UeVVW2!_a%Wd6Q0t$mR)GFrs8$bpNCw{ndj5q0A#ULL z9~A|0ql+C1P?Qg-$VaG1fQk&hBkf@_|6bIHo(DD1GA3?Ekk151Zep>`p%1+ z-2`a4g-X~oCM+;*#H}5g$P^{CL=%~!RKus~R+utm8c&F#xXqnlRJi~Z%%l!)$J+^U zd(Y^Wy5*`z|DVq2mNc$$^gVr}|LIkZZmH$S=%2dA(f9U@9#gxndh}mLMt}R-Mt8== zl-Spm1jxdqJGn5?PK*6mZE2UBVQ{M~Otv?vtq8Jh#?+8mKCUb&F@B)-F-!Kdwx9@;kb(f02cgVr(8cIN=v;;PB$h_<*q&xo#` zwzztYTN7<@`CV?kA+%vG@9LY&E$$Yc%ey1=8Vj3y=uN1f0z*jeJsoevJ`t z0pf(5XT->3HjZ(A&|Dsmw5Hk5k)sl==?OSQd9*&x`D>p`pSBY{v~B63EvdeVF+$ot zIDodKx|`7vZAtkcBf5Ipl8R>Er!6Tz!>u=jHq7O{eRH|h-O6)$Uzc8Se|sBf+7_(J z6hDtbY9F8yUX>4a<{|}GEaBY>YEy8<(p(^qx$r3;Fq2$o8`j%H{Epc!9u|+d+xcm} zBUo=cuDIT|1(+X=U_RM{IjwHP2=9!UwVwd<&)+x9k3}#)-iP_$*1?=stoCXB%)sdKtDxpTs-3f`wNWt7k9ujkk z^8+MCJQZpE6>d$jF&X&^xA0?Qp5pwquYI4k5B1Qtt%tTO_S?I(Jw1T7tUAi*h_0MbjH@RL+;1OAU&ngtuCGBg+UNZAomuwxnII3FS=&0|ddFVa;jJqWIFH z;pMQ$F3aSbqaA8*%#Zq{eO~tBsoJVtC2FD{?8o}v!k-`$dJApSH;Z4;OBV7lj$efG zUy9jr5nIzmTqIt!N(noOzqm+>)EdIxL#mMkQy$rX@ zukoH8uCr)EJx_>9T;(FYY!aJsXUHD5<6AV;!Y&$_V7NDed!tly*4UzP*>;re5Ia$p z7fA^B4XgKG)3iOf)M%`Pfs&_bBYY`;up?B;S!wweu!hX$(^g@|az z>hya@I2Gyos?ZjVHwzHHH$MstKF$?j=|`LvHGox+By}zV_{j|b-0c~wBJMvsu8f@G zNF5C!nOMb_bPD1bn{GKL8;fTH(vSmbP*rJzi20WD$qo(r>PFDe?RgqL9@I(2|gD6Uo{1i&$r;(tWRmxLe@5GuQ`sfDC2zOdEBV#iK^oR;SlwXH}OJ+X>ec-dAwS>U_G7m>Ihcukp02-ow z7h_#gyd>rd!TaU&o!p8)LF94`Ka^jGJapPOLBEYg@^>idBNX0s8j@x)B=O`1km&b{ zN4-T)be{-*kYF78P<}lk44;V4p=uv$-k|s`+z%l?vH_6k{dje2ySowwu4U_7rRET? zHxeE7S$&sME{R2btn;h}-s>-IFgog!CYv@`-_vA&egNVmdZi6pbs>WK@eP>quwO#H zSaIgx6Y?(%KyC?Hnfp=%^+w1k^;sN~57KxKQSvYnFG=T55UIH$>HQ(;vFq>Ayg<1} zDEUoFK1<2(QgRa|Bna~Nka!lUz>-+(#hfL)vg8-3toyj&96#A}J$i>kHANd7NyOEq zP5KU*<7VERG)IujBQJj+#dw1zLxq^~!O;x$)>Dy;<$tvf%; zmA9n4R2V2BU1xN)yQ;zza3N+?$Gj%`h7+zuL zwA*JP*fYfrCEi+Ngnpb7+Kuely~S>)EaB#x782~$K2F`EmA~d4keYp3t1eR}tzt&DB1UrIL0;t?>U2yDw;ms)cv$i?w=sr zPk8DW4o@MTSDyGfl``EYu8r=nBS}jZTh@!$dCR)bQpA3>AB@fRwenKDKxqBSPY`MM zQgR<9#6jSD?mFY^H(b;0jhIi8lq=7tyoBP{h)9}Axr_vqHm@_KJ@z)m4NwGpFbzq{ z^DL0{1TfBZ1~b6g-x$u*0$GRi-y)_n=lL~xo-RLv#LLjq4p`C`l;Y(?O(Do=rF^a? zpe49ee<-{+Z^NhI$Tn_ng!Ir8X&oB=8dZE6b|Z%gv#;}nkv9lJlbUUYGLs;ADbF^& zjIg2V*B1GtfxcS5&PXDorQ3K}#Q5zmze0StK_X5<7ubj4C(+Lgr1GYSTlNpIp4CB`qcnXOe$K z732OH5I7MkH5@3H8QcWqZ&R&5r{pgvAw8)C#Ra0sE0RHel}c<%4kL+%v)tjw)yz}b zAxh@@D%|Nu+K_v&8*U^2j4lNwvy^;;k_shfDB*|GH!0T}CL{ln?sfG+el6erF5X1c zySg8VmKZS%J^8D+l{H6h&gxlxNPkWLp7}-N$HoZaV))M(*MElCm@xGZaPXCx)#VS+ zOO?6`Kbw#to<4xDnv8`JGs5(vWn790K$Oga2^B;~+zdt0;AHQDh4`2l4nD*Y^8&B5 z`Vqy|IEjl%j(B;>NnOP821Kcpki!uM-Y0-tu~S1*4ch&aF4YM30TDxkJRM+2c51Fs zO^0G*UZqmXVb1}lV^PyW%@prO#hfheB6)CHQ)Cd+5O)*UCG+ES zK8rTPBI_oxzl%5v#5mBo$VUR?IC#)Oo&{1|p^i;X3HW_73fa`USOia0CLa-=fx(2kkW18JCVm z?C8`s)lRBSr_e8$ayQrT_jY!09#J)7=UNgR851{%f{W9&$d3mcL4?d;j({tp?R>zM z8+*7iHjpcpTEKWAS9T5H3LO>+xnjznGP=Q>4?q;sD!1W^F+B|1@u#Nffdm2@4)*{l~AU!3dKzv z>op;6Mnsbzk2D#~7@{|NXADn`G>olVSO0)FpPr?EOc9vih1Qt``adBY8Xvsm#Gc;& z7=o>{;HUxqy#ylgDf+$^9*@P|ThE@^EdD(N?|Gq1R1Lc9J?+}n8-hcxrG#2qTx>~a zb(sF!LmlUDgBv(+Nt%u~7T_&M)ByP%l*m7%gkR?Tvg9}VAK~8go8)9p_6^ z0mL)IW|t|D;KlfCWic6X7fcXola$b#l8+R~0&*Oie`vMmoG*L@eESHiE}-DEV7jBt>7$r^F;n17Ju1eqTykHi|^1r;}I9L{CU2VWUo27+R!dB@)Tr zM>Vwy7Y6!~v?$R2Sb=0HuwX!k7zl~xeXX`OD8cg&pSDd(@-3qtd8MyixYWM||{44XJ^*`;>n)Kmzoe=TvZ1AqsWNr$VE( zq?Wq9r|L$-Buw4DQ<2enQa{>28mPbKRO4t9X&T)?Hc-F+RP$&HX#sbjO(GjNN*$6K zY?D;wslcFzm6NopA^6rtztzIGx~CNJZIc>?Z=2N!z%6P$z;?9(;8wK}U{q}axJ}&v zuw%*Gz%X@SxlP$#DdVTWA z_|eB7ABiVVoIL(S^3=$e^#=Owi*tqiT;WNQ%I7l#0yfYTk7drKzB-*FBZTCL9!O{z z=ETI~bJ=W$OtP9Wh|Pgjz4o_mY>o*R|ERiD_pmD~O#q%fahbc!cZZaa28}pbSHkt6 z@u^eFb#Qn31xcFl@i=imcbRV>yN9Qu%Ddr%>Q#Lcv=d>dc*qRPT^-V(;d~h0=}opg z>Ub#g4JCE)Jb1}c&9+z(Gc2e{X;rNlWsoBi@!|J)*+)O&Vs}P03 zkcpp$sw7D!Pm)tJxihJm zB$7)HB(>Dp45IbAqcoN2T$arrjXd?t*YY!|Y^nfXbPp6dChOi5&QJtLjjmvptOR|` z!%DSB!$~6wkAAEN=OV3#%=S#e)Yn5}Yw72bd6FyS3Q&0i)q_>jD703-JAYnprnQEW zOlOmY%-1+^$Tr9Xn?d_$02ifqL-jxWYAMuyapc|Bt>xBzms|I};azAwRFs>S!`l{> zZAJN+C1qP#eg=NU?FcJ=5TF<~0>Gz|-OB=BmY?|@X7pNrDb#augs1#8P5J4v{B&_U z!irxJ@R+iJFWJ2ezaT$-4JsnpW@=+WlCDMJ%a5d|=%Pv7s*8AbNKeU6c~$o=_@a7< zZ%U4NbuUpSdO zJ5990%yc%B%_Wm$Go}I9R2BvV?ExAD+mK9Z=$lNY3I#HKW)7ML0%H+K2ZAVq2ROij zT&n@_g0vh6edkdY20z;>iHieWjyA)TZ(okgg$NcVm?S5#XVE7v78j0@U?q26nCN08 zmY}#;VyrP87bCF*#T^%y6^oIX$12MTpX%viWX5VROT=AO79;lr@L24bFyo6Qg@2XYjVdE^8QC9DVN}7O#d7fOf!!*9@>1vQtnT92V81kd|<#m zXtWV5=AT0PS!&`;ZLxH(8dB!;RD<0f$|CqwMGYl5!%(x1Hmc!i@i#II2VGguL0A3> zXe}I%6;G}|-d=$MZ!Teu8QP97@^<`!D;--?OZuTDYZG2GozGF5dO7{ox^?y5tv1R! zj((G#JP+P7DHv0$^|qCzm&=lLzdb5-jpv)zyg7>sRj9sM-OQ{it* zKOL{yPoqx#l-C~EUC~F=KebW9xh9P~Z?%cm}K|U_7^|8@t_fJcrd* zwJi}gMyT)h+dKc8)9*-!N!os}^pDIsQoxO9crl->KT)6X({}FX6oNGgI4cWftF^QTT6zn{H``imzvC@f|A|^q#DW%VzckLH$rm&AG8TLWaPojT-Ck+lG=_Oc(?2^o?HU#y8Ye^&F;01%U1S>@j0RSpCAQk(Nc&r8b zp}5|!re@|cm)uzAJ_P-?Hh@MV!;~&(QuCKwWG72$08;`b5Kjq6$yyy8X457B(U^I zdO&n9Q2u~wXNGEYCUdj-6v^my_U=G%OzVn;T6#SQ{+XHNvsx~j)F#hmW>dO%x{#UG zbj6Abvta4D**p!a*YXNcZ>)iA!6MNiLe)P^9xk*V~sGN`&qRLgp5Hf4Jm>eDf>h975Y}HU@|8Ou7JuLJ)E3{ z$k+f!Y^QAB9w3dnr7NnQo624Zm+Mb43(52 z>U{HfNjZE2k=GIYUj9(J?gd9t8G7?L#b4$48;Jh{_g!J3w$Lztbi|67M%XSImS&6j z7}z0-{g|iVkc&|ednN{wMi!|p`*+{)Kq>8oqDiv6eNv>O9PTZJdl@0Z2TI`s)VZjz z_mXlzP=c0l+22dk6j+B6f&$Q_m;H|w!^fF- z(c%^X0QuIO@Re*nfnM@I_OVB5*$V}VH2!ezyWc2>cbCGu7s5RkA6*9GM*2#TzH;Pn zDRTHN|3c*C#gog?9YuNPk3Fw=U#4r2SHz2qPSe@=mI*oP=>N9?m&mivnsb>eyo`@?kfGrK<|nla3MOeJfR_ z=Maa02JS&0)u$rLRsz(fVkxfmf^2+WR|=G{Joj~_oETP0V1L7BP12~?F2#+{ReAsW zy3+f)Qs`OthRl0kR|+k4Usp=!!npHJ*{yY@xFWOID8yAf9agCZM<^k<|GdNA38K&ZV)KY9lc>_xB-!F~h}BRGHnSyFNk!4U*U5gY?B z9~CxjRdhk*IEFoe;1q(#5S&JUB0BjZfLIgLlF&@nttBCkV>*9=AdUc=hNu8^8MOhr z7xfw%83#FJm~=V30pWFoEDH1b>F$X#`0G&mhPkpgILqMcVWUOr5Z;iaG(U zUDC-k*!+=Mh>4WfBEZYvS15dL6bhjJVFEZfK$!p#R0)RG=a7@SsERq#5}GFUvWN@l zHWFbNfQw+J6mI;%{P*U6y#19OFLy75V`~(>9RzP76EnyEz1S^;Ufo|^=rv7)a7)oJ z7ZOH7&K9V*Fc%VA&(3zJj^;vjkTHFnHwgKr8qTHEcGb)SXR3;)st9Msa<)Acj0cKv z#s;E$0yWF@u~^umqPgOMT%FB@9N3Cmmk}$?q0DHWJAua8+S6}5aL+2mZg*X??j#5Y=fHTn0>w4On^ok zE#tqj%mf?n_)H)SdU)ljn$s+s^Eec-V2#{p8G+8B>v&73t-?H~P2FTM&sqC8tfFGb z9|H4+&9^0<#qN?30T?O7EcOF*tDVV%N1*S`+pQ>unSH?Ue9L3gV9?Kv0!)K|`GhbC zxJ`=(%l3Bg0u?KD&)Hm#ZnXd{s40MD10PHQsMr>BGkNzU6rPMCz}p{>Dj~wLP=dHn{0F`W2rPYA-9e-CvYdw%HGkC1t1# zURf>fM_BQr_5}vA1YywJ0KH4-?qco*bQ_6?=$B;`_kx>HAYI(l_3GT~nOC#Lwu7(_ zT~#b?ZGXl45^e_DCDO|d+q_jJQuyGsw+m^k+}jmGW*%JCcUD!8Di6zd^(2a5CsiM1 zmugi19oN(f*1)!G`eC%wC%%e63d^Qnh?Z!+L?4S*)LFmoELu8 z%^D_{^9nHZQLwRvzj+WsP>sZlTbr%++3ajKJ=VNsB|e?eCdqUjPcb|0c@1EqRkyoP z=62fd0tsUX1(+)K+0#Riv#+in(Vfe8xj z1w49gYaHu|c=C0Kgd1<#B$pMIYa8EfYFXaey}YykMzMBHD7cR@j@3MTjsB{Z2b1WeesBeTRS-kX<5q<$v6rqG;Q9jK0cNg?H{s{iP z?UwvIoFaFbitZ|>$OU(tbtl}ML;lp9A{SgVFWH(b`$R0uwvcBX=gI^(%9UYoirnpZ zid=?M{t;)L#j(pXSCZESok=M{u z5>?FF-q=qYj;6#SV9p{8|G^n?01%7+q-Bdt8aY zjF8RmcJG-$dl>q}ks+&ia2u>g%*wofuIy)OM1?c6_g=ZDq-|1{#OI?ltZQf8QE4lT zoT&5#*VrouJzJ%-uIgdNF02+}N@5{m^|!UjFkgqq_~@aqDB4s}T{g)>bJ5dTu}yb| zv(Ci)c^bDMadrKyN%-G{L${wFdrP&EPR_G*T1P3a=1>sZh zr?qj`SX>|U89F`oyFmB{TfVntu_d+?cxc(*R`$nA{#e<+ujJoX9C~!ofAWS$3U-1T zxV8c6f48?0K4{(gmz!VSTyE_uwRXMsrPq@Sts{67)UK{qrr^COZ@*Q6-<1C``19af zBX8|n7&-+x35Ise{j9uqT=dkl`*qRKPoBYt?X?Y0Z( z`UV0VVB|{xVlF!5HJA=1W5X&&2b%RVurtpl)47b6E#RGE>AA^_alVzj0I}xbPwNE$ z+gBTQ{p@&Y!_dXCcUf2c^YcrAZOi`VvcIF`?^yIdfUVq74s3fTuh}D? z9XuMb6CDdpiGBpge=DLVB&YimbM<1P|2EijxK)qkK0!>o;&Hh35J^NIXX`Z6u8rqZ zu`4d@adet-y78e4?x>{Vsf)$z;zh;+@ z^gtV{O4bK0BUnqbd9r~-!0heW;P|s>E1CQIoRAb!u;|~qRaJVW3|PE`I3+rzY;B@8 z>rHq$2Cg0HUqc&*5(>0&?fA3nXk%gDagUDb03VO5_GV#GrgB?0xQsB!o2^h?WX(k5m`TgY{~|YI!~>Y?F-5N4%cKDHm}S4TzrRTH?FBfDYcIeah@rq?pg^bHZMjAw zc;P{~3(ckSn9sHtggG5#d=ahUn1}X1K#Jef>7yAki=iy~fqB?Ac@U?X~&# z*?hxVBSbcZ65(RfNt7~8D=)Z!+f*0<$zMS9i}0r%WipE3m6nxOZY9#SgfR4D&&xy&QR{6nSVN(pB7jyc9Wc#Y@pA7DI+Jbm9i0{vZMPy$>$Qr~wD$LF0eS zu~!_iHxMggvM?QRsRUsEXpd=NuU23W(6Co3u&nGiY}k){4w-!rVTHFR@R5(!0V_k} z(1a+s#*?hT(_*bOu0ipga?Fo_7*dU6W@k25=Cf#Md_0K0KoiygJ0nM#Cmxs@?A^{mCwKjeQ$ln$t##2er;GESRgZQ30(%e1k4P> zfx|$-tzz9PuIOTX6urR>R3#>ZcL)o57{M_BlrQSFvrABSirxd~T>Tgj*(FM*weUxj zE22yiZt2GYCJV&Q#-=e>%kA&RU0sa+!z)dc`)tfgzlN(p1&d6&`$;XgSGH@l+2Ol7D_Qf&gBF)qi6N*567_;+;8MD)tp~wpgNLS<7 zcHPhacL3T2^wuO(v3Ij(1A(BMWS%E*DV!b3ZnXCqBX#^H}3Ak$f?*cSKEzdmN?7tAe2~uygq70)SW+RWiRp$0t0 zMgUHhdeHbt=CaIIe;Z&~M?Z_Ma|mV;%ppJv6{?G;ib2Zp(oR)z6_f5i!0yODME(Y1 zpz2Y>M6P&TF4y~#^eyR|%5SCG_oVjsq+S0ZwSwb)Y2-tVBBIMe2wY?TVwAOXRbv4@TI_@H5739u5I+Vj=N}+{V#K_ B2~_|9 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_output_tool.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_output_tool.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fb7794ae70ffcba241a09a17cef401aaa83076a GIT binary patch literal 19614 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_responses.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_responses.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4729f3c120d5b5f5f417d64245e3aa18de337c25 GIT binary patch literal 2754 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 ie35Ud8ZULT$_S2$w-Hnrx39lX+O(?T}qL z7ZM3B`3HN*9`i%^6McmXe*uNZS2gx#9~Z`QO?7oob$wM|SBH&8)xq=ko8N|ilpN=G z8XSLMb8b71&`seu(vc!|65$I$x*NN`$L~T?bRFR&CBMuwUc8W0{0i%ZxSG`bT2lAx zJTAtKr0K&QzAVLyNy~4sUXI(zlE1|ILcE-;_$#be;?-o$Ut_%*uO}P+2J5x>T(aqJ zCg=V07}sS(HV<66C|fTI{soNNJYJH^7+;iEvH2o(Z3|$!KxLkI|tjQ zJP+HJye2R3%C@|CP>`487OWlE$X^HRF3T(McUijMINj|(DN|ixYhjvZCN!fg)qRY; zJLz86wM+LSJ%%azdSb>C^QDOryL!_0Rit$|j9v@d`eKsql7|Ob7T*oy7=BJUco01` zuomwOqm-2Ly)c#8U;wrH;LCKj1xBU&lQfMKLZ~12Y8!*u9)ZCo*3(9*8bNSvc{fW9 z+}e_g%tWQTDEHArx`+Q=A_DPc3L;4NfRf<#ZDFEmGu6V3EvPJxUOTS^57i26EdIK^ z^DUO?9hL0tW-{6V?9^!aI2fzUWM*$XH|>~68_hCMvkdCqc+V~mBNG@TAs7+ZV1$Iy zo|;JNxUO*jQR}o{0pbjR3XBZk0u;jlq-O-66o!s0zVwDdmR=SBsob~XhAk&~ZEPjf zdZbN=d#!`OkE_S_>!tx>Z?Vp>WbVI)DsU80sw_3P`^6kk`(yJ?fVrJ zY!aIwd;At9sBjs}aswkS1cNLio8}nB{uZU+1UY**<7_vK^{pPQ1*aPXGsvDqN{wWM zz?%5J>!}X8X8%`7~Z4%@V?09Q9MU1sG!3X5;U3_+m9;G*` z3+sNBTf=u~Ki`9k3nmNrl-b5SZvm5~0HiaJM2z_}BS;RCfB;tlQT75v`LlPYlrvR=Jz(;u#%N-ED>sXmr$ZHDK z!w4)dGLYQW8Fo|PbjG+kKR`JO+MT-kzHwi9QXHaVtvggJQ+QQ8be=f+Bg~Yh?x9dO zp_NHXU0E23H3x7PV5>~ML-AbvEKGIk{#cs|hF|ROZ*ZnNk3!u^vrgU?I+TO`R`nJd zMx;#V+I!=@>s@UjE1&)d%Q>OF9?-wgZU~6p|iV0 z4{aUO!NbWYHltMAQhr42MxY;MlUSl#j#V&>vxi|`3R?+uIEct>n|Ig?E||}i%9LH6 zdyk%u<1ppRi%VxKN2Np+cMxu#^&1p;jRMHSx^cRin@rbHip@_TLrFiyw|SQqRdG+K z&oD;2z+Ktr*A-h0aQ<=eL_xr<)Go|ns!wR+b3vY%UY-oV=&9%@g)4yjGtv!{EzfM~ zWBR6q#ym=gia74KvvHJ$qaHPD-Fq0Bkgo~u&Hnb8NqEoi?l~5^Y*d*%{g1_TFL1-R zb>^+^<@BXH$X}e9X2jQgko@^V+DG%PY^$?{YLe!(sm%;jAa4KM1oJzOt_N;sTtq%L xoaSRy;?u`%ap7i?$w?gDqDHPe5OsIkZHc<5yZH03<+aMK%1U{q>ioM_{tw`Dl*s@9 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_responses.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_responses.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8fba38c1d8b7ec4680cd2604175d1409098b0bc GIT binary patch literal 2245 zcmb7FOK)366rQ>F`fVqUlepM8oDHbam?)I~Mn0&CD&N z3M(qXrvD%dSmxibo&_6L`~@l=-UKKS_KlDUHG+zW^AM=)*w?zl@ zb7E605^jhs>|4gp^+R9u#X0!Si>qS8?QMzk*m*%*g!h7rcM0(? ziz|qCQFu?-;MyOAe86oh&a&LZW}0Vuh`E11+aCmW;|r;0aLI>7HloGdJWn3PNn(48 zmCp+^E6gJ!Q@eiJ_gHEjPvqIHyYf3TsM^Z?iOh^`pNwp4pT(KT$79>uUBr5nEy7uaM_i?DgNHp(+Y@2Ve1kssUnOi^pU41X@zUW)EW)P9I16oZTA@KJPC@U zmoP96pzgE8y;tfQJP)5YQN^u;tzn)?8$3nT6~TorgDg&^ZI|_op2bNaZEJ5TWGM@+ zy4W|Ox-Omo1`!A!*YCn`RpvjkF&DlFUbMgW4Lk4;*ny|E1j{_bi9M*GI#iV4fR{mG zo_a_?K*@C-#!qZk6WVc0;GNv-)euUUX}-p01l;TQ>`ah|^ipC+Oif4)f+of7l* zog^Q{iN3o{dm-o!p^x%sxB`G|`{Q_;6l#wB_$*Z0p?9@037F+Vl^pJ(s@abLqP}dkWEr>`XmW2^a+;vtSbUA z3|$h#8(6jc)`@Ej#EE5tirS>58cm~x@En#8;73uYuz-5(dQEl-s{aP4h+=(&V`qft z@#-se6aME-*PFgV5BwTptD8J>SJ`$JoJUT70kHpv)JS>`HUR_JwqZZxQvSjrGY>NJ zexL@!hoF&~j$Jv`+bFur1a-TGW%VHqlyZsl%&l4{hFc*kS$>S0CdS zG`Se=a-3~~|D%zpa|oucQbb-xJd@Cd<9wRMNi=UP^XvX8&YWT0UH4BYmOmUmBuc#D zaB#^sBPbc|T1HXXgX&LalZyVNww3%=p^ZYE3zw}e zd`%pt?NO|36&-GxO%zot?3Z18o4TGxi1Lq5JAt|0`?|N;YT4Qq-FerOTov6Pj=OK= z&2wFwI+?pMuAr_Cir}8msbkOG!1|pu7eylPQgt+)Z+q9hjoMbTTkE#iKmE=cjkSLP DYw8}7 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_responses_tracing.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_responses_tracing.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e83286c9db1778daa4d4f4997a595b867413ea01 GIT binary patch literal 22477 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 MkLa2RQD!Che;Qf;I{*Lx literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_responses_tracing.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_responses_tracing.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f2f4123b88ab51e3c9d2f2e2231b09c0c5d19b0 GIT binary patch literal 11136 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_result_cast.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_result_cast.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..694aa15aeddc5a1dc01572bb059f8cbb0ab8868e GIT binary patch literal 6161 zcmeGg-)kJldG>bi_nlw7&=1c?)yRB0FR9T1@=J+! z^^;+u#s-KQpHEDPE??TMCI$#Sq9&p4QBzPKEelHe2@c;>1V_ALuD~aLRcFTgR>`O; zAt!vLY?!tapRJp-2CG+XC$(5H^{Qso?ONS-BGjFBMxMJFsP2)u#+WLM zlW|gj-#D4`q#|GiH8d%x;$-wSqDVI#$u^h`a!97WWGGQ5TrrVT+%%T8ift@0Cr$Nb zjT)?Gnap4`3T&KLIF?qf>$F7mN)^XJT;}wet^+viiZLva`U9FggX~D2q0ha)%Zkm>@+Z9*_fc9w` z!(LI-ZJSmW>R=R2qbGnN?ZxUSR>%Dc1!1^!za=~I)NkH(ad0@GByJ~EbT}{RhRK8nc@w^+R|8zS+K{0;4BGgz`qJrOmKNEL>_6)l z0?5?VWN&Q2=b?sQO8&usBzwx=Y4e621A*ClO4d0W^a&rRPsrEjKupocR3n-2Ik1x% zR-VY(;trh51qeVhwjfEtMF3(+%X&cx$DLK8E{`F9==FC z>5%9@pik8I-~%;}`Iu^VCdwm7Ts@-p6yjbyNxVcnX~YPhW-y+Z62z2G%5mlB>fp3R zsZq3LOf;FyZdvteNnW<-M=~VnM&=17M~d)6XlhCkRtu*`Ybz59OZ>`f$Wt=5&if!} zc%ZDpGOa1TrnAJSqlkP#VM^jtdCHx7Sz(y{W0FlzAumWUYb#l-&SP}}s|l07j zo(4ha8LTjSrs!*^#U-pJx#YQ^LSPpTzM!OeZtrxr*^k@DNxTJ@1-K_@I(2&Beo)0b zN{tq+rJ7C+Ck4aWLcLP8D<)XBY5@k^UNm=QIjUB!S_^RZ!o1vxYfN7>kb={7g_{@h z@V6deGPR=B13m!a;=?wYv)pH=c3zjiBGx` zyPaHs!ew{Jqn!;Po)7f$ED55>&B8AU&a2!`u5WkZ(>7hHvQ|jy*hz`|8 zGP^B#|C;E{4cOlVu>BrjKY*n{G9c92*#^bd2FTB^rGXnM-bVS2^fPGeb0bBZ zw$gdGkz1cf8aW})vG$tZxYhabK_+EUkygjA>ZL#=FkiQ20}lhaZdMJ(#F zRO}3e%P!%^?@dMGhZfBtq;W{VfsBWQ`v&4}$^pJ97xa?5BTJUSOk0Du7C7490bD#c z*?Fh}ds0elO9LCyz?T;uNrNq3--K^TgPlf3MY)kB*yKNExBr6KYL@e{HVWF3oOyFswCOm6;hN*pAM!af+4gD}dHi=jkUFSa|m%$-lxP<(}>Yn4TL6w*r!6R;{h6D%Qn>q0Yhqisf zLp!|=Q(L#nU~sD}SL|CF1bKbQ-7bv24-@cpl>Hg1*8TE0-u=6ipP&5w%2s@6C)Twc zJGBvmGxsmo*0FWvxkwVj&myF2(2E`~jNfu@zsQ{R_|Wqn;00p*@I2PuU?U**pZcb| z#lMdBP0Ql9xxVR~_^>-MeKPt`PE2Q_4|9p>pGTErPRvhfXddP`Cp6cx88DCO$k>8eyIH$^wLnZ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_result_cast.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_result_cast.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30a15d537835424a1369db6501fd9487a0ee574d GIT binary patch literal 3140 zcmb_eOOG4J5$>KDa)v{4A5voZDG^AV>;(2cBv}tcYv_R_7kd#nR)P=#8Y6Zum(g&B z>27+%4S9)#90CZ|+0a3I^wlRHa>&^ypXZvBe?ol9S3TSfxsr2nNOVngb#-@rRbADc zw%ako@8|um`>i@-|Dw*~FF@xun)w@uWRhpB%iY%-`X1wK7<6l#M!c*(47;J@c@}je z$o#A^jJvUu1zB_0>b8dMZksa|s^JHCykAv>Yub7Nv<1v#B6+iO3t1_0&$E>?1 zTe6Msx?GW~_@0yeH!NBE2DY%hgqQxEe1cEoZfewrg;ZJMm9;y4mD{rM(K!D|nQ>+z zYwZv6G!wfjEYh}ke?{QjY{v)0|aSzSB31We@&-!eN)@PPWF1;sQ`cFbw5bTwK zRmNiPAWw%X3Cr3br?nfoIurw|hNf)m^iXJJMn!Iv(eN%SFE}mHAE#RCbdb?FOf==I zm2EE>X^Jd0R-}X~@pKK#Xo^b0-IAX(y@uYmyIc2-(q>B!w|elzRxwg}I(XMjGw+Tj z_MphO$Y$f{d}GnW+MAzeG@Fn314MIG zh&0a&o7w@qDTKqkK-RIPJ`eIkGQb0gx$7@k*(5od3-TAxq98xwGX(jMe8#8TdgytQ z&-|%3^&fh(U>cY&t^Yib-Za=p_({I+9S1V7wdeI?_6ak;#&|eo$6R;8Ma1PE#);?0 zT-HuCk4V#c(0ojz#6h#>G{L*Cj~Vr6wO81zCPNubYXc6xZW|blr}bml`&TX-0h@6& z4=tGaz!5LuXgVD6B95or{2goj4oh_x>pQGsa9nR!{Whskl8^F&t?TQa3hE+vQAa>v*H16Fsg}V zLzr}5(F*0t9p_#Y#b0P$=<>YSFZ3|ALj8G^rMWAY`bPwYieu8$bhVXD+OP`D&SK~4 zV(0o|=UUaN7Qb0pTy=JqR=80O&DY*uQoOv>yLzU2mv1-^RmaL*OTtb@s&}Az56x?^ z+O$BJ7x90hedVoieGC1i`c4PmM|1Uk1I&Nx`*YOxW6nuJH69fo64Y8eO?@PTKhxN5P%&&f$T|Ulxjw@fN$_)Wu+54Dw!8B;6sZ%8mXp{FOviqSmX7 zL>(i~o3e4b!7UV@I zx%$cZDXIF2>8W`o#VOhG1x5KK`6ZPFFd_Yt)Z&t2Fd1J|nirp(pO==Iu2)cbi^C>2 cKczG$)vkyYXd=kQVi4maGb1Bo5i^hl0E#jyGXMYp literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_run_config.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_run_config.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38ac10cb9bbe4356314cb658f27ab5f282086786 GIT binary patch literal 9070 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_run_config.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_run_config.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..174b66e4a6b4f0c36472a710f6b1991f80b5aef5 GIT binary patch literal 156 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_run_step_execution.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_run_step_execution.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..345762b8df5d80d82092ba69e53b7cb55c132675 GIT binary patch literal 47487 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 Lh3NIM?En5>Yohl~ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_run_step_execution.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_run_step_execution.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca24aead293bcdee7aec98143dccd6deb4e20b15 GIT binary patch literal 18291 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 Nal5zGag?^W{|{)@)++!2 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_run_step_processing.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_run_step_processing.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee7266a9fa528cbbcf79eaca6d78e8bf33ce8824 GIT binary patch literal 43343 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%0AHzUg3mh*^!sHwG?0 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 Jl6Z{W>c3QSa6141 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_strict_schema.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_strict_schema.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28d5086d1bc294139b925859ca575463db8dd7ce GIT binary patch literal 6603 zcmcIo%X8bt8OH)32vXG3dfMqkb)1gb)Rr7SGLzbg(Z8hA=vIt0J;HPRqd^ zvt8}DEtlu4cCAxy)zP0T2rL8AL~GUs8m;BuoOgkM+Kga zRk#s*+qxThdgDRR@i%CF|Kp%j#qWLm^e&p%2#qIV-$=0AC1YPmbKguvV(u9GR$}SR zSUfi+dYeMpG{0#c*qE~taUl3=9tc@^X3iV?HtjdDLxFxJrd5@sav+|FU*ZgHJR3i2 z-;@S+;jX2#zLa4cb|b%iPj!2Nio-zPITWcG>O=8(=Jkucy(>+<9O`B2UTf+@Gqo^B z&9$QmUt{|*-@DP&G zw+;=pg!eppcZF1_S5#+Zqbq|IoI>P>m;9|DiuEPz;XyZAp^a#6`^wGU;&N{8}$7IKi02UYuc%kX(4r#AL>A-wflR$fX|uL#Ai@y z&O%&zj&~FRy^?U8ZPg11<+_0d`MWoSdKIIgcc-lt*#3in1q02}l0an07~RKi5<Z#E8zLI@SErI`7BA;>jBq8&&)0fG} znqEtt?)rAH5vRgW?HG_s9Wu{o>(Er^2}AV(=nut_dFxi0PHx@m(}^OWt1)T_S&FRU zj;c_XV3fKl`1ww#0;yi7xp6dyRmfsi1*ikP(~i|F-J7Fko|*+}7O5c%&l(1HlYyPR zx%V)HY43oa3JNS4>NNhySU0R080j{?k*O|Wb_nhO_`p=Zhrux1rSr@pz?aRJz<-SS z-h9D)t7~c*PgGy0hR@WAb~@{Uy5F>ToE-_BSkqRo(5kOdLk^T}`%tJis6TQDZ;T#- z7ptA%_+y|4=Eq=)x-kFcRABY1m>oKXA*=rz?(#7h(t2h}`ME9~YqU-rIZ-!W4x^@cJl}V?+002+LTFImpynbqWEJY0)QqS) z^H}^&9*ZS}Zvb6bc{@bx#b6cplFgv7fGS`CSd%#;C z^%7JJns=b~DgrZ*^p+VSuqjbK0vuT*r6hm`!2rRIomhzt`bVguf5XZFsRIgQG+)p^ zXdm}51Nv_;6dXA&ClZJ0znas3wM73G=)Z!eo03S`l2e%^&_7A~Crg@UNi(M==?qJ< zK>wv(fr_`kf_(`J7v(G@x`~^gW_3hv=HyQGJmnnB$x2)pFV69)sU`1io=)^|D6yD{N}7kf}v` zcQ;2UQoTl2aDf`KLG>mzMA9W|`Erk=npph>|AkHTI)-pv#~J2TM7-{D-UjrGRGV{2|nTyXV772 z@}!1kiYYLJ`5)VH%|_-Qt%hg|bq9Bc*n*CwV2k@Nutg}gm@x({A*;j=i2%MR{dvmh zayk8}(R_g}N_&8k=M_SiUdK9zQX2zdeawxE8J~H9(%lk9+2P|`* zWuDglD49K*M@t+g4nn5tdbisS{HXB0$V0&z-vuTWp;^664cU;oNzFUdyi3hUBsv@NWpbX6x{qPc)da3jyMh_G%6=i z9EVXY%Vmn>y$&0%5Q`zcqV)RsNUp(A!T5>s)dZ`-k&|-jr6Rd$mNXRQi9w7bREr^pJ|H(I=XtNLT#$>2%W$vd zF;1-nIV+EGmeyLxZ#VC*Zs*GjqWDQ&uxn58f(j zt+EnZ&utDe)^NYVZ1Pj|INX40E8M4WBx5w28LeN1eKFYRS72*v>Motl52&Gh4k;je zkX6)N^CAhNO!iMrF1)U)TeP-tQ!hG~5x+7VWHz0WT8L`0ATTSVUQs8iV!2R7)E#KU zd-x!TJipzI0)=O3I&i~ug=?yh(Hl|*xaIgt2^FrhobgmDC22K`kjaKp{SXq>8Z{Tt zluIOO{bRqqQ?7!p(6SMRUEn?EPeb^V{N|GS%GTEC-n6B)a{4viZqZed0FB0 za(1@B?qwA`RLH{9S}~PY^Et&?0o8C?(^=*Da`0#)V8hj1(P_=*^FvkeETy0dGM8ex zK*i-5PTDyZXRpr8&lbX(;dd_Wmv>Haed^xqbmdMvc!%`Gq5gS<_YKRf;M;J=+;Ml# JopN3J{TILy*>nH^ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tool_converter.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_tool_converter.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82131817160517ff1f4e7d82dcb7bd3cb2e3c73b GIT binary patch literal 12692 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%0D^z~11|95q~t4=0Dlj|68!u`u;HsOmSKXllPAb- zXT%t2X55z~ZLrcLIrqY0e;XMWzL;Wy*c2%dJ{n~%WhuVd;0Hf7FKgL?K>@QfsHWz| zOe;Q>c~#YCrr^r};{9eS1fOu&R|)t8kja^vNl~Bd_M!W+go>mH=d42ctWF1MH}c*u zchpJ4ENElQtuR#$e~4t#IT+jv|II99@TQE!-)?=Y^(V7;BJKCW4a?!STj93%V!w>v z4)=WKC(({igCttB21)hJhMS?Kmo65Q>D%E^M!FPf|11PUfYs-p!7x@Zn_!-ww+#jS zf83TFYV>~)hz`|-K4^>%?F^+_tuX&w!mdSYi)ydG%f4&C6YHYdD->HfEomN&cX z??h^LGE=~hLU|4SLeusHB!`qa0Bk$h#=MAgyoA|B%n-U`-#2Uw7Vjvg{?&UIK4e3} zN9bEX0@g;%3y^*67X;x$Lf#~A#O{;r{~&`UGI*bK-X}*sBm*A@1)=Vm{4s&Y!uiD= qKYNK4ulIe5EuVy|gvUSK?h_L8sZR+!K8aQdJ0ETj2;UIcOW@yxvIDXJ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9826760709247eed201226ccf73e5fa59e5d0892 GIT binary patch literal 5974 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_tool_converter.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca0f0ed493e7caf5b26044b2a7ba266bc2b9cd5f GIT binary patch literal 4951 zcmcIoON<;x8Sd)GOwaqV>wUydvIzv()0?VZ z$2L6?%I3f!QGDWneAwlHQbVx9_oRrqnNC z#*WG(zwetdC#sC9{pzUJuZ`;c`l!)wm~l61j#~YeX?sz7)aiFjyA*Xt3;l)BVtc-~oc#sU^agfAuq{~~`I6d~h z67mfxV~I}VJu8Hf1-JYXasM&R0k|NoaKR>%J{(7?yP+ttt8H@(Hp?osgpw19)>H-Vx()_+m&7 zsTX%{CtLCOL?Tq83^fh~gQ?ByJGXU1#Ul|+J*L%H#xN$kXb*7Ber>eMxQvlsf-8ZU zH?YLNWZe1OW>&%yYmZH=#NKyO>wryI%DB5~O>FK>oYYR85A2DXy6UHiyYKN*>TWV# zrtwYtz{OZ8wGWuNI|q#W_v|HW;_(WvrrwaDT}sNB_0!S;+hd<#9k?fh&l15rf zntL{4;M}#<1%8At6}0}Iv?j0_TK_o(A#mGV%T+8MOTEmrlF0Em1wKugIw$-*<6sbLH;_y}e*p@x1r%qbO4r!zf*!^e7 zpgqz+{EpI$iQl1(w)%{;8yju;bE7f0(X{=;on-oTSaZx+)8voyC(@>gXDhd?HMI=8 z$;aj3YE0_fLfUxps&+QxxaT~eBl>`j#{&mM(t*%!a(hQ;e|m!Mv#;z+m+rGW-Di*Q z+}`M^wM&=QsClNR?%UeI7?CqyHA97?o0z$CJw=1(v^x$*!kj!^-3cW+NG%F7OGC9b zjuXgTB4H;XHybg}pT_eg!=LLoN#ah=={TF|I19lNX+kDLq3)b57y$#TKglg!%WQ_4 zQD{GDhbB}p1SO-LA zLueeTUH?GbR@KRIXqyJ`Jap%3PLOcYK)TM~DTeZ5=H|s-+k{bi#Of1kC5eM9KkDjb zq()@LAe6cj48kZ1q}Y*zc!VGdT?tgUDQHADUp1=)FfeIfF9e&h9EC|BZtXm<7%WurKKrr0?rUwDZ3T=Bs^9=`>bkjvoY(kk6E4liplR_d=7Mokq+Bs zT@m?Dv`Ps1mPG*%`C0Ls2dGcqMhM5DL88&m^_y6fY7!#uw7+7Gb3|8 ztYDgs!WCls8N4Cj=jFcRvSM8rWn26FUW90>~U*LTYzX7fI1eJ@qjXnFql1 zkP>|wpTQ#sXgJz1Fc{8c)*9p|r-@w;MDhEsw=$5DjI3SoPrAWxUHRXiNF7Z;C03bSM zIM4=n%e3(%Z{PC(9#!7q-Lz^vspXy^(a(EAX&W{hZwUCdn;Ee}}VUs$1YZ0&* z-OMLjYkSLEFOjJ1*9PxWm16ppE0 z$gBQ<3pJ3#9ZK{l`aM%x%Vn@gI@WTP$WbE4h&)MTjR;8r>|M+8bBbaYrf`Qw4tGH2 z3<1%|hXCm7Tx^B_f~;nz3b`HGKe2WI{GfF=WaJ zfb_h31$%PQ8(FE*^B;EZBzd?&9+EE`hMqF?%)Gl)R1wQLNw6WmM&wx{1dQ@IBF__{ zkYS-7uGgrh@@|IFuE3U{1aAV|>q_KrOXB4?FDn-F*QsE-VF<OzWvx&@M0`+(pB_Qf&0$vXRP;_V?ZGwT{YXSn$9Im{ZjH!StSNxZAm|A)9{ zPd$@ARl+-Q4(^Y9db{|vNkT5(FT(2sNktJpBeZXdK@!VAfrwzpm2Qo~TfumDgd&nk zLxpmtlohb37Q>+uO456|DRbp1YSDYVtPnX2;H+fP&*ep0936fO2)1IC2yU{C3Iu)Q zP;|-^#fW11yj^GVCfdEnwLh(M&9>F6L1xW*wil2o*QNMKVngc6Y)F^$vFvOlkfi;c z+dLd6!-2G^Tgtv$WGBqzH&eaSS?hPQxpi@CAWSmOeC|J!kE5g$ky_6R{9v#ZCREKw zg7UU9k?GfRR&zj2a7>uK2l4XzMBXJrpFK<&YU((%Z;acIjHLf^R!Y7?Z-%C-eVWzn R6@;tK+Rl>ex7wcH`VR}1ChPzJ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6cdc5a603188ace79896187726a6a212a68f757 GIT binary patch literal 18173 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 Z22lX9JDO9lZdU>MFdY?+?=W5F{{y%hb5#HU literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_trace_processor.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11601684afdffcac80051f282530a984d3dbb015 GIT binary patch literal 23022 zcmeG^Yj7LKc?aMC900x~KE#)#BPl5aWj;jeX);9Q)EX5vojUzMzMJmdC9 zzi;<;?*JVimhFs_(UREvcE8=d-Mziguor;jyX3sf8{{`Yk4S@LRyW-+LtK zHA#~+&l!n$=+!&wGrX!aDigoTFaZ*vm>>zl)q6%64Uy1jn1n5SghVL5id4bXccyx@ zhSX4;e5Q7^j?|5AAzNtLe`f1wJ*lT~;7r44BWa{?@J!R_HnI(1<>?wN^mOCX_1MMz zvKDTYzATewD}Nj0L}*US(^awkd^q-1t7?@FSV_{RRkuo{U8{k3yH*SF4y_L2o!SjX2Q2A0tck_mNw|Q0@VnOo(QDGUz-b=MtNG&6IAUqg zWX*rXqXmxmcS+IU+|Uz5Pv&(ssZOV}>FH!fO=6E!1G=G3=7^ftjXVrc6&*18%xcU` zGbjqxvNvr{>F*8h)v>6zXoJ~&aQNO8(;z6q<3@S}MWk>dyd(rU zM1km*hhhyJlWGq4#|@nr{bag-BA3$pb2EB2neL@`GkRyP)_o;pek@j3it!} zpqNxP@z_B+@$CTIKk)-nT+(DZ)&99{_f0jOl0Iv~Xo6sO%vv^~u%Oa8oYSbEY=wMM zk4R;TS4h4Kna$cfS(9nfapLE1K=DeCn8zLs^fWaDI->5T-pVX65b<%&6ok5z8SE}6$ZgW zrB9f3yLK90Gd-2yII;&S?S`L$1BA}kHvh#7^QYfx+WG3U^QT`rw;HLRKgW74?g$0N z~*OHzV%h#478Jdt#jQLo+Qx#mC6|mTW4k)lg9dlFnM^fVMbB;$GUv@3CP?dDA8R zn=TpPCADCmzZafjR&n$LMq3$R?iEod)C*`W99ITBv~Fk|$p{UOR`3O?2rx?_-U`3! zAW9tdWPR|H;pac!S}+fF(xkSso9L4 zNa>m6wRi9}xaE1@d-Uin&)hS+yJxN)j2ht=EI>V~BD9}p0bpef@+<(XtbwS}mw0Yg zpVh-(?R(Bh&%w1j8vd$!l(ljsYETMv%WwK-t_@%`h|zwG4q|jnbYnqdYqIVfS6%?Z{o zS~M}0$z4ij63DmAP{K$~>evFb2JCP;2`$q;P8(BZ%`gRlNS`7k2TYz90kV}zW|Miq zn2K?EHlNB}$wtGJiZNo1{KAZ2Z&-8toQWZ4;(#-8*qJ!QhQqqGZ=ftSXdpFFMPOt# zN~h)$iXxSqF;PCRbl-Y{6Z_#ou7a5tu3uLjx2B4JwJL|t}e;# z1-bpk7mITDYNVkQX)8q97Ui*0q@xh&0AMlFu^j0FVBt9$6(ZdwK-2#vM0bfK16Jg* z>qkUh!A=2UG1A5`%d}w!Y_$UU3(twlc>5mfULq5KqC9pdAo*)ba#KNWT9KO{at?UN z<>4XcD4!3?<^B{n2SL70q8RPP2w!s2hY?C0q#vTW4h_Zs^@ue=enbvJ#HllKK{EnW zgi7(C{}2=v$q|em#Rw%3@;F9EF*=6Pag0u2G>p**Mo&QGq?KlfiNh$dpTq*EF#0q` zpTP(ODTzFZ(I`Z&bZ^i4bWim795iqU8@UZp1<|W(zR|K=*ERnnC3-Cdx#dQ*DDQF* zy|bL?o#jODED}A4DA7AxlJS2@1~iZ>E6IQr`79DWE3aUu06~c!VwMr^V5KN*wE{2_ zJ*zU@aIvSXafT|&XE#ms{tNi+{o=CbJL1cFKs-SSz9NhZaoH9ixsXGJ)bLk?yO@MR3oR zuPzU+E~>uS>uWRWTZFE!s)y^V;xR)Kp7aAKdiMHqtRobxD*~u*)qMSEHJ1AJajwRl z6&--&cn8m0gFZf7jn)KJ7!&>osof`RHFoipXJ5;V3AJPIpmmSRg;AenO_)wzWr}qI z^?lL<1ulR!*PAw?p1H(FXHJ-$&15WVLfW8uKer}ON%Z~N3X1?0Ic=GBZqIqNOK?#& zo6D>E)d?Le3l#&v5l6Q&I|530)R;geR8z)2Y#^fA(kYc^PhbmYF+!=AO02g+4()yu zVk+9VE41_7>qx28LXx08{Q}fPLD_f)9vc@@&o3q}73wA|=@eD+n@=rVTi(8}C=af3 zE!~l2tLi$nhnn!m(fy4r zBU!L8RVoQbJP)iOxHCWn7J5af5XZa^D31B4I3|~gWByN|xbJtclTWs|AK>D?qsQ*n z^q>&O6e^B|9LfPwR2m$uxCrGRKsj)+rjH8|?E{Hp4_D)#;%W^4L06+t1o%1!<>2F5 zjgg16kw04pHF2k+AFnB7^R%&w_gMQw&_);KU_(>hgL3MJtMQX!nA`knv<)YJh}DQY z*%hHoRjnB#y8yrT#zj-*HZ#?mQuc1eT#IvS1NBANi?#}#hoivO@WQpyD63*eCxxk=G!N9Z<{HXsAif~Vs{odR-V8)_&h|0ixzpzp+yS6r$j2J z8{lfCXPF}DY%Y-_Y|n<>bR62%uwq1Xaoqvy3n(WdgQZfqYcTtjci1#U2Y5nE({!5vr86 z738*?rx%`DZtpM30}iF^6z~2i-u+Y9eTZoHPnBf+Uy=b0H@1=tSdmX*_pQ8wodN{y zK4O*;hVBd4Y6bGK`&MPR;bM;ki%bBD@~QQ8%3^?-a`@g{@Wq#_n5{;f9Mn7A!IWD9XdM`>h4J_2vFA-O^&h{miacFP#P@F6)c?G#R%AS)7&Q-tjHGlx^?tt%kDRv*4N*kyJ_Z z=VJHmuKWZ`bQIRyjfJB!pg z&aUOU`(V2bf^(eW7>Rpn;mD?i`n=S|EU1OGh(kpd>Vl&!mCvkwU0X$_duA@8AD<8E zWL7mskR$$%HP~>X%r?w?h=gsJpauCId2i=(SdX5Or0cGLtI{b|H zAlkHz7o8FuCPy^;mS+hjQk2{7C{ok5`LWf;U8Tmog~q+5#z%{dk6sV0*0tQ|_yS`m>*Dp0ab|)y3U21h9lGy{|MUYqal`S%BOmz_7iL(# z8Yws1=oyMy@WdmIr{umqT%Ne2&K^F4_7!&PZ4*d!FCaeZ^#Kmlph$iRT4ru=$mQ6U z;xho5Rn(I>aY+Y)tdmQKOksrdnDRM-ykYLpa3-Us^ZK-*rn9yevqL%Ld`_L6$)PtL za|3?AuFTCDcwMxiQhgL`Zqq(=!`QhIjF6L2hDfFmx{T2Yh@38UWCnAe#ponP8H}be z!l98gMqh@=)tPRuL&0R8*tVL<2g&%dZV&>kynroz&SNt&=0@zcEqOo7TCHy{)pr%@ zyGr%ZLVa|(e((HeSKHbb!RL5iA<_=+$LxPeZomGxNHJigED!cXc?m$=*jG^)l2Af0 zprYIi`;ZUDAr;CYpRzdQQzaSym*l6ALs}^Ytd!*y>=Yo7Lt2Fw<);?avWg6KzuPkM zF_gmYDG)m~5ltNH@Jd-Y`jYit?642#I81(UE)h;Fc~N?gtr4R7K#N477tF{vAMJ69S|a&?+Q1t{D?DIV+2?Malhf;5n3% z(>NQ5%j1o_R9y6iQgT^P@=z`x4^rNJom0ycQgXpd-8Bq6C<44Ya`7_`N?tWCw2PQ4 zZ{s5tyxYDD9^R@5z`KR_6z?0QHm3G3)P^Hg! z#_Epxc1n|;=$5&K@C7^*2xK~$w}>_z2T_x=`5YYSNTZm44LnEaVa=3!<+9Ewdij|e zERZI;(HH)LuHpeTHJ_f=`!MQ`szy$|qN@|&H=QTxsVNNA}|d&dYNHugy%w{EhO8LbmEm043O>|r)gXvdJKk>i)SEjO z&tE7s#IJ{LD_cs+_JXqg=4J4=*|D#v3D6K`M{k@%1xcyG&hvG5kEFN>NB;)^*d=7bxm14k3Szf_T0Rnl9Rd`W8x2Tp? zWT^Lgp_ji5wY_hk*YtOWx1SpH{d_R|X-{wh(jo)^>Vn_BKcj%UHuHfavCm7dd4PC( z@y{maQ{J^-<|!|Gl@Iygb@t6Y6aIAiDx8AW@c-kH6yYJOdujgjCC;0clUQgL9BjfYI@80Jja>mv7OzozcBw~ zQEq>$efR5U5NZLV`=Z=HMVGm%%lZ7w)xIY(X*dU7cCZ==5FP0P0M3!pbGvJL8TCt? zMSw6N#jur%;C5;^CJd@}VLm_5jwgncr6Sn)De0DP49>UXsl#XWYjC7Fn4C!?7=;f) zKs6?f=K+HAj5)AOZDF>q1Z+RiBZ_*;^iO1RhJMRyM(D&&&>5k=m z7kIU8(Z=s$SWaXvlRjR}+aDKlB7YG4!S@SEmo9-iUc|_W3l)WgW|iI-KI$ zMsPuqmoWNsjJ}D{%Mg_<817TL#)7dHDtQI!{T%BehXq=t0IhnZ_1mpK82Ovg?~g8c z-`KSr>VK<#&l~wd{r>C9s#5n#?YC-gHvhP9xpQ#2WoWT^e^EIAj4#wsQq+Q?mXzHE zWp_zAR8S5Tl_OZ8=36yIAbQnOc%T&O|7ED3v6P<-cZ}5f{;f7VvOVa&+K#c+1~ChK z-a(H6d!MaoT&&yd3vZJ=$I8Qf8V~Gqc->z2&8$3ocVnH^DXeTCh(OizvD;TZ3($^t z{nZQh4teaIUqDszB1UwrMc8*3BCzS3pEVNrF%8FxvdNac-c+b-Zna1kO3c9Ua@h%; zuDn}5avky2lzf4Z3?>ga%Do%%3?vyKmoa&aTfp5GY4eot7_N$*`mCCbsva1sIN2X1rE0NQMn&nyqR6z(YM^#|4%JHJ+}PVndN<- zU2K5L^z3h)vwBJ1Uy%0~<%8=HKb#Uu{JJ-}95wigguT#UabgPsjZ z#fE{Rybqn%H@CQ)JX3*Q(8W~)u~XdPj}dkcJaMms)~O$s*5QYM;4?rnwe*OE2K}(Y zW++|-ua$XS`Chi<>HjX%Hp(6xZ0-q z+zKm;eHUS$u}1>-5+Pi2i^HaNe?={@vVL-(Pp$I|%qHAXcO zP>7yXvOa+F@5gV+Q01TT4#<+K_qk0?w@=En+@^?`6zu{+XTvxDdAdZ8_ot&35?w{=g zd&=T76NTE8P*ZFME+}i&wo;_4fS(gNT#9sqZx98B3XvfI-UN>!hzgOzB|y{vCHe4T zWJn|#up%G6{+P%s*eO6z|01lsjBqzAMPaKIfZu#hRCWTlD-C!icNYvn<78f;WZwR!RR^DU$6Y&lo_z%}zqxN(d1FX?Dgu8p7MY)`9@Fzza^zW+ z$!IS^aq5D^K3VcQCU0VdWSlHuM!>hX{cE`fhK71?yj)vQM(|y1ufLz(;fhz(xeCGx}w}r zrg(G~sb!*~vc4*@u1tu<6w&491Cc7WSYf!ER6d zSfORDf(DzyAQoz_C`7;8LqCA2AiBW?ZO{*invE4G>bGDJ6m*!Ey^0M4C)ut7JIQ?p zk>@b_0!C=*&5`7L#m{J~}&f&-U zj*))>Jg#BmOAz1jc|4xqNYYEvH$uOXBEOM(ek)b|N~-=(>G*G?v0qDrzm}rEmiFEW zD4xJ#bN3wyu5Vm?bGp!b^7jbeX+lIxlp)eqp5#?t-tpp&yI!BC;hss0O0WT8MVlL-a_k=Zl_qceQyw=ehH^s2uRLSd{^KcdP7ayjvrAgY#co_B7q~ z`aO+^-F63|yLC1q#Hs-Tpu5dBA}neEbhp(;L<9n$yX`ijS|9+rtJ;V)oAP@O(_Uf! I4%6xXe+(lkJOBUy literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_trace_processor.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_trace_processor.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8d3e12e07214ec44eb262d37c3d07a3248e7b0a GIT binary patch literal 9436 zcmeHNNsJuVd9JOytE!jj84g!bQi~KRwq*`?Er}^olw{aW)K*N{vOBg*&3-jA-RzC8 zs>zv2wGo&!;2anU@X3Y@%mls+kO(;@KKNi5h64mSB!>ibv=24`BM5>Z2PO#e{r{`# zWoAT)Nsj43{q?qb_1<6KzrJY{i>`*>q0-M+{_AN?`xRBDez3=~ z?tFco>zU3%cVB%Um$RM4?*96IF6TN2x(DkAb1m`D&iS#e>SNf6UVuJBB?(sPH_EXQa>r4wjMh? zJgp4US2O{F@sG+juGeq{hd@HWLl{Dfv``nOuwCmco4oFHG6lZ+~8G%QYe7SQ4b6ud;7hc+b49^9`5Y5Qywl1GSX@+5>kH)b2 zB`P5RMZmgmjPy{C^t<}Vi1bY(GPZPGi}bcRG9$BXjjYJRS~J2NWur`#y=jbcQ7-st znAywg^0w!ZQ1iBp617pj5#M@gY_ zxk9CjX%h-b)A_Usg{0|hHCXa)4gI0-j+Sl(tqt5yRozkLvYO@PYQV7C&cx$j-zisB=E@CH?K>5fH_Os0mKLy;*9*FBxcXn2@*3LyU+QWSE{`TFL`W~m<3vsn zAx$Yyf^0l<9qRs(K$oAU!KXn|WRn$$^Y{#GnP8CR85(ky$T=eCiCiFZk;o+?FA#Z= z$V)_CCh|KV(>SX;$}nI_@^@*#XNX)T@(PhFM6MEfmB`|lur)aOXw0OGr6uhI%=v z{0z!73uT>!%Fa?dtL)rZW#2}>QrUluQmV3ZT!vBL`yQ$rE+eVz9J!5Ajuo{X<}tbu zL4l7IwK1ivvsl;OYcG!1uB)}f++gkR>%se&F)Q-C`oiwjW5i1s!Rvotm)Cf;%IJLJ z9pIUgD8lHJ*yudYUgpL|XRh|ZAbgMvRL#WdbQ^0*0efT@HfzT#1oGYM7EtCt&M~^@i9wANiXRinkD@whWrBB zcgZ5TyzI37GYTIgi)Ls|N#<-V!}zcupISTH&b_XE<0v6rScl=S9)_$&Mrgy)(qGg@ z#;xBM8KB>YG79}c96FmWWR@g1N9`=Rkh0AJJ6k_Oe~}zF`5{UrD#6*ZxeVt@I3E}+ zp)TYnMfDtd%QF(Hq;h_YRzK2hra zT!5caLQnvoA#qv-oU-Te1i3yJ9+sw{c?o0Dc&WOu6Jqo&I2wraFzASSmOH~>RUV~j zO6h z2W09rUE#B2*p>w+qo9|}HA8+KJu^5ZCuEG%)hU}nuKi9uyE%i)HgxJ15C`Yp1g=cr z4eoyMXJixL+*|D2qdjr%fkMVx2mN`*+ijEz3Es&0FOYK&C!X+@5w>um3|oY3SVV6r zf{Q=42xO=9O{F^Lee4$VH+>L+UZnUq2CQEt=`1TPgO8NfK*y4!$F@s zCgmnSEYE}ikeKS?M+(a)_LC-xndE`RN`9G2Um-$zdfIK0-=Nm75n*Lrr_vu1p{>bx zhOQ*fVubH z8X?#v|0vQu@}ZC_L#DtWEzB}uCO(xRG6&&VVGQ{jz$^SLb2CR;@f}U%ECii*OSXVq6YosFbv zKuh|;5>NP!U!ep=C2V#5B_gM)m7rg_<5!yS`a{`TS@9*Kz1i=H7Da%SJFO65Z&rZaCxlR`8XPLVfS5Dro<5g@rJwX;)V+@b4e4)Re4E;t&!}g-?>n zjnkip?Cm9Ojb&u{D(NPXqojO+{3bn3A`EFGYZn)!9}Z>DyWQvveIW;dNgz0xzPxGE~?0ChuG~UnU5oiNvb5xG7Yic;0TD8adk`2 z8U>?dx`u0=G{)jc;!4-}N1Berks1Sv&nQs`aXc_W{^=_a8weFbgDIhUc1EZSz|`F6 z@b~|0YYn{iDIJ1bLx(aY3<%~ggAJk5sS%?}j^>wN};nJ6H9(%Y0Ygat9X|ID3qA96jjqknhocEf6j1>Qdmo zi!Br;yoQT;I-3xr8WMxhQr9*41jfMk;;4e1ZF*?Wykv=&I8LqpkR~VQYdR$bkrwn( z-YBew;b3j)wN4A^-WlCVuP|}(s>sJzS=IZx@*`F>)`nW!Xq%fBX++rX4Bg2ZZksiv zl_?*6(_crjJJ%Srs94R)45m`XhbV(HP`-#W%}zh?w~aW@8)@W}a)wS8n~T%(}NK3aBdcx)_|(Hm-Q7>Bmm0!on^ zHQ&3cYoSHwDhU%ZA>5q1I0IQxAvi01ay6J4dAbA9!1ENMzdD0x%1l}{3jeTOzaix+ z-dimwbz+HDGmMRX5a;~f?UwBKrkF=gA%8;oE|uCuZW8Gb=@Qui%rqkU2sR~O=r8c3 z%1l^`u&)aySQVmehE;?M`i_hZ@?;pXx`2m?1U6FGgbL39v*1mFS+KO5V`hbR!Zej! zRtR#KX@+7R`%_l!!7nhgBsH$sl2;k=JhGVKF!1PGK4AWl>6zxAAg=@$xQLXyXNE|((@76=5r7Nr8OUMG=5Fp~YOVL7Za4DR8lv(g z9}#a5?qa39jaSE3=&ywz!^k@rsF1Zrr5(sheYm~Ya}n%Ovg_gGqA%&224xUBoJhIE zD2ve6B(0PFX1uq5MJsd30Ll!YjH2f$lnF~@MD{-CyCzs03ns@h$ce!dYulqNB8Oa* zO}(z*?U-!fM$mZjPf;pS2|ky@W%yrwr-KXXgO|v6^2J=v zhB*SqrehCG#oDO{2GuY=vdAiL3?Akr?k?6^|Az0VJ{?7aoJ&fMGXCbuwk2C=gpNc~ ziHumw4`P=+0rpX1hdPwIavTE@i}(K58K!A32ge(vz;WZ~xY-}}P)Xx_N;r{>al%P{ zj!LIN5X!RwBltZUc(FT~_7jKiqyga|@r%jW%5@?v*N>k^diD{<yH4Z& zHgs`bhI2Uvb9x$w^Rmq%2N2&vKSNl47p1u|n5j%DUAhyf=}w@gJDtWUFxw2~^bD?y z-uXwK!5NzisZ&rb);5YYKfKeIH!G|R6s+F>AJj3yh)UlgLSU8OCbAcP_Qe_YTjD~Z z+W0VO0;M<~w1n^Z%gcT<+|y{@rK!&mAwP7_bAn0y%YbsH0QEn_(y2#aNh%$!l;m%) zCS`=pa*CzzqHV^A%s6Pr&^zm(u|Xvjsc)L;7m(!ER4v63b`;)4gJkW#m&?HJvPW() z4p?@0R_vbrJ04GtnqA#C<)6^}&&PIpUdW7%4}0o_aYaky)-Es@mXz=k!DO?vCdZa4eY%=ECkYxt^~ z9Jad^!{k9pK#SJ|I?M#Q3=-%TzyS;$f&_4YAPAI8P^Ta{7zBbO2y&ux$oK!Rs(P8> zP*j4PGJ|^cwtn^AU;qF8TQy20N5kJ&mi}&O{ivq>Gd0HlOf)Xz3O>*^O=v=IYc-wk zM#nTXUF%r2jIKtqwJeY1YB}7Ew%y6s@*St<#N&lpfyaxrBJO6p)G61>JZ80LI+a?b zGh3VGcBVbo*;U)c^=x~0XHRVp*K_T?oqe@^y5^O=J>ISZny^1ML|!=etlEC`?c;Vq z6wyB56;}?5k|^KP?&`H?#Ehui(`tvrteC_7S+Ps(#{D_5N9@J@d9hFI$K4eN#6jE- zi)X|k+>h`o&x+@Gm80T$;iC7L=ZM31b#X)-<^JPw`fnWy$M?H1q97 z*ua7|)xX|ZZExyPo~KiTp1ztqBFaiH?8$Bu%#r?4-NyA6u3$fk5Nsn1VGguV7naD} z*G2Yz1_#RZ&%Y(TM(DW>x6|sjI*qp5pk25D9_=pr(ha>J#G%~uk->aaqRx6q2dKA1 zt;kLK1zHP{!%bcWhp@Vhj#te@b}~K6#WQh?hQzb*BU!*MqdX4?I=mt205XSSQ|Hl5 zP3}SK(UsHR2|O8`mYvg0UwEheRj=D; zde#gU49X}p!_bp|`!{kQdbUuM_QDjMLnWbTOB-myVp?YU`?k=M&paPfgd4JiU)N?7 z#lR&kpNLk?NZO06puqN|-&;j*WOux$UIHiO$AXx6?^Fyc{&g;e-O>iR&t3c`caB~6O59`i2v&{j21PVZr+17Yw)JsCfa z-vwC|nvvCQUAm}E(3D^=<L_i4fz*exBs`LKxo(jdQq-Tn-) zY-vLimxU{XD~BEG>*Wo=v;ak`g@s@L;UCc7pI_QEgz$tL`fj-F zxxDzJS?7&L0HF+dZ)eX|gUDLFgZqpm24Lok%3yS6(Ru)W4x_BvS(M?8Mb2f2E}=?8 zMzi&1qus7cZ&fz^4uM2e#Blw3uhj+?1yQ!`V@43|ud99W)cR7}zusuq32&l&J!mX? zv;tNGjCC4VnfKTUmZI_%u0rfjS0P|+lHh6 z!jx~L@BgK4K^`VPJVM1WDhQn93sjKektb2~4__sfw-ckwd76A0MZ(#nD53&h18Xkm zd3lzmoTK7A6&I+uNX5%kyh6pRRJ=yTm#O#)6<m%Z ziZ`hs0H5ZW6Fc)vvRT^AG{bxieG?2rN+*Ts0klB^)55%O4gu5adI*@l3pgc^o@@cK z0pEaZ05>4p64o#?um+i)24Ktve+lr+0<5!u>@4*&Wam=IzJ+lG**`@sf$SXDp%nPO z4Rpf>CCJW^T{_4yP}^Yvvx@@&cnZ|U7+7bqukH6;19$t>`055D2)M=N5P664ion>}he{JEe$+MV&`-HO?!LP>7@)_!h z-g>(CLa6`rJT{jiWqCn92Pu2Jy|Z`PvS-m1BO|AOVB8`ixPti~Px>t^-QP1lo{seo zE%@Owg$%mh?F8)7(dw@4aP8?Wrl^!yUF=RMY$KG5im6ph_81vf@|#pl`OI&Ofmj+H zs0-f<5Rve@3dhEms{2xzdHleuSTfj+Lem{!+AkQ_Qha+4{af%Y4Iun0MxS6snV~hp ziuK7>hTpLT%Sv0xuH@d^!x#cIQm8k|d+2%UjVv+yYVB5-p<&)SWGZ?K**jCD( z8tLugf+UvtH7@+^zC5A3m2;EoZb>fiL+Aiyr^5}NBOMNJHgR0f!^13xeh6cpr3R}7)7#da@Pme0~MW%fWDaw({{TR|9^ZLj;e z)h-+J<0|$$v}8VAGRp9Y$6dm6Y~`ySV~6qZ;DACzi4HCswq?OR zEb3)*&5+kIGQp?hKBj!Sj2DscDFtPwE#yhQT!FsY0LLLLV}h?t@Ei12@JFP1NrQ1% z2K{kZ23*Me*2Z{&`Rx{J#hBkHY*VDL48kwOZy8}j+h*97&xR$8mInyVq_#ZiQGI=e zCBs2DNL#6_%(7(I16w$d3|vF#>u_$6XUWLj{FHZ^OLhv4`aaej?G+=u*8;ek_lg;Z zFavzQp~Ip-!Fxm<@=@UW*nZL8B}gBY*iP7S=NuXWUVjV3S57L)iZUFpp%xW6MpD1- z;njNbyEOQHDt%VfP-F91{*9qKKcNCu)!{9_(+}^5BzOD*zJgQ6ta3C?& z-P0(Y3d`sAa7Sh+V=#%8{B0`Ur-H1~afeqnskcrA8>By`T7wEYn*1RZKcZp^#$OzR zaTQ(~fq8O1CD>l@6Z}4w2Jk=r9evHP%~&1`NzyVQ4HO(mecyvD4FWv((R)LLDao@P z=ymdbkxM}k*dP>UnJ{B-*bte0@QE>pY&q~NJY;h{M_l?nP2?>NUyv;9AHT>M?5&^wr~5yq^D@SWS}g+4-XErP&1@On5ABHY`{p5oKV z7${9>NiUe^1;6LH6k2w}R>zyC;$+ngeD}8JHW9B0Wov24Lz)87$fn;FEsEW_w_8X9 z^g{P`18rC$5C^0=+G-76wVzMdTf$pLj_sq%s-pe-B^JWCrCnY+&wqb^X+g=_D1t=7 zsrZv*vVZF9kiG4st+9x-kDDA)xE$$~f__aR3~3{67Zp`5r+%x^?s-Z8Q?ZLI+HaHz z+8%9cak5tkMjnUtk5!! zZs}R0Xp~LIaI6zXDvm9T5-OyBgmfg1)ESU(OIa9*OxiL_gvL$4%1z|%PA_(aXJx+6#8MEr~C<>*FvFX z9bK;AzJ)9l6nP65)0C1pqf}0u(g8|c)#M?}f$haH6b>yggOMFO4XkC}VhW~zMvD{i zHJ#EqNa)-^yz6oD|L-cp}O)+kW6} z8c~4{(&Qa-z-aoUM`IPjiP|`l8>ecqV4W4}FYzpOe8klqqo~UsdbS)mRTi|jP+Ng= zSTS4RQe?Hvk1p$4Xi?%lCPF5JnR8dWt1qeGDFZ&48cd7=-NDh|=P{1{{sc!;PRUxJ zU>A<-HKgp~xz(bA6HBz3VPyD0l=Hf`TGH>15sza*^a`1ba+PW~sgP6zR7^2u67_iu znc_F}SNNrhDO3SA#K5L0LmDqJe?eWrq(Tyml6upmORV+nNOx{+tseB6^u@qu_-UNH z%|glmhx$RH-%ruMg?@wwOh589$LS}?D!~QHAX!>BLnKOJohT}T;X-?Y^|Q@IHk9(I?_s<&0OHA?qgJLGY_2@lVSn+R zQqD96E0OPHi#Z%~%nN)^lUiWP*G?=jKtq~r&MIpR7UszBE-keF6Yo*II*M6vW(iD?3G+}JyA`n@h1 ziJwp5b&@d-ugh;xjk9laN`Vpk?aX+QGg|g@yYD32Fp&7cq-^EyP{DG|uE|rS;*Ra0 zTrPsQ*>dL z^c{s_W}BHg`Ff&)(b*lpo-o!&A+5JTRj;vN;mtnx!D zw&R~&QHJf7s2D>Ve+DpOrKk|Jgje?#7rkbJq=Ci?l^{}IH>plaRkks$YAK4)sT9c=-M2Ft z*j%oYS&UHZlx4-{89qKtJ`!?t+ms*C`Y%Ox@}&o-&Z7f$^=-(!(!_A^=B0OA!Z3^S zX;+ks`_yY$qur4U7nOS5R!EeaCq=E0gA5S)`!t$WOV^I556I>zh5(OoJ~e#ntuCCm zMK}nORBr9S<0b}*EcC@oZU59H&baf3Tsa!vz0GjIU%b`!Obu~NBakHcD#pz=Np4ZO zFVdy{x2d4SJ?GUV83Y{J3Q1a%aYhn@@Cz7I&#b8TVj!}= z+VSZtr8n@#wBQ>kj=%}17;pt(4Ocj%STuJRDg{e-vUUOgv-X_57uR#>$=f*?G&Zgx Mt`e>?u8OMvH)pYAPXGV_ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..379aa7f81f668f018c2195fe31f181a3c331af91 GIT binary patch literal 41461 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 ah+Z=zxGpXUic2omc*OEcNVbeAd;WhNDIU21 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..999e3894164b018cf8e7d9478f6946804415352b GIT binary patch literal 17494 zcmeHPYiu0Xb)Lt*UtB&#>PbH&Yi(H)C5nUvSvDQwh?qRG!lJ!)vWR*x0qZc3tFll8<#Go5f`at>e*tpo-5|+ zeZ{_dzL>A~7yIi2#R1M2tqs_Yh+q4M{O-CX`2u}ADh>Q1-b39%1l3Sz&QMC`rdfSAJbK5)8E5w{s`BbH0m#q16!%5F9 zSa!3nUb3?bjTy?VUi*-(BDuJV$eb=WM00jly+v(Vo~fYP$Wy2Z&*;+>aUF|6D;Un; zY{i~AU1~~Ekrly3EGMHPxumt2q=L_ZsTUm6vgKk5WV|&#-E35x2+=~+i7=Z9E>Wrq zCpll1#61Nmsbrsz56ow_>xpFerZ3xDpYjZC;LC(O%N5V@Qv-v@5#&6@+3GY8XhTaO8^4&*&9h zM3&7FtsNIp5o^V(I>HG%j?_deaYbL!U(h`Lxvz3=gu!L*rnpRZ&*mk= zdJ?sH^V!!spU5%9Z4;-4+{$ftamsP)nbXV){QM-qPku#yz8m1@o!iP!mS%A-M=Pvl zVlLCI^d-~!4s#T5rLp4sDWz*JO>4^M$dt7HS6=lotoi|ePED)ErA&;gDPQ7J2xCqm zY?mY04=ED2trV}kjJxtO!Ij5*18M*wY-IYH{z0s2jYEDF^u_(#cg~;NSFlpAxPkE_ z;}qIkuy(T$sESm3TxI8k0|iU=WA9%x9TVAz>?3mEgIw9ND$?d$2MZQO9z3>vp+=@uBU7k;VvXv3u@5Ot#D)EXr%yDV); z*s|R-1}HhXM#)M;tXl3@sT_K!h8zKR$k&<$QMSve_HL5MonD~-a)^2n>5P)RgU0JF zkkC*aQbTp9(C5V6X?0S^=Fiy`%Py6r(_fk?*J>qMnU^!oI*pi_gJin4A!GUt}#@R`DBj@TM>bMi3eiyR5?mFeT?@pT7R4Z>H_%a!M*{@y{ z7gNEPL&28=!Iy*TMO7V2dElBR57P)A-SZ4qytPNx_sld!We;nAOPndq%ck8VrroGL zG|m>&60P=X= z5)UUugRY9mVMNRrAl@J4@D=nVU;`^?0}9$~87&hE8sf`W&=FS9h!2aaH5D|JiYJI% z1s!Dt9c3XDbA=EDnXjM?QqpLjdb5|+v;kg7O@kLGuAQil6d`ewpp-6d{(*%c6pH+3 zZaJu0z)e6+f517RoY7K*^M)05oE7!zPy!sWsLW2w5e(!JpPQ3zDW&ZYHvY z$W{>d~ zDit+wE}pLVGz10qnG;tid;(C}1>`KCVoDJ>2^W}8I58H5`wFI$5mgJK=^TqPxdTkf zPk|JoYO_UILP^5FPHeW?s9L8bbqOW)vu-sL@-tMdYs_+1wLV{~c)Ok^#3Ie6=?Ohy zWSHid%Eu8~(A8$99Nr&kTYobzV>E4TiAGId!pJRYhSt{Fx^1>Jj4AM$+TvLxK zm!pKfv8j0L^QMZ}vCDDB;LjpGVFOLb69@t6GOoag`#H{14o2y(Q4SZSM>$MFfZ|X} z#2AT0$z*JKLLi0IbZZ460B&go06Pb{g%D_<#2Wk}2i3_OV{;Et>pssZk7wlCOSxJ~ z1UyK&C=A3zrIW37^WD-#dJMLRooU5kK4vc%y4H%%W!o4bPD$FiR>95u_&P z`r4^hYA)YSi+nrNO1CnY8?$8sFCPUr)LWDBB2svE1_J%~AMQtz5w*d*BTUGT!>}&+ znRcJRd@dr)G9t{fFwWU(9&!O;oPsb;<7a{+nDyvWA2{!0&PQC%`{w$AEH^+rr&7NcVd065Y&Ycv909F7^O|*e z&35w&F8bV&9Kc9!35}#_z3y^FBe|8vxQi>(df4a6l#i{-)h&%AOQCJk*GUtzpugtg z-u)ifL!g7Ncajd6C|HG#uxCVDt2CS_Nha%>0(OLaCy4B4(J$dK(&Tm`1QVPXOJhrt zcR)T2a!qFm??hP+UNgHOlY}5fp2a8Dk3*G!4J#^w{HfWh1fV45UEUeGD^N=MF9b>- zP@wbyC#EDa3s5k&XAR}(g4fQF!5(qaR%NEyP$;Tf;eHyeLg7BO{>}zfL#JJIw=l>u zTar6O=7@Zb$PKDwR+MUL7~D-z+2cf5{XB|Lp^^WwKioe_Q%@e9Vq!~B+jV};)Gy7qba zJtO$HWa?TYKLOX#UMN8N_eK1AQoj^TMZF352Un}Sd^X)2aQ{=TY^v;^g2AO%CxwDJTOJs7gj$?&3{C{-J9 z3i2jEMxIT=7y(4V)5B0KKVzS*!;(5xk;?x^>qJ=%n1&qw9meFyGl&gMLmm-6miUJG zF_!qn!xBIdVU0{!$J5CDzD5qBh@Z0VBNRcNOVY?Ip$O8*gRGG=j2oamYGu^6?hQeVonhTcG$q-Aa=M}PDeu+$3tg~Da2Ta zPtzi#+dHBgeLhX=IP*yu$CT5|o5DC7Z;ekDF`K5ZV`C}=A%BfRtX5bPJV~!%{maVSn^9mzCeWJjw}&jt!wrQqH0cd5Q?H zP@6)u;yl$y90n$}qxyziJi;U3+0~24ZN)$|OdO`}C4@r#BS)j(zgg@rGNQf7xgk^f z2d+?9&6IwgL;{=Avm^w3v$SJMze+jSlqPfK=WR;cXoG~%er`a|xAGNlvTkCl|*Eg689^cUY0BVxmO zxVDayJEl*Jw)3rgtM4U^oL4aU-)BC@aLfnumr}@3Wp46Nzngp&V_5Gnzsih$mvbl; z17msFz@bmS*aB0xpOy50Yc>xA%;r4YE89Y?n^y4#TQ{v;e(R>B72p~1H`6*DH;qp# zWkP<>bt*F_)j2I!I*rAozWC(&ZMuMf4ViR~Zb?!S@_8aJfXoDiK3VpV9q1T@KH>aR znCTZ%{Qu4U6E*QiK^&Yt^>W5{T{Np3OAx> zekOkl@9>6aI}RYaN0JLTW(dm+BUk94iZ+z5mXG138%1F(%r!v9swhe}QJES#gCP?` z9(4E16#o?>U7Z~6bh2#WV69pcxDK<7oE_NoI^JkahW>p1zk%owY?^m*AZ|Q#pvGm> zj*z3{l(7WwEu0fc3SBV*ISkQVA_^BqddaY(oYesmNxWL+EY53zUOnt~ z1=%gy0SdcC^pu|ZFcrz`5jq$7P+mkzXwdr*L4%H^s#~81ru`Vp{eQ)BA_pw@_E$AK z_Z1C@r%&Y10|65@655i{PU1w`z=4T@6KNch03Ru52HY6PJwPe!$RG4^k#2p!$VA~d zg`5k1{Sk3SNpywGF$^J*>g~L^`Zt{5BTic;iQM{1dbr z#dV+6IQ9-i6vyg7yF`j_K#cv`Kct^K3 zBLDJ&LDJuGk7xQTM2d@TlsrN(}iss6Y*^YKYqiH}oT~AzY!Y zp5@-%Qj=f%=qCT(Z!%h`o;q#wXu?@WD)5#;I_`i}&f0vFsL*$F0KK2u$AZ=5ZxWdz z@+~62ON57w7bR=?jjOM{zd&}2?x^&F@KFHpA%Jo99Ku^(Cn|p*xkKuooLK%*QUWkM zgt`8`w*1M5DfwW_krdJ)ki5E8EaCc@_;`%xt^*RZ=sX{nS{)0e0`-|>n zo5@020YaJ`IYETlljM_gqSeOyg6+hb3pSn#j#DF&#I4ge-^5YbNnDcQwyZ*=s4nJ| z-RDX7IpaP>09^8l8z3vqD!p8_PnWEUZR1YfKcZ%92!uc-uG#Qan;oD#=v?eA#D~_@ z03zb}VW@O#7woc+@7TDG!H@6Q-1a**I4SFgn^4^k3f^kj5W3Z3Fx2&&nxiY+YB8+8 zW=N!*gg;~$q@0A|yEeDv)b#J&4C3C+#t?_5wTw1B4mYj9p=mwqb7-nNW-HzXx-||p zu^Fd(d7MM$`5cm5qiS^X?wz3_@V+&~d&K%efcI@a@8dq;j97HbD$#;t_EJPD7>qC= z@i^j>E$=k)R(Zbzy!V6mewX(LLE4)@XqLyM6w*2l)Rn^WV?> zzt21)4F0#@KK@zn?3ljJV*eJLyz=vqzRJ5x`bB<`NRdd`9qS}Ky(O2C3J>UG>-Pe) zS_=7pB3d>p(^9$S!(1gh?BBUSKaI1vPKbc_d_sAUJ>-}+4b|# zrB*}CIO*Buf+Teb%~=>>g?P&VQ(aQ;=>8ql%|9mcT_UXU|Ae|VaC7LBmjR>CmE6N8 zsP1b-{)|W%$U7StjP~M#e?eYr5~S-SnBBp2jY*vZU*#)JZrG2NZ=+lYKTt(JCVd?* z9ZV{s7a|u0qf)xwq+fgxVZ%kED_w3n8pL%ynDF^EQx}7&yf;(bnDA-XsHef9x_VYc z$VuReTNwaM@YO)cUgYmkmOlh>KRDpa&kW}RL+l$&PTs4+^XkFu*DS(!nmz#7mC@xp z2!{rZ0GAKCUS%NvOLMU~bPf9!FE%HAk-qxH<|K>sbzf}umI@}}hWsDTAYUQFQ@T#E zQHbA&5a`;?>Q+bhQpJJp2sM|R^%JN41vZtK~pvumb?{PaC#uLjF{lk+h6=F1cCUvzdbx}8Wq)->wzG*hRT?Y?cn?=f{y3BP9HK!c5l8n&GI|bsTGBv< zhM7F`{*HbUhdaMSX4Eg(j>f@R3cyl*s(=rZL!T;8O6Pt>R^)m#eI#x=@0OL9W!1)qpnjAScjMHZcseV@*9zz6 z@%f`P`9)Py%qh>$Zui?rg*D)XQT`((un0@ABr6VubMtq~mL%C!9PUbTKaC*K@AT8~ zplSL#a|XAwn=&xFN2zw6wZ|zWh@2rpmjLxc$-Mc|Y;TyKhnTIDF_MzaoqWQ;rYjFB+L6M#)dK01;fN+tSo!--UG<3KbK Wi|P5{d<>x+LgR^i?(PKrCI1g9_qgT& literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_tracing.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc92ec4fae13b20a6dc94a7f9bca6a6199e2e131 GIT binary patch literal 21302 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 QzhZhYy(VR62arntFBQt@(EtDd literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bedc4f5d9ff9e2ad9dd0fdbeacf7c9a0035bf5b7 GIT binary patch literal 29045 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 L@}3QHbSVEHfcU*S literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.5.pyc b/tests/__pycache__/test_tracing_errors.cpython-313-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6fc80e9acef7188c083e8dbbd291879612e3c5f GIT binary patch literal 33694 zcmeHweQ+DcmFEmF0Pzief!~rqQeU7bQWQzaqD0I3vL(`@O$=ds4LLAGf)s5EU>cxf zE6JA5zTMSsvX$J`W~1HOt(Cp5igmYFrCeMWC7Y^~b-60nS zZ7ZvaYpeF&>zVEufI|aNs7vI{5bWvh>DTk7=S@%l-h18isn_cgU>lgqPMzB#2>*Zy zw%OE(`00-=g76(d7G!Z)7!rvX#JFYHLacNz4cXF`pfF@771|L8aljGluye>oTw0!+ zxFJs(_6&K6m*&}qeM5fYAF3pkLsg_|sG3v{)sUK@T2ea{Ac3JeQa4mj>SZ4Hjzz3K@uElC9Okkq;063w9{j*;mt!^$d;k4 zWNT13e{!8%+a$ask)ASY3>a$MJ!J{i-@#@G ziHbX(Or|pN%ycT59)Yy=Q1Ug{Imcd`O-vk}9?yiFitW%;BAHR@pO}l22@;>4d3-K8 zjucPLWoG9xitj`ccKoQaDL*|%rf0u0O%kx5no5wwgi?7j{_5BqNv4mzI-W>OKu-0^ z)I?(DaNDVBLuAWg=u#8m1DN*yMCFJ`+pP zON^aOq|@=Kgi=M1XqO+$q*60V-KwnEczk9?31I%2cyb~&IY~>Z*_D`$*Tm`!S(KW| zL}vU zDMW;@ChF`6?xR`5Erva%ErWZOPVQjFr+=oY5kB1@_JHX$mp99EA7 zIEs2$ZrI0S`o2wwh_YR-*k?Hb-R2qK^A+(s))AWrD4|1yzkcXmaf6zw)R@3Ko}?vO z!}1~)cGjd-cBGC(q=Dmtkcw>A3OpfB?1HPVW#z+lET-?Fb8tp%eWL7ouCLG1uU(Jq zjyA9|;U*T-_idb?{g64aDeo*TdN~$@ma1lP#xvtG4)q&&+{nu5do${%MAwel>MhE-gElM1olXT@d5V_ivRAaE#M(;sc1b(!f{?TuXo7kRu^8MLiYjP^!s;+c7lUA0HItU0un9(NK|FeE(7H*O6!%&u#Zb%0*kw`i<4ATo zITcGJ*~UqViN#&Twm{`9QDzQRb12Q>0~++PmU)PzkX(lEeH;&LpFj>EkWrf_`KkC^pS3e zeO6_>0D6Ny+mUzKyBVJl(fRD6MO|ONY@t~_ zB0U1bIzh3|zJ~W$u>-bR|ElBCsrjbe3sUcGPt~%waoJb%vCZnWpFeij zA-F2Pwc$@T{MorhXWK1b?YCa~lb0?AF3se8y-SYXtfTjyRdBW6wF|zcZ+z+ebkSi(Qgi9>$(?uj?BA{Lh7db=#rH8%V80o zm!DFzZ%RkMoidhSsYU51)Xk5mkp<}}oQ-Efg*=JnSDpVO>FBb%X~`YTxr14$f6*OW zlKSD7ZNsqOcLADS%gagqEVT%w85uwRk<|Z5Kyda^`Mc?C`{#5INcroB91*|xo~vuwkojB=oMb(1-2jk?v>z~;U+u{lSG;UJ^b_o!y3nxx7+O;S}9UjcLro9Yx8KU|ZfpJgkf)c4R* zTyw)(ijBi4Ceo={woZ)gyowq|>s5e*%i$6+k$xUiF7*~=Y{U^0=~OH`k2{?TxT-E% zn#)(wk~-R`;hH3!7mgEkX5entn#o5VHV>%iz@SOO47wROGQlH7HA&dZ(2Yq4Xp(ek z5{kCt7;n zG@x4p61_a+#1e=6HN{Yw` zYB>oQiUfkaEoDT%={0nRZ zWsmHGK&hRH1D4WjQGxjY913lqh?#<762P*J@rjApS-A4)Sq8jR5S=DCBqBIMQlyvC zsib0?m^(Y0CWDy$C71{_M0Lzp~_!4V9OVsH$D$1!*UgX0*SzyMJ$8NvXc zOp0&GFvLP$dKbu(n2Qe!fwzu8Mlg5^gQqbV#QvDc<23lvcA0^S4fC4S3H8vwIsFY zB#I+9UVM4p+p*Aj_*(ys&HuCGe{^J@`MqrPOW8AM*Gqy3I>w*n#;259Cd zEb|^_{*t9uF!jw|TI8S1GCyURCUh6(Oz5;sem;5zjS0#paz-;RR#EbNUY1=4e`>vU z?7z9Le(n7O|EqQDynEYQfs2Q}HT=!tHv>8MwybOWidA%W+_M7X`KPBG(yWhN!uFl> z&8Oa)yVUxw>uS$`-T$Ng3&)>d+71XB_I}n+sxZAdLMlj{AeL;H&`fwOa&NfKos#A8Z*xtn6fifNpN! z4Hk7kSOaWUvuiVlr~o!yvb!X(SV;OK;z;2XoI>>?HyT^3+4sW9<_SSR@bI8VW z5c&@f?z-B`Mpi{x7i2~$vz~%#!^8(&fu*nrI=7!@a2cYad!|vId zV6%~fO&s+WYz1JmSg+8;dR@7hKK(80E{hyCvg@K?Q|HTEx;PccVoOn3)WPNk4mL~g zyB;Li+-L-wL$n60J3g$$O78<7l1U_- z!JsHYtfdH%ss+&9Rx?7L1=<(jKfMC2ly8JAgofApa{l94--h*rv5&c=&7=ABy? zwm*7x?%GT9+efpG@Lj6~Fb}g*F4Tt>ogHhLDR+DU%#>e5%(w3+?N<|j-*L6&+Noay z^8s3{mEW>m?D@N5$>{ujM`^@>1%kG9H_r{!iV^->2 zbZ=aez?9pY4PsdEyD-Qza#Al#EkbEV#*cp_^>VXwzt;_+D${ z(_oAo5T6Fai6B)un*!N*0qn;N*@l|A^e&3@LtHu0>z*;JLnqq zROBkEAtw*;p*@iTzA(hr@;uMCTgnpkBF9dTZfMJ-d6xWg&sBF@4BqKB+0I_K-3Sk* zB+wPHaoVB?Na+?v_lJTF0F${rQ;hmqQG-3RisePP1yr|t>cFAfYM|z?x$-QZ5deW^ zHq4|Hk<2{SQ;~>B{oE(nD;xYT!Tzg*Epv}`*%z^cpS%CLQG=hmUAG5@x#pW)I|4uk z38-}vp%{mB<~Gm>2S1EEI-t<0%mEyyf&qoya1Qg*Ha;w>TzCHY{Vt1tP6hl|%2i!f z&9hv;ZqtF3T+Oe94SOpzuXKm*O>Srp#*}`GP@B)80jo{(hI~f%;AQs_>?<7ZMYsoQ z(go1&#}PM`s~hYj+WZOq8u+kKKRrBGzK93ps_wb}XppP=vhEA|sOo14>b0{Ns1iC^ zoLwX1k}4u}YEy|>O%3;{9&C0Ttge&v^A_dYp(3IPclvz*t{RG#=7aB|C3%k|H)@Iq zhk?diBAd+QBTo^bL6L|dRlImr5kL^`SO}b$I#0OFIKcpL%R-b_zAEN>S)~w-rB6GI)lA=X4)Rcg6eop!E~sx5Gmt$R|Uxv7)xM)9ST8(zK)a? zm)I)RWDbnX4F^U5Nq4rEYeH9j1&lnjsC&CMt9$S)zE5YfgMF+R6|j4 zoknP$XK#gsYI1GUO|EEWW;0hOQi*gjqrvO*K>bztPalTyd;z@n)hv5!REJ)_{rnRE zmHoAAW~p2MkyQ=k4^L*vbTa{ZbP0d$!vHc3zdVP02f^Q?Y*aqBavGoFcZxL#nzB};m z2QE&%^QBzPfhF&O^N%mPtCrj?Id@A|8opEkw!bB5SpCgQ!&!F=PpXkcY52k(o|n^8 zaA0-m1+3Fp+K~DP2twMac#)gZ@NIA7!|mbxMN}OezftpkMqLkafeV(Nl?b;Ldn@YoU9bVv6H$$^ z+6JApL@eRzg5r<_n_Wdx(yt{VAqOcrVq2v`fZ>~A(jK}M6>I?YTvUbNey-mD^&av` z=(5)^gf)c>42)0}NGa1PCDw3ER`~E5>Nm8pslr8;iX%jCi z&?Xj)V=7ak+6c0EZK4}L?cPxsX9Nkzef&v2jC%Wmt7a(0#YDinafjeZD zxPXHz;symo^>c3BjC$S$B06HQd!2a4|0eS6Qo3*=x>|9oY-q2CAKs@8DVO(oO->5A2>gQdfRLPXSSn8Z59i zdo%?^MYME@Q6>sh zS**99x%_b|kk%$cdz+Wm6%fVhy&q|9Hb^Vy3ltD75ickpe0d6p{~P8?OkL#j6cD~V z1;hiBb8QlLi>83^X$pu9%o?5o_CiKSYqz!%QU(@=!Xf0jkVyUj3Mtn3>2Z>Qn66a_ ziRYgA9Ib+JWhGs&08^>exz7;M<`jdfVLJ>~2eB?t6u=~I6$|4qlFeVnZjq`UR5ikG zNS^Z>rd0R9EO14Fx!t=;RWXoWtg;U)+GbQS^yn%E-EZ5dOQ<*K5|BmJ>W(5ubX|fM zlaE0F3p^QCm@1+}IF3{&FwoTpPhgVk5JG-khoEZ=s4KQnZLpX8K9ZqUK%FkFPB8B2 zF}QE*p4-?6s@?!CWCeNyRc){>Uu|H#rdXct;Lo9k{{;W(3cxRG=nihx)`K~o>H$<= zZ8XQ<_H2H8?0e6@_58cmtG4g^=C>T2_dIsXEOW(XMWk;7r0-h`>8mE$UGIP^?yk4n z-`VnJ7ep>~<=nfLB<(jZ?V_evJqHdgO1lvEYk4_61qW7_UckzYrCAC}zXNW(SPh)! zQWr17BT(XZ8i3zIhw+|w+pi>k&~c^Z>U}#8tfc_(gm;6Bwixb^uKS;=ghXyrAiUF; z>)Q+Qk9OK1G{?qA!Qfq*8D7VKvdQ#egDJiu0tmBPT(i6bOz}q3y)z;dnC|oI$MV*Q z7`DTsU$CJQLE)xPkqhDycBlm?*rM2j(oL44MX)k@Ynx197WE~UWbRuQ4X}C*27f(| z`<8>je?2(RYvz8M(CRge2bqr~z?FdGwV)8?Bg!?VL&M}{*>9p9Kvx12YzwXg)>V1{ z7)2E`G~wcDfVirjlMRT=77a4~!=`9)6_()$#(ZM5j%o8ohdLFJ**CP*8%tA(28z4-UK97Z)(Z~Q=Q@LrXzchLUcT6$a6esEbA_!Hb-w) zQ-ip{16WkKXU$I?4|Hl%`C3g4ceN2vOHi##5Z38gF~|KyxYO?gMN-8|>qx3tN!{_F znL8fnG^_zx6Tlwix5Z38@+4K8aBDEgmM*EhM>WCyALvt#xwQHW+;D?8Nb4q3X+4=I zM_)8)4X(Si@)l*HKykk=A*Rn#gj0dE1`W@xdFcl$t*vJA0l2@-)bU`CLDaQq9`4U~ zJa}MouHk-I3?PV;ubJaPJ99knP&lWV_sJ}9s)9-Me^-nLcN0>W)obQ?P@ozhb6Dlq zG587u=Q@lCJCTI7po6JoFmomW|I=iGL2jH-Oj{=+E;O*lp%5&|*ON0jxU>fnvwSsgn}~$LJ-v4v=$Lu7vs5sM0h{!fwK32m=oK z$yXuyIX3+ey_?KCBsPtZk>Rx2G;QP5v5c`T%RQfbGHro;QPOKi^=l z0`)SyCduAG*0r~;73J56(|xmn+od)h<>@EiXD8ZCy z8$`exfaZR}a_?d8FIj2@Qy2EoBL8HT$?0fGR@=v{s7Y;?DokqAGU*ZIjn=m8>bvsH zQvc(*{>PX4hjabI*}f;Uj+6Irl0$wS`f0$U<6&QWP4!Hu)^@OD9G%Dg(9C47Hl4|S z;zs-Xi4Qv7Z^=gDzn*JCfVw6$;2d(*HQ}gtEGYieFOD@xKdl@Ygv7izFxKpwZ`%v; zg=QOsd724C^lzZm-T|=^H51%cYFvK4V2Lw{E^%g}FEGvoXX84+(OhpJb1b+Xxp_z& zUoLbrU*i70XPCoRJixwhEn#VVbV;KdAG5AC$J}K)YZnm~zE$Y9#vF4uon!9Fn`7=R zYYrkJEd4B|dI8<4#O@&<6#}J!+8m#83o4_A(V-WXf#;*}a8SEQ!_H)4LlG|ZNgtd$ z(8d(wPNxE{d__xh>|eAbmu%Tz@*ML@!>nDd(aJ+uRS2kH4MTltd>nGn&A^d~MgepI zYgty+rSUmZo?^_`t&!gyDV}fUY0C!Dv-sdq7r7*IH`-Bd!nWHUEB`9+F~6S zG=l}u)dkJas;d$E`A=D{edDSO-AUT5Ykih5Bua&h7fl_2e}%=R3UQIGlAH#wCtJzni)yloA-HZ)w!#E_!>8T&=vxG< zJl0yUnPX@6Rt!F}N4*uMIHDBP>!7IDnTL8^WsQn{GpHB*9m?Vo z{fTu@(#okoTC0A;(rR1>kn;tox26DD-Vf@1U~*o-+k-;AwfGWWC)68Y?=-oN zKK=FUE{hz$mL-dv3S_aqs4VKJx8b*JWwpi;f_N7OOYh*$LMN;%mGlE%F+MJW58cuU z?%Ef24d!B;%U0@Y-3FiStwu|)(Px{D#0nuTc@s~B$dyp6H-OwWtLtw&Fc;ZZ4ZjC4 zMrVWTkQIke`n>Q509l{J6X2`a;G#W($)_-Q8iP>`xU;r;8ATzhx`G!j&W^HAMrrTAV$=Nh$EWFM`DoREWj4<4O!b*sKw9)(Y;zd!-vIoBaj zwq?`|1dHc3;wm3s3(icZGtt%GG=BkC`@j#asc^Fwr)f{lK?Y3BCWtd(F-q-gcXSnh zr7@;{AFoTD00W<{>mrG-km*bUzL3DaW~WqWd8!Y}1lD7RX+7{=HzGlTt`4bGsX3s? z$|RC${5(9tcP}`*;3cHh?h#Qip?xIn1*!H9O|OJH-gq$vqZC2$sYG4cH|11~TqPc^ z!o#jf`1rc|F*(}LDn6=BgOyI`H{R2v7K>7)A!2uRDGuz0flF8?1wSe|OH$+TjdpNs zp~^qRG>UXKPF{v(G}5mn$Kk8;5hR_$;4B8`F?bz=KgR(5U&!BLK)ZYF2I&-bb$a?3 zBo)U2b&mX_Chekq+1tKi#eL@$y8nf(<*p6#SE>sbsMYHD*a9(nC;lHU C8I9@y literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7b9429d923311ada782eede1e9a80392c18dc62 GIT binary patch literal 12537 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.5.pyc b/tests/__pycache__/test_tracing_errors.cpython-39-pytest-8.3.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16af2c316f4171671e48d2ac7bb73109fdb376ea GIT binary patch literal 14330 zcmc&*Ym6jUb?&NuOuwe5r{_KU>V4SWvDdphvwi{I#RfkKEW|7Zn%M2$xizyh?#FPe z#@-!z5O}NzAyzmLkq4V-_D2wl5(y!M5X!HBNR%MN4}_vpgdzn+v6LT(MES7^-hAJ= zRn^tAGvjd*lOFZ$Q}>>G>zrG4&v(x`cN&9(`2;@iT>Q!6+8v3+Kk*{|N#o*?M8b00 zRwAJi%4#NBR?V_V+fBQcl;2bgz#pXldY-RRBO66-P%{% zhq?ikZSHR!s2vbG*F4xdR6EqF)~c<;wZp9=wIi*gwWHFOZysyiQM;pcXYEca;S`qd zT0X7{s`zSR!>Y{$*Mq7g*LMfkL#iy-_XO9&sv_4Xo=m6_HTr5ojXHxH$=ba^`83QYO?{)5p`5}=9K;6#LThR`6Om6 zztCv6J6^+E>a^YSxK2LWzJj0Jg)1wLdTeRlo5}g&SmeTy0Nn2Xg`ChHhwD?o^jk2i0sI%p7xy9Owu0| zIX}G8Xg}8Q8h$wxGpeaP>7chL8`iy!caQUsd!q58BSOri{mDhgt1m3I8_jx0RIIlg z*KI61{;;$J>eszar|FO1R#l&GG@Jey>n}Chs*vD}`*0{bX_Q~29RT8VafHsOBkG-~adYr` z;-!T9=V&i^x$Bnx4yYkgZJWy^;`lK+x{y!*Jvm5$qbPLpx+TA<>y{e4mKsm2XK-Cs z7?JXPHSs6lRznjBRqke%EVK`J!{DfN2d-Ni)+-h@TTkMfUQc-=&?JkVSFtDt+@R6=dQwMnjS_PPbGZ;*>LG^z-_lSU&(uD7O9H>-L@^v1eLZ+ydsUa8k?_d9A-jYV?V?n9Kz8-rZ7eh+0K4Y^=qVqDLYtBa=uu1g}Z zd1B*2LwYq4a3G3OPt)%n9M9kDk;*Rjgm-VNJNwHqzRPq%27{QSa_cDZa_52K`)Xz%nemf#KRrhWG8 z4QuW69miL$oSAX+p9+ZT*(#~iGwyNz&d#_q`St2qQ~SV->nEE|+s~NgeIw;3!OLQn z#Tgb4+#G-W@`^L>IjTBa6~+_Q#g13KSrs*|S-OoSUXs$YfHk!K;KeKX$&)ClZuxe@5O9h>aC`>!9O|+z>!RLa+`~PHB4OvPl5Jb~dn1!7T61>B`T@Q_va{BAEqwtTx8Z0G zm4c(+#_H+esAs8UHvuF~sq}`u2&}xCT(^L(NiU@aHY_i#QeI{g+eDRH0@i}Z_OdFE z7U157wP-EEN&+`?n|TkGUr@KOTmFeZE<%}a2J>;x=i%xV!Lzbw~H=7)Ng=8w1KSOrd*Lelc7G~`U$xL z+@=1b0q#x;y^}^$&c+yrZ7#V1UR|f^0T+K%BWl z7_Ex=Ivh!CyRV4E25Y|{QW|7CEHshrNPui+Znq-a(eoGdG~Qx;0EM4i=yd!7H>NN( z?rPJ!k<^FST4igR4X&SSgqu&_YWUVgy`POK)d4tgHR&TPh;RBBi#u7+_=)Y%Go((k zIK|>Liq})5XcN}%j~i}T;{cfmc1~0&LVt~5TCw`_l6R>}7wOMH*z-2Yh#Mjq=TIaY#b!GJ*{~7cuyv|y#blgG z8tY6!#YF2DNYPC8B-ON1A7_<9@%k-%^aH$n5QRV5Y`_-wX4JsxLui_r7Mv3()1%~_ zYA96~sMZUuQC@NA}kn!4Jiv2`~dC4UJ+REV^Hu^1Pc&|h&cY49JK%Xm*fbL zpe!`)ChKSohXW)S+#CUth&+M6G6Kss8X>_bdW>z01ml7P#Q+J$`ZTfO1MI2^Z2XtN zA-Vtwu2HT?8(K)w7hK;ZSAYb6MXmq|#)aNVBltA|5=<%ytMnnk9yJ7pARuFUs{H{H z7$3)We;@ZBQ3o)S2QgoVB8hExStK?{;E9w52@VNOB&Y^RVCHr!5*&^rf&K{ITTMfU z=tkkvl5)&$$<-fa1Ibx3tDodhT&`=U_F(nnE{A>|y>2t69PiD{ysq=6 zFR$Xa(jO^_PuBO(p+n2q4+DmU{V!L|F8<1Un zwu?xTm!To9Vq}?a2KML#BPOcU%^*gGJ$u)Z7+JZKxx9*kD82eeVtqqy|MJS$VD15PYMmoCcm&}YD&;0i|dM-HgW~%vJcjB ze>W@I3)_q z!6g~stpuFT#cwrd!eJ*6Qwyi+MD7gqfl1r)4$ptqgnMm@|LsE*^d zy`7OO2yDebVv$2~SFbOzSYjcVF-|Jl&HMS@^LlIh`JNlVo&Aj=UFEy8nwPIA)}sapg)6g`k;pcbF1wd9b|Kw_;8MF-5>#?+4y&l_4 z$XsBHl+d3=aRXl$(3i+ta@{9o3H3RAzfUQDwhcfsyw!792z&+nrE3id?q?i8rnuPc1 ze0)=a`=DSmmq>b zCWSKirqTSNnbs>lV7COAsohy>GKq8y|l z8#1~vt}l@*fWm9!3ZQUQXaWc8=xB`z6mkwmkqQyH0w|mS6i#-FL8{AW6;E{$3JZ7^ z1b0AXcTk{ks!t=^{UJu)O(WYq2M(i=t)HS?kw(*zMz*WS)dds-3ftr&4ZWGn8S3t& z(LN&=pinZ_fWpBT6z)+&fx<(f+tb0zjsQz9+*Js&w;qz&T!3pcmfir9pKM&5*WxN1 z!KDPbW~{Ymcfq%KHn@jyNWGX`35XyE0nj^+rGA!U0kV+vF=mj zfVCcmvy~KUo%YI`!*sS_txMpLfEI#CbX7(Ihbt{K5u_61eptAn7r37g4$!c16(mNC z(^Z0%Myh0aaCt1S(k5*XV$V2wO)vx%VGrFeCPi}IRDd}XTbz~mqYt&9`!yN4UsEx; zeoDE#DUs{vl#4XvLg*^Sb(36zIfzG-E5Mv-p=qI6NB3)A;C@k?{YI`pn*+d{gOU4X zwAs%6niltKDsaCJ^=XuJPvTCyX=J-!1BcPb#$F(BWrDr-Os`X)?`VxP6XqO;IZCx%&~Ij63|~e$ zJuV7A!*gbBrT6GZ={5G;LqbIMB-`N*AxG0)*$NLcdsgr7(A&L$B64<^??@XrCq*|W z6L)!vcHHS1wX=5iSAdbqsQo!)Lhej4Umk;!FJs(3DCx_Id~aK~g6Gbnym?CU=5Qud zO3eA2{03$|%$49!rDt*_l54-3-0{5H=Xc4LLoZLJg){g2{!YCVJzPL zIV16sE%_J5-h=!C;zl6{03=18Aa`COSCBC=bUeQ>6w8>D`!XiOFshZeoG}^c(}rid zcGU*5ev`6N3&7X+C~KtAXiTG@kt@iU{3E%7j0v+16@t3_(lv=|gwtg~#$;T{!PLHt z$sRQXVBtt>kTIFwk}>%f?mMFP;lBGZUk4(IZTE9%kys*J4`zh~(m5Ci5PZ~4iGG){Q1=~cta2P81`%jqte9^iO=KgQQM}{Kvoc9 z)I82L5051JkWS(IUcNX)8yw3r2b_6wJ$e2DPE3cM{Zf0O<5#@kggH)*1xL)`=*vx$ z`j0U{?}l4mCcm;6o@tNb=67@EvgnmanuwwPQ_%CQ2tR!g`1t?=J$gzwgpFeWNhF?d z_!>K5oKe|~pilgl5ZHJOo4}S}8Y=W)37!zaZ;u+P z@m^0gu?5mz!Tm?nBu1OUd`(9Z+wR9iVsi-KqDW~{Wjq#vxFdRm2c5S+1hD_SmN=Lt zuPu;@TTj0%N0#f++wRtik}L}TuOa4ih^uf6efHol#G+1j#{GqO)G1tFZ!caw76L+$ z_Ky_ZIeGHG1S|a&&e01jwgIRoLwN+C^dFGzK^9`o9|DEkjcfx3SW63I{1KXLld1yvn`74}O`H@bqEBk)WxeD%IL;UzlJ*CJI^&aF;y4*Z ze{pbF6ISHZggdY9-U@ zWN>KN>{}RaGbga~3;4exb6Qy*uwN3uMrv06eaY1$WR<;#r0tkZlIIU8k=W(`Dy--Z z{sYK`UpvW0#w`6*tD#>+qRqY1p2t5-(fgCrYnsO-Bw;1_Ak9Fa{xS=Kxp~hvN%h`Y zp}4d1vk#km)Hyu8g!=%BqJ6|FV#83hPZUP+0QV1+@hJ1-_#VUe0KWH@;V}yJ4%Uw5 HGS+_sBX-RQ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors_streamed.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing_errors_streamed.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4ecdf7fc9457cef596dc5a38c35b8660e0994eb GIT binary patch literal 29781 zcmeHQeQX=YmER?o6u(7EqCP0eq9sc<{UKSFWyc?~D#^|V70U_NbkfodOG}ijL?+cC zl{iiVxXIz_HbFlQ*VfnT;f&yLsDT5{Z34ta1DqolxZo~!s7SWlJqJCYE)M?Z&N;gm z#CN#fd$XL`CA~H!M{W~e#`4VU?7W#bvu|hT_ukC#y4&p#z#V<*AIFB53&L;kf;o#C z5wHKiBnaOZWI+}Og|JA(AjVCDCSs>3P)my_i*?jCFow~&_b3bG>HN?OBhq%FLX ztfcomgR8Tek>0pN_755p)$g zx{g|*S&%COf?T!V(`{DSC+incnqOC1=OXB;b#xo#I=LnwJS~x~MU+shD`Df9DHOPh z?1x0fnMkG5*+h0coyrWswYe|#JOuW=&rc>tc8(8c;lk24mP}|;;LN+9Lg$fud4??@16~AvDvbcnldACy#Y{HP$frBXg>xRJ zR;ic52()(86cn1^4%{>Mn45%AG3L+T=EF`(iq){RsKi>4vdH#yGHTuWupp$LV`V-h zj;x11t!4Qm0T$Cj7#bYWvW=qbd~)ALQ+K|9WLK<#<%uj~F)eK7{A`27k*0!Lf?7Tn zgPyNuamF*_G67uacx-0*w9trpk0`rj_oBG0FQ_M|mCs`Eq}i+QSp^|w)6uM8d}|>*&2l68@CaQdUY2(q5Xio$ZM{g#w8vJm5_F}r z4q&$+g#NL!QdpQ2WuZq*S$f3DXJczvX7)y;96D|sj0U8&;{TzvRxY`;@*ZUmE3pu1 zvb4szW{HPU#?!%6JE%u+0JW)?*lqk=YmGfTd3iQSIaev;&LIQrjxzdoV$g{A*TYa zY8Ebsa_yq{Uq~-&&-QM5`4(zuPi@KeXk&X_yCpxiFMJ%fu^wlkIu1|n16kgnN{Zf3 zzssJ?$d3!YXU5-<04r6lr{9Xtro^~-D;nBOw1?e`_|mxK|d)eX2^?PCwg7zZMs>#>+oc9IGY>^b_Qw2wZXA;Hh9zP z*pbL&k|ay3cmNBVeExx}ICoVnbpC&2ZRTKlYGNb^Few?NtqcN~2tsr7ASOujsRbU{ zt1Aw8QyT%1qDC0B58Bm^CB;VoHaT8gOF7y|8>=|RGvldDHjx@mD&<ApC3$#qB9Wq4 zl_0!O?EDdtO~@;DzG9(W74ncEy`n+_jZS8!CbEhcCpcYHoXD|S1pQkH>054eJl%b9&2zaK09O@W`SF?k1ml=5`K1tHqbQbgo`J@hf zRvGHY`TLGJ$5C0~_X~t}8CzC3oHBniW6ko~h~l&|-F5kM6o*6CB>} zG=8h`&yQTNH(&PE%==cn?^|&qdTQ;AulJbsQcdH0&6=5-HFGuVJ`zN`@4L%?@4Q$F}h+ zHF7~3c(seC%@kaz$rpj@S$z(61*EJvDEatuo(C$2d0r6-PUX2_0L=1S(|If7tZfP1hV{Vb^Y~>-4~p}d8r%za?R)${T+j@&!^2u-R$ZD_%*-}cOB->@jdXY^mc%7ygS1!qXa;&S1PiQ*C z4z3;x3b86ya|EW{#SWAw_-&$&)ku|ICDcNo^m5Cdywa=P4=F2PrLIb6jRttWdiyZWPkR`F6 zXT?(ZrcoQfPa|po78L;57sDmOH?1B+F0~%z+#!6^s0_K&sDP`=lDRcCLaA~!U{uAG zUK$ODw5(_GxyXJa`6y5$RI5Nnr}XM%CFpoJfKDZqUQ9^2N)(h{nzV9d3rK6Np|r{c zeSN=4Yu%DdEAP=oNh_xUX|2;eabwxOx6&Fgk`Ip{*GCTBk@CkW5*{M z+*2WchM;+l_%W08bS9lrEF)8gCNpFQCikNE1@v&XNczz0M~?%1GJuym(c6XIgXlek z-oxlUf?gQCLG*AAM;=8F-#Ci8$!>^+$|>e1Lzub;y~of)z)c=UFM=L?c?7Z-y(oJ4 zAydS|WE3y8CE_%pNa)pKk6&vd`L;=R@Xv^ux%i@DfS zxoCVg{FT|YiJa@e6<76lzc%YyamC$ue00{`nsc>XbC=m2H_Ud6^+xsQXqltsYl7Kg zJ?_f~-|YPeuWz9DAJ(hFjWUQF+e#DvlO=wHiN9l4*YN7qE}G@Pj1sT2L<72GQwDVT zL|z}QAi?HtSsZn*Vs;cLrY_e9iyy zoO5N)vFaM=K30EZ2G$E1pghu~4;{j)b+h&RUz<9y{0+zH&VShU^KEkve`$Vw?63P? zoBDXJet+)E@tOLs1>x{ECCvCmpY@L??rf0WWbYC9{KA%50uhV-*m5#u%y<90jb zKU?7#2$r2~2;1P|+zS8ht)6pRdLe%PftEdO(tAx-^jrITAZZwC;SN#l%ZQt=**-nE zS+=veK?gTi2TLx`xDCE7frC5S~(R+YqhSwSs?}eeUGKJ#z;N@-_{o4Mit*0=v)?v zZ||AR7x4+B3gBBN{(w`BOzbg^dD%CNb^U_yZ5_wA59l6MK3artON|!A=KF>vmr;&X zSsNE5qnrw4w4tPoYWTL1v=_~-1V7WEMk9PmdXSJKQ628YdEdPOpNJDb8Tcem zV-ace763o1DE#DWpW~{bbrPuYDQ5l``tB|mog+v|?6nYFjgt=e&V>de!#s~*qU zA~(z?z)j2!J699BU|)SVhS)Xp_SF~dtM3Ox?EMHWx4hMII{Ay$ryI`f|0Ku~Kxviy zn&o)s-vv(fy*c>P!MVQ2=R=VnKK`2JLUdweRTeOU~MP=du~+vYga)!MSW+ z0&{g&E{J~7-?1K^Fe7!bs|%2uk@5TQOI>+b*|+lXP2$_3w*EHrbh*^uAWc__{Q=u_ zl^NqTW=s!=7;g|U-e`yX(`}Cajb+p8!#22h_W}QIkLR4b7vkq#Ekj$S^IcZ-xAgZw zk}<6OTNr5W#vBW4`EeaT4xH58YOtj_On1uhWJQNbXV$gKW$csOA;LO-DP=Bc*0si5 zjDrYFQ;Ho-S&dD&Vhoj=)agvPl`JjFjlPKbDI$_Hwf>gJjg$3vf(z z5f0f{3SHN2&KrBBs1)JmS1kvd`xt;>%O~97z)fYWj+8al!fK9ku#_qTWA8VjHoC*% zFW9srzr%rp*@z(kl)X_q>~Qcsd04l@p(4f#5h8}5i}Az3uvUwqLSCC>v7ToYP-Cw~ zZ3x0{rG`lo8hcqG3*i#MuvU*Dms*dqg6Y&vqcY@9qXMofb#)l$E@M4VGACa?Av0sI zLq`LOj$#LIp5I753cxTke8D0My6)2%>tf~TO5e!Rv=EHFeBlLaLw<)t8SHR49>aBJ z)G_Huxpds{cNV0z)=*jz3~POTze#J|l1nS^(M3rsrvhoM)AjcsLs|ny@60T^~uW3SuL*t_I1A1&k)!LU1qgCccNw-L{=?d(4L^0Md0!LZxF*qd)x z-q`DjdLlbmIa-+hJumCW1#gq_XmP8tmys{kh{~ZRD|kU8DyIS&Z7M0F$a;aK>yYv8h12gk>1{r&6gBuF0!D#eVL39$zxY>Xf-uyas zeMu^6q()!|wJGaNzd=K2J%%j#(L0FVIC>OmS7S`G@ZLM5?ZJwunb${x5Le1n&O2FT z1B-Mm%J{3|@8_^|4S(Z&w#fX;48Zl80T`{|Y%{Uir7r;8*WiB!_rn$eaZlwXccpH5 zbGh~CLjbGGt4b$ds;JBPTF`*pbZXUX`L>*A`-gT57I4i|U_f4RV$U0U-~7_gzI3MY z%=-`_Y_hHyD?F%TLtLde>d{ww@T9^|rI{8W^HIH(Y3YX69tS6V1tP0R0Vl zb8<20f8x~8jBj8etFJ!?{hw-ka?e}CXIg*p^y&TYG<*`!zpQ1}xBExY}qLXvvjsy9O$;4?J#3}gBjDiMT~D2 zG5&xZ^8ZqF>?|+)r88`Ui}RKKp^)eNs$Ph{x3c9izm)S>(XZ<7fg~K2xr5gmop?P6 z{`W!oMqr~9G07q^x5Bw0`z6_oYkg#iSjH?Nt73jIoxb|_kfNC3YonMCB*;l&2yWQo zbWch=t~lbjd14C2l(^!G$GL%3vj{prKx7`s!@NYn^7Y*m*7sL0693N#QE)Er-_Gsky8FwEA`Z{l}2jY9sjo4Ma_mnHyjQEH^jMiWz7i zjFy|#$QM%65=)awH}HO+#y z99-SRu4xmAJRoYV&ljG~j||-{OG7c2+WDyvz*gXB{HOT_VQacF2FW+A*-A z>}(L0fVYX~R{3}D@SN-Eh4}gHEqhi=@3mOb55W@fwnE^J3QCG;FWTGd;Zz6}3f<&7Taxq*YvMj-+)}x#|&^qX-3{kd51zeSv%&lz=)wnS(mrJ`2S!y(l zMb@zpf}X%l1nS^Q6>s>)~@xSu~cy?kk%^Q6F1I%Z>805Bp-k*tBVkuiYyI~!CfQE zC6f6fKA{f*S#k{vAWH+DV;pVX6|$`1$Z`{tdMzwMmYOUu5z(^ZXi>{XW4VqZ%fOP$ zD94D5fd$DZrve!bl$22oS=RG~-V1-ecSe>Cw;)UME$BnlLX8@J6+Yr44VehOLyG-S z;xOIMnpqs6#8LA|0ha7ySn^JmX+td8V2CBD<(ZnAH3VtHHn`F2L$4pbrP)l(t;{Fy zb~Ewf5M+&tAX(?fllcR=$)7=+F2euJzZ);<=9Xj?I2LUto@?85>V?_1fn4j(Ghdx) zeK@z`kvS=R#a@m_Y0cHFykK9YU&7t~PT!1gXd(98(7b)sMfm2hwXlR6PSk=Ii#z;2frduB4s++iqub#ZA>iF0AC(CEBHnxVOVvL>5uPrqC zA>6+K|1*B@jtZBZ&6lcbFV#1}L3l1JRbfyoEe_w@aWLCpV4&p_T0ysLTr4;V3=XB& z45~34RjHHBTf`0#&IwaZw%D8^r$6_LeU1xYEsmqq86Pfx*SnWtS^FPl_c8#!e0%ux zgr(3aK3~%N2po5RP-2JS+XR;YAfKD6_%pfP*XU*y>!= z885)4t`D6MF0)!JvMT9H4y(z>|w@)jbk9NaESS~(R+YlW`Ajb;1ZN^7M~S~*_;iK~jB zrV5D-Am9Qa@ja9IB0g0}j9XBtyC-LR`8g!s&g#*^B1o*gO~#`|H5-lP8agV}F8PSc z!R?}Clv9C>)|Qk}4HDOJNWAbd?aq)mFhsbS6=rBS*$U~8^77Kd$K!ln20!&f*MaAM z59pKITib4DZ)G#V1({oKIhV@;4WX^kZGP5pGA{8e!pX2mI`e#Lcsvb9nolI*2<#=s z&E$Jf=k1TrQui0Ml6StDSGI(?sJwQuCx`CFt&5z6k!nwLZc7B$F zE`LT_>0JGqpA5~`Z_5R?&q+NT>ULhRZzzDeGrqos0Nt*6`-Y454G46d72jF+t#!x6 zSw~=A3d~4>EAG0__CeD3qeXQ68{j&4!8+;^LsqH*B!F22@&oiRh9}nJdG0Ef?Z(B)qf?Lo%aIA- zWIQDzf8IVlu}`Ust0(lgtB2ad;rZ<(`5YO~CgFHQcAmacmQPd9`}<3*>>#ZSPM(J| zgo3h2YK$y5e^@Q&d3QgNv#*0Rw@=2&Vo20*uN9ed`!{QMkE8ct# zKI{-WY$C2z{P}x@KTVO z>(sOGsZvw%P(2@Dh|~z0*96rz*ayO+}U3s?)l17wal}t8Bb$CC=5!RQ^zHYzGi2k(srtQnjkO@;|q9 zq^!!iKla}1LH7*6p$N!i9Q!n)db)f1<4w;)PygO~{pPmY?GWJT9!-t@dy63a3ntiM zQ6u8*aXlijCOl6+5xR**xeNa1v+c znv1v~Pa1R&c!-DQSq6&-N=V6omv{$CN$EftDH|v!#4pfnK){r#=Ysp%A%|5tp zU_Dtsuz_sw3D39!0d}L>IZ&d-Sxk1+3SF55*;qh5r>@?n0_wSR^;!z3=hoHRTrD&S zvZq#%i=Xjy!Slm|v=-1>iLSM_0_u5n_1X)lSE{SGr9sI4Cs)a3wZiif*;+u0<+>I- zvfFDAZHwnsJvtd7!z3~`&WazI zOioND70WABemVr3CmbO7&t!JHVv+n|Rm%BG!SOb7~r%&(1I!E-1xmkKZ&c4g|Y zvrh;Wv9huh6lBXzQ%nqDZ}bSz8JeZkbCVE~SPpB)1Qdl_EH~g`G40$W1Vx-Lp?y=( z6!2@H#!*%prmt=*3aLi{Dx=A+_pJB(r_ zD<7z4G3^ZVojq98Dawu~k9C^5GUFpVL$$0-pq|CFa}%H2dmwYTF{_sbtsINN$d|J? zqnS||fu3|U`dB&bY*hP*C_80WK~&ad^&a(`o@#t|d!b*a$3H(#lH8 z?pA5DRS;q}U7aRvgjfvUUPk=k5jsq~t>Vp4TrLUP+C|!?J+zk9*R{TmUj z>>L$kVYe8w>=q|p4Eb4E_KS!)bksOl9pF~)|A*XKy6oJ_N3c zh9|zV+*)SDAAcBb4LW2SzF=a`pfj*hyJr@QSwi?jp-0dXYGrXYgU3hMsKY$_2>hbT z<%)uya6YpppbPb2za7?RUbE(c+VI3H|NRc|mQspQNnJ^y*w#zvf-)p>a081OQ@^K*~*2kef9|2pe^ zX8aDRVXexw^j-1Q@t>kl8jC@RIc-W`jL zhNH2_iSg+0Jx4N{Ncy4_4bXTP*5R?_MUx`MBB!E?-?@A#H)x0<`npHR%>(U@XY z7vL8q#SCRJ*n&X^2HWmc?tghAI+TnK`?mOK!4}_WJn6ev;@A^OB%&lqyV!w^O`P8G zDXvr%3tiuzX-S-nPmT}!0PaM6^qzbG7JP7X8PMgU<U94FmiY;C*zGEfBls z)!zVpK=(Ff-S$>SCqy(lTWL|pw=SA!)7aove+|JUxKf+|a1=vuq~b2caV#=E8Kqc| zz+z2MtP`j487o%6powULlwzifOvb{AB#8n;3@dIm7LL9Me3Brz)+U>;GqIecPrY&;2LqFBe{0OJx$by&SkS~NU59zOx7kaC_qoQRA7 zV4J|!M&jgDBpFtp(y7F#;_0K$9{@F_UMXWmqAyR3M`9FK6L=E{ik&|lvK?u~aaSyK zl>8pDhNdWgz)eRJljBK63=@10DNdwV?Q*L=(WOsxstJ|A?fS${vJLy&k<-)`eVLAg z2QK@v4&`855O_P$MozU2!LP3^J^{bhvCWjo#AX=elkr#^KBfc>!~8dmzv2XcQmqrG zm3k}|1}+>OkA{;V4+zuWbC}*f2|?euYEJwPemS2CXN6m?iVMSO*QT@k=Op{ARFjr! z{(9quXQpen&PW}%T;4f%&77y~GmF`6J-h#oO>mTach$F7{neQp_NI@$RkPkT*S%{l z1TVFuy}jqGb5*Nmt2U*pHqBIRzAK1!@0+WC<0|>g46zjCSOiD&ti9=`z3JW^hv2Dw zeC+g`d-08)l(hH!Y8s@Sb+gi5^>13*d#;D4)W{8K?{_+QUOFqKX5WNbFX~IM z6jWu+LCfb?^D@wJsxq08%j`og7WQ>!xF^Xm|SBDY+P)2`KL_an)ZZ*-@mA3ngEge7C%~NXRhIAY$&Xh=JrPS=3P-{|Of~BA;YYtjI@8xBn-&DiE2OZZY zf7N}hB~_nkoL`3s6!~ps-TA)%bn@(xxyrS(m2K(Dwv@E*JKd?uwwWz^r=9yCb)JO8ef7>r1fI4QU^=&9A7D8EGH%#xtQop2YGC`oAvin{(FAI(=!U zFC}%|aQbGYF8E6|VVLuG4u(FHmzKI%>IRf%)%f-6QrG8+y};`@$u{YB>Y z+oitU()*oaUytqmE;Gh=m@&Uc#Q1Iz<9qB-eyYgPUr{vWmA#M{f@b)EQk>UNdejXQ z&0^A{keQX2%|X4?NRlmVwcZ9YoSo*F2$Ci)H)6G5MTFBBJ>(+xz7K)qCX**EB_Wb+ zvb|koHF=+=5SJFo9ChjX;w#`bv5iZ(d_oA{6Z8mhsR_#wKtdhj@^;oEm0~R+=K(2} zOLk|ZSU8vG91OFsfD~&L>x%UOQY=`PtA{2%0l9!@QMrJRl@A!NS&OL@t0YT`w%c?P4W2g*<>mQO7$2m!WWW*KmXk>^q)Hu9_(#ba7+13YO|8<2t*FnA$U!jlVW z{jlz{sMJQ3U5+3g(`p$~r_}kLdx7HZ(hewcWgN`!cm@{E&6B4E};W1d9#@`EcGXB!e-!L75Io*kpf2tKu zm}1Tms1tJn&vPliykb0`Lnq9YF=q(Z_w)dnf&tATT#D0!z3_I#L06!gwWFQmpYXO&Ug2X`0>-mNJ)4c?RdiNpSau%evIIs~UNb7M1w7iI zy9;9;W5l;{wUX+I80Qpf+MevPEr4|GSh2Tw5=t|)p!3ROMVu150 zK?eF%!WhE1GhKQ(PN5ix4!AI(#fKzh=TJ$7%lFb)@}v)i1Kqw){~Zp3uWqsn0;POB z0_aR@MHLL}hD-hiir6VACskOsCNex6J_UC^Ho>5r3gQ!_78^uOnIb)(h{qJm@Z_n9 z1bGCr`!MLo0GDWFF9!QC;JBUa$K(MF9>w4>3=U#2fWaUJk7IBM16+oY!x(TdO^!g) zUrZkb`37Ek0)qes$o&LfF9JD=K@fvu7#zm{2UZbZCMPlRBnD4GkSK;A3laN#L{9}gP?-hSe&VV6XX<>=*8-5VR4-eW;t^GxY&20;%)bI@wSv_`)5TG!q|D2U~$Y! z&1tFmj#cn9oPS~3y>X_c|4P>fYks})pEjnBJ(UW5GZhR^4?H*B5=psE+;UaE`O36w z%`Nw;^CQ#lwJF!Sd3TZBamQ@8SnpImM8zBx&kJUY^}IK8`pe$Cn7)I-zgj;P?i4}f zTsO`99m~9nnZIYLc}#t$gBJNWqs-eZ)1cnDNrQTsOx_>uAwvYE6Dgya=e@LgHZ60G z-d~!p?7!-`{K~amzgfL*+PVI%it~Nn9sJJVTNR(rIM=5f8|Fd8vGJ}MD9`Snl1Q^Y za|jzYPuD&3*5rlN?>H`R`CmOh>zR4%>Dl(sf9ZQ`^7EOxXHw6G({<0Kr0O|mDEVos|HMcc<24nDdu{k}NX~WmxhC#xoUpf$LKD=ZO>Wb9uGj6{r)ZJnQy4 z%~PGyUQwFbA@=v$rgoY!zRQgHy&}Y~iXz0Xn(R>is?)Kzrs!(LfDIBKuBtfH;rVcD zFT_9E(tM;*`gOGx!-oFdkTnFo@WTPI*AO26*7jiV7z{7~7PpCD-jEMC=4uB$IF{{f z0no;k%)TX!Di#5;OF&-$u@2dp0kJrf=NxRwz5*b&hIPgI01%7wH$61)1hBwm2nzs; zgzz;%j{pafEC=DG?4}^rlLcanbqfbWFu~}-Hz5VFS}*uM&p7~w;v0@0Y^i|nZ#*c7 z)oL3;=7qIkaZ@ap6htMAn{F%&snkZ4Q->gS3F@?3Kvl_-r8QWvRJ$68^>Pra)nO#6 z!$?DTnx#hkkp*JQ=yJ7O2Vzn7PY)gKhKt@MOfmSvk#PrrSdCjbcmi&%FyvMQvD*E8 z-MF=K*}0XEC}RZ*5VR3A{4E7}E#THl-FO?9URiFfGU5*avDG=?PzA9to8=yPg4h+~ z`5Zc73<1O<_@IZeVm!owEqjNt=a(0Vt>GXR7vDKY4v5uwfpwuP53@xrn~mi2Vm9fz{v0?h9^?Cz#X#*fD%)%&Q$qt*f&0ihIP}dedA60 z#;=2h_3MZwcmAUJa`e58m+P-Q^F^>Ez|eB}Ez9{WKd!yh_wL|N250)7nDqy~|HNCC z&u7Zz)X`wN{Mgw;bI$5nXG7ZAkdiuXI2&dq&_j2md>H2Zo!iYb(ozRY-GI`p8oz#B z>c{}ezQ!k7#rIdQ>u)em6-oW&(v(x|FSSj%%or~=V}7ZK@p2L46?P~;)!^uFDVo|a zV1vX5?G=Zto)0a(5dTPOKD<@>Xp$0?E?sC%V( zrfhxelcjAYoh83Twz5}k8^TU02KAQE$y!1Pbme zxyDx0(*`+&Z8Pg?KcJ>IXwK+drJxij46a(VodZx8<&il@d?Y9?u=fYuwg%9QE%#*) z1)0T9Ms)W6R!y@R@a1#_$sBusgl`-~8tY>1Re}DH1Kefeeh%z7btt^hM$nKt?KuEd)k~J&C4O%g(KQL<{0pUJJOjK{wvUyuGsA+GxZdAiVGei$Mcl!Zq-)9*C6$ zO9ERNhiPXJU%<*BOIKY&b?_xwI`|di`i!P_bq3@r5`rXxlOPxg$4^DW3_MbW;6+KN zv9)D(ZYIccP@J5=0GD?Z1(RWjDXvK{f@KO_RdtIG8%)i&qCNQ{*3VRPk&LgkC3MJW zy{GDs_vpLYs#>MDfN47$rxssFmQHyKBSE!NrwH9&n+w0kuzg%^k>@dZ0Rx=33CjO- z;J4z?2B$gRh{pjLIxY)tt`@^(*h&FR;`{@Yw8ZY33QjL5H`{j zb0ska>j_*2=jer*X1Gby45NCS?F$}`M-#DR2Ce@&RR0V3pLhxoKn_~>l+C%zRMX~S z>)A&Ec^8*2mNQpclkzsBD!K8}hUwy-lxNpxb_-UU_hhM(*IYRA&e37{y6KY5AD67XFgjh*%FgqklJ?wjp%$7cmMfg7SayRnpkDBl z&$@lr-M$M|mpo~A@7V)$Wi_*9>(gcHXUhCwVr_Asi~Pps0uyVCJ5}bNw+gP(S!aFP zS)Y;yFBGMn>u04w^>11lOgZa$QjOe@2G4Edd1);L7Z#SD!8Y}!b*bx6!Jp}h7r7}7 z-g4J`we7sWj#7xnJ}A2u{8jBWSL*0zz8Tggy3l1!4Z6O%CG zgu~@gkoy{{!*7ASAWv&?3F^=T zWEXj;z?YIlWJ@}Bay-b^teW+A0WB4Ql72x;S;}^l4b#KO7YiUm5hWxUwRssUtDV_8 z`~GTk@P1IP)nPGEgIBS5PzrHL1(Wi`gKSlq*(VDMeBEl%aCOe;>4Ao+N_z=>rtP+5 zgCmi#7l^F9?Nd9o=hNK8cVy`_qY*6YPYTQo6| zwoza)c@Tvk)EomBh&p*tDJJ(~unz)oex~!jrr!7W@#3QxJca??6mSq@0~kDx!66I| zV4w+u1~JJcLTncRN`!RsADFE&8vu?$OfLy)CxCtg@@E)O2~ZGY$1&K3!8inq?+0kk zmip*#Dhw!2-Q@lQXyQF=X41T<*yrQ&Dlp8TA{=!$02*f8a;^E{(H}ki*3<8pFI#@% znO@sF?Rw;6qs)1W8HF?!5Yl)aKu81o#S2gGsc_En7>ZM_=YF8Lt>_};%Jv=rgIm>hv1jX4=yc<F}Z@#8;1jHCYvC!oY4tTcsLH4EVZpk+m($)^!Hs#cRRZ z)}p%;KMqPzW6rM>r@Q1-OHrl6~ zg$3`olUeK2z{niNdJ!L#VCB-mXk`2eQVSLpG>i>)167801Gq&;>)pp%)6SF&(aNPd z%gUvupr?pdE?O^!T&OckzrUo-@M$ir4NtWO5c+cS8kavXGVq3L zi8Q+bZOn{I@;O+p<5n(O9b^8jH{y>hPR;Bn=Q1|^o@}jkV@|!lmCJT^ql`sk#X4%Z zbp>#1qanAVm5WA!kF}nd^#`PJwMKiN(A^ zGuY&-7`y_(nT>jBKr{xf+Wm%_eHL^}wm1~{yGPJE&(NTPCm=t2FLjD7iM&%Gx=Ag+YG_eGg zJ~U%FO+cZ^Cb(FFoP{FyGavEOXUc4n!sAicg_DejhvI~Q`$^R$rs_AI{0%hq2{u;) zdRPN1k~**=$+itq1qU3?&JI;0n9|G%d^A$0 z245M!4yA^E9S(lbd@cH`jo0c^!N@XM!R)I&+9m$7UfSn3{n97yTW|g4YBR>ym@$97 zh;jenZb(cwSIXU<>7BbEo+a`?QNe3yAhH!=g&K(HKRA)&SWu#l1+n#YnsjdK^ zbAg1+f#Y)IW7tzD`qZSYBf1!w)l9=hoFsfJ|+L(T|Y zHiCxGRjUOHW{<89eo_79A%Yt`av}%w`zf=zlWro~}N@;DZ zInZOANKGidy4+g6?A*%53I%a1uLay%t{d+!gIgSI$wO2Zn0)hL&sStWdk`Jjz8>1@S1aMFnQs zoM6nOT;va8F!i$s9?hIf$KfqGg9S+tw;778V$6*oeD*+fBRC1ARSyiP;rHt{qmaMC zOV43IjSTSJ2x|THa8m52A}>={bBV(I2I4bTVRg42R&!!!k zzxKHK1FQg42Z6Tf;w>r9R@l`+9RzB-I&Qg}FYE{RfGO9ej|+&-JFN(lZ2)b{Gu{Db zs@C7IZ_vARXuZ~#_8!j1ZN7ijzTu{Q!$YSI%v0YVmxI84nNI?oYk710XBE@Uy+5zG z)c2#o9}fN;90WG^rkWl>2Z8uny0;oeh@q_{_y!r-goPMQum8fv+ExJ{^}nD zKcDd)x^^P%ed6rEoU>xqSqBh2CH36Ek0rnt5_(drFwFVGPX}c3(ozpg-GI`p8oz#B z>S4|Sivm^Rlzsi)BJ)(c)W2Jr>Jhcy+4+B_e&_Coxl&CN&Zq+gd?F{}mWfK}oU{2cj#vnHg)+m#T>`syNl zlNP?aXj&s&G#@?+pLArlVq>w9xKaxHG3e(n;Y%2q?_XXN3780geVI};q<*YY^-PMB zdIGzIePpUn(Q5iFoA4<6@TRtD@$aD6BQRy$#(DFs0K2wa&2#0|b9IgI0SuRwN+JOB^>n$Y|e1iqDXXb2uLz7&Us zT=O5OWuv5>L3hOM(htISD^G%aR{g4_O>hY?8mm}Y{uCDT>0e|BZxD?8SR2N$TDDOz zZqEYaj)HtUqpnr!g{`_+w{SM{%Fl{Duup&7;ld$xz;KzyMvaOL5R5NDr3R}xb?~#+ z)iTs?YPDeL=v=b22F5jNjN8?~xQl~vt&ZW+683t)(@+nMt}HO_q08=K9T;z8HFUHa z>slAF3dnq!(#SGEqlSYmaEDl8$gK#*eTbt-eJ=*IELL))#{*xy@c2+OzlI9<6Aocvep1CchNV3fK-)O+bi z4GF8Q$pf&Y-lVFpDY}Y!guaaPNKS4SQf}legy3;4wY*uRL&Xa zjLrPdpyokD%Xdv2Ex$2%c0YBbPoZ+{hM8SQ)3w1=%`xWE&_SIapym8b-KMt>PuKON zYIn^@yKmWx@hfpNRoiaZw`U>Vw6`xGyt`x8zWt_sJK|ku>327Od-Hj5+EF_z)uyG| zho<9ygr?)|@048%{+<88Cbe)E`Cl#oz8!-f!K-~$0gL#4&AR?d^ZUJ0ze}1j zi~SDUlw`(uks0!*93sYDBF5czC_h!{=wDMb_T4{CRdEj+^!!4`C^M=nOxaX7B~JKQ6`W*8-N3pTm#9Z=-PU zUS(i1wpZ0}9w)fRf`s~Fr=O)T@SdX&97ZJ74Ags`X#l@?zE^zXV~J$wXds%Hh{qDq zgRzOpr9?1JpQB|EzVeA(efMQJ`S21uv;NOOR^+qP4DtOW( z2shy~CX-|ErOH@>dX!LV!V{-a+SIE0#~6#pT1oUJGM0?OH#-yH<~0dfN>L_H-Sa^w zpy#nMD>wuhi;b$?_F;}!%>nCT?PMTddWak{BR@lc5we1DVh}IijWsb2y2|eAc4W3WAHi#KfvH`FnAjSDo{jm zoH~79!PpxZAV60&*tT7&Q^P$Z1$i(%3W>jk5R42&@e`rxe+$B&39otnS#W+LRQ`+L z{zO=RS1J}wcZICr_f3M>bk}4RO}`f)xa$$bJ`p^KnryGv{l~glu{x7{`i zriyut&9~=VZ5DenS0Oh4xR`UfUhK?VhS>a`oU5BfYvwA%?mP#?yU+RW2ymLWr(FXmou7I$$fcL9}F(K_dDo;TyUWt)2LeU_eoYpK6uf&BT>917}q X8xWh{Wz@uhgZVY_nF*SpkMsWl6{ClS literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_tracing_errors_streamed.cpython-39-pytest-8.3.4.pyc b/tests/__pycache__/test_tracing_errors_streamed.cpython-39-pytest-8.3.4.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61d6378adae651d9ff4a2a46186b39070b815181 GIT binary patch literal 12834 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