struct-sdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ """Claude Agent SDK auto-instrumentation.
2
+
3
+ Patches ``ClaudeAgentOptions`` to automatically inject OTel env vars into
4
+ every Claude Code subprocess, regardless of whether the user calls
5
+ ``query()`` or ``ClaudeSDKClient``.
6
+
7
+ Subagents inherit the parent's env vars automatically.
8
+
9
+ Auto-applied by struct.init() when the claude-agent-sdk package is installed.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import functools
15
+ import logging
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ if TYPE_CHECKING:
19
+ from struct_sdk.core import StructSDK
20
+
21
+ logger = logging.getLogger("struct_sdk.claude_agent")
22
+
23
+
24
+ def patch(sdk: StructSDK) -> None:
25
+ """Patch claude_agent_sdk to inject OTel env vars. Raises ImportError if not installed."""
26
+ import claude_agent_sdk
27
+ from claude_agent_sdk import ClaudeAgentOptions
28
+
29
+ if getattr(claude_agent_sdk, "__struct_patched", False):
30
+ return
31
+
32
+ # Patch ClaudeAgentOptions.__init__ to merge OTel env vars into every instance.
33
+ # This covers both query() and ClaudeSDKClient usage patterns.
34
+ original_init = ClaudeAgentOptions.__init__
35
+
36
+ otel_env = _build_otel_env(sdk)
37
+
38
+ @functools.wraps(original_init)
39
+ def patched_init(self: Any, *args: Any, **kwargs: Any) -> None:
40
+ original_init(self, *args, **kwargs)
41
+ # Merge OTel env vars into the options' env dict
42
+ existing_env = getattr(self, "env", None) or {}
43
+ self.env = {**existing_env, **otel_env}
44
+
45
+ patched_init.__struct_wrapped__ = True # type: ignore[attr-defined]
46
+ patched_init.__struct_original__ = original_init # type: ignore[attr-defined]
47
+ ClaudeAgentOptions.__init__ = patched_init # type: ignore[method-assign]
48
+ claude_agent_sdk.__struct_patched = True # type: ignore[attr-defined]
49
+
50
+
51
+ def unpatch() -> None:
52
+ """Remove patches."""
53
+ try:
54
+ import claude_agent_sdk
55
+ from claude_agent_sdk import ClaudeAgentOptions
56
+ except ImportError:
57
+ return
58
+
59
+ if not getattr(claude_agent_sdk, "__struct_patched", False):
60
+ return
61
+
62
+ if getattr(ClaudeAgentOptions.__init__, "__struct_wrapped__", False):
63
+ ClaudeAgentOptions.__init__ = ClaudeAgentOptions.__init__.__struct_original__ # type: ignore[method-assign,union-attr]
64
+
65
+ claude_agent_sdk.__struct_patched = False # type: ignore[attr-defined]
66
+
67
+
68
+ def _build_otel_env(sdk: StructSDK) -> dict[str, str]:
69
+ """Build env vars for Claude Code's built-in OTel export."""
70
+ env = {
71
+ "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
72
+ "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
73
+ "OTEL_METRICS_EXPORTER": "otlp",
74
+ "OTEL_LOGS_EXPORTER": "otlp",
75
+ "OTEL_TRACES_EXPORTER": "otlp",
76
+ "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
77
+ "OTEL_EXPORTER_OTLP_ENDPOINT": sdk._endpoint,
78
+ "OTEL_EXPORTER_OTLP_HEADERS": f"x-struct-ingest-key={sdk._ingest_key}",
79
+ "OTEL_LOG_TOOL_DETAILS": "1",
80
+ }
81
+ if sdk.capture_content:
82
+ env["OTEL_LOG_TOOL_CONTENT"] = "1"
83
+ env["OTEL_LOG_USER_PROMPTS"] = "1"
84
+ env["OTEL_LOG_RAW_API_BODIES"] = "1"
85
+ return env