agentrun-sdk 0.1.2__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.

Potentially problematic release.


This version of agentrun-sdk might be problematic. Click here for more details.

Files changed (115) hide show
  1. agentrun_operation_sdk/cli/__init__.py +1 -0
  2. agentrun_operation_sdk/cli/cli.py +19 -0
  3. agentrun_operation_sdk/cli/common.py +21 -0
  4. agentrun_operation_sdk/cli/runtime/__init__.py +1 -0
  5. agentrun_operation_sdk/cli/runtime/commands.py +203 -0
  6. agentrun_operation_sdk/client/client.py +75 -0
  7. agentrun_operation_sdk/operations/runtime/__init__.py +8 -0
  8. agentrun_operation_sdk/operations/runtime/configure.py +101 -0
  9. agentrun_operation_sdk/operations/runtime/launch.py +82 -0
  10. agentrun_operation_sdk/operations/runtime/models.py +31 -0
  11. agentrun_operation_sdk/services/runtime.py +152 -0
  12. agentrun_operation_sdk/utils/logging_config.py +72 -0
  13. agentrun_operation_sdk/utils/runtime/config.py +94 -0
  14. agentrun_operation_sdk/utils/runtime/container.py +280 -0
  15. agentrun_operation_sdk/utils/runtime/entrypoint.py +203 -0
  16. agentrun_operation_sdk/utils/runtime/schema.py +56 -0
  17. agentrun_sdk/__init__.py +7 -0
  18. agentrun_sdk/agent/__init__.py +25 -0
  19. agentrun_sdk/agent/agent.py +696 -0
  20. agentrun_sdk/agent/agent_result.py +46 -0
  21. agentrun_sdk/agent/conversation_manager/__init__.py +26 -0
  22. agentrun_sdk/agent/conversation_manager/conversation_manager.py +88 -0
  23. agentrun_sdk/agent/conversation_manager/null_conversation_manager.py +46 -0
  24. agentrun_sdk/agent/conversation_manager/sliding_window_conversation_manager.py +179 -0
  25. agentrun_sdk/agent/conversation_manager/summarizing_conversation_manager.py +252 -0
  26. agentrun_sdk/agent/state.py +97 -0
  27. agentrun_sdk/event_loop/__init__.py +9 -0
  28. agentrun_sdk/event_loop/event_loop.py +499 -0
  29. agentrun_sdk/event_loop/streaming.py +319 -0
  30. agentrun_sdk/experimental/__init__.py +4 -0
  31. agentrun_sdk/experimental/hooks/__init__.py +15 -0
  32. agentrun_sdk/experimental/hooks/events.py +123 -0
  33. agentrun_sdk/handlers/__init__.py +10 -0
  34. agentrun_sdk/handlers/callback_handler.py +70 -0
  35. agentrun_sdk/hooks/__init__.py +49 -0
  36. agentrun_sdk/hooks/events.py +80 -0
  37. agentrun_sdk/hooks/registry.py +247 -0
  38. agentrun_sdk/models/__init__.py +10 -0
  39. agentrun_sdk/models/anthropic.py +432 -0
  40. agentrun_sdk/models/bedrock.py +649 -0
  41. agentrun_sdk/models/litellm.py +225 -0
  42. agentrun_sdk/models/llamaapi.py +438 -0
  43. agentrun_sdk/models/mistral.py +539 -0
  44. agentrun_sdk/models/model.py +95 -0
  45. agentrun_sdk/models/ollama.py +357 -0
  46. agentrun_sdk/models/openai.py +436 -0
  47. agentrun_sdk/models/sagemaker.py +598 -0
  48. agentrun_sdk/models/writer.py +449 -0
  49. agentrun_sdk/multiagent/__init__.py +22 -0
  50. agentrun_sdk/multiagent/a2a/__init__.py +15 -0
  51. agentrun_sdk/multiagent/a2a/executor.py +148 -0
  52. agentrun_sdk/multiagent/a2a/server.py +252 -0
  53. agentrun_sdk/multiagent/base.py +92 -0
  54. agentrun_sdk/multiagent/graph.py +555 -0
  55. agentrun_sdk/multiagent/swarm.py +656 -0
  56. agentrun_sdk/py.typed +1 -0
  57. agentrun_sdk/session/__init__.py +18 -0
  58. agentrun_sdk/session/file_session_manager.py +216 -0
  59. agentrun_sdk/session/repository_session_manager.py +152 -0
  60. agentrun_sdk/session/s3_session_manager.py +272 -0
  61. agentrun_sdk/session/session_manager.py +73 -0
  62. agentrun_sdk/session/session_repository.py +51 -0
  63. agentrun_sdk/telemetry/__init__.py +21 -0
  64. agentrun_sdk/telemetry/config.py +194 -0
  65. agentrun_sdk/telemetry/metrics.py +476 -0
  66. agentrun_sdk/telemetry/metrics_constants.py +15 -0
  67. agentrun_sdk/telemetry/tracer.py +563 -0
  68. agentrun_sdk/tools/__init__.py +17 -0
  69. agentrun_sdk/tools/decorator.py +569 -0
  70. agentrun_sdk/tools/executor.py +137 -0
  71. agentrun_sdk/tools/loader.py +152 -0
  72. agentrun_sdk/tools/mcp/__init__.py +13 -0
  73. agentrun_sdk/tools/mcp/mcp_agent_tool.py +99 -0
  74. agentrun_sdk/tools/mcp/mcp_client.py +423 -0
  75. agentrun_sdk/tools/mcp/mcp_instrumentation.py +322 -0
  76. agentrun_sdk/tools/mcp/mcp_types.py +63 -0
  77. agentrun_sdk/tools/registry.py +607 -0
  78. agentrun_sdk/tools/structured_output.py +421 -0
  79. agentrun_sdk/tools/tools.py +217 -0
  80. agentrun_sdk/tools/watcher.py +136 -0
  81. agentrun_sdk/types/__init__.py +5 -0
  82. agentrun_sdk/types/collections.py +23 -0
  83. agentrun_sdk/types/content.py +188 -0
  84. agentrun_sdk/types/event_loop.py +48 -0
  85. agentrun_sdk/types/exceptions.py +81 -0
  86. agentrun_sdk/types/guardrails.py +254 -0
  87. agentrun_sdk/types/media.py +89 -0
  88. agentrun_sdk/types/session.py +152 -0
  89. agentrun_sdk/types/streaming.py +201 -0
  90. agentrun_sdk/types/tools.py +258 -0
  91. agentrun_sdk/types/traces.py +5 -0
  92. agentrun_sdk-0.1.2.dist-info/METADATA +51 -0
  93. agentrun_sdk-0.1.2.dist-info/RECORD +115 -0
  94. agentrun_sdk-0.1.2.dist-info/WHEEL +5 -0
  95. agentrun_sdk-0.1.2.dist-info/entry_points.txt +2 -0
  96. agentrun_sdk-0.1.2.dist-info/top_level.txt +3 -0
  97. agentrun_wrapper/__init__.py +11 -0
  98. agentrun_wrapper/_utils/__init__.py +6 -0
  99. agentrun_wrapper/_utils/endpoints.py +16 -0
  100. agentrun_wrapper/identity/__init__.py +5 -0
  101. agentrun_wrapper/identity/auth.py +211 -0
  102. agentrun_wrapper/memory/__init__.py +6 -0
  103. agentrun_wrapper/memory/client.py +1697 -0
  104. agentrun_wrapper/memory/constants.py +103 -0
  105. agentrun_wrapper/memory/controlplane.py +626 -0
  106. agentrun_wrapper/py.typed +1 -0
  107. agentrun_wrapper/runtime/__init__.py +13 -0
  108. agentrun_wrapper/runtime/app.py +473 -0
  109. agentrun_wrapper/runtime/context.py +34 -0
  110. agentrun_wrapper/runtime/models.py +25 -0
  111. agentrun_wrapper/services/__init__.py +1 -0
  112. agentrun_wrapper/services/identity.py +192 -0
  113. agentrun_wrapper/tools/__init__.py +6 -0
  114. agentrun_wrapper/tools/browser_client.py +325 -0
  115. agentrun_wrapper/tools/code_interpreter_client.py +186 -0
@@ -0,0 +1,322 @@
1
+ """OpenTelemetry instrumentation for Model Context Protocol (MCP) tracing.
2
+
3
+ Enables distributed tracing across MCP client-server boundaries by injecting
4
+ OpenTelemetry context into MCP request metadata (_meta field) and extracting
5
+ it on the server side, creating unified traces that span from agent calls
6
+ through MCP tool executions.
7
+
8
+ Based on: https://github.com/traceloop/openllmetry/tree/main/packages/opentelemetry-instrumentation-mcp
9
+ Related issue: https://github.com/modelcontextprotocol/modelcontextprotocol/issues/246
10
+ """
11
+
12
+ from contextlib import _AsyncGeneratorContextManager, asynccontextmanager
13
+ from dataclasses import dataclass
14
+ from typing import Any, AsyncGenerator, Callable, Tuple
15
+
16
+ from mcp.shared.message import SessionMessage
17
+ from mcp.types import JSONRPCMessage, JSONRPCRequest
18
+ from opentelemetry import context, propagate
19
+ from wrapt import ObjectProxy, register_post_import_hook, wrap_function_wrapper
20
+
21
+
22
+ @dataclass(slots=True, frozen=True)
23
+ class ItemWithContext:
24
+ """Wrapper for items that need to carry OpenTelemetry context.
25
+
26
+ Used to preserve tracing context across async boundaries in MCP sessions,
27
+ ensuring that distributed traces remain connected even when messages are
28
+ processed asynchronously.
29
+
30
+ Attributes:
31
+ item: The original item being wrapped
32
+ ctx: The OpenTelemetry context associated with the item
33
+ """
34
+
35
+ item: Any
36
+ ctx: context.Context
37
+
38
+
39
+ def mcp_instrumentation() -> None:
40
+ """Apply OpenTelemetry instrumentation patches to MCP components.
41
+
42
+ This function instruments three key areas of MCP communication:
43
+ 1. Client-side: Injects tracing context into tool call requests
44
+ 2. Transport-level: Extracts context from incoming messages
45
+ 3. Session-level: Manages bidirectional context flow
46
+
47
+ The patches enable distributed tracing by:
48
+ - Adding OpenTelemetry context to the _meta field of MCP requests
49
+ - Extracting and activating context on the server side
50
+ - Preserving context across async message processing boundaries
51
+ """
52
+
53
+ def patch_mcp_client(wrapped: Callable[..., Any], instance: Any, args: Any, kwargs: Any) -> Any:
54
+ """Patch MCP client to inject OpenTelemetry context into tool calls.
55
+
56
+ Intercepts outgoing MCP requests and injects the current OpenTelemetry
57
+ context into the request's _meta field for tools/call methods. This
58
+ enables server-side context extraction and trace continuation.
59
+
60
+ Args:
61
+ wrapped: The original function being wrapped
62
+ instance: The instance the method is being called on
63
+ args: Positional arguments to the wrapped function
64
+ kwargs: Keyword arguments to the wrapped function
65
+
66
+ Returns:
67
+ Result of the wrapped function call
68
+ """
69
+ if len(args) < 1:
70
+ return wrapped(*args, **kwargs)
71
+
72
+ request = args[0]
73
+ method = getattr(request.root, "method", None)
74
+
75
+ if method != "tools/call":
76
+ return wrapped(*args, **kwargs)
77
+
78
+ try:
79
+ if hasattr(request.root, "params") and request.root.params:
80
+ # Handle Pydantic models
81
+ if hasattr(request.root.params, "model_dump") and hasattr(request.root.params, "model_validate"):
82
+ params_dict = request.root.params.model_dump()
83
+ # Add _meta with tracing context
84
+ meta = params_dict.setdefault("_meta", {})
85
+ propagate.get_global_textmap().inject(meta)
86
+
87
+ # Recreate the Pydantic model with the updated data
88
+ # This preserves the original model type and avoids serialization warnings
89
+ params_class = type(request.root.params)
90
+ try:
91
+ request.root.params = params_class.model_validate(params_dict)
92
+ except Exception:
93
+ # Fallback to dict if model recreation fails
94
+ request.root.params = params_dict
95
+
96
+ elif isinstance(request.root.params, dict):
97
+ # Handle dict params directly
98
+ meta = request.root.params.setdefault("_meta", {})
99
+ propagate.get_global_textmap().inject(meta)
100
+
101
+ return wrapped(*args, **kwargs)
102
+
103
+ except Exception:
104
+ return wrapped(*args, **kwargs)
105
+
106
+ def transport_wrapper() -> Callable[
107
+ [Callable[..., Any], Any, Any, Any], _AsyncGeneratorContextManager[tuple[Any, Any]]
108
+ ]:
109
+ """Create a wrapper for MCP transport connections.
110
+
111
+ Returns a context manager that wraps transport read/write streams
112
+ with context extraction capabilities. The wrapped reader will
113
+ automatically extract OpenTelemetry context from incoming messages.
114
+
115
+ Returns:
116
+ An async context manager that yields wrapped transport streams
117
+ """
118
+
119
+ @asynccontextmanager
120
+ async def traced_method(
121
+ wrapped: Callable[..., Any], instance: Any, args: Any, kwargs: Any
122
+ ) -> AsyncGenerator[Tuple[Any, Any], None]:
123
+ async with wrapped(*args, **kwargs) as result:
124
+ try:
125
+ read_stream, write_stream = result
126
+ except ValueError:
127
+ read_stream, write_stream, _ = result
128
+ yield TransportContextExtractingReader(read_stream), write_stream
129
+
130
+ return traced_method
131
+
132
+ def session_init_wrapper() -> Callable[[Any, Any, Tuple[Any, ...], dict[str, Any]], None]:
133
+ """Create a wrapper for MCP session initialization.
134
+
135
+ Wraps session message streams to enable bidirectional context flow.
136
+ The reader extracts and activates context, while the writer preserves
137
+ context for async processing.
138
+
139
+ Returns:
140
+ A function that wraps session initialization
141
+ """
142
+
143
+ def traced_method(
144
+ wrapped: Callable[..., Any], instance: Any, args: Tuple[Any, ...], kwargs: dict[str, Any]
145
+ ) -> None:
146
+ wrapped(*args, **kwargs)
147
+ reader = getattr(instance, "_incoming_message_stream_reader", None)
148
+ writer = getattr(instance, "_incoming_message_stream_writer", None)
149
+ if reader and writer:
150
+ instance._incoming_message_stream_reader = SessionContextAttachingReader(reader)
151
+ instance._incoming_message_stream_writer = SessionContextSavingWriter(writer)
152
+
153
+ return traced_method
154
+
155
+ # Apply patches
156
+ wrap_function_wrapper("mcp.shared.session", "BaseSession.send_request", patch_mcp_client)
157
+
158
+ register_post_import_hook(
159
+ lambda _: wrap_function_wrapper(
160
+ "mcp.server.streamable_http", "StreamableHTTPServerTransport.connect", transport_wrapper()
161
+ ),
162
+ "mcp.server.streamable_http",
163
+ )
164
+
165
+ register_post_import_hook(
166
+ lambda _: wrap_function_wrapper("mcp.server.session", "ServerSession.__init__", session_init_wrapper()),
167
+ "mcp.server.session",
168
+ )
169
+
170
+
171
+ class TransportContextExtractingReader(ObjectProxy):
172
+ """A proxy reader that extracts OpenTelemetry context from MCP messages.
173
+
174
+ Wraps an async message stream reader to automatically extract and activate
175
+ OpenTelemetry context from the _meta field of incoming MCP requests. This
176
+ enables server-side trace continuation from client-injected context.
177
+
178
+ The reader handles both SessionMessage and JSONRPCMessage formats, and
179
+ supports both dict and Pydantic model parameter structures.
180
+ """
181
+
182
+ def __init__(self, wrapped: Any) -> None:
183
+ """Initialize the context-extracting reader.
184
+
185
+ Args:
186
+ wrapped: The original async stream reader to wrap
187
+ """
188
+ super().__init__(wrapped)
189
+
190
+ async def __aenter__(self) -> Any:
191
+ """Enter the async context manager by delegating to the wrapped object."""
192
+ return await self.__wrapped__.__aenter__()
193
+
194
+ async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
195
+ """Exit the async context manager by delegating to the wrapped object."""
196
+ return await self.__wrapped__.__aexit__(exc_type, exc_value, traceback)
197
+
198
+ async def __aiter__(self) -> AsyncGenerator[Any, None]:
199
+ """Iterate over messages, extracting and activating context as needed.
200
+
201
+ For each incoming message, checks if it contains tracing context in
202
+ the _meta field. If found, extracts and activates the context for
203
+ the duration of message processing, then properly detaches it.
204
+
205
+ Yields:
206
+ Messages from the wrapped stream, processed under the appropriate
207
+ OpenTelemetry context
208
+ """
209
+ async for item in self.__wrapped__:
210
+ if isinstance(item, SessionMessage):
211
+ request = item.message.root
212
+ elif type(item) is JSONRPCMessage:
213
+ request = item.root
214
+ else:
215
+ yield item
216
+ continue
217
+
218
+ if isinstance(request, JSONRPCRequest) and request.params:
219
+ # Handle both dict and Pydantic model params
220
+ if hasattr(request.params, "get"):
221
+ # Dict-like access
222
+ meta = request.params.get("_meta")
223
+ elif hasattr(request.params, "_meta"):
224
+ # Direct attribute access for Pydantic models
225
+ meta = getattr(request.params, "_meta", None)
226
+ else:
227
+ meta = None
228
+
229
+ if meta:
230
+ extracted_context = propagate.extract(meta)
231
+ restore = context.attach(extracted_context)
232
+ try:
233
+ yield item
234
+ continue
235
+ finally:
236
+ context.detach(restore)
237
+ yield item
238
+
239
+
240
+ class SessionContextSavingWriter(ObjectProxy):
241
+ """A proxy writer that preserves OpenTelemetry context with outgoing items.
242
+
243
+ Wraps an async message stream writer to capture the current OpenTelemetry
244
+ context and associate it with outgoing items. This enables context
245
+ preservation across async boundaries in MCP session processing.
246
+ """
247
+
248
+ def __init__(self, wrapped: Any) -> None:
249
+ """Initialize the context-saving writer.
250
+
251
+ Args:
252
+ wrapped: The original async stream writer to wrap
253
+ """
254
+ super().__init__(wrapped)
255
+
256
+ async def __aenter__(self) -> Any:
257
+ """Enter the async context manager by delegating to the wrapped object."""
258
+ return await self.__wrapped__.__aenter__()
259
+
260
+ async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
261
+ """Exit the async context manager by delegating to the wrapped object."""
262
+ return await self.__wrapped__.__aexit__(exc_type, exc_value, traceback)
263
+
264
+ async def send(self, item: Any) -> Any:
265
+ """Send an item while preserving the current OpenTelemetry context.
266
+
267
+ Captures the current context and wraps the item with it, enabling
268
+ the receiving side to restore the appropriate tracing context.
269
+
270
+ Args:
271
+ item: The item to send through the stream
272
+
273
+ Returns:
274
+ Result of sending the wrapped item
275
+ """
276
+ ctx = context.get_current()
277
+ return await self.__wrapped__.send(ItemWithContext(item, ctx))
278
+
279
+
280
+ class SessionContextAttachingReader(ObjectProxy):
281
+ """A proxy reader that restores OpenTelemetry context from wrapped items.
282
+
283
+ Wraps an async message stream reader to detect ItemWithContext instances
284
+ and restore their associated OpenTelemetry context during processing.
285
+ This completes the context preservation cycle started by SessionContextSavingWriter.
286
+ """
287
+
288
+ def __init__(self, wrapped: Any) -> None:
289
+ """Initialize the context-attaching reader.
290
+
291
+ Args:
292
+ wrapped: The original async stream reader to wrap
293
+ """
294
+ super().__init__(wrapped)
295
+
296
+ async def __aenter__(self) -> Any:
297
+ """Enter the async context manager by delegating to the wrapped object."""
298
+ return await self.__wrapped__.__aenter__()
299
+
300
+ async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
301
+ """Exit the async context manager by delegating to the wrapped object."""
302
+ return await self.__wrapped__.__aexit__(exc_type, exc_value, traceback)
303
+
304
+ async def __aiter__(self) -> AsyncGenerator[Any, None]:
305
+ """Iterate over items, restoring context for ItemWithContext instances.
306
+
307
+ For items wrapped with context, temporarily activates the associated
308
+ OpenTelemetry context during processing, then properly detaches it.
309
+ Regular items are yielded without context modification.
310
+
311
+ Yields:
312
+ Unwrapped items processed under their associated OpenTelemetry context
313
+ """
314
+ async for item in self.__wrapped__:
315
+ if isinstance(item, ItemWithContext):
316
+ restore = context.attach(item.ctx)
317
+ try:
318
+ yield item.item
319
+ finally:
320
+ context.detach(restore)
321
+ else:
322
+ yield item
@@ -0,0 +1,63 @@
1
+ """Type definitions for MCP integration."""
2
+
3
+ from contextlib import AbstractAsyncContextManager
4
+ from typing import Any, Dict
5
+
6
+ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
7
+ from mcp.client.streamable_http import GetSessionIdCallback
8
+ from mcp.shared.memory import MessageStream
9
+ from mcp.shared.message import SessionMessage
10
+ from typing_extensions import NotRequired
11
+
12
+ from ...types.tools import ToolResult
13
+
14
+ """
15
+ MCPTransport defines the interface for MCP transport implementations. This abstracts
16
+ communication with an MCP server, hiding details of the underlying transport mechanism (WebSocket, stdio, etc.).
17
+
18
+ It represents an async context manager that yields a tuple of read and write streams for MCP communication.
19
+ When used with `async with`, it should establish the connection and yield the streams, then clean up
20
+ when the context is exited.
21
+
22
+ The read stream receives messages from the client (or exceptions if parsing fails), while the write
23
+ stream sends messages to the client.
24
+
25
+ Example implementation (simplified):
26
+ ```python
27
+ @contextlib.asynccontextmanager
28
+ async def my_transport_implementation():
29
+ # Set up connection
30
+ read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
31
+ write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
32
+
33
+ # Start background tasks to handle actual I/O
34
+ async with anyio.create_task_group() as tg:
35
+ tg.start_soon(reader_task, read_stream_writer)
36
+ tg.start_soon(writer_task, write_stream_reader)
37
+
38
+ # Yield the streams to the caller
39
+ yield (read_stream, write_stream)
40
+ ```
41
+ """
42
+ # GetSessionIdCallback was added for HTTP Streaming but was not applied to the MessageStream type
43
+ # https://github.com/modelcontextprotocol/python-sdk/blob/ed25167fa5d715733437996682e20c24470e8177/src/mcp/client/streamable_http.py#L418
44
+ _MessageStreamWithGetSessionIdCallback = tuple[
45
+ MemoryObjectReceiveStream[SessionMessage | Exception], MemoryObjectSendStream[SessionMessage], GetSessionIdCallback
46
+ ]
47
+ MCPTransport = AbstractAsyncContextManager[MessageStream | _MessageStreamWithGetSessionIdCallback]
48
+
49
+
50
+ class MCPToolResult(ToolResult):
51
+ """Result of an MCP tool execution.
52
+
53
+ Extends the base ToolResult with MCP-specific structured content support.
54
+ The structuredContent field contains optional JSON data returned by MCP tools
55
+ that provides structured results beyond the standard text/image/document content.
56
+
57
+ Attributes:
58
+ structuredContent: Optional JSON object containing structured data returned
59
+ by the MCP tool. This allows MCP tools to return complex data structures
60
+ that can be processed programmatically by agents or other tools.
61
+ """
62
+
63
+ structuredContent: NotRequired[Dict[str, Any]]