diff --git a/.gitignore b/.gitignore index 37d20996..f0416924 100644 --- a/.gitignore +++ b/.gitignore @@ -145,4 +145,4 @@ cython_debug/ # CAI files .cai/ -.vscode/ +.vscode/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..7273e68f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,35 @@ +stages: + - build + - setup + - test # unit tests validation + - ctf + +variables: + DOCKER_HOST: tcp://docker:2375 + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: "" + VERSION: "0.1" + DISTRO: ubuntu:22.04 + # CI_DEBUG_TRACE: "true" + # GIT_FETCH_TIMEOUT: 300 + +services: + - name: docker:dind + alias: docker + +include: + - project: 'aliasrobotics/alias_research/cai' + ref: $CI_COMMIT_REF_NAME + file: + # - 'ci/build/.build.yml' # build + #- 'ci/setup/.setup.yml' # setup + - 'ci/test/.test.yml' + # - 'ci/ctfs/.ctf.yml' # ctf + + # - project: 'aliasrobotics/alias_research/cai' + # ref: $CI_COMMIT_REF_NAME + # file: 'ci/test/.test.yml' + # rules: + # - if: $CI_COMMIT_BRANCH == "main" + # when: never + # - if: $CI_COMMIT_BRANCH diff --git a/ci/test/.test.yml b/ci/test/.test.yml new file mode 100644 index 00000000..7f4d74e6 --- /dev/null +++ b/ci/test/.test.yml @@ -0,0 +1,285 @@ +.use_base_container: &use_base_container + stage: test + image: "${CI_REGISTRY_IMAGE}:latest" + services: + - name: docker:dind + alias: docker + +.run_test: &run_test + <<: *use_base_container + script: + - pip3 install -e . + - pip install inline-snapshot pytest-asyncio graphviz pytest-mock + - pytest -s $TEST_PATH + tags: + - p40 + - x86 + rules: + - if: $CI_COMMIT_BRANCH + when: on_success + +🛠️ tools test_function_tool_decorator: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_function_tool_decorator.py + +🛠️ tools test_function_tool: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_function_tool.py + +🛠️ tools test_handoff_tool: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_handoff_tool.py + +🛠️ tools test_output_tool: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_output_tool.py + +🛠️ tools test_tool_choice_reset: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_tool_choice_reset.py + + +🛠️ tools test_tool_converter: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_tool_converter.py + +🛠️ tools test_tool_generic_linux_command: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_tool_generic_linux_command.py + +🛠️ tools test_tool_use_behavior: + <<: *run_test + variables: + TEST_PATH: tests/tools/test_tool_use_behavior.py + + +🤖 agents test_agent_config: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_agent_config.py + +🤖 agents test_agent_hooks: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_agent_hooks.py + +# 🤖 agents test_agent_one_tool: +# <<: *run_test +# variables: +# TEST_PATH: tests/agents/test_agent_one_tool.py + +🤖 agents test_agent_prompt_system_master_template: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_agent_prompt_system_master_template.py + +🤖 agents test_agent_runner: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_agent_runner.py + +🤖 agents test_agent_runner_streamed: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_agent_runner_streamed.py + +🤖 agents test_global_hooks: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_global_hooks.py + +🤖 agents test_guardrails: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_guardrails.py + +🤖 agents test_items_helpers: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_items_helpers.py + +🤖 agents test_max_turns: + <<: *run_test + variables: + TEST_PATH: tests/agents/test_max_turns.py + +# ⚙️ core test_openai_chatcompletions: +# <<: *run_test +# variables: +# TEST_PATH: tests/core/test_openai_chatcompletions.py + +⚙️ core test_openai_chatcompletions_converter: + <<: *run_test + variables: + TEST_PATH: tests/core/test_openai_chatcompletions_converter.py + +# ⚙️ core test_openai_chatcompletions_stream: +# <<: *run_test +# variables: +# TEST_PATH: tests/core/test_openai_chatcompletions_stream.py + +⚙️ core test_openai_responses_converter: + <<: *run_test + variables: + TEST_PATH: tests/core/test_openai_responses_converter.py + +# ⚙️ core test_responses: +# <<: *run_test +# variables: +# TEST_PATH: tests/core/test_responses.py + +⚙️ core test_run_config: + <<: *run_test + variables: + TEST_PATH: tests/core/test_run_config.py + +⚙️ core test_run_step_execution: + <<: *run_test + variables: + TEST_PATH: tests/core/test_run_step_execution.py + +⚙️ core test_run_step_processing: + <<: *run_test + variables: + TEST_PATH: tests/core/test_run_step_processing.py + +✏️ tracing test_agent_tracing: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_agent_tracing.py + +✏️ tracing test_processor_api_key: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_processor_api_key.py + +✏️ tracing test_responses_tracing: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_responses_tracing.py + +✏️ tracing test_tracing_errors_streamed: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_tracing_errors_streamed.py + +✏️ tracing test_tracing_errors: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_tracing_errors.py + +✏️ tracing test_tracing: + <<: *run_test + variables: + TEST_PATH: tests/tracing/test_tracing.py + +🎤 voice test_input.py: + <<: *run_test + variables: + TEST_PATH: tests/voice/test_input.py + +🎤 voice test_openai_stt.py: + <<: *run_test + variables: + TEST_PATH: tests/voice/test_openai_stt.py + +🎤 voice test_openai_tts.py: + <<: *run_test + variables: + TEST_PATH: tests/voice/test_openai_tts.py + +🎤 voice test_pipeline.py: + <<: *run_test + variables: + TEST_PATH: tests/voice/test_pipeline.py + +🎤 voice test_workflow.py: + <<: *run_test + variables: + TEST_PATH: tests/voice/test_workflow.py + +📀 mcp test_caching.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_caching.py + +📀 mcp test_connect_disconnect.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_connect_disconnect.py + +📀 mcp test_mcp_tracing.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_mcp_tracing.py + +📀 mcp test_mcp_util.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_mcp_util.py +📀 mcp test_mcp_tracing.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_mcp_tracing.py + +📀 mcp test_server_errors.py: + <<: *run_test + variables: + TEST_PATH: tests/mcp/test_server_errors.py + +▪️ others test_computer_action.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_computer_action.py + +▪️ others test_pretty_print.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_pretty_print.py + +▪️ others test_result_cast.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_result_cast.py + +# ▪️ others test_config.py: +# <<: *run_test +# variables: +# TEST_PATH: tests/others/test_config.py + +▪️ others test_strict_schema.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_strict_schema.py + +▪️ others test_doc_parsing.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_doc_parsing.py + +▪️ others test_trace_processor.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_trace_processor.py + +▪️ others test_extension_filters.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_extension_filters.py + +▪️ others test_visualization.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_visualization.py + +▪️ others test_function_schema.py: + <<: *run_test + variables: + TEST_PATH: tests/others/test_function_schema.py diff --git a/src/cai/prompts/core/system_master_template.md b/src/cai/prompts/core/system_master_template.md index b3ad0c1a..f2b25053 100644 --- a/src/cai/prompts/core/system_master_template.md +++ b/src/cai/prompts/core/system_master_template.md @@ -24,7 +24,10 @@ import os from cai.util import cli_print_tool_call - from cai.rag.vector_db import get_previous_memory + try: + from cai.rag.vector_db import get_previous_memory + except Exception as e: + print(e) from cai import is_caiextensions_memory_available # Get system prompt from agent if provided @@ -101,9 +104,14 @@ ${reasoning_content} netifaces = None # Gather system info - os_name = platform.system() - hostname = socket.gethostname() - ip_addr = socket.gethostbyname(hostname) + try: + hostname = socket.gethostname() + ip_addr = socket.gethostbyname(hostname) + os_name = platform.system() + except: + hostname = "local0" + ip_addr = "127.0.0.1" + os_name = "Linux" # Retrieve tun0 address if netifaces is installed and tun0 exists tun0_addr = None diff --git a/src/cai/sdk/agents/extensions/visualization.py b/src/cai/sdk/agents/extensions/visualization.py index 5fb35062..74021e06 100644 --- a/src/cai/sdk/agents/extensions/visualization.py +++ b/src/cai/sdk/agents/extensions/visualization.py @@ -2,9 +2,9 @@ from typing import Optional import graphviz # type: ignore -from agents import Agent -from agents.handoffs import Handoff -from agents.tool import Tool +from cai.sdk.agents import Agent +from cai.sdk.agents.handoffs import Handoff +from cai.sdk.agents.tool import Tool def get_main_graph(agent: Agent) -> str: diff --git a/tests/test_agent_config.py b/tests/agents/test_agent_config.py similarity index 98% rename from tests/test_agent_config.py rename to tests/agents/test_agent_config.py index 44339dad..19f97cdf 100644 --- a/tests/test_agent_config.py +++ b/tests/agents/test_agent_config.py @@ -1,7 +1,7 @@ import pytest from pydantic import BaseModel -from agents import Agent, Handoff, RunContextWrapper, Runner, handoff +from cai.sdk.agents import Agent, Handoff, RunContextWrapper, Runner, handoff @pytest.mark.asyncio diff --git a/tests/test_agent_hooks.py b/tests/agents/test_agent_hooks.py similarity index 97% rename from tests/test_agent_hooks.py rename to tests/agents/test_agent_hooks.py index 33107cba..a43ef10f 100644 --- a/tests/test_agent_hooks.py +++ b/tests/agents/test_agent_hooks.py @@ -7,14 +7,14 @@ 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 cai.sdk.agents.agent import Agent +from cai.sdk.agents.lifecycle import AgentHooks +from cai.sdk.agents.run import Runner +from cai.sdk.agents.run_context import RunContextWrapper, TContext +from cai.sdk.agents.tool import Tool -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/agents/test_agent_one_tool.py b/tests/agents/test_agent_one_tool.py new file mode 100644 index 00000000..457e51f8 --- /dev/null +++ b/tests/agents/test_agent_one_tool.py @@ -0,0 +1,63 @@ +""" +This module contains tests for the one-tool agent functionality, specifically +for the CTF agent. It includes tests to verify the agent's instructions and +configuration, as well as its ability to execute a Linux command using the +generic_linux_command tool. +""" + +import pytest +from tests.fake_model import FakeModel +from tests.core.test_responses import ( + get_text_message, + get_function_tool_call, + get_function_tool, +) +from cai.sdk.agents import Runner +from cai.agents.one_tool import transfer_to_one_tool_agent +from cai.agents.one_tool import one_tool_agent + +@pytest.mark.asyncio +async def test_ctf_agent_instructions_and_configuration(): + """Test the CTF agent's instructions and configuration.""" + agent = transfer_to_one_tool_agent() + + # Check if the agent has the expected tool + assert any(tool.name == "generic_linux_command" for tool in agent.tools) + + # Ensure the agent has instructions set + assert agent.instructions is not None + + # Verify the agent's name + assert agent.name == "CTF agent" + +@pytest.mark.asyncio +async def test_ctf_agent_executes_linux_command(): + """Test the CTF agent's ability to execute a Linux command.""" + model = FakeModel() + agent = transfer_to_one_tool_agent() + agent.model = model + + # Set up the model's expected outputs for the command execution + model.add_multiple_turn_outputs( + [ + [ + get_text_message("executing comando..."), + get_function_tool_call("generic_linux_command", '{"command": "ls"}') + ], + [ + get_text_message("result of the command: flag{12345}") + ] + ] + ) + + # Run the agent with a specific input + result = await Runner.run(agent, input="List files") + + # Verify the final output of the command execution + assert result.final_output == "result of the command: flag{12345}" + + # Ensure the number of raw responses is as expected + assert len(result.raw_responses) == 2 + + # Check if the command tool was used in the input list + assert any("generic_linux_command" in str(item) for item in result.to_input_list()) \ No newline at end of file diff --git a/tests/agents/test_agent_prompt_system_master_template.py b/tests/agents/test_agent_prompt_system_master_template.py new file mode 100644 index 00000000..ef440e86 --- /dev/null +++ b/tests/agents/test_agent_prompt_system_master_template.py @@ -0,0 +1,47 @@ +""" +This module contains tests for the Mako template rendering of the system master template +used in the agent framework. It includes tests to verify the correct rendering of the +template with various configurations, including the presence of agent instructions and +handling of environment variables. +""" + +import os +import pytest +from mako.template import Template + +# Fixture to load the Mako template for the system master template +@pytest.fixture +def template(): + return Template(filename="src/cai/prompts/core/system_master_template.md") + +# Fixture to create a base agent with predefined instructions +@pytest.fixture +def base_agent(): + return type('Agent', (), {'instructions': 'Test instructions'})() + +def test_master_template_basic(template, base_agent): + """Test basic master template rendering without optional components.""" + result = template.render(agent=base_agent, reasoning_content=None, ctf_instructions="") + print(result) + # Verify that the agent's instructions are included in the rendered template + assert 'Test instructions' in result + # Ensure that the CTF_INSIDE placeholder is not present + assert 'CTF_INSIDE' not in result + +def test_master_template_with_env_vars(template, base_agent): + """Test master template with environment variables and vector DB.""" + # Set an environment variable for the CTF name + os.environ['CTF_NAME'] = 'test_ctf' + result = template.render(agent=base_agent, reasoning_content=None, ctf_instructions="") + # Verify that the agent's instructions are included in the rendered template + assert "Test instructions" in result + # Clean up by deleting the environment variable + del os.environ['CTF_NAME'] + +def test_master_template_no_instructions(template): + """Test master template without agent instructions.""" + # Create an agent with empty instructions + agent = type('Agent', (), {'instructions': ''})() + result = template.render(agent=agent, reasoning_content=None, ctf_instructions="") + # Verify that the rendered template starts with an empty string + assert result.strip().startswith('') diff --git a/tests/test_agent_runner.py b/tests/agents/test_agent_runner.py similarity index 98% rename from tests/test_agent_runner.py rename to tests/agents/test_agent_runner.py index ce0c5804..0b84dbaf 100644 --- a/tests/test_agent_runner.py +++ b/tests/agents/test_agent_runner.py @@ -6,7 +6,7 @@ from typing import Any import pytest from typing_extensions import TypedDict -from agents import ( +from cai.sdk.agents import ( Agent, GuardrailFunctionOutput, Handoff, @@ -23,11 +23,11 @@ from agents import ( UserError, handoff, ) -from agents.agent import ToolsToFinalOutputResult -from agents.tool import FunctionToolResult, function_tool +from cai.sdk.agents.agent import ToolsToFinalOutputResult +from cai.sdk.agents.tool import FunctionToolResult, function_tool -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/test_agent_runner_streamed.py b/tests/agents/test_agent_runner_streamed.py similarity index 98% rename from tests/test_agent_runner_streamed.py rename to tests/agents/test_agent_runner_streamed.py index 87a76a70..4d42ad97 100644 --- a/tests/test_agent_runner_streamed.py +++ b/tests/agents/test_agent_runner_streamed.py @@ -6,7 +6,7 @@ from typing import Any import pytest from typing_extensions import TypedDict -from agents import ( +from cai.sdk.agents import ( Agent, GuardrailFunctionOutput, Handoff, @@ -20,12 +20,12 @@ from agents import ( UserError, handoff, ) -from agents.items import RunItem -from agents.run import RunConfig -from agents.stream_events import AgentUpdatedStreamEvent +from cai.sdk.agents.items import RunItem +from cai.sdk.agents.run import RunConfig +from cai.sdk.agents.stream_events import AgentUpdatedStreamEvent -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/test_global_hooks.py b/tests/agents/test_global_hooks.py similarity index 98% rename from tests/test_global_hooks.py rename to tests/agents/test_global_hooks.py index 45854410..2617ee91 100644 --- a/tests/test_global_hooks.py +++ b/tests/agents/test_global_hooks.py @@ -7,10 +7,10 @@ from typing import Any import pytest from typing_extensions import TypedDict -from agents import Agent, RunContextWrapper, RunHooks, Runner, TContext, Tool +from cai.sdk.agents import Agent, RunContextWrapper, RunHooks, Runner, TContext, Tool -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/test_guardrails.py b/tests/agents/test_guardrails.py similarity index 98% rename from tests/test_guardrails.py rename to tests/agents/test_guardrails.py index c9f318c3..c1be6a4b 100644 --- a/tests/test_guardrails.py +++ b/tests/agents/test_guardrails.py @@ -4,7 +4,7 @@ from typing import Any import pytest -from agents import ( +from cai.sdk.agents import ( Agent, GuardrailFunctionOutput, InputGuardrail, @@ -13,7 +13,7 @@ from agents import ( TResponseInputItem, UserError, ) -from agents.guardrail import input_guardrail, output_guardrail +from cai.sdk.agents.guardrail import input_guardrail, output_guardrail def get_sync_guardrail(triggers: bool, output_info: Any | None = None): diff --git a/tests/test_items_helpers.py b/tests/agents/test_items_helpers.py similarity index 99% rename from tests/test_items_helpers.py rename to tests/agents/test_items_helpers.py index 90fe6475..eb4a2fd0 100644 --- a/tests/test_items_helpers.py +++ b/tests/agents/test_items_helpers.py @@ -20,7 +20,7 @@ from openai.types.responses.response_output_text import ResponseOutputText from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary from openai.types.responses.response_reasoning_item_param import ResponseReasoningItemParam -from agents import ( +from cai.sdk.agents import ( Agent, ItemHelpers, MessageOutputItem, diff --git a/tests/test_max_turns.py b/tests/agents/test_max_turns.py similarity index 94% rename from tests/test_max_turns.py rename to tests/agents/test_max_turns.py index f01bb18f..280cda96 100644 --- a/tests/test_max_turns.py +++ b/tests/agents/test_max_turns.py @@ -5,10 +5,10 @@ import json import pytest from typing_extensions import TypedDict -from agents import Agent, MaxTurnsExceeded, Runner +from cai.sdk.agents import Agent, MaxTurnsExceeded, Runner -from .fake_model import FakeModel -from .test_responses import get_function_tool, get_function_tool_call, get_text_message +from tests.fake_model import FakeModel +from tests.core.test_responses import get_function_tool, get_function_tool_call, get_text_message @pytest.mark.asyncio diff --git a/tests/conftest.py b/tests/conftest.py index e096797e..952d2e06 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ from cai.sdk.agents.models.openai_responses import OpenAIResponsesModel from cai.sdk.agents.tracing import set_trace_processors from cai.sdk.agents.tracing.setup import GLOBAL_TRACE_PROVIDER -from .testing_processor import SPAN_PROCESSOR_TESTING +from tests.testing_processor import SPAN_PROCESSOR_TESTING # This fixture will run once before any tests are executed diff --git a/tests/test_openai_chatcompletions.py b/tests/core/test_openai_chatcompletions.py similarity index 92% rename from tests/test_openai_chatcompletions.py rename to tests/core/test_openai_chatcompletions.py index 9a53e2b7..afb45526 100644 --- a/tests/test_openai_chatcompletions.py +++ b/tests/core/test_openai_chatcompletions.py @@ -22,7 +22,7 @@ from openai.types.responses import ( ResponseOutputText, ) -from agents import ( +from cai.sdk.agents import ( ModelResponse, ModelSettings, ModelTracing, @@ -30,8 +30,9 @@ from agents import ( OpenAIProvider, generation_span, ) -from agents.models.fake_id import FAKE_RESPONSES_ID - +from cai.sdk.agents.models.fake_id import FAKE_RESPONSES_ID +import os +cai_model = os.getenv('CAI_MODEL', "qwen2.5:14b") @pytest.mark.allow_call_model_methods @pytest.mark.asyncio @@ -57,7 +58,7 @@ async def test_get_response_with_text_message(monkeypatch) -> None: return chat monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) resp: ModelResponse = await model.get_response( system_instructions=None, input="", @@ -105,7 +106,7 @@ async def test_get_response_with_refusal(monkeypatch) -> None: return chat monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) resp: ModelResponse = await model.get_response( system_instructions=None, input="", @@ -121,8 +122,8 @@ async def test_get_response_with_refusal(monkeypatch) -> None: 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.requests == 1 + assert resp.usage.input_tokens == 5 assert resp.usage.output_tokens == 0 @@ -154,7 +155,7 @@ async def test_get_response_with_tool_call(monkeypatch) -> None: return chat monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) resp: ModelResponse = await model.get_response( system_instructions=None, input="", @@ -173,7 +174,6 @@ async def test_get_response_with_tool_call(monkeypatch) -> None: 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: """ @@ -208,7 +208,7 @@ async def test_fetch_response_non_stream(monkeypatch) -> None: ) completions = DummyCompletions() dummy_client = DummyClient(completions) - model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore + model = OpenAIChatCompletionsModel(model=cai_model, 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( @@ -222,12 +222,12 @@ async def test_fetch_response_non_stream(monkeypatch) -> None: 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["store"] is True - assert kwargs["model"] == "gpt-4" + assert kwargs["model"] == cai_model assert kwargs["messages"][0]["role"] == "system" assert kwargs["messages"][0]["content"] == "sys" assert kwargs["messages"][1]["role"] == "user" @@ -245,7 +245,7 @@ async def test_fetch_response_stream(monkeypatch) -> None: object along with the underlying async stream. The OpenAI client call should include `stream_options` to request usage-delimited chunks. """ - + os.environ['CAI_STREAM'] = 'true' async def event_stream() -> AsyncIterator[ChatCompletionChunk]: if False: # pragma: no cover yield # pragma: no cover @@ -265,7 +265,7 @@ async def test_fetch_response_stream(monkeypatch) -> None: completions = DummyCompletions() dummy_client = DummyClient(completions) - model = OpenAIChatCompletionsModel(model="gpt-4", openai_client=dummy_client) # type: ignore + model = OpenAIChatCompletionsModel(model=cai_model, openai_client=dummy_client) # type: ignore with generation_span(disabled=True) as span: response, stream = await model._fetch_response( system_instructions=None, @@ -285,8 +285,8 @@ async def test_fetch_response_stream(monkeypatch) -> None: # Response is a proper openai Response assert isinstance(response, Response) assert response.id == FAKE_RESPONSES_ID - assert response.model == "gpt-4" + assert response.model == cai_model assert response.object == "response" assert response.output == [] # We returned the async iterator produced by our dummy. - assert hasattr(stream, "__aiter__") + assert hasattr(stream, "__aiter__") \ No newline at end of file diff --git a/tests/test_openai_chatcompletions_converter.py b/tests/core/test_openai_chatcompletions_converter.py similarity index 97% rename from tests/test_openai_chatcompletions_converter.py rename to tests/core/test_openai_chatcompletions_converter.py index 73acb8ab..4dc464dd 100644 --- a/tests/test_openai_chatcompletions_converter.py +++ b/tests/core/test_openai_chatcompletions_converter.py @@ -38,11 +38,11 @@ from openai.types.responses import ( ) 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 +from cai.sdk.agents.agent_output import AgentOutputSchema +from cai.sdk.agents.exceptions import UserError +from cai.sdk.agents.items import TResponseInputItem +from cai.sdk.agents.models.fake_id import FAKE_RESPONSES_ID +from cai.sdk.agents.models.openai_chatcompletions import _Converter def test_message_to_output_items_with_text_only(): @@ -202,7 +202,7 @@ def test_convert_tool_choice_handles_standard_and_named_options() -> None: 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(None).__class__.__name__ == "str" assert _Converter.convert_tool_choice("auto") == "auto" assert _Converter.convert_tool_choice("required") == "required" assert _Converter.convert_tool_choice("none") == "none" @@ -221,9 +221,9 @@ def test_convert_response_format_returns_not_given_for_plain_text_and_dict_for_s 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(None).__class__.__name__ == "NoneType" assert ( - _Converter.convert_response_format(AgentOutputSchema(str)).__class__.__name__ == "NotGiven" + _Converter.convert_response_format(AgentOutputSchema(str)).__class__.__name__ == "NoneType" ) # For e.g. integer output, we expect a response_format dict schema = AgentOutputSchema(int) diff --git a/tests/test_openai_chatcompletions_stream.py b/tests/core/test_openai_chatcompletions_stream.py similarity index 95% rename from tests/test_openai_chatcompletions_stream.py rename to tests/core/test_openai_chatcompletions_stream.py index 7add92a6..f17606b9 100644 --- a/tests/test_openai_chatcompletions_stream.py +++ b/tests/core/test_openai_chatcompletions_stream.py @@ -17,11 +17,13 @@ from openai.types.responses import ( 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 +from cai.sdk.agents.model_settings import ModelSettings +from cai.sdk.agents.models.interface import ModelTracing +from cai.sdk.agents.models.openai_chatcompletions import OpenAIChatCompletionsModel +from cai.sdk.agents.models.openai_provider import OpenAIProvider +import os +cai_model = os.getenv('CAI_MODEL', "qwen2.5:14b") @pytest.mark.allow_call_model_methods @pytest.mark.asyncio @@ -69,7 +71,7 @@ async def test_stream_response_yields_events_for_text_content(monkeypatch) -> No return resp, fake_stream() monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) output_events = [] async for event in model.stream_response( system_instructions=None, @@ -158,7 +160,7 @@ async def test_stream_response_yields_events_for_refusal_content(monkeypatch) -> return resp, fake_stream() monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) output_events = [] async for event in model.stream_response( system_instructions=None, @@ -245,7 +247,7 @@ async def test_stream_response_yields_events_for_tool_call(monkeypatch) -> None: return resp, fake_stream() monkeypatch.setattr(OpenAIChatCompletionsModel, "_fetch_response", patched_fetch_response) - model = OpenAIProvider(use_responses=False).get_model("gpt-4") + model = OpenAIProvider(use_responses=False).get_model(cai_model) output_events = [] async for event in model.stream_response( system_instructions=None, diff --git a/tests/test_openai_responses_converter.py b/tests/core/test_openai_responses_converter.py similarity index 98% rename from tests/test_openai_responses_converter.py rename to tests/core/test_openai_responses_converter.py index 34cbac5c..3da2a207 100644 --- a/tests/test_openai_responses_converter.py +++ b/tests/core/test_openai_responses_converter.py @@ -27,7 +27,7 @@ import pytest from openai import NOT_GIVEN from pydantic import BaseModel -from agents import ( +from cai.sdk.agents import ( Agent, AgentOutputSchema, Computer, @@ -40,7 +40,7 @@ from agents import ( function_tool, handoff, ) -from agents.models.openai_responses import Converter +from cai.sdk.agents.models.openai_responses import Converter def test_convert_tool_choice_standard_values(): diff --git a/tests/test_responses.py b/tests/core/test_responses.py similarity index 98% rename from tests/test_responses.py rename to tests/core/test_responses.py index 6b91bf8c..1b4fec47 100644 --- a/tests/test_responses.py +++ b/tests/core/test_responses.py @@ -9,7 +9,7 @@ from openai.types.responses import ( ResponseOutputText, ) -from agents import ( +from cai.sdk.agents import ( Agent, FunctionTool, Handoff, diff --git a/tests/test_run_config.py b/tests/core/test_run_config.py similarity index 94% rename from tests/test_run_config.py rename to tests/core/test_run_config.py index 51835ab6..9de52d13 100644 --- a/tests/test_run_config.py +++ b/tests/core/test_run_config.py @@ -2,11 +2,11 @@ from __future__ import annotations import pytest -from agents import Agent, RunConfig, Runner -from agents.models.interface import Model, ModelProvider +from cai.sdk.agents import Agent, RunConfig, Runner +from cai.sdk.agents.models.interface import Model, ModelProvider -from .fake_model import FakeModel -from .test_responses import get_text_message +from tests.fake_model import FakeModel +from tests.core.test_responses import get_text_message class DummyProvider(ModelProvider): diff --git a/tests/test_run_step_execution.py b/tests/core/test_run_step_execution.py similarity index 98% rename from tests/test_run_step_execution.py rename to tests/core/test_run_step_execution.py index 16c62c84..32dd6c88 100644 --- a/tests/test_run_step_execution.py +++ b/tests/core/test_run_step_execution.py @@ -5,7 +5,7 @@ from typing import Any import pytest from pydantic import BaseModel -from agents import ( +from cai.sdk.agents import ( Agent, MessageOutputItem, ModelResponse, @@ -19,7 +19,7 @@ from agents import ( TResponseInputItem, Usage, ) -from agents._run_impl import ( +from cai.sdk.agents._run_impl import ( NextStepFinalOutput, NextStepHandoff, NextStepRunAgain, @@ -27,7 +27,7 @@ from agents._run_impl import ( SingleStepResult, ) -from .test_responses import ( +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/test_run_step_processing.py b/tests/core/test_run_step_processing.py similarity index 99% rename from tests/test_run_step_processing.py rename to tests/core/test_run_step_processing.py index 2a6634ac..a80b7134 100644 --- a/tests/test_run_step_processing.py +++ b/tests/core/test_run_step_processing.py @@ -10,7 +10,7 @@ from openai.types.responses.response_computer_tool_call import ActionClick from openai.types.responses.response_reasoning_item import ResponseReasoningItem, Summary from pydantic import BaseModel -from agents import ( +from cai.sdk.agents import ( Agent, Computer, ComputerTool, @@ -23,9 +23,9 @@ from agents import ( ToolCallItem, Usage, ) -from agents._run_impl import RunImpl +from cai.sdk.agents._run_impl import RunImpl -from .test_responses import ( +from tests.core.test_responses import ( get_final_output_message, get_function_tool, get_function_tool_call, diff --git a/tests/fake_model.py b/tests/fake_model.py index ecbb7583..a5cff359 100644 --- a/tests/fake_model.py +++ b/tests/fake_model.py @@ -5,19 +5,19 @@ from typing import Any from openai.types.responses import Response, ResponseCompletedEvent -from agents.agent_output import AgentOutputSchema -from agents.handoffs import Handoff -from agents.items import ( +from cai.sdk.agents.agent_output import AgentOutputSchema +from cai.sdk.agents.handoffs import Handoff +from cai.sdk.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 +from cai.sdk.agents.model_settings import ModelSettings +from cai.sdk.agents.models.interface import Model, ModelTracing +from cai.sdk.agents.tool import Tool +from cai.sdk.agents.tracing import SpanError, generation_span +from cai.sdk.agents.usage import Usage class FakeModel(Model): diff --git a/tests/helpers.py b/tests/helpers.py new file mode 100644 index 00000000..26db6791 --- /dev/null +++ b/tests/helpers.py @@ -0,0 +1,58 @@ +import json +import shutil +from typing import Any + +from mcp import Tool as MCPTool +from mcp.types import CallToolResult, TextContent + +from cai.sdk.agents.mcp import MCPServer + +tee = shutil.which("tee") or "" +assert tee, "tee not found" + + +# Added dummy stream classes for patching stdio_client to avoid real I/O during tests +class DummyStream: + async def send(self, msg): + pass + + async def receive(self): + raise Exception("Dummy receive not implemented") + + +class DummyStreamsContextManager: + async def __aenter__(self): + return (DummyStream(), DummyStream()) + + async def __aexit__(self, exc_type, exc_val, exc_tb): + pass + + +class FakeMCPServer(MCPServer): + def __init__(self, tools: list[MCPTool] | None = None): + self.tools: list[MCPTool] = tools or [] + self.tool_calls: list[str] = [] + self.tool_results: list[str] = [] + + def add_tool(self, name: str, input_schema: dict[str, Any]): + self.tools.append(MCPTool(name=name, inputSchema=input_schema)) + + async def connect(self): + pass + + async def cleanup(self): + pass + + async def list_tools(self): + return self.tools + + async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None) -> CallToolResult: + self.tool_calls.append(tool_name) + self.tool_results.append(f"result_{tool_name}_{json.dumps(arguments)}") + return CallToolResult( + content=[TextContent(text=self.tool_results[-1], type="text")], + ) + + @property + def name(self) -> str: + return "fake_mcp_server" diff --git a/tests/mcp/helpers.py b/tests/mcp/helpers.py index 8ff153c1..98b0202c 100644 --- a/tests/mcp/helpers.py +++ b/tests/mcp/helpers.py @@ -1,58 +1,21 @@ -import json -import shutil -from typing import Any - -from mcp import Tool as MCPTool -from mcp.types import CallToolResult, TextContent - -from agents.mcp import MCPServer - -tee = shutil.which("tee") or "" -assert tee, "tee not found" +try: + from cai.sdk.agents.voice import StreamedAudioResult +except ImportError: + pass -# Added dummy stream classes for patching stdio_client to avoid real I/O during tests -class DummyStream: - async def send(self, msg): - pass +async def extract_events(result: StreamedAudioResult) -> tuple[list[str], list[bytes]]: + """Collapse pipeline stream events to simple labels for ordering assertions.""" + flattened: list[str] = [] + audio_chunks: list[bytes] = [] - async def receive(self): - raise Exception("Dummy receive not implemented") - - -class DummyStreamsContextManager: - async def __aenter__(self): - return (DummyStream(), DummyStream()) - - async def __aexit__(self, exc_type, exc_val, exc_tb): - pass - - -class FakeMCPServer(MCPServer): - def __init__(self, tools: list[MCPTool] | None = None): - self.tools: list[MCPTool] = tools or [] - self.tool_calls: list[str] = [] - self.tool_results: list[str] = [] - - def add_tool(self, name: str, input_schema: dict[str, Any]): - self.tools.append(MCPTool(name=name, inputSchema=input_schema)) - - async def connect(self): - pass - - async def cleanup(self): - pass - - async def list_tools(self): - return self.tools - - async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None) -> CallToolResult: - self.tool_calls.append(tool_name) - self.tool_results.append(f"result_{tool_name}_{json.dumps(arguments)}") - return CallToolResult( - content=[TextContent(text=self.tool_results[-1], type="text")], - ) - - @property - def name(self) -> str: - return "fake_mcp_server" + async for ev in result.stream(): + if ev.type == "voice_stream_event_audio": + if ev.data is not None: + audio_chunks.append(ev.data.tobytes()) + flattened.append("audio") + elif ev.type == "voice_stream_event_lifecycle": + flattened.append(ev.event) + elif ev.type == "voice_stream_event_error": + flattened.append("error") + return flattened, audio_chunks diff --git a/tests/mcp/test_caching.py b/tests/mcp/test_caching.py index cac409e6..ee53640e 100644 --- a/tests/mcp/test_caching.py +++ b/tests/mcp/test_caching.py @@ -3,9 +3,9 @@ from unittest.mock import AsyncMock, patch import pytest from mcp.types import ListToolsResult, Tool as MCPTool -from agents.mcp import MCPServerStdio +from cai.sdk.agents.mcp import MCPServerStdio -from .helpers import DummyStreamsContextManager, tee +from tests.helpers import DummyStreamsContextManager, tee @pytest.mark.asyncio diff --git a/tests/mcp/test_connect_disconnect.py b/tests/mcp/test_connect_disconnect.py index b0013039..7565eddb 100644 --- a/tests/mcp/test_connect_disconnect.py +++ b/tests/mcp/test_connect_disconnect.py @@ -3,9 +3,9 @@ from unittest.mock import AsyncMock, patch import pytest from mcp.types import ListToolsResult, Tool as MCPTool -from agents.mcp import MCPServerStdio +from cai.sdk.agents.mcp import MCPServerStdio -from .helpers import DummyStreamsContextManager, tee +from tests.helpers import DummyStreamsContextManager, tee @pytest.mark.asyncio diff --git a/tests/mcp/test_mcp_tracing.py b/tests/mcp/test_mcp_tracing.py index b71954b5..2c832e56 100644 --- a/tests/mcp/test_mcp_tracing.py +++ b/tests/mcp/test_mcp_tracing.py @@ -1,12 +1,12 @@ import pytest from inline_snapshot import snapshot -from agents import Agent, Runner +from cai.sdk.agents import Agent, Runner -from ..fake_model import FakeModel -from ..test_responses import get_function_tool, get_function_tool_call, get_text_message -from ..testing_processor import SPAN_PROCESSOR_TESTING, fetch_normalized_spans -from .helpers import FakeMCPServer +from tests.fake_model import FakeModel +from tests.core.test_responses import get_function_tool, get_function_tool_call, get_text_message +from tests.testing_processor import SPAN_PROCESSOR_TESTING, fetch_normalized_spans +from tests.helpers import FakeMCPServer @pytest.mark.asyncio diff --git a/tests/mcp/test_mcp_util.py b/tests/mcp/test_mcp_util.py index 345df996..5e8d6da8 100644 --- a/tests/mcp/test_mcp_util.py +++ b/tests/mcp/test_mcp_util.py @@ -5,11 +5,11 @@ import pytest from mcp.types import Tool as MCPTool from pydantic import BaseModel -from agents import FunctionTool, RunContextWrapper -from agents.exceptions import AgentsException, ModelBehaviorError -from agents.mcp import MCPServer, MCPUtil +from cai.sdk.agents import FunctionTool, RunContextWrapper +from cai.sdk.agents.exceptions import AgentsException, ModelBehaviorError +from cai.sdk.agents.mcp import MCPServer, MCPUtil -from .helpers import FakeMCPServer +from tests.helpers import FakeMCPServer class Foo(BaseModel): diff --git a/tests/mcp/test_runner_calls_mcp.py b/tests/mcp/test_runner_calls_mcp.py index 3319c097..35eefd6b 100644 --- a/tests/mcp/test_runner_calls_mcp.py +++ b/tests/mcp/test_runner_calls_mcp.py @@ -3,11 +3,11 @@ import json import pytest from pydantic import BaseModel -from agents import Agent, ModelBehaviorError, Runner, UserError +from cai.sdk.agents import Agent, ModelBehaviorError, Runner, UserError -from ..fake_model import FakeModel -from ..test_responses import get_function_tool_call, get_text_message -from .helpers import FakeMCPServer +from tests.fake_model import FakeModel +from tests.core.test_responses import get_function_tool_call, get_text_message +from tests.helpers import FakeMCPServer @pytest.mark.asyncio diff --git a/tests/mcp/test_server_errors.py b/tests/mcp/test_server_errors.py index bdca7ce6..705ef64d 100644 --- a/tests/mcp/test_server_errors.py +++ b/tests/mcp/test_server_errors.py @@ -1,7 +1,7 @@ import pytest -from agents.exceptions import UserError -from agents.mcp.server import _MCPServerWithClientSession +from cai.sdk.agents.exceptions import UserError +from cai.sdk.agents.mcp.server import _MCPServerWithClientSession class CrashingClientSessionServer(_MCPServerWithClientSession): diff --git a/tests/test_computer_action.py b/tests/others/test_computer_action.py similarity index 98% rename from tests/test_computer_action.py rename to tests/others/test_computer_action.py index 70dcabd5..9c4e9cff 100644 --- a/tests/test_computer_action.py +++ b/tests/others/test_computer_action.py @@ -21,7 +21,7 @@ from openai.types.responses.response_computer_tool_call import ( ResponseComputerToolCall, ) -from agents import ( +from cai.sdk.agents import ( Agent, AgentHooks, AsyncComputer, @@ -31,8 +31,8 @@ from agents import ( RunContextWrapper, RunHooks, ) -from agents._run_impl import ComputerAction, ToolRunComputerAction -from agents.items import ToolCallOutputItem +from cai.sdk.agents._run_impl import ComputerAction, ToolRunComputerAction +from cai.sdk.agents.items import ToolCallOutputItem class LoggingComputer(Computer): diff --git a/tests/test_config.py b/tests/others/test_config.py similarity index 55% rename from tests/test_config.py rename to tests/others/test_config.py index dba854db..f5de277b 100644 --- a/tests/test_config.py +++ b/tests/others/test_config.py @@ -3,28 +3,31 @@ 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 +from cai.sdk.agents import set_default_openai_api, set_default_openai_client, set_default_openai_key +from cai.sdk.agents.models.openai_chatcompletions import OpenAIChatCompletionsModel +from cai.sdk.agents.models.openai_provider import OpenAIProvider +from cai.sdk.agents.models.openai_responses import OpenAIResponsesModel +import os +cai_model = os.getenv('CAI_MODEL', "qwen2.5:14b") + 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") + OpenAIProvider(use_responses=False).get_model(cai_model) def test_cc_set_default_openai_key(): set_default_openai_key("test_key") - chat_model = OpenAIProvider(use_responses=False).get_model("gpt-4") + chat_model = OpenAIProvider(use_responses=False).get_model(cai_model) 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") + chat_model = OpenAIProvider(use_responses=False).get_model(cai_model) assert chat_model._client.api_key == "test_key" # type: ignore @@ -32,33 +35,33 @@ 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") + OpenAIProvider(use_responses=True).get_model(cai_model) def test_resp_set_default_openai_key(): set_default_openai_key("test_key") - resp_model = OpenAIProvider(use_responses=True).get_model("gpt-4") + resp_model = OpenAIProvider(use_responses=True).get_model(cai_model) 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") + resp_model = OpenAIProvider(use_responses=True).get_model(cai_model) assert resp_model._client.api_key == "test_key" # type: ignore def test_set_default_openai_api(): - assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel), ( + assert isinstance(OpenAIProvider().get_model(cai_model), OpenAIResponsesModel), ( "Default should be responses" ) set_default_openai_api("chat_completions") - assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIChatCompletionsModel), ( + assert isinstance(OpenAIProvider().get_model(cai_model), OpenAIChatCompletionsModel), ( "Should be chat completions model" ) set_default_openai_api("responses") - assert isinstance(OpenAIProvider().get_model("gpt-4"), OpenAIResponsesModel), ( + assert isinstance(OpenAIProvider().get_model(cai_model), OpenAIResponsesModel), ( "Should be responses model" ) diff --git a/tests/test_doc_parsing.py b/tests/others/test_doc_parsing.py similarity index 97% rename from tests/test_doc_parsing.py rename to tests/others/test_doc_parsing.py index 6c7a95db..4fc13ee3 100644 --- a/tests/test_doc_parsing.py +++ b/tests/others/test_doc_parsing.py @@ -1,4 +1,4 @@ -from agents.function_schema import generate_func_documentation +from cai.sdk.agents.function_schema import generate_func_documentation def func_foo_google(a: int, b: float) -> str: diff --git a/tests/test_extension_filters.py b/tests/others/test_extension_filters.py similarity index 97% rename from tests/test_extension_filters.py rename to tests/others/test_extension_filters.py index 4cb017aa..bb70ce67 100644 --- a/tests/test_extension_filters.py +++ b/tests/others/test_extension_filters.py @@ -1,8 +1,8 @@ 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 ( +from cai.sdk.agents import Agent, HandoffInputData +from cai.sdk.agents.extensions.handoff_filters import remove_all_tools +from cai.sdk.agents.items import ( HandoffOutputItem, MessageOutputItem, ToolCallOutputItem, diff --git a/tests/test_function_schema.py b/tests/others/test_function_schema.py similarity index 98% rename from tests/test_function_schema.py rename to tests/others/test_function_schema.py index ef1e9c22..9f184d72 100644 --- a/tests/test_function_schema.py +++ b/tests/others/test_function_schema.py @@ -6,9 +6,9 @@ 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 +from cai.sdk.agents import RunContextWrapper +from cai.sdk.agents.exceptions import UserError +from cai.sdk.agents.function_schema import function_schema def no_args_function(): @@ -99,7 +99,7 @@ def varargs_function(x: int, *numbers: float, flag: bool = False, **kwargs: Any) def test_varargs_function(): """Test a function that uses *args and **kwargs.""" - func_schema = function_schema(varargs_function) + func_schema = function_schema(varargs_function, strict_json_schema=False) # Check JSON schema structure assert isinstance(func_schema.params_json_schema, dict) assert func_schema.params_json_schema.get("title") == "varargs_function_args" diff --git a/tests/test_pretty_print.py b/tests/others/test_pretty_print.py similarity index 94% rename from tests/test_pretty_print.py rename to tests/others/test_pretty_print.py index b2218a27..b3d6efef 100644 --- a/tests/test_pretty_print.py +++ b/tests/others/test_pretty_print.py @@ -4,12 +4,12 @@ import pytest from inline_snapshot import snapshot from pydantic import BaseModel -from agents import Agent, Runner -from agents.agent_output import _WRAPPER_DICT_KEY -from agents.util._pretty_print import pretty_print_result, pretty_print_run_result_streaming +from cai.sdk.agents import Agent, Runner +from cai.sdk.agents.agent_output import _WRAPPER_DICT_KEY +from cai.sdk.agents.util._pretty_print import pretty_print_result, pretty_print_run_result_streaming from tests.fake_model import FakeModel -from .test_responses import get_final_output_message, get_text_message +from tests.core.test_responses import get_final_output_message, get_text_message @pytest.mark.asyncio diff --git a/tests/test_result_cast.py b/tests/others/test_result_cast.py similarity index 97% rename from tests/test_result_cast.py rename to tests/others/test_result_cast.py index ec17e327..44cc6ca9 100644 --- a/tests/test_result_cast.py +++ b/tests/others/test_result_cast.py @@ -3,7 +3,7 @@ from typing import Any import pytest from pydantic import BaseModel -from agents import Agent, RunResult +from cai.sdk.agents import Agent, RunResult def create_run_result(final_output: Any) -> RunResult: diff --git a/tests/test_strict_schema.py b/tests/others/test_strict_schema.py similarity index 97% rename from tests/test_strict_schema.py rename to tests/others/test_strict_schema.py index c35e9adf..70b88efd 100644 --- a/tests/test_strict_schema.py +++ b/tests/others/test_strict_schema.py @@ -1,7 +1,7 @@ import pytest -from agents.exceptions import UserError -from agents.strict_schema import ensure_strict_json_schema +from cai.sdk.agents.exceptions import UserError +from cai.sdk.agents.strict_schema import ensure_strict_json_schema def test_empty_schema_has_additional_properties_false(): diff --git a/tests/test_trace_processor.py b/tests/others/test_trace_processor.py similarity index 96% rename from tests/test_trace_processor.py rename to tests/others/test_trace_processor.py index 72318caa..d11904cb 100644 --- a/tests/test_trace_processor.py +++ b/tests/others/test_trace_processor.py @@ -5,11 +5,11 @@ 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 +from cai.sdk.agents.tracing.processor_interface import TracingProcessor +from cai.sdk.agents.tracing.processors import BackendSpanExporter, BatchTraceProcessor +from cai.sdk.agents.tracing.span_data import AgentSpanData +from cai.sdk.agents.tracing.spans import SpanImpl +from cai.sdk.agents.tracing.traces import TraceImpl def get_span(processor: TracingProcessor) -> SpanImpl[AgentSpanData]: diff --git a/tests/test_visualization.py b/tests/others/test_visualization.py similarity index 97% rename from tests/test_visualization.py rename to tests/others/test_visualization.py index 6aa86774..caa35184 100644 --- a/tests/test_visualization.py +++ b/tests/others/test_visualization.py @@ -3,14 +3,14 @@ from unittest.mock import Mock import graphviz # type: ignore import pytest -from agents import Agent -from agents.extensions.visualization import ( +from cai.sdk.agents import Agent +from cai.sdk.agents.extensions.visualization import ( draw_graph, get_all_edges, get_all_nodes, get_main_graph, ) -from agents.handoffs import Handoff +from cai.sdk.agents.handoffs import Handoff @pytest.fixture diff --git a/tests/test_function_tool.py b/tests/tools/test_function_tool.py similarity index 98% rename from tests/test_function_tool.py rename to tests/tools/test_function_tool.py index 0a57aea8..a824ba32 100644 --- a/tests/test_function_tool.py +++ b/tests/tools/test_function_tool.py @@ -5,8 +5,8 @@ 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 +from cai.sdk.agents import FunctionTool, ModelBehaviorError, RunContextWrapper, function_tool +from cai.sdk.agents.tool import default_tool_error_function def argless_function() -> str: diff --git a/tests/test_function_tool_decorator.py b/tests/tools/test_function_tool_decorator.py similarity index 98% rename from tests/test_function_tool_decorator.py rename to tests/tools/test_function_tool_decorator.py index 903dd123..a62a2db2 100644 --- a/tests/test_function_tool_decorator.py +++ b/tests/tools/test_function_tool_decorator.py @@ -4,8 +4,8 @@ from typing import Any, Optional import pytest -from agents import function_tool -from agents.run_context import RunContextWrapper +from cai.sdk.agents import function_tool +from cai.sdk.agents.run_context import RunContextWrapper class DummyContext: diff --git a/tests/test_handoff_tool.py b/tests/tools/test_handoff_tool.py similarity index 99% rename from tests/test_handoff_tool.py rename to tests/tools/test_handoff_tool.py index a2a06208..54288163 100644 --- a/tests/test_handoff_tool.py +++ b/tests/tools/test_handoff_tool.py @@ -4,7 +4,7 @@ import pytest from openai.types.responses import ResponseOutputMessage, ResponseOutputText from pydantic import BaseModel -from agents import ( +from cai.sdk.agents import ( Agent, Handoff, HandoffInputData, diff --git a/tests/test_output_tool.py b/tests/tools/test_output_tool.py similarity index 95% rename from tests/test_output_tool.py rename to tests/tools/test_output_tool.py index 86c4b3b5..f8e8adce 100644 --- a/tests/test_output_tool.py +++ b/tests/tools/test_output_tool.py @@ -4,9 +4,9 @@ import pytest from pydantic import BaseModel from typing_extensions import TypedDict -from agents import Agent, AgentOutputSchema, ModelBehaviorError, Runner, UserError -from agents.agent_output import _WRAPPER_DICT_KEY -from agents.util import _json +from cai.sdk.agents import Agent, AgentOutputSchema, ModelBehaviorError, Runner, UserError +from cai.sdk.agents.agent_output import _WRAPPER_DICT_KEY +from cai.sdk.agents.util import _json def test_plain_text_output(): diff --git a/tests/test_tool_choice_reset.py b/tests/tools/test_tool_choice_reset.py similarity index 97% rename from tests/test_tool_choice_reset.py rename to tests/tools/test_tool_choice_reset.py index f95117fd..93895e1a 100644 --- a/tests/test_tool_choice_reset.py +++ b/tests/tools/test_tool_choice_reset.py @@ -1,10 +1,10 @@ import pytest -from agents import Agent, ModelSettings, Runner -from agents._run_impl import AgentToolUseTracker, RunImpl +from cai.sdk.agents import Agent, ModelSettings, Runner +from cai.sdk.agents._run_impl import AgentToolUseTracker, RunImpl -from .fake_model import FakeModel -from .test_responses import get_function_tool, get_function_tool_call, get_text_message +from tests.fake_model import FakeModel +from tests.core.test_responses import get_function_tool, get_function_tool_call, get_text_message class TestToolChoiceReset: diff --git a/tests/test_tool_converter.py b/tests/tools/test_tool_converter.py similarity index 85% rename from tests/test_tool_converter.py rename to tests/tools/test_tool_converter.py index 1b6ebcf9..0cef9abc 100644 --- a/tests/test_tool_converter.py +++ b/tests/tools/test_tool_converter.py @@ -1,10 +1,10 @@ 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 +from cai.sdk.agents import Agent, Handoff, function_tool, handoff +from cai.sdk.agents.exceptions import UserError +from cai.sdk.agents.models.openai_chatcompletions import ToolConverter +from cai.sdk.agents.tool import FileSearchTool, WebSearchTool def some_function(a: str, b: list[int]) -> str: diff --git a/tests/tools/test_tool_generic_linux_command.py b/tests/tools/test_tool_generic_linux_command.py new file mode 100644 index 00000000..c4c427c2 --- /dev/null +++ b/tests/tools/test_tool_generic_linux_command.py @@ -0,0 +1,51 @@ +""" +This module contains tests for the generic Linux command tool functionality. +It includes tests for executing regular commands and handling invalid commands. +""" + +import pytest +import json +from unittest.mock import MagicMock +from cai.tools.reconnaissance.generic_linux_command import generic_linux_command + +async def test_generic_linux_command_regular_commands(): + """Test the execution of a regular command using the generic Linux command tool.""" + mock_ctx = MagicMock() # Create a mock context for the command execution + params = { + "command": "echo", # Command to be executed + "args": "'hello'" # Arguments for the command + } + + # Invoke the tool with the specified parameters and await the result + result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params)) + + # Assert that the result matches the expected output + assert result.replace("\n", "") == 'hello' + +async def test_generic_linux_command_ls(): + """Test the execution of the 'ls' command using the generic Linux command tool.""" + mock_ctx = MagicMock() # Create a mock context for the command execution + params = { + "command": "ls", # Command to be executed + "args": "-l" # Arguments for the command + } + + # Invoke the tool with the specified parameters and await the result + result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params)) + + # Assert that the output contains 'total', which is typical for 'ls -l' + assert "total" in result + +async def test_generic_linux_command_invalid_command(): + """Test the handling of an invalid command using the generic Linux command tool.""" + mock_ctx = MagicMock() # Create a mock context for the command execution + params = { + "command": "invalid_command", # Invalid command to be executed + "args": "" # No arguments for the command + } + + # Invoke the tool with the specified parameters and await the result + result = await generic_linux_command.on_invoke_tool(mock_ctx, json.dumps(params)) + + # Assert that the result indicates the command was not found + assert "not found" in result diff --git a/tests/test_tool_use_behavior.py b/tests/tools/test_tool_use_behavior.py similarity index 98% rename from tests/test_tool_use_behavior.py rename to tests/tools/test_tool_use_behavior.py index 6a673b7a..355392b2 100644 --- a/tests/test_tool_use_behavior.py +++ b/tests/tools/test_tool_use_behavior.py @@ -7,7 +7,7 @@ from typing import cast import pytest from openai.types.responses.response_input_item_param import FunctionCallOutput -from agents import ( +from cai.sdk.agents import ( Agent, FunctionToolResult, RunConfig, @@ -16,9 +16,9 @@ from agents import ( ToolsToFinalOutputResult, UserError, ) -from agents._run_impl import RunImpl +from cai.sdk.agents._run_impl import RunImpl -from .test_responses import get_function_tool +from tests.core.test_responses import get_function_tool def _make_function_tool_result( diff --git a/tests/test_agent_tracing.py b/tests/tracing/test_agent_tracing.py similarity index 98% rename from tests/test_agent_tracing.py rename to tests/tracing/test_agent_tracing.py index bb16cab2..34535120 100644 --- a/tests/test_agent_tracing.py +++ b/tests/tracing/test_agent_tracing.py @@ -5,11 +5,11 @@ import asyncio import pytest from inline_snapshot import snapshot -from agents import Agent, RunConfig, Runner, trace +from cai.sdk.agents import Agent, RunConfig, Runner, trace -from .fake_model import FakeModel -from .test_responses import get_text_message -from .testing_processor import assert_no_traces, fetch_normalized_spans +from tests.fake_model import FakeModel +from tests.core.test_responses import get_text_message +from tests.testing_processor import assert_no_traces, fetch_normalized_spans @pytest.mark.asyncio diff --git a/tests/tracing/test_processor_api_key.py b/tests/tracing/test_processor_api_key.py index b0a0218a..98e36a2e 100644 --- a/tests/tracing/test_processor_api_key.py +++ b/tests/tracing/test_processor_api_key.py @@ -1,6 +1,6 @@ import pytest -from agents.tracing.processors import BackendSpanExporter +from cai.sdk.agents.tracing.processors import BackendSpanExporter @pytest.mark.asyncio diff --git a/tests/test_responses_tracing.py b/tests/tracing/test_responses_tracing.py similarity index 96% rename from tests/test_responses_tracing.py rename to tests/tracing/test_responses_tracing.py index 40bdfafb..79205adc 100644 --- a/tests/test_responses_tracing.py +++ b/tests/tracing/test_responses_tracing.py @@ -3,11 +3,11 @@ from inline_snapshot import snapshot 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 cai.sdk.agents import ModelSettings, ModelTracing, OpenAIResponsesModel, trace +from cai.sdk.agents.tracing.span_data import ResponseSpanData from tests import fake_model -from .testing_processor import assert_no_spans, fetch_normalized_spans, fetch_ordered_spans +from tests.testing_processor import assert_no_spans, fetch_normalized_spans, fetch_ordered_spans class DummyTracing: diff --git a/tests/test_tracing.py b/tests/tracing/test_tracing.py similarity index 98% rename from tests/test_tracing.py rename to tests/tracing/test_tracing.py index 8f763509..a796efda 100644 --- a/tests/test_tracing.py +++ b/tests/tracing/test_tracing.py @@ -6,7 +6,7 @@ from typing import Any import pytest from inline_snapshot import snapshot -from agents.tracing import ( +from cai.sdk.agents.tracing import ( Span, Trace, agent_span, @@ -16,9 +16,9 @@ from agents.tracing import ( handoff_span, trace, ) -from agents.tracing.spans import SpanError +from cai.sdk.agents.tracing.spans import SpanError -from .testing_processor import ( +from tests.testing_processor import ( SPAN_PROCESSOR_TESTING, assert_no_traces, fetch_events, diff --git a/tests/test_tracing_errors.py b/tests/tracing/test_tracing_errors.py similarity index 99% rename from tests/test_tracing_errors.py rename to tests/tracing/test_tracing_errors.py index 6d698bcc..3233606a 100644 --- a/tests/test_tracing_errors.py +++ b/tests/tracing/test_tracing_errors.py @@ -7,7 +7,7 @@ import pytest from inline_snapshot import snapshot from typing_extensions import TypedDict -from agents import ( +from cai.sdk.agents import ( Agent, GuardrailFunctionOutput, InputGuardrail, @@ -19,15 +19,15 @@ from agents import ( TResponseInputItem, ) -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.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_normalized_spans +from tests.testing_processor import fetch_normalized_spans @pytest.mark.asyncio diff --git a/tests/test_tracing_errors_streamed.py b/tests/tracing/test_tracing_errors_streamed.py similarity index 99% rename from tests/test_tracing_errors_streamed.py rename to tests/tracing/test_tracing_errors_streamed.py index 416793e7..0d6b1d22 100644 --- a/tests/test_tracing_errors_streamed.py +++ b/tests/tracing/test_tracing_errors_streamed.py @@ -8,7 +8,7 @@ import pytest from inline_snapshot import snapshot from typing_extensions import TypedDict -from agents import ( +from cai.sdk.agents import ( Agent, GuardrailFunctionOutput, InputGuardrail, @@ -22,15 +22,15 @@ from agents import ( TResponseInputItem, ) -from .fake_model import FakeModel -from .test_responses import ( +from tests.fake_model import FakeModel +from tests.core.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_normalized_spans +from tests.testing_processor import fetch_normalized_spans @pytest.mark.asyncio diff --git a/tests/voice/fake_models.py b/tests/voice/fake_models.py index 109ee4cb..9f4c1545 100644 --- a/tests/voice/fake_models.py +++ b/tests/voice/fake_models.py @@ -7,7 +7,7 @@ import numpy as np import numpy.typing as npt try: - from agents.voice import ( + from cai.sdk.agents.voice import ( AudioInput, StreamedAudioInput, StreamedTranscriptionSession, diff --git a/tests/voice/helpers.py b/tests/voice/helpers.py index ae902dc1..98b0202c 100644 --- a/tests/voice/helpers.py +++ b/tests/voice/helpers.py @@ -1,5 +1,5 @@ try: - from agents.voice import StreamedAudioResult + from cai.sdk.agents.voice import StreamedAudioResult except ImportError: pass diff --git a/tests/voice/test_input.py b/tests/voice/test_input.py index d41d870d..454fce60 100644 --- a/tests/voice/test_input.py +++ b/tests/voice/test_input.py @@ -5,9 +5,9 @@ import numpy as np import pytest try: - from agents import UserError - from agents.voice import AudioInput, StreamedAudioInput - from agents.voice.input import DEFAULT_SAMPLE_RATE, _buffer_to_audio_file + from cai.sdk.agents import UserError + from cai.sdk.agents.voice import AudioInput, StreamedAudioInput + from cai.sdk.agents.voice.input import DEFAULT_SAMPLE_RATE, _buffer_to_audio_file except ImportError: pass diff --git a/tests/voice/test_openai_stt.py b/tests/voice/test_openai_stt.py index 89b5cca7..1e62e141 100644 --- a/tests/voice/test_openai_stt.py +++ b/tests/voice/test_openai_stt.py @@ -9,11 +9,11 @@ import numpy as np import pytest try: - from agents.voice import OpenAISTTTranscriptionSession, StreamedAudioInput, STTModelSettings - from agents.voice.exceptions import STTWebsocketConnectionError - from agents.voice.models.openai_stt import EVENT_INACTIVITY_TIMEOUT + from cai.sdk.agents.voice import OpenAISTTTranscriptionSession, StreamedAudioInput, STTModelSettings + from cai.sdk.agents.voice.exceptions import STTWebsocketConnectionError + from cai.sdk.agents.voice.models.openai_stt import EVENT_INACTIVITY_TIMEOUT - from .fake_models import FakeStreamedAudioInput + from tests.voice.fake_models import FakeStreamedAudioInput except ImportError: pass diff --git a/tests/voice/test_openai_tts.py b/tests/voice/test_openai_tts.py index b18f9e8c..ea93b3f8 100644 --- a/tests/voice/test_openai_tts.py +++ b/tests/voice/test_openai_tts.py @@ -6,7 +6,7 @@ from typing import Any import pytest try: - from agents.voice import OpenAITTSModel, TTSModelSettings + from cai.sdk.agents.voice import OpenAITTSModel, TTSModelSettings except ImportError: pass diff --git a/tests/voice/test_pipeline.py b/tests/voice/test_pipeline.py index 51904468..49704d99 100644 --- a/tests/voice/test_pipeline.py +++ b/tests/voice/test_pipeline.py @@ -5,10 +5,10 @@ import numpy.typing as npt import pytest try: - from agents.voice import AudioInput, TTSModelSettings, VoicePipeline, VoicePipelineConfig + from cai.sdk.agents.voice import AudioInput, TTSModelSettings, VoicePipeline, VoicePipelineConfig - from .fake_models import FakeStreamedAudioInput, FakeSTT, FakeTTS, FakeWorkflow - from .helpers import extract_events + from tests.voice.fake_models import FakeStreamedAudioInput, FakeSTT, FakeTTS, FakeWorkflow + from tests.mcp.helpers import extract_events except ImportError: pass diff --git a/tests/voice/test_workflow.py b/tests/voice/test_workflow.py index 3f18c049..296467d2 100644 --- a/tests/voice/test_workflow.py +++ b/tests/voice/test_workflow.py @@ -8,10 +8,10 @@ from inline_snapshot import snapshot from openai.types.responses import ResponseCompletedEvent from openai.types.responses.response_text_delta_event import ResponseTextDeltaEvent -from agents import Agent, Model, ModelSettings, ModelTracing, Tool -from agents.agent_output import AgentOutputSchema -from agents.handoffs import Handoff -from agents.items import ( +from cai.sdk.agents import Agent, Model, ModelSettings, ModelTracing, Tool +from cai.sdk.agents.agent_output import AgentOutputSchema +from cai.sdk.agents.handoffs import Handoff +from cai.sdk.agents.items import ( ModelResponse, TResponseInputItem, TResponseOutputItem, @@ -19,10 +19,10 @@ from agents.items import ( ) try: - from agents.voice import SingleAgentVoiceWorkflow + from cai.sdk.agents.voice import SingleAgentVoiceWorkflow - from ..fake_model import get_response_obj - from ..test_responses import get_function_tool, get_function_tool_call, get_text_message + from tests.fake_model import get_response_obj + from tests.core.test_responses import get_function_tool, get_function_tool_call, get_text_message except ImportError: pass