mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add base_url parameter to AnthropicClient and RawAnthropicClient (#5685)
* feat(anthropic): add base_url parameter to AnthropicClient and RawAnthropicClient Add base_url support to AnthropicSettings TypedDict, RawAnthropicClient, and AnthropicClient so users can point the client at Foundry or other Anthropic-compatible endpoints without having to construct AsyncAnthropic manually. - Add base_url field to AnthropicSettings (resolved from ANTHROPIC_BASE_URL env var) - Add base_url parameter to RawAnthropicClient.__init__ and pass it to AsyncAnthropic - Add base_url parameter to AnthropicClient.__init__ and forward to super - Add unit tests for base_url on both client classes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Add `base_url` parameter to `AnthropicClient` and `RawAnthropicClient` Fixes #5683 * test: add ANTHROPIC_BASE_URL env fallback tests for issue #5683 Add unit tests verifying that both AnthropicClient and RawAnthropicClient pick up base_url from the ANTHROPIC_BASE_URL environment variable via load_settings when base_url is not passed explicitly as a constructor arg. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(anthropic): explicit base_url kwarg beats ANTHROPIC_BASE_URL env var (#5683) Add regression tests asserting that when both ANTHROPIC_BASE_URL is set in the environment *and* an explicit base_url kwarg is passed to AnthropicClient / RawAnthropicClient, the explicit kwarg wins. This closes the priority-ordering contract (explicit arg > env var) that the existing tests left implicit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
Copilot
parent
44381c051b
commit
8bb4692678
@@ -216,10 +216,12 @@ class AnthropicSettings(TypedDict, total=False):
|
||||
Keys:
|
||||
api_key: The Anthropic API key.
|
||||
chat_model: The Anthropic chat model.
|
||||
base_url: Optional base URL for the Anthropic API endpoint.
|
||||
"""
|
||||
|
||||
api_key: SecretString | None
|
||||
chat_model: str | None
|
||||
base_url: str | None
|
||||
|
||||
|
||||
class RawAnthropicClient(
|
||||
@@ -248,6 +250,7 @@ class RawAnthropicClient(
|
||||
*,
|
||||
api_key: str | None = None,
|
||||
model: str | None = None,
|
||||
base_url: str | None = None,
|
||||
anthropic_client: AnthropicAsyncClient | None = None,
|
||||
additional_beta_flags: list[str] | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
@@ -259,6 +262,8 @@ class RawAnthropicClient(
|
||||
Keyword Args:
|
||||
api_key: The Anthropic API key to use for authentication.
|
||||
model: The model to use.
|
||||
base_url: Optional base URL for the Anthropic API endpoint. Useful for Foundry or
|
||||
other compatible deployments. Falls back to ``ANTHROPIC_BASE_URL`` env variable.
|
||||
anthropic_client: An existing Anthropic client to use. If not provided, one will be created.
|
||||
This can be used to further configure the client before passing it in.
|
||||
For instance if you need to set a different base_url for testing or private deployments.
|
||||
@@ -284,6 +289,13 @@ class RawAnthropicClient(
|
||||
api_key="your_anthropic_api_key",
|
||||
)
|
||||
|
||||
# Or with a custom base URL (e.g. for Foundry-compatible endpoints)
|
||||
client = RawAnthropicClient(
|
||||
model="claude-sonnet-4-5-20250929",
|
||||
api_key="your_anthropic_api_key",
|
||||
base_url="https://custom-anthropic-endpoint.com",
|
||||
)
|
||||
|
||||
# Or loading from a .env file
|
||||
client = RawAnthropicClient(env_file_path="path/to/.env")
|
||||
|
||||
@@ -316,12 +328,14 @@ class RawAnthropicClient(
|
||||
env_prefix="ANTHROPIC_",
|
||||
api_key=api_key,
|
||||
chat_model=model,
|
||||
base_url=base_url,
|
||||
env_file_path=env_file_path,
|
||||
env_file_encoding=env_file_encoding,
|
||||
)
|
||||
|
||||
api_key_secret = anthropic_settings.get("api_key")
|
||||
model_setting = anthropic_settings.get("chat_model")
|
||||
base_url_setting = anthropic_settings.get("base_url")
|
||||
|
||||
if anthropic_client is None:
|
||||
if api_key_secret is None:
|
||||
@@ -332,6 +346,7 @@ class RawAnthropicClient(
|
||||
|
||||
anthropic_client = AsyncAnthropic(
|
||||
api_key=api_key_secret.get_secret_value(),
|
||||
base_url=base_url_setting,
|
||||
default_headers={"User-Agent": get_user_agent()},
|
||||
)
|
||||
|
||||
@@ -1409,6 +1424,7 @@ class AnthropicClient(
|
||||
*,
|
||||
api_key: str | None = None,
|
||||
model: str | None = None,
|
||||
base_url: str | None = None,
|
||||
anthropic_client: AnthropicAsyncClient | None = None,
|
||||
additional_beta_flags: list[str] | None = None,
|
||||
additional_properties: dict[str, Any] | None = None,
|
||||
@@ -1422,6 +1438,8 @@ class AnthropicClient(
|
||||
Keyword Args:
|
||||
api_key: The Anthropic API key to use for authentication.
|
||||
model: The model to use.
|
||||
base_url: Optional base URL for the Anthropic API endpoint. Useful for Foundry or
|
||||
other compatible deployments. Falls back to ``ANTHROPIC_BASE_URL`` env variable.
|
||||
anthropic_client: An existing Anthropic client to use. If not provided, one will be created.
|
||||
This can be used to further configure the client before passing it in.
|
||||
For instance if you need to set a different base_url for testing or private deployments.
|
||||
@@ -1448,6 +1466,13 @@ class AnthropicClient(
|
||||
api_key="your_anthropic_api_key",
|
||||
)
|
||||
|
||||
# Or with a custom base URL (e.g. for Foundry-compatible endpoints)
|
||||
client = AnthropicClient(
|
||||
model="claude-sonnet-4-5-20250929",
|
||||
api_key="your_anthropic_api_key",
|
||||
base_url="https://custom-anthropic-endpoint.com",
|
||||
)
|
||||
|
||||
# Or loading from a .env file
|
||||
client = AnthropicClient(env_file_path="path/to/.env")
|
||||
|
||||
@@ -1477,6 +1502,7 @@ class AnthropicClient(
|
||||
super().__init__(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
base_url=base_url,
|
||||
anthropic_client=anthropic_client,
|
||||
additional_beta_flags=additional_beta_flags,
|
||||
additional_properties=additional_properties,
|
||||
|
||||
@@ -149,6 +149,108 @@ def test_anthropic_client_init_auto_create_client(
|
||||
assert client.model == anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"]
|
||||
|
||||
|
||||
def test_anthropic_client_init_with_base_url(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test AnthropicClient accepts a base_url and passes it to the underlying AsyncAnthropic client."""
|
||||
custom_url = "https://custom-anthropic-endpoint.com"
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
assert custom_url in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
def test_raw_anthropic_client_init_with_base_url(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test RawAnthropicClient accepts a base_url and passes it to the underlying AsyncAnthropic client."""
|
||||
custom_url = "https://custom-anthropic-endpoint.com"
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=custom_url,
|
||||
)
|
||||
|
||||
assert custom_url in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_anthropic_client_init_base_url_from_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test AnthropicClient picks up base_url from ANTHROPIC_BASE_URL env variable when not passed explicitly."""
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
)
|
||||
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_raw_anthropic_client_init_base_url_from_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test RawAnthropicClient picks up base_url from ANTHROPIC_BASE_URL env variable when not passed explicitly."""
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
)
|
||||
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_anthropic_client_init_explicit_base_url_wins_over_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that an explicit base_url kwarg takes priority over ANTHROPIC_BASE_URL env variable."""
|
||||
explicit_url = "https://explicit-endpoint.example.com"
|
||||
client = AnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=explicit_url,
|
||||
)
|
||||
|
||||
assert explicit_url in str(client.anthropic_client.base_url)
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] not in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"override_env_param_dict",
|
||||
[{"ANTHROPIC_BASE_URL": "https://env-base-url.example.com"}],
|
||||
indirect=True,
|
||||
)
|
||||
def test_raw_anthropic_client_init_explicit_base_url_wins_over_env(
|
||||
anthropic_unit_test_env: dict[str, str],
|
||||
) -> None:
|
||||
"""Test that an explicit base_url kwarg takes priority over ANTHROPIC_BASE_URL env variable."""
|
||||
explicit_url = "https://explicit-endpoint.example.com"
|
||||
client = RawAnthropicClient(
|
||||
api_key=anthropic_unit_test_env["ANTHROPIC_API_KEY"],
|
||||
model=anthropic_unit_test_env["ANTHROPIC_CHAT_MODEL"],
|
||||
base_url=explicit_url,
|
||||
)
|
||||
|
||||
assert explicit_url in str(client.anthropic_client.base_url)
|
||||
assert anthropic_unit_test_env["ANTHROPIC_BASE_URL"] not in str(client.anthropic_client.base_url)
|
||||
|
||||
|
||||
def test_anthropic_client_init_missing_api_key() -> None:
|
||||
"""Test AnthropicClient initialization when API key is missing."""
|
||||
with patch("agent_framework_anthropic._chat_client.load_settings") as mock_load:
|
||||
|
||||
Reference in New Issue
Block a user