mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Redesign Python exception hierarchy (#4082)
* [BREAKING] Redesign Python exception hierarchy Replace the flat ServiceException family with domain-scoped branches: - AgentException (with InvalidAuth, InvalidRequest, InvalidResponse, ContentFilter) - ChatClientException (same consistent suberrors) - IntegrationException (same + InitializationError) - WorkflowException (Runner, Convergence, Checkpoint, Validation, Action, Declarative) - ContentError (AdditionItemMismatch) - ToolException / ToolExecutionException (unchanged) - MiddlewareException / MiddlewareTermination (unchanged) Key changes: - All Service* exceptions removed (ServiceException, ServiceInitializationError, etc.) - AgentExecutionException split into AgentInvalidRequest/ResponseException - AgentInvocationError removed, split into AgentInvalidRequest/ResponseException - Workflow exceptions moved from _workflows/_exceptions.py into main exceptions.py - _workflows/__init__.py emptied; main __init__.py imports directly from submodules - Purview exceptions re-parented under IntegrationException hierarchy - Init validation errors use built-in ValueError/TypeError instead of custom exceptions - CODING_STANDARD.md updated with hierarchy design and rationale Fixes microsoft/agent-framework#3410 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify ToolException vs ToolExecutionException docstrings ToolException: base class for all tool-related exceptions (preconditions, connection/init failures). ToolExecutionException: runtime call failures (tool call failed, reconnect failed, MCP errors). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix remaining stale imports from agent_framework._workflows - azurefunctions: _context.py, _app.py, _serialization.py, test_func_utils.py used 'from agent_framework._workflows import X' which broke after emptying _workflows/__init__.py; changed to direct submodule imports - azure-ai-search: test still referenced ServiceInitializationError; updated to ValueError to match production code Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
7f606a2e3a
commit
5ee06853a1
@@ -21,7 +21,6 @@ from agent_framework import (
|
||||
)
|
||||
from agent_framework._settings import SecretString
|
||||
from agent_framework.azure import AzureOpenAIAssistantsClient
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
|
||||
skip_if_azure_integration_tests_disabled = pytest.mark.skipif(
|
||||
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
|
||||
@@ -120,7 +119,7 @@ def test_azure_assistants_client_init_auto_create_client(
|
||||
|
||||
def test_azure_assistants_client_init_validation_fail() -> None:
|
||||
"""Test AzureOpenAIAssistantsClient initialization with validation failure."""
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
# Force failure by providing invalid deployment name type - this should cause validation to fail
|
||||
AzureOpenAIAssistantsClient(deployment_name=123, api_key="valid-key") # type: ignore
|
||||
|
||||
@@ -128,7 +127,7 @@ def test_azure_assistants_client_init_validation_fail() -> None:
|
||||
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True)
|
||||
def test_azure_assistants_client_init_missing_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test AzureOpenAIAssistantsClient initialization with missing deployment name."""
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
AzureOpenAIAssistantsClient(api_key=azure_openai_unit_test_env.get("AZURE_OPENAI_API_KEY", "test-key"))
|
||||
|
||||
|
||||
@@ -607,7 +606,7 @@ def test_azure_assistants_client_no_authentication_error() -> None:
|
||||
}
|
||||
|
||||
# Test missing authentication raises error
|
||||
with pytest.raises(ServiceInitializationError, match="api_key, credential, or a client"):
|
||||
with pytest.raises(ValueError, match="api_key, credential, or a client"):
|
||||
AzureOpenAIAssistantsClient(
|
||||
deployment_name="test-deployment",
|
||||
endpoint="https://test-endpoint.openai.azure.com",
|
||||
|
||||
@@ -28,7 +28,7 @@ from agent_framework import (
|
||||
)
|
||||
from agent_framework._telemetry import USER_AGENT_KEY
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException
|
||||
from agent_framework.exceptions import ChatClientException
|
||||
from agent_framework.openai import (
|
||||
ContentFilterResultSeverity,
|
||||
OpenAIContentFilterException,
|
||||
@@ -93,13 +93,13 @@ def test_init_endpoint(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_CHAT_DEPLOYMENT_NAME"]], indirect=True)
|
||||
def test_init_with_empty_deployment_name(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
AzureOpenAIChatClient()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_ENDPOINT", "AZURE_OPENAI_BASE_URL"]], indirect=True)
|
||||
def test_init_with_empty_endpoint_and_base_url(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
AzureOpenAIChatClient()
|
||||
|
||||
|
||||
@@ -554,7 +554,7 @@ async def test_bad_request_non_content_filter(
|
||||
|
||||
azure_chat_client = AzureOpenAIChatClient()
|
||||
|
||||
with pytest.raises(ServiceResponseException, match="service failed to complete the prompt"):
|
||||
with pytest.raises(ChatClientException, match="service failed to complete the prompt"):
|
||||
await azure_chat_client.get_response(
|
||||
messages=chat_history,
|
||||
)
|
||||
|
||||
@@ -21,7 +21,6 @@ from agent_framework import (
|
||||
tool,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
|
||||
skip_if_azure_integration_tests_disabled = pytest.mark.skipif(
|
||||
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
|
||||
@@ -81,7 +80,7 @@ def test_init(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
|
||||
def test_init_validation_fail() -> None:
|
||||
# Test successful initialization
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
AzureOpenAIResponsesClient(api_key="34523", deployment_name={"test": "dict"}) # type: ignore
|
||||
|
||||
|
||||
@@ -113,7 +112,7 @@ def test_init_with_default_header(azure_openai_unit_test_env: dict[str, str]) ->
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME"]], indirect=True)
|
||||
def test_init_with_empty_model_id(azure_openai_unit_test_env: dict[str, str]) -> None:
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
AzureOpenAIResponsesClient()
|
||||
|
||||
|
||||
@@ -212,7 +211,7 @@ def test_create_client_from_project_with_endpoint() -> None:
|
||||
|
||||
def test_create_client_from_project_missing_endpoint() -> None:
|
||||
"""Test _create_client_from_project raises error when endpoint is missing."""
|
||||
with pytest.raises(ServiceInitializationError, match="project endpoint is required"):
|
||||
with pytest.raises(ValueError, match="project endpoint is required"):
|
||||
AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=None,
|
||||
project_endpoint=None,
|
||||
@@ -222,7 +221,7 @@ def test_create_client_from_project_missing_endpoint() -> None:
|
||||
|
||||
def test_create_client_from_project_missing_credential() -> None:
|
||||
"""Test _create_client_from_project raises error when credential is missing."""
|
||||
with pytest.raises(ServiceInitializationError, match="credential is required"):
|
||||
with pytest.raises(ValueError, match="credential is required"):
|
||||
AzureOpenAIResponsesClient._create_client_from_project(
|
||||
project_client=None,
|
||||
project_endpoint="https://test-project.services.ai.azure.com",
|
||||
|
||||
@@ -9,7 +9,7 @@ from azure.core.credentials_async import AsyncTokenCredential
|
||||
from agent_framework.azure._entra_id_authentication import (
|
||||
resolve_credential_to_token_provider,
|
||||
)
|
||||
from agent_framework.exceptions import ServiceInvalidAuthError
|
||||
from agent_framework.exceptions import ChatClientInvalidAuthException
|
||||
|
||||
TOKEN_ENDPOINT = "https://cognitiveservices.azure.com/.default"
|
||||
|
||||
@@ -51,11 +51,11 @@ def test_resolve_callable_provider_passthrough() -> None:
|
||||
|
||||
|
||||
def test_resolve_missing_endpoint_raises() -> None:
|
||||
"""Test that missing token endpoint raises ServiceInvalidAuthError."""
|
||||
"""Test that missing token endpoint raises ChatClientInvalidAuthException."""
|
||||
mock_credential = MagicMock(spec=TokenCredential)
|
||||
|
||||
with pytest.raises(ServiceInvalidAuthError, match="A token endpoint must be provided"):
|
||||
with pytest.raises(ChatClientInvalidAuthException, match="A token endpoint must be provided"):
|
||||
resolve_credential_to_token_provider(mock_credential, "")
|
||||
|
||||
with pytest.raises(ServiceInvalidAuthError, match="A token endpoint must be provided"):
|
||||
with pytest.raises(ChatClientInvalidAuthException, match="A token endpoint must be provided"):
|
||||
resolve_credential_to_token_provider(mock_credential, None) # type: ignore[arg-type]
|
||||
|
||||
@@ -245,9 +245,8 @@ class TestOverrideTypeValidation:
|
||||
"""Test override type validation."""
|
||||
|
||||
def test_invalid_type_raises(self) -> None:
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
|
||||
with pytest.raises(ServiceInitializationError, match="Invalid type for setting 'api_key'"):
|
||||
with pytest.raises(ValueError, match="Invalid type for setting 'api_key'"):
|
||||
load_settings(SimpleSettings, env_prefix="TEST_", api_key={"bad": "type"})
|
||||
|
||||
def test_valid_types_accepted(self) -> None:
|
||||
|
||||
@@ -9,7 +9,6 @@ from openai.types.beta.assistant import Assistant
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from agent_framework import Agent, normalize_tools, tool
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
from agent_framework.openai import OpenAIAssistantProvider, OpenAIAssistantsClient
|
||||
from agent_framework.openai._shared import from_assistant_tools, to_assistant_tools
|
||||
|
||||
@@ -99,7 +98,6 @@ class WeatherResponse(BaseModel):
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Initialization Tests
|
||||
|
||||
|
||||
@@ -141,7 +139,7 @@ class TestOpenAIAssistantProviderInit:
|
||||
"responses_model_id": None,
|
||||
}
|
||||
|
||||
with pytest.raises(ServiceInitializationError) as exc_info:
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
OpenAIAssistantProvider()
|
||||
|
||||
assert "API key is required" in str(exc_info.value)
|
||||
@@ -191,7 +189,6 @@ class TestOpenAIAssistantProviderContextManager:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region create_agent Tests
|
||||
|
||||
|
||||
@@ -366,7 +363,6 @@ class TestOpenAIAssistantProviderCreateAgent:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region get_agent Tests
|
||||
|
||||
|
||||
@@ -454,7 +450,6 @@ class TestOpenAIAssistantProviderGetAgent:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region as_agent Tests
|
||||
|
||||
|
||||
@@ -540,7 +535,6 @@ class TestOpenAIAssistantProviderAsAgent:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Tool Conversion Tests
|
||||
|
||||
|
||||
@@ -643,7 +637,6 @@ class TestToolConversion:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Tool Validation Tests
|
||||
|
||||
|
||||
@@ -702,7 +695,6 @@ class TestToolValidation:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Tool Merging Tests
|
||||
|
||||
|
||||
@@ -760,10 +752,8 @@ class TestToolMerging:
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Integration Tests
|
||||
|
||||
|
||||
skip_if_openai_integration_tests_disabled = pytest.mark.skipif(
|
||||
os.getenv("RUN_INTEGRATION_TESTS", "false").lower() != "true"
|
||||
or os.getenv("OPENAI_API_KEY", "") in ("", "test-dummy-key"),
|
||||
|
||||
@@ -22,7 +22,6 @@ from agent_framework import (
|
||||
SupportsChatGetResponse,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.exceptions import ServiceInitializationError
|
||||
from agent_framework.openai import OpenAIAssistantsClient
|
||||
|
||||
skip_if_openai_integration_tests_disabled = pytest.mark.skipif(
|
||||
@@ -145,7 +144,7 @@ def test_init_auto_create_client(
|
||||
|
||||
def test_init_validation_fail() -> None:
|
||||
"""Test OpenAIAssistantsClient initialization with validation failure."""
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
# Force failure by providing invalid model ID type
|
||||
OpenAIAssistantsClient(model_id=123, api_key="valid-key") # type: ignore
|
||||
|
||||
@@ -153,14 +152,14 @@ def test_init_validation_fail() -> None:
|
||||
@pytest.mark.parametrize("exclude_list", [["OPENAI_CHAT_MODEL_ID"]], indirect=True)
|
||||
def test_init_missing_model_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test OpenAIAssistantsClient initialization with missing model ID."""
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIAssistantsClient(api_key=openai_unit_test_env.get("OPENAI_API_KEY", "test-key"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["OPENAI_API_KEY"]], indirect=True)
|
||||
def test_init_missing_api_key(openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test OpenAIAssistantsClient initialization with missing API key."""
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIAssistantsClient(model_id="gpt-4")
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ from agent_framework import (
|
||||
SupportsChatGetResponse,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.exceptions import ServiceInitializationError, ServiceResponseException
|
||||
from agent_framework.exceptions import ChatClientException
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework.openai._exceptions import OpenAIContentFilterException
|
||||
|
||||
@@ -42,7 +42,7 @@ def test_init(openai_unit_test_env: dict[str, str]) -> None:
|
||||
|
||||
def test_init_validation_fail() -> None:
|
||||
# Test successful initialization
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIChatClient(api_key="34523", model_id={"test": "dict"}) # type: ignore
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ def test_init_base_url_from_settings_env() -> None:
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["OPENAI_CHAT_MODEL_ID"]], indirect=True)
|
||||
def test_init_with_empty_model_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIChatClient()
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ def test_init_with_empty_model_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
def test_init_with_empty_api_key(openai_unit_test_env: dict[str, str]) -> None:
|
||||
model_id = "test_model_id"
|
||||
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIChatClient(
|
||||
model_id=model_id,
|
||||
)
|
||||
@@ -235,7 +235,7 @@ async def test_exception_message_includes_original_error_details() -> None:
|
||||
|
||||
with (
|
||||
patch.object(client.client.chat.completions, "create", side_effect=mock_error),
|
||||
pytest.raises(ServiceResponseException) as exc_info,
|
||||
pytest.raises(ChatClientException) as exc_info,
|
||||
):
|
||||
await client._inner_get_response(messages=messages, options={}) # type: ignore
|
||||
|
||||
@@ -779,11 +779,11 @@ def test_prepare_options_without_model_id(openai_unit_test_env: dict[str, str])
|
||||
|
||||
def test_prepare_options_without_messages(openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test that prepare_options raises error when messages are missing."""
|
||||
from agent_framework.exceptions import ServiceInvalidRequestError
|
||||
from agent_framework.exceptions import ChatClientInvalidRequestException
|
||||
|
||||
client = OpenAIChatClient()
|
||||
|
||||
with pytest.raises(ServiceInvalidRequestError, match="Messages are required"):
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="Messages are required"):
|
||||
client._prepare_options([], {})
|
||||
|
||||
|
||||
@@ -932,7 +932,7 @@ async def test_streaming_exception_handling(openai_unit_test_env: dict[str, str]
|
||||
|
||||
with (
|
||||
patch.object(client.client.chat.completions, "create", side_effect=mock_error),
|
||||
pytest.raises(ServiceResponseException),
|
||||
pytest.raises(ChatClientException),
|
||||
):
|
||||
async for _ in client._inner_get_response(messages=messages, stream=True, options={}): # type: ignore
|
||||
pass
|
||||
|
||||
@@ -15,9 +15,7 @@ from openai.types.chat.chat_completion_message import ChatCompletionMessage
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agent_framework import ChatResponseUpdate, Message
|
||||
from agent_framework.exceptions import (
|
||||
ServiceResponseException,
|
||||
)
|
||||
from agent_framework.exceptions import ChatClientException
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
|
||||
|
||||
@@ -182,7 +180,7 @@ async def test_cmc_general_exception(
|
||||
chat_history.append(Message(role="user", text="hello world"))
|
||||
|
||||
openai_chat_completion = OpenAIChatClient()
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await openai_chat_completion.get_response(
|
||||
messages=chat_history,
|
||||
)
|
||||
|
||||
@@ -35,11 +35,7 @@ from agent_framework import (
|
||||
SupportsChatGetResponse,
|
||||
tool,
|
||||
)
|
||||
from agent_framework.exceptions import (
|
||||
ServiceInitializationError,
|
||||
ServiceInvalidRequestError,
|
||||
ServiceResponseException,
|
||||
)
|
||||
from agent_framework.exceptions import ChatClientException, ChatClientInvalidRequestException
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
from agent_framework.openai._exceptions import OpenAIContentFilterException
|
||||
|
||||
@@ -106,7 +102,7 @@ def test_init(openai_unit_test_env: dict[str, str]) -> None:
|
||||
|
||||
def test_init_validation_fail() -> None:
|
||||
# Test successful initialization
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIResponsesClient(api_key="34523", model_id={"test": "dict"}) # type: ignore
|
||||
|
||||
|
||||
@@ -138,7 +134,7 @@ def test_init_with_default_header(openai_unit_test_env: dict[str, str]) -> None:
|
||||
|
||||
@pytest.mark.parametrize("exclude_list", [["OPENAI_RESPONSES_MODEL_ID"]], indirect=True)
|
||||
def test_init_with_empty_model_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIResponsesClient()
|
||||
|
||||
|
||||
@@ -146,7 +142,7 @@ def test_init_with_empty_model_id(openai_unit_test_env: dict[str, str]) -> None:
|
||||
def test_init_with_empty_api_key(openai_unit_test_env: dict[str, str]) -> None:
|
||||
model_id = "test_model_id"
|
||||
|
||||
with pytest.raises(ServiceInitializationError):
|
||||
with pytest.raises(ValueError):
|
||||
OpenAIResponsesClient(
|
||||
model_id=model_id,
|
||||
)
|
||||
@@ -192,8 +188,8 @@ async def test_get_response_with_invalid_input() -> None:
|
||||
|
||||
client = OpenAIResponsesClient(model_id="invalid-model", api_key="test-key")
|
||||
|
||||
# Test with empty messages which should trigger ServiceInvalidRequestError
|
||||
with pytest.raises(ServiceInvalidRequestError, match="Messages are required"):
|
||||
# Test with empty messages which should trigger ChatClientInvalidRequestException
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="Messages are required"):
|
||||
await client.get_response(messages=[])
|
||||
|
||||
|
||||
@@ -201,7 +197,7 @@ async def test_get_response_with_all_parameters() -> None:
|
||||
"""Test get_response with all possible parameters to cover parameter handling logic."""
|
||||
client = OpenAIResponsesClient(model_id="test-model", api_key="test-key")
|
||||
# Test with comprehensive parameter set - should fail due to invalid API key
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await client.get_response(
|
||||
messages=[Message(role="user", text="Test message")],
|
||||
options={
|
||||
@@ -244,7 +240,7 @@ async def test_web_search_tool_with_location() -> None:
|
||||
)
|
||||
|
||||
# Should raise an authentication error due to invalid API key
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await client.get_response(
|
||||
messages=[Message(role="user", text="What's the weather?")],
|
||||
options={"tools": [web_search_tool], "tool_choice": "auto"},
|
||||
@@ -258,7 +254,7 @@ async def test_code_interpreter_tool_variations() -> None:
|
||||
# Test code interpreter using static method
|
||||
code_tool = OpenAIResponsesClient.get_code_interpreter_tool()
|
||||
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await client.get_response(
|
||||
messages=[Message("user", ["Run some code"])],
|
||||
options={"tools": [code_tool]},
|
||||
@@ -267,7 +263,7 @@ async def test_code_interpreter_tool_variations() -> None:
|
||||
# Test code interpreter with files using static method
|
||||
code_tool_with_files = OpenAIResponsesClient.get_code_interpreter_tool(file_ids=["file1", "file2"])
|
||||
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await client.get_response(
|
||||
messages=[Message(role="user", text="Process these files")],
|
||||
options={"tools": [code_tool_with_files]},
|
||||
@@ -303,7 +299,7 @@ async def test_hosted_file_search_tool_validation() -> None:
|
||||
file_search_tool = OpenAIResponsesClient.get_file_search_tool(vector_store_ids=["vs_123"])
|
||||
|
||||
# Test using file search tool - may raise various exceptions depending on API response
|
||||
with pytest.raises((ValueError, ServiceInvalidRequestError, ServiceResponseException)):
|
||||
with pytest.raises((ValueError, ChatClientInvalidRequestException, ChatClientException)):
|
||||
await client.get_response(
|
||||
messages=[Message("user", ["Test"])],
|
||||
options={"tools": [file_search_tool]},
|
||||
@@ -331,7 +327,7 @@ async def test_chat_message_parsing_with_function_calls() -> None:
|
||||
]
|
||||
|
||||
# This should exercise the message parsing logic - will fail due to invalid API key
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
await client.get_response(messages=messages)
|
||||
|
||||
|
||||
@@ -401,7 +397,7 @@ async def test_bad_request_error_non_content_filter() -> None:
|
||||
mock_error.code = "invalid_request"
|
||||
|
||||
with patch.object(client.client.responses, "parse", side_effect=mock_error):
|
||||
with pytest.raises(ServiceResponseException) as exc_info:
|
||||
with pytest.raises(ChatClientException) as exc_info:
|
||||
await client.get_response(
|
||||
messages=[Message(role="user", text="Test message")],
|
||||
options={"response_format": OutputStruct},
|
||||
@@ -996,7 +992,7 @@ def test_response_format_with_conflicting_definitions() -> None:
|
||||
response_format = {"type": "json_schema", "format": {"type": "json_schema", "name": "Test", "schema": {}}}
|
||||
text_config = {"format": {"type": "json_object"}}
|
||||
|
||||
with pytest.raises(ServiceInvalidRequestError, match="Conflicting response_format definitions"):
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="Conflicting response_format definitions"):
|
||||
client._prepare_response_and_text_format(response_format=response_format, text_config=text_config)
|
||||
|
||||
|
||||
@@ -1092,7 +1088,7 @@ def test_response_format_json_schema_missing_schema() -> None:
|
||||
|
||||
response_format = {"type": "json_schema", "json_schema": {"name": "NoSchema"}}
|
||||
|
||||
with pytest.raises(ServiceInvalidRequestError, match="json_schema response_format requires a schema"):
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="json_schema response_format requires a schema"):
|
||||
client._prepare_response_and_text_format(response_format=response_format, text_config=None)
|
||||
|
||||
|
||||
@@ -1102,7 +1098,7 @@ def test_response_format_unsupported_type() -> None:
|
||||
|
||||
response_format = {"type": "unsupported_format"}
|
||||
|
||||
with pytest.raises(ServiceInvalidRequestError, match="Unsupported response_format"):
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="Unsupported response_format"):
|
||||
client._prepare_response_and_text_format(response_format=response_format, text_config=None)
|
||||
|
||||
|
||||
@@ -1112,7 +1108,7 @@ def test_response_format_invalid_type() -> None:
|
||||
|
||||
response_format = "invalid" # Not a Pydantic model or mapping
|
||||
|
||||
with pytest.raises(ServiceInvalidRequestError, match="response_format must be a Pydantic model or mapping"):
|
||||
with pytest.raises(ChatClientInvalidRequestException, match="response_format must be a Pydantic model or mapping"):
|
||||
client._prepare_response_and_text_format(response_format=response_format, text_config=None) # type: ignore
|
||||
|
||||
|
||||
@@ -1689,7 +1685,7 @@ def test_streaming_annotation_added_with_unknown_type() -> None:
|
||||
|
||||
|
||||
async def test_service_response_exception_includes_original_error_details() -> None:
|
||||
"""Test that ServiceResponseException messages include original error details in the new format."""
|
||||
"""Test that ChatClientException messages include original error details in the new format."""
|
||||
client = OpenAIResponsesClient(model_id="test-model", api_key="test-key")
|
||||
messages = [Message(role="user", text="test message")]
|
||||
|
||||
@@ -1704,7 +1700,7 @@ async def test_service_response_exception_includes_original_error_details() -> N
|
||||
|
||||
with (
|
||||
patch.object(client.client.responses, "parse", side_effect=mock_error),
|
||||
pytest.raises(ServiceResponseException) as exc_info,
|
||||
pytest.raises(ChatClientException) as exc_info,
|
||||
):
|
||||
await client.get_response(messages=messages, options={"response_format": OutputStruct})
|
||||
|
||||
@@ -1719,7 +1715,7 @@ async def test_get_response_streaming_with_response_format() -> None:
|
||||
messages = [Message(role="user", text="Test streaming with format")]
|
||||
|
||||
# It will fail due to invalid API key, but exercises the code path
|
||||
with pytest.raises(ServiceResponseException):
|
||||
with pytest.raises(ChatClientException):
|
||||
|
||||
async def run_streaming():
|
||||
async for _ in client.get_response(
|
||||
|
||||
@@ -6,9 +6,9 @@ from typing import Any, cast
|
||||
|
||||
import pytest
|
||||
|
||||
from agent_framework import WorkflowCheckpointException
|
||||
from agent_framework._workflows._checkpoint_encoding import (
|
||||
_TYPE_MARKER, # type: ignore
|
||||
CheckpointDecodingError,
|
||||
decode_checkpoint_value,
|
||||
encode_checkpoint_value,
|
||||
)
|
||||
@@ -178,13 +178,13 @@ def test_decode_plain_list() -> None:
|
||||
|
||||
|
||||
def test_decode_raises_on_type_mismatch() -> None:
|
||||
"""Test that decoding raises CheckpointDecodingError when type doesn't match."""
|
||||
"""Test that decoding raises WorkflowCheckpointException when type doesn't match."""
|
||||
# Encode a SampleRequest but tamper with the type marker
|
||||
encoded = encode_checkpoint_value(SampleRequest(request_id="r1", prompt="p1"))
|
||||
assert isinstance(encoded, dict)
|
||||
encoded[_TYPE_MARKER] = "nonexistent.module:FakeClass"
|
||||
|
||||
with pytest.raises(CheckpointDecodingError, match="Type mismatch"):
|
||||
with pytest.raises(WorkflowCheckpointException, match="Type mismatch"):
|
||||
decode_checkpoint_value(encoded)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user