Coverage for src/infra/clients/anthropic_client.py: 14%

1"""Shared Anthropic client factory for mala orchestrator.

2

3 This module provides a centralized way to create Anthropic clients with:

4- Consistent configuration from MalaConfig (api_key, base_url)

5- Automatic Braintrust wrapping for observability when available

6

7 Usage:

8 from src.infra.clients.anthropic_client import create_anthropic_client

9 from src.infra.io.config import MalaConfig

10

11 config = MalaConfig.from_env()

12 client = create_anthropic_client(

13 api_key=config.llm_api_key,

14 base_url=config.llm_base_url,

15 timeout=120.0,

16 )

17

18 # Client is ready to use with Braintrust tracing enabled (if configured)

19 response = client.messages.create(

20 model="claude-sonnet-4-20250514",

21 max_tokens=1024,

22 messages=[{"role": "user", "content": "Hello"}],

23 )

24"""

25

26 from __future__ import annotations

27

28 from typing import Any

29

30

31 def create_anthropic_client(

32 *,

33 api_key: str | None = None,

34 base_url: str | None = None,

35 timeout: float | None = None,

36) -> Any: # noqa: ANN401 - Return type is dynamic (Anthropic or wrapped client)

37 """Create an Anthropic client with consistent configuration and tracing.

38

39 This factory function centralizes Anthropic client creation to ensure:

40 1. Configuration is applied consistently (api_key, base_url)

41 2. Braintrust tracing is automatically enabled when available

42 3. Error handling and telemetry work uniformly across components

43

44 Args:

45 api_key: Anthropic API key. If not provided, the client will use

46 the ANTHROPIC_API_KEY environment variable.

47 base_url: Optional base URL for API requests. Use this to route

48 requests through proxies.

49 timeout: Optional timeout in seconds for API requests.

50

51 Returns:

52 An Anthropic client instance, optionally wrapped with Braintrust

53 tracing if available.

54

55 Raises:

56 RuntimeError: If the anthropic package is not installed.

57

58 Example:

59 from src.infra.clients.anthropic_client import create_anthropic_client

60

61 # Basic usage with env var for API key

62 client = create_anthropic_client()

63

64 # With explicit configuration

65 client = create_anthropic_client(

66 api_key="sk-...",

67 base_url="https://proxy.example.com/v1",

68 timeout=60.0,

69 )

70

71 # Braintrust tracing is automatic when BRAINTRUST_API_KEY is set

72 """

73 try:

74 from anthropic import Anthropic # type: ignore[import-untyped]

75 except ImportError as e:

76 raise RuntimeError(

77 "anthropic package is required. Install with: uv add anthropic"

78 ) from e

79

80 # Build client kwargs, only including non-None values

81 client_kwargs: dict[str, object] = {}

82 if api_key is not None:

83 client_kwargs["api_key"] = api_key

84 if base_url is not None:

85 client_kwargs["base_url"] = base_url

86 if timeout is not None:

87 client_kwargs["timeout"] = timeout

88

89 # Create the base client

90 client = Anthropic(**client_kwargs)

91

92 # Wrap with Braintrust for observability (no-op if Braintrust not configured)

93 # This provides automatic tracing of all LLM calls when BRAINTRUST_API_KEY is set

94 try:

95 from braintrust import wrap_anthropic

96

97 client = wrap_anthropic(client)

98 except ImportError:

99 pass # Braintrust not installed, proceed without tracing

100

101 return client