solace-agent-mesh 1.0.5__py3-none-any.whl → 1.0.7__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 solace-agent-mesh might be problematic. Click here for more details.

Files changed (142) hide show
  1. solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
  2. solace_agent_mesh/agent/adk/{filesystem_artifact_service.py → artifacts/filesystem_artifact_service.py} +14 -15
  3. solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +440 -0
  4. solace_agent_mesh/agent/adk/callbacks.py +123 -159
  5. solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +316 -0
  6. solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +414 -0
  7. solace_agent_mesh/agent/adk/mcp_content_processor.py +665 -0
  8. solace_agent_mesh/agent/adk/services.py +35 -1
  9. solace_agent_mesh/agent/adk/setup.py +85 -45
  10. solace_agent_mesh/agent/adk/tool_wrapper.py +19 -3
  11. solace_agent_mesh/agent/protocol/event_handlers.py +1 -1
  12. solace_agent_mesh/agent/sac/app.py +67 -0
  13. solace_agent_mesh/agent/sac/component.py +14 -86
  14. solace_agent_mesh/assets/docs/404.html +3 -3
  15. solace_agent_mesh/assets/docs/assets/js/04989206.b9dfe831.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/0e682baa.b3bbde9a.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/1023fc19.364235d5.js +1 -0
  18. solace_agent_mesh/assets/docs/assets/js/1523c6b4.1b0ec6f9.js +1 -0
  19. solace_agent_mesh/assets/docs/assets/js/166ab619.e8f3a7c7.js +1 -0
  20. solace_agent_mesh/assets/docs/assets/js/21ceee5f.3bf39250.js +1 -0
  21. solace_agent_mesh/assets/docs/assets/js/3d406171.7d02a73b.js +1 -0
  22. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/442a8107.b3159bb2.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/4c2787c2.fc6804f2.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/5b4258a4.0d080cd9.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/75384d09.ccd480c4.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/768e31b0.8b51cd70.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/945fb41e.c63791d1.js +1 -0
  29. solace_agent_mesh/assets/docs/assets/js/{9eff14a2.036c35ea.js → 9eff14a2.472b0310.js} +1 -1
  30. solace_agent_mesh/assets/docs/assets/js/a3a92b25.4b7fa6a2.js +1 -0
  31. solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/ae4415af.7a2f0bbf.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/b7006a3a.73a79653.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/beecea0d.ae31f6a7.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/c2c06897.587b4af5.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/{cd3d4052.ca6eed8c.js → cd3d4052.b6535013.js} +1 -1
  37. solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +1 -0
  38. solace_agent_mesh/assets/docs/assets/js/f897a61a.0aa29dbb.js +1 -0
  39. solace_agent_mesh/assets/docs/assets/js/main.d79f063b.js +2 -0
  40. solace_agent_mesh/assets/docs/assets/js/runtime~main.6415ad00.js +1 -0
  41. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +28 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +6 -6
  43. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +8 -8
  44. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +5 -5
  45. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +5 -5
  46. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +34 -5
  47. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +5 -5
  49. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +6 -6
  51. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +72 -0
  52. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +5 -5
  53. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +7 -7
  54. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +35 -16
  55. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +17 -11
  57. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +5 -5
  59. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +6 -6
  61. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +6 -6
  62. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +6 -6
  63. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  64. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +8 -8
  65. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +14 -14
  66. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +8 -8
  67. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +6 -6
  69. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +35 -23
  70. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  71. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +6 -6
  72. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  73. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  74. solace_agent_mesh/assets/docs/lunr-index-1756146501924.json +1 -0
  75. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  76. solace_agent_mesh/assets/docs/search-doc-1756146501924.json +1 -0
  77. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  78. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  79. solace_agent_mesh/cli/__init__.py +1 -1
  80. solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +1 -1
  81. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +67 -10
  82. solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +2 -2
  83. solace_agent_mesh/cli/commands/eval_cmd.py +8 -2
  84. solace_agent_mesh/cli/commands/init_cmd/__init__.py +20 -2
  85. solace_agent_mesh/cli/commands/init_cmd/env_step.py +25 -1
  86. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +45 -1
  87. solace_agent_mesh/cli/utils.py +21 -12
  88. solace_agent_mesh/client/webui/frontend/static/assets/main-BucUdn9m.js +673 -0
  89. solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
  90. solace_agent_mesh/common/a2a_protocol.py +1 -1
  91. solace_agent_mesh/common/utils/initializer.py +4 -6
  92. solace_agent_mesh/common/utils/mime_helpers.py +60 -1
  93. solace_agent_mesh/config_portal/backend/server.py +1 -1
  94. solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-xSu2leR8.js → _index-MqsrTd6g.js} +9 -9
  95. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-950eb3be.js → manifest-28271392.js} +1 -1
  96. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  97. solace_agent_mesh/core_a2a/core_a2a_llm.txt +1 -1
  98. solace_agent_mesh/core_a2a/service.py +1 -1
  99. solace_agent_mesh/evaluation/run.py +149 -15
  100. solace_agent_mesh/evaluation/summary_builder.py +5 -3
  101. solace_agent_mesh/gateway/http_sse/dependencies.py +1 -1
  102. solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +1 -1
  103. solace_agent_mesh/gateway/http_sse/services/task_service.py +1 -1
  104. solace_agent_mesh/llm_detail.txt +2 -2
  105. solace_agent_mesh/templates/agent_template.yaml +1 -1
  106. solace_agent_mesh/templates/plugin_agent_config_template.yaml +3 -3
  107. solace_agent_mesh/templates/plugin_readme_template.md +1 -1
  108. solace_agent_mesh/templates/shared_config.yaml +8 -1
  109. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/METADATA +5 -2
  110. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/RECORD +114 -109
  111. solace_agent_mesh/assets/docs/assets/js/04989206.da8246cd.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/0e682baa.79f0ab22.js +0 -1
  113. solace_agent_mesh/assets/docs/assets/js/1023fc19.8e6d174c.js +0 -1
  114. solace_agent_mesh/assets/docs/assets/js/1523c6b4.91c7bc01.js +0 -1
  115. solace_agent_mesh/assets/docs/assets/js/166ab619.7d97ccaf.js +0 -1
  116. solace_agent_mesh/assets/docs/assets/js/21ceee5f.614fa8dd.js +0 -1
  117. solace_agent_mesh/assets/docs/assets/js/3d406171.9b081d5f.js +0 -1
  118. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.36090198.js +0 -1
  119. solace_agent_mesh/assets/docs/assets/js/442a8107.5ba94b65.js +0 -1
  120. solace_agent_mesh/assets/docs/assets/js/4c2787c2.66ee00e9.js +0 -1
  121. solace_agent_mesh/assets/docs/assets/js/5b4258a4.bda20761.js +0 -1
  122. solace_agent_mesh/assets/docs/assets/js/75384d09.c3991823.js +0 -1
  123. solace_agent_mesh/assets/docs/assets/js/768e31b0.a12673db.js +0 -1
  124. solace_agent_mesh/assets/docs/assets/js/945fb41e.74d728aa.js +0 -1
  125. solace_agent_mesh/assets/docs/assets/js/a3a92b25.26ca071f.js +0 -1
  126. solace_agent_mesh/assets/docs/assets/js/aba87c2f.a6b84da6.js +0 -1
  127. solace_agent_mesh/assets/docs/assets/js/ae4415af.96189a93.js +0 -1
  128. solace_agent_mesh/assets/docs/assets/js/b7006a3a.38c0cf3d.js +0 -1
  129. solace_agent_mesh/assets/docs/assets/js/bb2ef573.56931473.js +0 -1
  130. solace_agent_mesh/assets/docs/assets/js/c2c06897.63b76e9e.js +0 -1
  131. solace_agent_mesh/assets/docs/assets/js/f284c35a.5aff74ab.js +0 -1
  132. solace_agent_mesh/assets/docs/assets/js/f897a61a.862b0514.js +0 -1
  133. solace_agent_mesh/assets/docs/assets/js/main.946fa17b.js +0 -2
  134. solace_agent_mesh/assets/docs/assets/js/runtime~main.aa687c82.js +0 -1
  135. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +0 -17
  136. solace_agent_mesh/assets/docs/lunr-index-1755275703209.json +0 -1
  137. solace_agent_mesh/assets/docs/search-doc-1755275703209.json +0 -1
  138. solace_agent_mesh/client/webui/frontend/static/assets/main-DzKPMTRs.js +0 -673
  139. /solace_agent_mesh/assets/docs/assets/js/{main.946fa17b.js.LICENSE.txt → main.d79f063b.js.LICENSE.txt} +0 -0
  140. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/WHEEL +0 -0
  141. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/entry_points.txt +0 -0
  142. {solace_agent_mesh-1.0.5.dist-info → solace_agent_mesh-1.0.7.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,414 @@
1
+ """
2
+ Intelligent MCP Callback Functions
3
+
4
+ This module contains the refactored MCP callback functions that use intelligent
5
+ content processing to save MCP tool responses as appropriately typed artifacts.
6
+ """
7
+
8
+ import json
9
+ import uuid
10
+ from datetime import datetime, timezone
11
+ from typing import Any, Dict, TYPE_CHECKING, List, Optional
12
+ from enum import Enum
13
+ from pydantic import BaseModel
14
+
15
+ from google.adk.tools import ToolContext, BaseTool
16
+ from solace_ai_connector.common.log import log
17
+
18
+ from .mcp_content_processor import MCPContentProcessor, MCPContentProcessorConfig
19
+ from ...agent.utils.artifact_helpers import (
20
+ save_artifact_with_metadata,
21
+ DEFAULT_SCHEMA_MAX_KEYS,
22
+ )
23
+ from ...agent.utils.context_helpers import get_original_session_id
24
+
25
+
26
+ if TYPE_CHECKING:
27
+ from ...agent.sac.component import SamAgentComponent
28
+
29
+
30
+ class McpSaveStatus(str, Enum):
31
+ """Enumeration for the status of an MCP save operation."""
32
+
33
+ SUCCESS = "success"
34
+ PARTIAL_SUCCESS = "partial_success"
35
+ ERROR = "error"
36
+
37
+
38
+ class SavedArtifactInfo(BaseModel):
39
+ """
40
+ A Pydantic model to hold the details of a successfully saved artifact.
41
+ This mirrors the dictionary structure returned by save_artifact_with_metadata.
42
+ """
43
+
44
+ status: str
45
+ data_filename: str
46
+ data_version: int
47
+ metadata_filename: str
48
+ metadata_version: int
49
+ message: str
50
+
51
+
52
+ class McpSaveResult(BaseModel):
53
+ """
54
+ The definitive, type-safe result of an MCP response save operation.
55
+
56
+ Attributes:
57
+ status: The overall status of the save operation.
58
+ message: A human-readable summary of the outcome.
59
+ artifacts_saved: A list of successfully created "intelligent" artifacts.
60
+ fallback_artifact: An optional artifact representing the raw JSON response,
61
+ created only if intelligent processing failed.
62
+ """
63
+
64
+ status: McpSaveStatus
65
+ message: str
66
+ artifacts_saved: List[SavedArtifactInfo] = []
67
+ fallback_artifact: Optional[SavedArtifactInfo] = None
68
+
69
+
70
+ async def save_mcp_response_as_artifact_intelligent(
71
+ tool: BaseTool,
72
+ tool_context: ToolContext,
73
+ host_component: "SamAgentComponent",
74
+ mcp_response_dict: Dict[str, Any],
75
+ original_tool_args: Dict[str, Any],
76
+ ) -> McpSaveResult:
77
+ """
78
+ Intelligently processes and saves MCP tool response content as typed artifacts.
79
+
80
+ This function uses intelligent content processing to:
81
+ - Detect and parse different content types (text, image, audio, resource)
82
+ - Create appropriately typed artifacts with proper MIME types
83
+ - Generate enhanced metadata based on content analysis
84
+ - Fall back to raw JSON saving if intelligent processing fails
85
+
86
+ Args:
87
+ tool: The MCPTool instance that generated the response.
88
+ tool_context: The ADK ToolContext.
89
+ host_component: The A2A_ADK_HostComponent instance for accessing config and services.
90
+ mcp_response_dict: The raw MCP tool response dictionary.
91
+ original_tool_args: The original arguments passed to the MCP tool.
92
+
93
+ Returns:
94
+ An McpSaveResult object containing the structured result of the operation,
95
+ including status, a list of successfully saved artifacts, and any
96
+ fallback artifact.
97
+ """
98
+ log_identifier = f"[IntelligentMCPCallback:{tool.name}]"
99
+ log.debug("%s Starting intelligent MCP response artifact saving...", log_identifier)
100
+
101
+ processor_config_dict = host_component.get_config("mcp_intelligent_processing", {})
102
+ processor_config = MCPContentProcessorConfig.from_dict(processor_config_dict)
103
+
104
+ saved_artifacts: List[SavedArtifactInfo] = []
105
+ failed_artifacts: List[Dict[str, Any]] = []
106
+ fallback_artifact: Optional[SavedArtifactInfo] = None
107
+ overall_status = McpSaveStatus.SUCCESS
108
+
109
+ try:
110
+ if not processor_config.enable_intelligent_processing:
111
+ log.info(
112
+ "%s Intelligent processing disabled, using raw JSON fallback.",
113
+ log_identifier,
114
+ )
115
+ fallback_dict = await _save_raw_mcp_response_fallback(
116
+ tool,
117
+ tool_context,
118
+ host_component,
119
+ mcp_response_dict,
120
+ original_tool_args,
121
+ )
122
+ if fallback_dict.get("status") in ["success", "partial_success"]:
123
+ fallback_artifact = SavedArtifactInfo(**fallback_dict)
124
+ status = McpSaveStatus.SUCCESS
125
+ else:
126
+ status = McpSaveStatus.ERROR
127
+ return McpSaveResult(
128
+ status=status,
129
+ message="Intelligent processing disabled; saved raw JSON as fallback.",
130
+ fallback_artifact=fallback_artifact,
131
+ )
132
+
133
+ processor = MCPContentProcessor(tool.name, original_tool_args)
134
+ content_items = processor.process_mcp_response(mcp_response_dict)
135
+
136
+ if not content_items:
137
+ log.warning(
138
+ "%s No content items found, falling back to raw JSON.", log_identifier
139
+ )
140
+ fallback_dict = await _save_raw_mcp_response_fallback(
141
+ tool,
142
+ tool_context,
143
+ host_component,
144
+ mcp_response_dict,
145
+ original_tool_args,
146
+ )
147
+ if fallback_dict.get("status") in ["success", "partial_success"]:
148
+ fallback_artifact = SavedArtifactInfo(**fallback_dict)
149
+ return McpSaveResult(
150
+ status=McpSaveStatus.PARTIAL_SUCCESS,
151
+ message="No content items found in MCP response; saved raw JSON as fallback.",
152
+ fallback_artifact=fallback_artifact,
153
+ )
154
+
155
+ log.info(
156
+ "%s Processing %d content items intelligently.",
157
+ log_identifier,
158
+ len(content_items),
159
+ )
160
+
161
+ for item in content_items:
162
+ try:
163
+ if hasattr(item, "uri"):
164
+ item.uri = str(item.uri)
165
+ result_dict = await _save_content_item_as_artifact(
166
+ item, tool_context, host_component
167
+ )
168
+ if result_dict.get("status") in ["success", "partial_success"]:
169
+ saved_artifacts.append(SavedArtifactInfo(**result_dict))
170
+ else:
171
+ log.warning(
172
+ "%s Failed to save content item: %s",
173
+ log_identifier,
174
+ result_dict.get("message", "Unknown error"),
175
+ )
176
+ overall_status = McpSaveStatus.PARTIAL_SUCCESS
177
+ failed_artifacts.append(result_dict)
178
+ except Exception as e:
179
+ if not processor_config.fallback_to_raw_on_error:
180
+ raise
181
+ log.exception("%s Error saving content item: %s", log_identifier, e)
182
+ overall_status = McpSaveStatus.PARTIAL_SUCCESS
183
+ failed_artifacts.append({"status": "error", "message": str(e)})
184
+
185
+ if not saved_artifacts:
186
+ if failed_artifacts:
187
+ first_error_msg = failed_artifacts[0].get("message", "Unknown error")
188
+ log.warning(
189
+ "%s No items saved successfully. First error: %s",
190
+ log_identifier,
191
+ first_error_msg,
192
+ )
193
+ return McpSaveResult(
194
+ status=McpSaveStatus.ERROR,
195
+ message=f"Content processing failed. First error: {first_error_msg}",
196
+ )
197
+
198
+ fallback_dict = await _save_raw_mcp_response_fallback(
199
+ tool,
200
+ tool_context,
201
+ host_component,
202
+ mcp_response_dict,
203
+ original_tool_args,
204
+ )
205
+ if fallback_dict.get("status") in ["success", "partial_success"]:
206
+ fallback_artifact = SavedArtifactInfo(**fallback_dict)
207
+ return McpSaveResult(
208
+ status=McpSaveStatus.PARTIAL_SUCCESS,
209
+ message="Content processing failed for all items; saved raw JSON as fallback.",
210
+ fallback_artifact=fallback_artifact,
211
+ )
212
+
213
+ if processor_config_dict.get("save_raw_alongside_intelligent", False):
214
+ try:
215
+ fallback_dict = await _save_raw_mcp_response_fallback(
216
+ tool,
217
+ tool_context,
218
+ host_component,
219
+ mcp_response_dict,
220
+ original_tool_args,
221
+ )
222
+ if fallback_dict.get("status") in ["success", "partial_success"]:
223
+ fallback_artifact = SavedArtifactInfo(**fallback_dict)
224
+ except Exception as e:
225
+ log.warning(
226
+ "%s Failed to save raw JSON alongside: %s", log_identifier, e
227
+ )
228
+
229
+ log.info(
230
+ "%s Intelligent processing complete: %d artifacts saved, status: %s",
231
+ log_identifier,
232
+ len(saved_artifacts),
233
+ overall_status.value,
234
+ )
235
+ return McpSaveResult(
236
+ status=overall_status,
237
+ artifacts_saved=saved_artifacts,
238
+ fallback_artifact=fallback_artifact,
239
+ message=f"Successfully processed {len(saved_artifacts)} content items.",
240
+ )
241
+
242
+ except Exception as e:
243
+ log.exception(
244
+ "%s Error in intelligent MCP response processing: %s", log_identifier, e
245
+ )
246
+ if processor_config.fallback_to_raw_on_error:
247
+ log.info(
248
+ "%s Falling back to raw JSON due to processing error.", log_identifier
249
+ )
250
+ try:
251
+ fallback_dict = await _save_raw_mcp_response_fallback(
252
+ tool,
253
+ tool_context,
254
+ host_component,
255
+ mcp_response_dict,
256
+ original_tool_args,
257
+ )
258
+ if fallback_dict.get("status") in ["success", "partial_success"]:
259
+ fallback_artifact = SavedArtifactInfo(**fallback_dict)
260
+ return McpSaveResult(
261
+ status=McpSaveStatus.PARTIAL_SUCCESS,
262
+ artifacts_saved=saved_artifacts,
263
+ fallback_artifact=fallback_artifact,
264
+ message=f"Intelligent processing failed, saved raw JSON as fallback: {e}",
265
+ )
266
+ except Exception as fallback_error:
267
+ log.exception(
268
+ "%s Fallback also failed: %s", log_identifier, fallback_error
269
+ )
270
+
271
+ return McpSaveResult(
272
+ status=McpSaveStatus.ERROR,
273
+ artifacts_saved=saved_artifacts,
274
+ fallback_artifact=None,
275
+ message=f"Failed to save MCP response as artifact: {e}",
276
+ )
277
+
278
+
279
+ async def _save_content_item_as_artifact(
280
+ content_item,
281
+ tool_context: ToolContext,
282
+ host_component: "SamAgentComponent",
283
+ ) -> Dict[str, Any]:
284
+ """Save a single processed content item as an artifact."""
285
+
286
+ log_identifier = f"[IntelligentMCPCallback:SaveContentItem:{content_item.filename}]"
287
+
288
+ try:
289
+ artifact_service = host_component.artifact_service
290
+ if not artifact_service:
291
+ raise ValueError("ArtifactService is not available on host_component.")
292
+
293
+ app_name = host_component.agent_name
294
+ user_id = tool_context._invocation_context.user_id
295
+ session_id = get_original_session_id(tool_context._invocation_context)
296
+ schema_max_keys = host_component.get_config(
297
+ "schema_max_keys", DEFAULT_SCHEMA_MAX_KEYS
298
+ )
299
+ artifact_timestamp = datetime.now(timezone.utc)
300
+
301
+ log.debug(
302
+ "%s Saving content item: type=%s, mime_type=%s, size=%d bytes",
303
+ log_identifier,
304
+ content_item.content_type,
305
+ content_item.mime_type,
306
+ len(content_item.content_bytes),
307
+ )
308
+
309
+ save_result = await save_artifact_with_metadata(
310
+ artifact_service=artifact_service,
311
+ app_name=app_name,
312
+ user_id=user_id,
313
+ session_id=session_id,
314
+ filename=content_item.filename,
315
+ content_bytes=content_item.content_bytes,
316
+ mime_type=content_item.mime_type,
317
+ metadata_dict=content_item.metadata,
318
+ timestamp=artifact_timestamp,
319
+ schema_max_keys=schema_max_keys,
320
+ tool_context=tool_context,
321
+ )
322
+
323
+ log.info(
324
+ "%s Content item saved as artifact '%s' (version %s). Status: %s",
325
+ log_identifier,
326
+ save_result.get("data_filename", content_item.filename),
327
+ save_result.get("data_version", "N/A"),
328
+ save_result.get("status"),
329
+ )
330
+
331
+ return save_result
332
+
333
+ except Exception as e:
334
+ log.exception("%s Error saving content item as artifact: %s", log_identifier, e)
335
+ return {
336
+ "status": "error",
337
+ "data_filename": content_item.filename,
338
+ "message": f"Failed to save content item as artifact: {e}",
339
+ }
340
+
341
+
342
+ async def _save_raw_mcp_response_fallback(
343
+ tool: BaseTool,
344
+ tool_context: ToolContext,
345
+ host_component: "SamAgentComponent",
346
+ mcp_response_dict: Dict[str, Any],
347
+ original_tool_args: Dict[str, Any],
348
+ ) -> Dict[str, Any]:
349
+ """
350
+ Fallback function to save the raw MCP response as a JSON artifact.
351
+ This is the original behavior, used when intelligent processing is disabled or fails.
352
+ """
353
+ log_identifier = f"[IntelligentMCPCallback:{tool.name}:RawFallback]"
354
+ log.debug("%s Saving raw MCP response as JSON artifact...", log_identifier)
355
+
356
+ try:
357
+ a2a_context = tool_context.state.get("a2a_context", {})
358
+ logical_task_id = a2a_context.get("logical_task_id", "unknownTask")
359
+ task_id_suffix = logical_task_id[-6:]
360
+ random_suffix = uuid.uuid4().hex[:6]
361
+ filename = f"{task_id_suffix}_{tool.name}_raw_{random_suffix}.json"
362
+
363
+ content_bytes = json.dumps(mcp_response_dict, indent=2).encode("utf-8")
364
+ mime_type = "application/json"
365
+ artifact_timestamp = datetime.now(timezone.utc)
366
+
367
+ metadata_for_saving = {
368
+ "description": f"Raw JSON response from MCP tool {tool.name}",
369
+ "source_tool_name": tool.name,
370
+ "source_tool_args": original_tool_args,
371
+ "processing_type": "raw_fallback",
372
+ }
373
+
374
+ artifact_service = host_component.artifact_service
375
+ app_name = host_component.agent_name
376
+ user_id = tool_context._invocation_context.user_id
377
+ session_id = get_original_session_id(tool_context._invocation_context)
378
+ schema_max_keys = host_component.get_config(
379
+ "schema_max_keys", DEFAULT_SCHEMA_MAX_KEYS
380
+ )
381
+
382
+ save_result = await save_artifact_with_metadata(
383
+ artifact_service=artifact_service,
384
+ app_name=app_name,
385
+ user_id=user_id,
386
+ session_id=session_id,
387
+ filename=filename,
388
+ content_bytes=content_bytes,
389
+ mime_type=mime_type,
390
+ metadata_dict=metadata_for_saving,
391
+ timestamp=artifact_timestamp,
392
+ schema_max_keys=schema_max_keys,
393
+ tool_context=tool_context,
394
+ )
395
+
396
+ log.info(
397
+ "%s Raw MCP response saved as artifact '%s' (version %s). Status: %s",
398
+ log_identifier,
399
+ save_result.get("data_filename", filename),
400
+ save_result.get("data_version", "N/A"),
401
+ save_result.get("status"),
402
+ )
403
+
404
+ return save_result
405
+
406
+ except Exception as e:
407
+ log.exception(
408
+ "%s Error saving raw MCP response as artifact: %s", log_identifier, e
409
+ )
410
+ return {
411
+ "status": "error",
412
+ "data_filename": filename if "filename" in locals() else "unknown_filename",
413
+ "message": f"Failed to save raw MCP response as artifact: {e}",
414
+ }