jaf-py 2.5.10__py3-none-any.whl → 2.5.11__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.
- jaf/__init__.py +154 -57
- jaf/a2a/__init__.py +42 -21
- jaf/a2a/agent.py +79 -126
- jaf/a2a/agent_card.py +87 -78
- jaf/a2a/client.py +30 -66
- jaf/a2a/examples/client_example.py +12 -12
- jaf/a2a/examples/integration_example.py +38 -47
- jaf/a2a/examples/server_example.py +56 -53
- jaf/a2a/memory/__init__.py +0 -4
- jaf/a2a/memory/cleanup.py +28 -21
- jaf/a2a/memory/factory.py +155 -133
- jaf/a2a/memory/providers/composite.py +21 -26
- jaf/a2a/memory/providers/in_memory.py +89 -83
- jaf/a2a/memory/providers/postgres.py +117 -115
- jaf/a2a/memory/providers/redis.py +128 -121
- jaf/a2a/memory/serialization.py +77 -87
- jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
- jaf/a2a/memory/tests/test_cleanup.py +211 -94
- jaf/a2a/memory/tests/test_serialization.py +73 -68
- jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
- jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
- jaf/a2a/memory/types.py +91 -53
- jaf/a2a/protocol.py +95 -125
- jaf/a2a/server.py +90 -118
- jaf/a2a/standalone_client.py +30 -43
- jaf/a2a/tests/__init__.py +16 -33
- jaf/a2a/tests/run_tests.py +17 -53
- jaf/a2a/tests/test_agent.py +40 -140
- jaf/a2a/tests/test_client.py +54 -117
- jaf/a2a/tests/test_integration.py +28 -82
- jaf/a2a/tests/test_protocol.py +54 -139
- jaf/a2a/tests/test_types.py +50 -136
- jaf/a2a/types.py +58 -34
- jaf/cli.py +21 -41
- jaf/core/__init__.py +7 -1
- jaf/core/agent_tool.py +93 -72
- jaf/core/analytics.py +257 -207
- jaf/core/checkpoint.py +223 -0
- jaf/core/composition.py +249 -235
- jaf/core/engine.py +817 -519
- jaf/core/errors.py +55 -42
- jaf/core/guardrails.py +276 -202
- jaf/core/handoff.py +47 -31
- jaf/core/parallel_agents.py +69 -75
- jaf/core/performance.py +75 -73
- jaf/core/proxy.py +43 -44
- jaf/core/proxy_helpers.py +24 -27
- jaf/core/regeneration.py +220 -129
- jaf/core/state.py +68 -66
- jaf/core/streaming.py +115 -108
- jaf/core/tool_results.py +111 -101
- jaf/core/tools.py +114 -116
- jaf/core/tracing.py +269 -210
- jaf/core/types.py +371 -151
- jaf/core/workflows.py +209 -168
- jaf/exceptions.py +46 -38
- jaf/memory/__init__.py +1 -6
- jaf/memory/approval_storage.py +54 -77
- jaf/memory/factory.py +4 -4
- jaf/memory/providers/in_memory.py +216 -180
- jaf/memory/providers/postgres.py +216 -146
- jaf/memory/providers/redis.py +173 -116
- jaf/memory/types.py +70 -51
- jaf/memory/utils.py +36 -34
- jaf/plugins/__init__.py +12 -12
- jaf/plugins/base.py +105 -96
- jaf/policies/__init__.py +0 -1
- jaf/policies/handoff.py +37 -46
- jaf/policies/validation.py +76 -52
- jaf/providers/__init__.py +6 -3
- jaf/providers/mcp.py +97 -51
- jaf/providers/model.py +360 -279
- jaf/server/__init__.py +1 -1
- jaf/server/main.py +7 -11
- jaf/server/server.py +514 -359
- jaf/server/types.py +208 -52
- jaf/utils/__init__.py +17 -18
- jaf/utils/attachments.py +111 -116
- jaf/utils/document_processor.py +175 -174
- jaf/visualization/__init__.py +1 -1
- jaf/visualization/example.py +111 -110
- jaf/visualization/functional_core.py +46 -71
- jaf/visualization/graphviz.py +154 -189
- jaf/visualization/imperative_shell.py +7 -16
- jaf/visualization/types.py +8 -4
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/METADATA +2 -2
- jaf_py-2.5.11.dist-info/RECORD +97 -0
- jaf_py-2.5.10.dist-info/RECORD +0 -96
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
jaf/core/tool_results.py
CHANGED
|
@@ -12,32 +12,38 @@ from collections.abc import Awaitable
|
|
|
12
12
|
from dataclasses import dataclass
|
|
13
13
|
from typing import Any, Callable, Dict, Generic, List, Literal, Optional, TypeVar, Union
|
|
14
14
|
|
|
15
|
-
T = TypeVar(
|
|
16
|
-
TArgs = TypeVar(
|
|
17
|
-
TResult = TypeVar(
|
|
18
|
-
TContext = TypeVar(
|
|
15
|
+
T = TypeVar("T")
|
|
16
|
+
TArgs = TypeVar("TArgs")
|
|
17
|
+
TResult = TypeVar("TResult")
|
|
18
|
+
TContext = TypeVar("TContext")
|
|
19
19
|
|
|
20
20
|
# Type aliases matching TypeScript
|
|
21
|
-
ToolResultStatus = Literal[
|
|
21
|
+
ToolResultStatus = Literal["success", "error", "validation_error", "permission_denied", "not_found"]
|
|
22
|
+
|
|
22
23
|
|
|
23
24
|
class ToolResultStatus:
|
|
24
25
|
"""Tool result status constants."""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
|
|
27
|
+
SUCCESS = "success"
|
|
28
|
+
ERROR = "error"
|
|
29
|
+
VALIDATION_ERROR = "validation_error"
|
|
30
|
+
PERMISSION_DENIED = "permission_denied"
|
|
31
|
+
NOT_FOUND = "not_found"
|
|
32
|
+
|
|
30
33
|
|
|
31
34
|
@dataclass(frozen=True)
|
|
32
35
|
class ToolErrorInfo:
|
|
33
36
|
"""Error information for tool results."""
|
|
37
|
+
|
|
34
38
|
code: str
|
|
35
39
|
message: str
|
|
36
40
|
details: Optional[Any] = None
|
|
37
41
|
|
|
42
|
+
|
|
38
43
|
@dataclass(frozen=True)
|
|
39
44
|
class ToolMetadata:
|
|
40
45
|
"""Metadata for tool execution."""
|
|
46
|
+
|
|
41
47
|
execution_time_ms: Optional[int] = None
|
|
42
48
|
tool_name: Optional[str] = None
|
|
43
49
|
# Allow additional fields
|
|
@@ -47,44 +53,49 @@ class ToolMetadata:
|
|
|
47
53
|
"""Convert to dictionary for JSON serialization."""
|
|
48
54
|
result = {}
|
|
49
55
|
if self.execution_time_ms is not None:
|
|
50
|
-
result[
|
|
56
|
+
result["executionTimeMs"] = self.execution_time_ms
|
|
51
57
|
if self.tool_name is not None:
|
|
52
|
-
result[
|
|
58
|
+
result["toolName"] = self.tool_name
|
|
53
59
|
if self.extra:
|
|
54
60
|
result.update(self.extra)
|
|
55
61
|
return result
|
|
56
62
|
|
|
63
|
+
|
|
57
64
|
@dataclass(frozen=True)
|
|
58
65
|
class ToolResult(Generic[T]):
|
|
59
66
|
"""Standardized tool result with status, data, and metadata."""
|
|
67
|
+
|
|
60
68
|
status: ToolResultStatus
|
|
61
69
|
data: Optional[T] = None
|
|
62
70
|
error: Optional[ToolErrorInfo] = None
|
|
63
71
|
metadata: Optional[ToolMetadata] = None
|
|
64
72
|
|
|
73
|
+
|
|
65
74
|
# Common error codes - matching TypeScript constants
|
|
66
75
|
class ToolErrorCodes:
|
|
67
76
|
"""Common error codes for tool execution."""
|
|
77
|
+
|
|
68
78
|
# Validation errors
|
|
69
|
-
INVALID_INPUT =
|
|
70
|
-
MISSING_REQUIRED_FIELD =
|
|
71
|
-
INVALID_FORMAT =
|
|
79
|
+
INVALID_INPUT = "INVALID_INPUT"
|
|
80
|
+
MISSING_REQUIRED_FIELD = "MISSING_REQUIRED_FIELD"
|
|
81
|
+
INVALID_FORMAT = "INVALID_FORMAT"
|
|
72
82
|
|
|
73
83
|
# Permission errors
|
|
74
|
-
PERMISSION_DENIED =
|
|
75
|
-
INSUFFICIENT_PERMISSIONS =
|
|
84
|
+
PERMISSION_DENIED = "PERMISSION_DENIED"
|
|
85
|
+
INSUFFICIENT_PERMISSIONS = "INSUFFICIENT_PERMISSIONS"
|
|
76
86
|
|
|
77
87
|
# Resource errors
|
|
78
|
-
NOT_FOUND =
|
|
79
|
-
RESOURCE_UNAVAILABLE =
|
|
88
|
+
NOT_FOUND = "NOT_FOUND"
|
|
89
|
+
RESOURCE_UNAVAILABLE = "RESOURCE_UNAVAILABLE"
|
|
80
90
|
|
|
81
91
|
# Execution errors
|
|
82
|
-
EXECUTION_FAILED =
|
|
83
|
-
TIMEOUT =
|
|
84
|
-
EXTERNAL_SERVICE_ERROR =
|
|
92
|
+
EXECUTION_FAILED = "EXECUTION_FAILED"
|
|
93
|
+
TIMEOUT = "TIMEOUT"
|
|
94
|
+
EXTERNAL_SERVICE_ERROR = "EXTERNAL_SERVICE_ERROR"
|
|
85
95
|
|
|
86
96
|
# Generic
|
|
87
|
-
UNKNOWN_ERROR =
|
|
97
|
+
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
|
98
|
+
|
|
88
99
|
|
|
89
100
|
class ToolResponse:
|
|
90
101
|
"""Helper functions for creating standardized tool results."""
|
|
@@ -95,98 +106,94 @@ class ToolResponse:
|
|
|
95
106
|
tool_metadata = None
|
|
96
107
|
if metadata:
|
|
97
108
|
tool_metadata = ToolMetadata(
|
|
98
|
-
execution_time_ms=metadata.get(
|
|
99
|
-
tool_name=metadata.get(
|
|
100
|
-
extra={
|
|
101
|
-
|
|
109
|
+
execution_time_ms=metadata.get("executionTimeMs"),
|
|
110
|
+
tool_name=metadata.get("toolName"),
|
|
111
|
+
extra={
|
|
112
|
+
k: v for k, v in metadata.items() if k not in ["executionTimeMs", "toolName"]
|
|
113
|
+
},
|
|
102
114
|
)
|
|
103
115
|
|
|
104
|
-
return ToolResult(
|
|
105
|
-
status='success',
|
|
106
|
-
data=data,
|
|
107
|
-
metadata=tool_metadata
|
|
108
|
-
)
|
|
116
|
+
return ToolResult(status="success", data=data, metadata=tool_metadata)
|
|
109
117
|
|
|
110
118
|
@staticmethod
|
|
111
119
|
def error(
|
|
112
120
|
code: str,
|
|
113
121
|
message: str,
|
|
114
122
|
details: Optional[Any] = None,
|
|
115
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
123
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
116
124
|
) -> ToolResult[None]:
|
|
117
125
|
"""Create an error tool result."""
|
|
118
126
|
tool_metadata = None
|
|
119
127
|
if metadata:
|
|
120
128
|
tool_metadata = ToolMetadata(
|
|
121
|
-
execution_time_ms=metadata.get(
|
|
122
|
-
tool_name=metadata.get(
|
|
123
|
-
extra={
|
|
124
|
-
|
|
129
|
+
execution_time_ms=metadata.get("executionTimeMs"),
|
|
130
|
+
tool_name=metadata.get("toolName"),
|
|
131
|
+
extra={
|
|
132
|
+
k: v for k, v in metadata.items() if k not in ["executionTimeMs", "toolName"]
|
|
133
|
+
},
|
|
125
134
|
)
|
|
126
135
|
|
|
127
136
|
return ToolResult(
|
|
128
|
-
status=
|
|
137
|
+
status="error",
|
|
129
138
|
error=ToolErrorInfo(code=code, message=message, details=details),
|
|
130
|
-
metadata=tool_metadata
|
|
139
|
+
metadata=tool_metadata,
|
|
131
140
|
)
|
|
132
141
|
|
|
133
142
|
@staticmethod
|
|
134
143
|
def validation_error(
|
|
135
|
-
message: str,
|
|
136
|
-
details: Optional[Any] = None,
|
|
137
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
144
|
+
message: str, details: Optional[Any] = None, metadata: Optional[Dict[str, Any]] = None
|
|
138
145
|
) -> ToolResult[None]:
|
|
139
146
|
"""Create a validation error tool result."""
|
|
140
147
|
tool_metadata = None
|
|
141
148
|
if metadata:
|
|
142
149
|
tool_metadata = ToolMetadata(
|
|
143
|
-
execution_time_ms=metadata.get(
|
|
144
|
-
tool_name=metadata.get(
|
|
145
|
-
extra={
|
|
146
|
-
|
|
150
|
+
execution_time_ms=metadata.get("executionTimeMs"),
|
|
151
|
+
tool_name=metadata.get("toolName"),
|
|
152
|
+
extra={
|
|
153
|
+
k: v for k, v in metadata.items() if k not in ["executionTimeMs", "toolName"]
|
|
154
|
+
},
|
|
147
155
|
)
|
|
148
156
|
|
|
149
157
|
return ToolResult(
|
|
150
|
-
status=
|
|
158
|
+
status="validation_error",
|
|
151
159
|
error=ToolErrorInfo(
|
|
152
|
-
code=ToolErrorCodes.INVALID_INPUT,
|
|
153
|
-
message=message,
|
|
154
|
-
details=details
|
|
160
|
+
code=ToolErrorCodes.INVALID_INPUT, message=message, details=details
|
|
155
161
|
),
|
|
156
|
-
metadata=tool_metadata
|
|
162
|
+
metadata=tool_metadata,
|
|
157
163
|
)
|
|
158
164
|
|
|
159
165
|
@staticmethod
|
|
160
166
|
def permission_denied(
|
|
161
167
|
message: str,
|
|
162
168
|
required_permissions: Optional[List[str]] = None,
|
|
163
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
169
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
164
170
|
) -> ToolResult[None]:
|
|
165
171
|
"""Create a permission denied tool result."""
|
|
166
172
|
tool_metadata = None
|
|
167
173
|
if metadata:
|
|
168
174
|
tool_metadata = ToolMetadata(
|
|
169
|
-
execution_time_ms=metadata.get(
|
|
170
|
-
tool_name=metadata.get(
|
|
171
|
-
extra={
|
|
172
|
-
|
|
175
|
+
execution_time_ms=metadata.get("executionTimeMs"),
|
|
176
|
+
tool_name=metadata.get("toolName"),
|
|
177
|
+
extra={
|
|
178
|
+
k: v for k, v in metadata.items() if k not in ["executionTimeMs", "toolName"]
|
|
179
|
+
},
|
|
173
180
|
)
|
|
174
181
|
|
|
175
182
|
return ToolResult(
|
|
176
|
-
status=
|
|
183
|
+
status="permission_denied",
|
|
177
184
|
error=ToolErrorInfo(
|
|
178
185
|
code=ToolErrorCodes.PERMISSION_DENIED,
|
|
179
186
|
message=message,
|
|
180
|
-
details={
|
|
187
|
+
details={"requiredPermissions": required_permissions}
|
|
188
|
+
if required_permissions
|
|
189
|
+
else None,
|
|
181
190
|
),
|
|
182
|
-
metadata=tool_metadata
|
|
191
|
+
metadata=tool_metadata,
|
|
183
192
|
)
|
|
184
193
|
|
|
185
194
|
@staticmethod
|
|
186
195
|
def not_found(
|
|
187
|
-
resource: str,
|
|
188
|
-
identifier: Optional[str] = None,
|
|
189
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
196
|
+
resource: str, identifier: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None
|
|
190
197
|
) -> ToolResult[None]:
|
|
191
198
|
"""Create a not found tool result."""
|
|
192
199
|
message = f"{resource} not found"
|
|
@@ -196,36 +203,38 @@ class ToolResponse:
|
|
|
196
203
|
tool_metadata = None
|
|
197
204
|
if metadata:
|
|
198
205
|
tool_metadata = ToolMetadata(
|
|
199
|
-
execution_time_ms=metadata.get(
|
|
200
|
-
tool_name=metadata.get(
|
|
201
|
-
extra={
|
|
202
|
-
|
|
206
|
+
execution_time_ms=metadata.get("executionTimeMs"),
|
|
207
|
+
tool_name=metadata.get("toolName"),
|
|
208
|
+
extra={
|
|
209
|
+
k: v for k, v in metadata.items() if k not in ["executionTimeMs", "toolName"]
|
|
210
|
+
},
|
|
203
211
|
)
|
|
204
212
|
|
|
205
213
|
return ToolResult(
|
|
206
|
-
status=
|
|
214
|
+
status="not_found",
|
|
207
215
|
error=ToolErrorInfo(
|
|
208
216
|
code=ToolErrorCodes.NOT_FOUND,
|
|
209
217
|
message=message,
|
|
210
|
-
details={
|
|
218
|
+
details={"resource": resource, "identifier": identifier},
|
|
211
219
|
),
|
|
212
|
-
metadata=tool_metadata
|
|
220
|
+
metadata=tool_metadata,
|
|
213
221
|
)
|
|
214
222
|
|
|
223
|
+
|
|
215
224
|
def with_error_handling(
|
|
216
|
-
tool_name: str,
|
|
217
|
-
executor: Callable[[TArgs, TContext], Union[TResult, Awaitable[TResult]]]
|
|
225
|
+
tool_name: str, executor: Callable[[TArgs, TContext], Union[TResult, Awaitable[TResult]]]
|
|
218
226
|
) -> Callable[[TArgs, TContext], Awaitable[ToolResult[TResult]]]:
|
|
219
227
|
"""
|
|
220
228
|
Tool execution wrapper that provides standardized error handling.
|
|
221
|
-
|
|
229
|
+
|
|
222
230
|
Args:
|
|
223
231
|
tool_name: Name of the tool for logging and metadata
|
|
224
232
|
executor: The actual tool execution function
|
|
225
|
-
|
|
233
|
+
|
|
226
234
|
Returns:
|
|
227
235
|
Wrapped function that returns a ToolResult
|
|
228
236
|
"""
|
|
237
|
+
|
|
229
238
|
async def wrapper(args: TArgs, context: TContext) -> ToolResult[TResult]:
|
|
230
239
|
start_time = time.time() * 1000 # Convert to milliseconds like TypeScript Date.now()
|
|
231
240
|
|
|
@@ -234,16 +243,15 @@ def with_error_handling(
|
|
|
234
243
|
|
|
235
244
|
# Handle both sync and async executors
|
|
236
245
|
result = executor(args, context)
|
|
237
|
-
if hasattr(result,
|
|
246
|
+
if hasattr(result, "__await__"): # Check if it's awaitable
|
|
238
247
|
result = await result
|
|
239
248
|
|
|
240
249
|
execution_time = int(time.time() * 1000 - start_time)
|
|
241
250
|
print(f"[TOOL:{tool_name}] Completed successfully in {execution_time}ms")
|
|
242
251
|
|
|
243
|
-
return ToolResponse.success(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
})
|
|
252
|
+
return ToolResponse.success(
|
|
253
|
+
result, {"executionTimeMs": execution_time, "toolName": tool_name}
|
|
254
|
+
)
|
|
247
255
|
|
|
248
256
|
except Exception as error:
|
|
249
257
|
execution_time = int(time.time() * 1000 - start_time)
|
|
@@ -253,72 +261,74 @@ def with_error_handling(
|
|
|
253
261
|
return ToolResponse.error(
|
|
254
262
|
ToolErrorCodes.EXECUTION_FAILED,
|
|
255
263
|
str(error),
|
|
256
|
-
{
|
|
257
|
-
{
|
|
264
|
+
{"stack": traceback.format_exc()},
|
|
265
|
+
{"executionTimeMs": execution_time, "toolName": tool_name},
|
|
258
266
|
)
|
|
259
267
|
|
|
260
268
|
return ToolResponse.error(
|
|
261
269
|
ToolErrorCodes.UNKNOWN_ERROR,
|
|
262
|
-
|
|
270
|
+
"Unknown error occurred",
|
|
263
271
|
error,
|
|
264
|
-
{
|
|
272
|
+
{"executionTimeMs": execution_time, "toolName": tool_name},
|
|
265
273
|
)
|
|
266
274
|
|
|
267
275
|
return wrapper
|
|
268
276
|
|
|
277
|
+
|
|
269
278
|
def require_permissions(
|
|
270
|
-
required_permissions: List[str]
|
|
279
|
+
required_permissions: List[str],
|
|
271
280
|
) -> Callable[[TContext], Optional[ToolResult[None]]]:
|
|
272
281
|
"""
|
|
273
282
|
Permission checking helper.
|
|
274
|
-
|
|
283
|
+
|
|
275
284
|
Args:
|
|
276
285
|
required_permissions: List of permissions required
|
|
277
|
-
|
|
286
|
+
|
|
278
287
|
Returns:
|
|
279
288
|
Function that checks permissions and returns ToolResult if denied, None if allowed
|
|
280
289
|
"""
|
|
290
|
+
|
|
281
291
|
def check_permissions(context: TContext) -> Optional[ToolResult[None]]:
|
|
282
292
|
# Try to get permissions from context
|
|
283
|
-
user_permissions = getattr(context,
|
|
293
|
+
user_permissions = getattr(context, "permissions", None)
|
|
284
294
|
if user_permissions is None or not isinstance(user_permissions, list):
|
|
285
295
|
user_permissions = []
|
|
286
296
|
|
|
287
297
|
missing_permissions = [
|
|
288
|
-
perm for perm in required_permissions
|
|
289
|
-
if perm not in user_permissions
|
|
298
|
+
perm for perm in required_permissions if perm not in user_permissions
|
|
290
299
|
]
|
|
291
300
|
|
|
292
301
|
if missing_permissions:
|
|
293
302
|
return ToolResponse.permission_denied(
|
|
294
303
|
f"Missing required permissions: {', '.join(missing_permissions)}",
|
|
295
|
-
required_permissions
|
|
304
|
+
required_permissions,
|
|
296
305
|
)
|
|
297
306
|
|
|
298
307
|
return None # No error
|
|
299
308
|
|
|
300
309
|
return check_permissions
|
|
301
310
|
|
|
311
|
+
|
|
302
312
|
def tool_result_to_string(result: ToolResult[Any]) -> str:
|
|
303
313
|
"""
|
|
304
314
|
Convert ToolResult to string for backward compatibility with existing tools.
|
|
305
|
-
|
|
315
|
+
|
|
306
316
|
Args:
|
|
307
317
|
result: The ToolResult to convert
|
|
308
|
-
|
|
318
|
+
|
|
309
319
|
Returns:
|
|
310
320
|
String representation of the result
|
|
311
321
|
"""
|
|
312
|
-
if result.status ==
|
|
322
|
+
if result.status == "success":
|
|
313
323
|
# For successful results, include metadata if available
|
|
314
324
|
if result.metadata:
|
|
315
325
|
success_obj = {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
"status": "success",
|
|
327
|
+
"data": result.data,
|
|
328
|
+
"metadata": result.metadata.to_dict(),
|
|
319
329
|
}
|
|
320
330
|
return json.dumps(success_obj, default=str)
|
|
321
|
-
|
|
331
|
+
|
|
322
332
|
# If no metadata, return just the data for backward compatibility
|
|
323
333
|
if isinstance(result.data, str):
|
|
324
334
|
return result.data
|
|
@@ -326,15 +336,15 @@ def tool_result_to_string(result: ToolResult[Any]) -> str:
|
|
|
326
336
|
|
|
327
337
|
# For errors, return a structured error message
|
|
328
338
|
error_obj = {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
339
|
+
"error": result.status,
|
|
340
|
+
"code": result.error.code if result.error else "UNKNOWN",
|
|
341
|
+
"message": result.error.message if result.error else "Unknown error",
|
|
332
342
|
}
|
|
333
343
|
|
|
334
344
|
if result.error and result.error.details:
|
|
335
|
-
error_obj[
|
|
345
|
+
error_obj["details"] = result.error.details
|
|
336
346
|
|
|
337
347
|
if result.metadata:
|
|
338
|
-
error_obj[
|
|
348
|
+
error_obj["metadata"] = result.metadata.to_dict()
|
|
339
349
|
|
|
340
350
|
return json.dumps(error_obj, indent=2, default=str)
|