agentkeeper-runtime-sdk 0.1.0b1__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.
- agentkeeper_runtime_sdk/__init__.py +45 -0
- agentkeeper_runtime_sdk/anthropic.py +441 -0
- agentkeeper_runtime_sdk/azure_openai.py +314 -0
- agentkeeper_runtime_sdk/bedrock.py +187 -0
- agentkeeper_runtime_sdk/claude_managed_agents.py +387 -0
- agentkeeper_runtime_sdk/client.py +359 -0
- agentkeeper_runtime_sdk/langchain.py +410 -0
- agentkeeper_runtime_sdk/openai_agents.py +296 -0
- agentkeeper_runtime_sdk/py.typed +1 -0
- agentkeeper_runtime_sdk-0.1.0b1.dist-info/METADATA +289 -0
- agentkeeper_runtime_sdk-0.1.0b1.dist-info/RECORD +12 -0
- agentkeeper_runtime_sdk-0.1.0b1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from .anthropic import (
|
|
2
|
+
wrap_anthropic_client,
|
|
3
|
+
wrap_anthropic_runnable_tool,
|
|
4
|
+
wrap_anthropic_tool,
|
|
5
|
+
)
|
|
6
|
+
from .bedrock import wrap_bedrock_runtime_client
|
|
7
|
+
from .azure_openai import wrap_azure_openai_client
|
|
8
|
+
from .claude_managed_agents import wrap_claude_managed_agents_client
|
|
9
|
+
from .client import (
|
|
10
|
+
AgentKeeperRuntimeClient,
|
|
11
|
+
TrackResult,
|
|
12
|
+
create_agentkeeper_runtime_client,
|
|
13
|
+
find_raw_payload_keys,
|
|
14
|
+
)
|
|
15
|
+
from .langchain import (
|
|
16
|
+
create_langchain_callback_handler,
|
|
17
|
+
create_langgraph_callback_handler,
|
|
18
|
+
wrap_langchain_tool,
|
|
19
|
+
wrap_langgraph_tool,
|
|
20
|
+
)
|
|
21
|
+
from .openai_agents import (
|
|
22
|
+
wrap_openai_agents_run,
|
|
23
|
+
wrap_openai_agents_sdk,
|
|
24
|
+
wrap_openai_agents_tool,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AgentKeeperRuntimeClient",
|
|
29
|
+
"TrackResult",
|
|
30
|
+
"create_agentkeeper_runtime_client",
|
|
31
|
+
"find_raw_payload_keys",
|
|
32
|
+
"create_langchain_callback_handler",
|
|
33
|
+
"create_langgraph_callback_handler",
|
|
34
|
+
"wrap_langchain_tool",
|
|
35
|
+
"wrap_langgraph_tool",
|
|
36
|
+
"wrap_bedrock_runtime_client",
|
|
37
|
+
"wrap_azure_openai_client",
|
|
38
|
+
"wrap_claude_managed_agents_client",
|
|
39
|
+
"wrap_openai_agents_run",
|
|
40
|
+
"wrap_openai_agents_sdk",
|
|
41
|
+
"wrap_openai_agents_tool",
|
|
42
|
+
"wrap_anthropic_client",
|
|
43
|
+
"wrap_anthropic_runnable_tool",
|
|
44
|
+
"wrap_anthropic_tool",
|
|
45
|
+
]
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from typing import Any, Callable, Iterator, Mapping, Optional
|
|
5
|
+
|
|
6
|
+
from .client import (
|
|
7
|
+
AgentKeeperRuntimeClient,
|
|
8
|
+
ToolPolicyDecision,
|
|
9
|
+
compact,
|
|
10
|
+
create_agentkeeper_runtime_client,
|
|
11
|
+
get_value,
|
|
12
|
+
number_value,
|
|
13
|
+
object_keys,
|
|
14
|
+
safe_error_name,
|
|
15
|
+
string_value,
|
|
16
|
+
telemetry_metadata,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
_RUNNABLE_TOOL_WRAPPED = "_agentkeeper_anthropic_runnable_tool_wrapped"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _client_from(value: AgentKeeperRuntimeClient | Mapping[str, Any]) -> AgentKeeperRuntimeClient:
|
|
23
|
+
if isinstance(value, AgentKeeperRuntimeClient):
|
|
24
|
+
return value
|
|
25
|
+
return create_agentkeeper_runtime_client(**dict(value))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _base_event(options: Mapping[str, Any]) -> dict[str, Any]:
|
|
29
|
+
return {
|
|
30
|
+
"runtime_integration": "anthropic_sdk",
|
|
31
|
+
"runtime_service": options.get("runtime_service") or options.get("runtimeService"),
|
|
32
|
+
"runtime_environment": options.get("runtime_environment") or options.get("runtimeEnvironment"),
|
|
33
|
+
**dict(options.get("event") or {}),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _request_metadata(request: Mapping[str, Any], operation: str, options: Mapping[str, Any]) -> dict[str, Any]:
|
|
38
|
+
ids = telemetry_metadata(
|
|
39
|
+
runtime_service=options.get("runtime_service") or options.get("runtimeService"),
|
|
40
|
+
runtime_environment=options.get("runtime_environment") or options.get("runtimeEnvironment"),
|
|
41
|
+
run_id=options.get("run_id") or options.get("runId"),
|
|
42
|
+
trace_id=options.get("trace_id") or options.get("traceId"),
|
|
43
|
+
span_id=options.get("span_id") or options.get("spanId"),
|
|
44
|
+
user_id=options.get("user_id") or options.get("userId"),
|
|
45
|
+
convo_id=options.get("convo_id") or options.get("convoId"),
|
|
46
|
+
)
|
|
47
|
+
tools = request.get("tools")
|
|
48
|
+
tool_choice = request.get("tool_choice") if isinstance(request.get("tool_choice"), Mapping) else {}
|
|
49
|
+
return compact({
|
|
50
|
+
**ids,
|
|
51
|
+
"operation": operation,
|
|
52
|
+
"model": string_value(request.get("model")),
|
|
53
|
+
"max_tokens": number_value(request.get("max_tokens")),
|
|
54
|
+
"message_count": len(request.get("messages")) if isinstance(request.get("messages"), list) else None,
|
|
55
|
+
"tool_count": len(tools) if isinstance(tools, list) else None,
|
|
56
|
+
"tool_names": [
|
|
57
|
+
name for name in (string_value(get_value(tool, "name")) for tool in tools or []) if name
|
|
58
|
+
][:50] or None,
|
|
59
|
+
"tool_choice_type": string_value(tool_choice.get("type")) if isinstance(tool_choice, Mapping) else None,
|
|
60
|
+
"tool_choice_name": string_value(tool_choice.get("name")) if isinstance(tool_choice, Mapping) else None,
|
|
61
|
+
"stream": request.get("stream") is True or operation.endswith(".stream"),
|
|
62
|
+
"has_system": bool(request.get("system")),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _usage_fields(response: Any) -> dict[str, Any]:
|
|
67
|
+
usage = get_value(response, "usage") or {}
|
|
68
|
+
return compact({
|
|
69
|
+
"token_input": number_value(get_value(usage, "input_tokens")) or number_value(get_value(usage, "inputTokens")),
|
|
70
|
+
"token_output": number_value(get_value(usage, "output_tokens")) or number_value(get_value(usage, "outputTokens")),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _model_name(request: Mapping[str, Any], response: Any = None) -> Optional[str]:
|
|
75
|
+
return string_value(get_value(response, "model")) or string_value(request.get("model"))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _response_metadata(response: Any, metadata: Mapping[str, Any]) -> dict[str, Any]:
|
|
79
|
+
content = get_value(response, "content")
|
|
80
|
+
if not isinstance(content, list):
|
|
81
|
+
content = []
|
|
82
|
+
tool_names = []
|
|
83
|
+
for block in content:
|
|
84
|
+
if string_value(get_value(block, "type")) == "tool_use":
|
|
85
|
+
name = string_value(get_value(block, "name"))
|
|
86
|
+
if name:
|
|
87
|
+
tool_names.append(name)
|
|
88
|
+
return compact({
|
|
89
|
+
**dict(metadata),
|
|
90
|
+
"response_id": string_value(get_value(response, "id")),
|
|
91
|
+
"role": string_value(get_value(response, "role")),
|
|
92
|
+
"stop_reason": string_value(get_value(response, "stop_reason") or get_value(response, "stopReason")),
|
|
93
|
+
"stop_sequence_present": get_value(response, "stop_sequence") is not None,
|
|
94
|
+
"content_block_count": len(content) or None,
|
|
95
|
+
"tool_use_count": len(tool_names) or None,
|
|
96
|
+
"tool_use_names": tool_names[:50] or None,
|
|
97
|
+
"stream_event_count": number_value(get_value(response, "event_count")),
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class _StreamProxy:
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
stream: Any,
|
|
105
|
+
track_once: Callable[[Any, Optional[BaseException]], None],
|
|
106
|
+
model: Optional[str],
|
|
107
|
+
) -> None:
|
|
108
|
+
self._stream = stream
|
|
109
|
+
self._track_once = track_once
|
|
110
|
+
self._model = model
|
|
111
|
+
self._tracked = False
|
|
112
|
+
|
|
113
|
+
def _track(self, response: Any, error: Optional[BaseException] = None) -> None:
|
|
114
|
+
if self._tracked:
|
|
115
|
+
return
|
|
116
|
+
self._tracked = True
|
|
117
|
+
self._track_once(response, error)
|
|
118
|
+
|
|
119
|
+
def __getattr__(self, name: str) -> Any:
|
|
120
|
+
value = getattr(self._stream, name)
|
|
121
|
+
if name in {
|
|
122
|
+
"get_final_message",
|
|
123
|
+
"final_message",
|
|
124
|
+
"finalMessage",
|
|
125
|
+
"done",
|
|
126
|
+
"run_until_done",
|
|
127
|
+
"runUntilDone",
|
|
128
|
+
} and callable(value):
|
|
129
|
+
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
130
|
+
try:
|
|
131
|
+
result = value(*args, **kwargs)
|
|
132
|
+
except BaseException as error:
|
|
133
|
+
self._track({"model": self._model}, error)
|
|
134
|
+
raise
|
|
135
|
+
if inspect.isawaitable(result):
|
|
136
|
+
async def wait() -> Any:
|
|
137
|
+
try:
|
|
138
|
+
resolved = await result
|
|
139
|
+
except BaseException as error:
|
|
140
|
+
self._track({"model": self._model}, error)
|
|
141
|
+
raise
|
|
142
|
+
self._track(resolved)
|
|
143
|
+
return resolved
|
|
144
|
+
|
|
145
|
+
return wait()
|
|
146
|
+
self._track(result)
|
|
147
|
+
return result
|
|
148
|
+
|
|
149
|
+
return wrapped
|
|
150
|
+
return value
|
|
151
|
+
|
|
152
|
+
def __iter__(self) -> Iterator[Any]:
|
|
153
|
+
event_count = 0
|
|
154
|
+
try:
|
|
155
|
+
for event in self._stream:
|
|
156
|
+
event_count += 1
|
|
157
|
+
yield event
|
|
158
|
+
except BaseException as error:
|
|
159
|
+
self._track({"model": self._model, "event_count": event_count}, error)
|
|
160
|
+
raise
|
|
161
|
+
self._track({"model": self._model, "event_count": event_count})
|
|
162
|
+
|
|
163
|
+
def __enter__(self) -> Any:
|
|
164
|
+
entered = self._stream.__enter__()
|
|
165
|
+
if entered is self._stream:
|
|
166
|
+
return self
|
|
167
|
+
self._stream = entered
|
|
168
|
+
return self
|
|
169
|
+
|
|
170
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> Any:
|
|
171
|
+
return self._stream.__exit__(exc_type, exc, tb)
|
|
172
|
+
|
|
173
|
+
async def __aenter__(self) -> Any:
|
|
174
|
+
entered = await self._stream.__aenter__()
|
|
175
|
+
if entered is not self._stream:
|
|
176
|
+
self._stream = entered
|
|
177
|
+
return self
|
|
178
|
+
|
|
179
|
+
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> Any:
|
|
180
|
+
return await self._stream.__aexit__(exc_type, exc, tb)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _request_from_args(args: tuple[Any, ...], kwargs: Mapping[str, Any]) -> dict[str, Any]:
|
|
184
|
+
if args and isinstance(args[0], Mapping):
|
|
185
|
+
return {**dict(args[0]), **dict(kwargs)}
|
|
186
|
+
return dict(kwargs)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _prepare_tool_runner_request(request: dict[str, Any], ak: AgentKeeperRuntimeClient, options: Mapping[str, Any]) -> dict[str, Any]:
|
|
190
|
+
tools = request.get("tools")
|
|
191
|
+
if not isinstance(tools, list):
|
|
192
|
+
return request
|
|
193
|
+
return {
|
|
194
|
+
**request,
|
|
195
|
+
"tools": [
|
|
196
|
+
wrap_anthropic_runnable_tool(tool, ak, **options)
|
|
197
|
+
if (isinstance(tool, Mapping) and callable(tool.get("run"))) or callable(getattr(tool, "run", None))
|
|
198
|
+
else tool
|
|
199
|
+
for tool in tools
|
|
200
|
+
],
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class _AnthropicResourceProxy:
|
|
205
|
+
def __init__(self, resource: Any, ak: AgentKeeperRuntimeClient, options: Mapping[str, Any], prefix: str = "messages") -> None:
|
|
206
|
+
self._resource = resource
|
|
207
|
+
self._ak = ak
|
|
208
|
+
self._options = dict(options)
|
|
209
|
+
self._prefix = prefix
|
|
210
|
+
|
|
211
|
+
def __getattr__(self, name: str) -> Any:
|
|
212
|
+
value = getattr(self._resource, name)
|
|
213
|
+
if name in {"create", "stream", "tool_runner", "toolRunner"} and callable(value):
|
|
214
|
+
return self._wrap_method(name, value)
|
|
215
|
+
return value
|
|
216
|
+
|
|
217
|
+
def _track_start(self, request: Mapping[str, Any], operation: str, metadata: Mapping[str, Any]) -> None:
|
|
218
|
+
self._ak.track({
|
|
219
|
+
**_base_event(self._options),
|
|
220
|
+
"event_kind": "model_call",
|
|
221
|
+
"capability": "model_only",
|
|
222
|
+
"model_provider": "anthropic",
|
|
223
|
+
"model_name": _model_name(request),
|
|
224
|
+
"run_id": metadata.get("run_id"),
|
|
225
|
+
"trace_id": metadata.get("trace_id"),
|
|
226
|
+
"span_id": metadata.get("span_id"),
|
|
227
|
+
"verdict": "observed",
|
|
228
|
+
"evidence_summary": f"Anthropic SDK {operation} observed",
|
|
229
|
+
"metadata": metadata,
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
def _track_completion(
|
|
233
|
+
self,
|
|
234
|
+
request: Mapping[str, Any],
|
|
235
|
+
operation: str,
|
|
236
|
+
metadata: Mapping[str, Any],
|
|
237
|
+
response: Any,
|
|
238
|
+
error: Optional[BaseException] = None,
|
|
239
|
+
) -> None:
|
|
240
|
+
self._ak.track({
|
|
241
|
+
**_base_event(self._options),
|
|
242
|
+
"event_kind": "model_call",
|
|
243
|
+
"capability": "post_run",
|
|
244
|
+
"model_provider": "anthropic",
|
|
245
|
+
"model_name": _model_name(request, response),
|
|
246
|
+
**_usage_fields(response),
|
|
247
|
+
"run_id": metadata.get("run_id"),
|
|
248
|
+
"trace_id": metadata.get("trace_id"),
|
|
249
|
+
"span_id": metadata.get("span_id"),
|
|
250
|
+
"verdict": "observed" if error else "passed",
|
|
251
|
+
"severity": "medium" if error else "low",
|
|
252
|
+
"evidence_summary": (
|
|
253
|
+
f"Anthropic SDK {operation} errored: {safe_error_name(error)}"
|
|
254
|
+
if error
|
|
255
|
+
else f"Anthropic SDK {operation} completed"
|
|
256
|
+
),
|
|
257
|
+
"metadata": _response_metadata(response, metadata),
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
def _wrap_method(self, name: str, method: Callable[..., Any]) -> Callable[..., Any]:
|
|
261
|
+
operation = f"{self._prefix}.{name}"
|
|
262
|
+
|
|
263
|
+
def wrapped(*args: Any, **kwargs: Any) -> Any:
|
|
264
|
+
request = _request_from_args(args, kwargs)
|
|
265
|
+
if name in {"tool_runner", "toolRunner"}:
|
|
266
|
+
request = _prepare_tool_runner_request(request, self._ak, self._options)
|
|
267
|
+
args = (request,) if args and isinstance(args[0], Mapping) else args
|
|
268
|
+
kwargs = request if not args else kwargs
|
|
269
|
+
metadata = _request_metadata(request, operation, self._options)
|
|
270
|
+
self._track_start(request, operation, metadata)
|
|
271
|
+
tracked = False
|
|
272
|
+
|
|
273
|
+
def track_once(response: Any, error: Optional[BaseException] = None) -> None:
|
|
274
|
+
nonlocal tracked
|
|
275
|
+
if tracked:
|
|
276
|
+
return
|
|
277
|
+
tracked = True
|
|
278
|
+
self._track_completion(request, operation, metadata, response, error)
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
result = method(*args, **kwargs)
|
|
282
|
+
except BaseException as error:
|
|
283
|
+
track_once({}, error)
|
|
284
|
+
raise
|
|
285
|
+
|
|
286
|
+
if inspect.isawaitable(result):
|
|
287
|
+
async def wait() -> Any:
|
|
288
|
+
try:
|
|
289
|
+
resolved = await result
|
|
290
|
+
except BaseException as error:
|
|
291
|
+
track_once({}, error)
|
|
292
|
+
raise
|
|
293
|
+
if name in {"stream", "tool_runner", "toolRunner"}:
|
|
294
|
+
return _StreamProxy(resolved, track_once, _model_name(request))
|
|
295
|
+
track_once(resolved)
|
|
296
|
+
return resolved
|
|
297
|
+
|
|
298
|
+
return wait()
|
|
299
|
+
|
|
300
|
+
if name == "stream" or request.get("stream") is True or name in {"tool_runner", "toolRunner"}:
|
|
301
|
+
return _StreamProxy(result, track_once, _model_name(request))
|
|
302
|
+
|
|
303
|
+
track_once(result)
|
|
304
|
+
return result
|
|
305
|
+
|
|
306
|
+
return wrapped
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class _AnthropicClientProxy:
|
|
310
|
+
def __init__(self, client: Any, ak: AgentKeeperRuntimeClient, options: Mapping[str, Any]) -> None:
|
|
311
|
+
self._client = client
|
|
312
|
+
self._ak = ak
|
|
313
|
+
self._options = dict(options)
|
|
314
|
+
|
|
315
|
+
def __getattr__(self, name: str) -> Any:
|
|
316
|
+
value = getattr(self._client, name)
|
|
317
|
+
if name == "messages" and value is not None:
|
|
318
|
+
return _AnthropicResourceProxy(value, self._ak, self._options, "messages")
|
|
319
|
+
if name == "beta" and value is not None:
|
|
320
|
+
return _BetaProxy(value, self._ak, self._options)
|
|
321
|
+
return value
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class _BetaProxy:
|
|
325
|
+
def __init__(self, beta: Any, ak: AgentKeeperRuntimeClient, options: Mapping[str, Any]) -> None:
|
|
326
|
+
self._beta = beta
|
|
327
|
+
self._ak = ak
|
|
328
|
+
self._options = dict(options)
|
|
329
|
+
|
|
330
|
+
def __getattr__(self, name: str) -> Any:
|
|
331
|
+
value = getattr(self._beta, name)
|
|
332
|
+
if name == "messages" and value is not None:
|
|
333
|
+
return _AnthropicResourceProxy(value, self._ak, self._options, "beta.messages")
|
|
334
|
+
return value
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def wrap_anthropic_client(
|
|
338
|
+
anthropic_client: Any,
|
|
339
|
+
client_or_options: AgentKeeperRuntimeClient | Mapping[str, Any],
|
|
340
|
+
**options: Any,
|
|
341
|
+
) -> Any:
|
|
342
|
+
return _AnthropicClientProxy(anthropic_client, _client_from(client_or_options), options)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _tool_name(tool: Any, explicit: Optional[str]) -> str:
|
|
346
|
+
return (
|
|
347
|
+
explicit
|
|
348
|
+
or string_value(get_value(tool, "name"))
|
|
349
|
+
or string_value(get_value(tool, "type"))
|
|
350
|
+
or string_value(getattr(tool.__class__, "__name__", None))
|
|
351
|
+
or "anthropic_sdk_tool"
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class _RunnableToolProxy:
|
|
356
|
+
def __init__(self, tool: Any, run: Callable[..., Any]) -> None:
|
|
357
|
+
self._tool = tool
|
|
358
|
+
self.run = run
|
|
359
|
+
|
|
360
|
+
def __getattr__(self, name: str) -> Any:
|
|
361
|
+
return getattr(self._tool, name)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def wrap_anthropic_runnable_tool(
|
|
365
|
+
tool: Any,
|
|
366
|
+
client_or_options: AgentKeeperRuntimeClient | Mapping[str, Any],
|
|
367
|
+
*,
|
|
368
|
+
tool_name: Optional[str] = None,
|
|
369
|
+
evaluate: Optional[Callable[[Any], ToolPolicyDecision | Any]] = None,
|
|
370
|
+
evaluate_tool: Optional[Callable[[str, Any], ToolPolicyDecision | Any]] = None,
|
|
371
|
+
**options: Any,
|
|
372
|
+
) -> Any:
|
|
373
|
+
if isinstance(tool, Mapping) and tool.get(_RUNNABLE_TOOL_WRAPPED):
|
|
374
|
+
return tool
|
|
375
|
+
if getattr(tool, _RUNNABLE_TOOL_WRAPPED, False):
|
|
376
|
+
return tool
|
|
377
|
+
|
|
378
|
+
ak = _client_from(client_or_options)
|
|
379
|
+
name = _tool_name(tool, tool_name)
|
|
380
|
+
event = {
|
|
381
|
+
**_base_event(options),
|
|
382
|
+
"runtime_integration": "anthropic_sdk",
|
|
383
|
+
"tool_type": "anthropic_sdk",
|
|
384
|
+
"metadata": {"tool_runner": True, **dict(options.get("metadata") or {})},
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
def evaluator(tool_args: Any) -> ToolPolicyDecision | Any:
|
|
388
|
+
if evaluate:
|
|
389
|
+
return evaluate(tool_args)
|
|
390
|
+
if evaluate_tool:
|
|
391
|
+
return evaluate_tool(name, tool_args)
|
|
392
|
+
return {"verdict": "passed"}
|
|
393
|
+
|
|
394
|
+
def original_run(*args: Any, **kwargs: Any) -> Any:
|
|
395
|
+
run = tool.get("run") if isinstance(tool, Mapping) else getattr(tool, "run")
|
|
396
|
+
return run(*args, **kwargs)
|
|
397
|
+
|
|
398
|
+
def wrapped_run(*args: Any, **kwargs: Any) -> Any:
|
|
399
|
+
tool_args = args[0] if args else dict(kwargs)
|
|
400
|
+
return ak.run_guarded_tool(name, tool_args, lambda: original_run(*args, **kwargs), evaluate=evaluator, event=event)
|
|
401
|
+
|
|
402
|
+
if isinstance(tool, Mapping):
|
|
403
|
+
return {**dict(tool), "run": wrapped_run, _RUNNABLE_TOOL_WRAPPED: True}
|
|
404
|
+
wrapped = _RunnableToolProxy(tool, wrapped_run)
|
|
405
|
+
setattr(wrapped, _RUNNABLE_TOOL_WRAPPED, True)
|
|
406
|
+
return wrapped
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def wrap_anthropic_tool(
|
|
410
|
+
tool_name: str,
|
|
411
|
+
execute: Callable[[Any], Any],
|
|
412
|
+
client_or_options: AgentKeeperRuntimeClient | Mapping[str, Any],
|
|
413
|
+
*,
|
|
414
|
+
evaluate: Optional[Callable[[Any], ToolPolicyDecision | Any]] = None,
|
|
415
|
+
evaluate_tool: Optional[Callable[[str, Any], ToolPolicyDecision | Any]] = None,
|
|
416
|
+
**options: Any,
|
|
417
|
+
) -> Callable[[Any], Any]:
|
|
418
|
+
ak = _client_from(client_or_options)
|
|
419
|
+
|
|
420
|
+
def evaluator(tool_args: Any) -> ToolPolicyDecision | Any:
|
|
421
|
+
if evaluate:
|
|
422
|
+
return evaluate(tool_args)
|
|
423
|
+
if evaluate_tool:
|
|
424
|
+
return evaluate_tool(tool_name, tool_args)
|
|
425
|
+
return {"verdict": "passed"}
|
|
426
|
+
|
|
427
|
+
def wrapped(tool_args: Any) -> Any:
|
|
428
|
+
return ak.run_guarded_tool(
|
|
429
|
+
tool_name,
|
|
430
|
+
tool_args,
|
|
431
|
+
lambda: execute(tool_args),
|
|
432
|
+
evaluate=evaluator,
|
|
433
|
+
event={
|
|
434
|
+
**_base_event(options),
|
|
435
|
+
"runtime_integration": "anthropic_sdk",
|
|
436
|
+
"tool_type": "anthropic_sdk",
|
|
437
|
+
},
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
wrapped.__name__ = getattr(execute, "__name__", tool_name)
|
|
441
|
+
return wrapped
|