sunholo 0.143.1__py3-none-any.whl → 0.143.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.
@@ -38,12 +38,19 @@ except ImportError:
38
38
 
39
39
  try:
40
40
  from ...mcp.vac_mcp_server import VACMCPServer
41
- from mcp.server.models import InitializationOptions
42
- from mcp import JSONRPCMessage, ErrorData, INTERNAL_ERROR
41
+ from mcp.server import Server
42
+ from mcp.types import JSONRPCMessage, ErrorData, INTERNAL_ERROR
43
43
  except ImportError:
44
44
  VACMCPServer = None
45
- InitializationOptions = None
45
+ Server = None
46
46
  JSONRPCMessage = None
47
+ ErrorData = None
48
+ INTERNAL_ERROR = None
49
+
50
+ try:
51
+ from ...a2a.vac_a2a_agent import VACA2AAgent
52
+ except (ImportError, SyntaxError):
53
+ VACA2AAgent = None
47
54
 
48
55
 
49
56
  # Cache dictionary to store validated API keys
@@ -81,7 +88,9 @@ if __name__ == "__main__":
81
88
  mcp_servers: List[Dict[str, Any]] = None,
82
89
  async_stream:bool=False,
83
90
  add_langfuse_eval:bool=True,
84
- enable_mcp_server:bool=False):
91
+ enable_mcp_server:bool=False,
92
+ enable_a2a_agent:bool=False,
93
+ a2a_vac_names: List[str] = None):
85
94
  self.app = app
86
95
  self.stream_interpreter = stream_interpreter
87
96
  self.vac_interpreter = vac_interpreter or partial(self.vac_interpreter_default)
@@ -102,6 +111,15 @@ if __name__ == "__main__":
102
111
  vac_interpreter=self.vac_interpreter
103
112
  )
104
113
 
114
+ # A2A agent initialization
115
+ self.enable_a2a_agent = enable_a2a_agent
116
+ self.vac_a2a_agent = None
117
+ self.a2a_vac_names = a2a_vac_names
118
+ if self.enable_a2a_agent and VACA2AAgent:
119
+ # Extract base URL from request context during route handling
120
+ # For now, initialize with placeholder - will be updated in route handlers
121
+ self.vac_a2a_agent = None # Initialized lazily in route handlers
122
+
105
123
  self.additional_routes = additional_routes if additional_routes is not None else []
106
124
  self.async_stream = async_stream
107
125
  self.add_langfuse_eval = add_langfuse_eval
@@ -177,6 +195,15 @@ if __name__ == "__main__":
177
195
  # MCP server endpoint
178
196
  if self.enable_mcp_server and self.vac_mcp_server:
179
197
  self.app.route('/mcp', methods=['POST', 'GET'])(self.handle_mcp_server)
198
+
199
+ # A2A agent endpoints
200
+ if self.enable_a2a_agent:
201
+ self.app.route('/.well-known/agent.json', methods=['GET'])(self.handle_a2a_agent_card)
202
+ self.app.route('/a2a/tasks/send', methods=['POST'])(self.handle_a2a_task_send)
203
+ self.app.route('/a2a/tasks/sendSubscribe', methods=['POST'])(self.handle_a2a_task_send_subscribe)
204
+ self.app.route('/a2a/tasks/get', methods=['POST'])(self.handle_a2a_task_get)
205
+ self.app.route('/a2a/tasks/cancel', methods=['POST'])(self.handle_a2a_task_cancel)
206
+ self.app.route('/a2a/tasks/pushNotification/set', methods=['POST'])(self.handle_a2a_push_notification)
180
207
 
181
208
  self.register_additional_routes()
182
209
 
@@ -1037,11 +1064,8 @@ if __name__ == "__main__":
1037
1064
 
1038
1065
  try:
1039
1066
  # Process the request through the server
1040
- await server.run(
1041
- read_messages(),
1042
- write_queue,
1043
- InitializationOptions() if InitializationOptions else None
1044
- )
1067
+ # Use the server's run method with HTTP transport
1068
+ await server.run()
1045
1069
  except Exception as e:
1046
1070
  log.error(f"Error processing MCP request: {e}")
1047
1071
  await write_queue.put(None)
@@ -1059,15 +1083,8 @@ if __name__ == "__main__":
1059
1083
  loop = asyncio.new_event_loop()
1060
1084
  asyncio.set_event_loop(loop)
1061
1085
  try:
1062
- responses = loop.run_until_complete(process_request())
1063
-
1064
- # Parse and return the response
1065
- if responses:
1066
- # The response should be a single JSON-RPC response
1067
- response_data = json_module.loads(responses[0])
1068
- return jsonify(response_data)
1069
- else:
1070
- return jsonify({"error": "No response from MCP server"}), 500
1086
+ response_data = loop.run_until_complete(process_request())
1087
+ return jsonify(response_data)
1071
1088
 
1072
1089
  except Exception as e:
1073
1090
  log.error(f"MCP server error: {str(e)}")
@@ -1101,4 +1118,224 @@ if __name__ == "__main__":
1101
1118
  "transport": "http",
1102
1119
  "endpoint": "/mcp",
1103
1120
  "tools": ["vac_stream", "vac_query"] if self.vac_interpreter else ["vac_stream"]
1104
- })
1121
+ })
1122
+
1123
+ def _get_or_create_a2a_agent(self):
1124
+ """Get or create the A2A agent instance with current request context."""
1125
+ if not self.enable_a2a_agent or not VACA2AAgent:
1126
+ return None
1127
+
1128
+ if self.vac_a2a_agent is None:
1129
+ # Extract base URL from current request
1130
+ base_url = request.url_root.rstrip('/')
1131
+
1132
+ self.vac_a2a_agent = VACA2AAgent(
1133
+ base_url=base_url,
1134
+ stream_interpreter=self.stream_interpreter,
1135
+ vac_interpreter=self.vac_interpreter,
1136
+ vac_names=self.a2a_vac_names
1137
+ )
1138
+
1139
+ return self.vac_a2a_agent
1140
+
1141
+ def handle_a2a_agent_card(self):
1142
+ """Handle A2A agent card discovery request."""
1143
+ agent = self._get_or_create_a2a_agent()
1144
+ if not agent:
1145
+ return jsonify({"error": "A2A agent not enabled"}), 501
1146
+
1147
+ return jsonify(agent.get_agent_card())
1148
+
1149
+ def handle_a2a_task_send(self):
1150
+ """Handle A2A task send request."""
1151
+ agent = self._get_or_create_a2a_agent()
1152
+ if not agent:
1153
+ return jsonify({"error": "A2A agent not enabled"}), 501
1154
+
1155
+ try:
1156
+ data = request.get_json()
1157
+ if not data:
1158
+ return jsonify({
1159
+ "jsonrpc": "2.0",
1160
+ "error": {
1161
+ "code": -32700,
1162
+ "message": "Parse error: Invalid JSON"
1163
+ },
1164
+ "id": None
1165
+ }), 400
1166
+
1167
+ # Run async handler
1168
+ loop = asyncio.new_event_loop()
1169
+ asyncio.set_event_loop(loop)
1170
+ try:
1171
+ response = loop.run_until_complete(agent.handle_task_send(data))
1172
+ return jsonify(response)
1173
+ finally:
1174
+ loop.close()
1175
+
1176
+ except Exception as e:
1177
+ log.error(f"A2A task send error: {e}")
1178
+ return jsonify({
1179
+ "jsonrpc": "2.0",
1180
+ "error": {
1181
+ "code": -32603,
1182
+ "message": f"Internal error: {str(e)}"
1183
+ },
1184
+ "id": data.get("id") if 'data' in locals() else None
1185
+ }), 500
1186
+
1187
+ def handle_a2a_task_send_subscribe(self):
1188
+ """Handle A2A task send with subscription (SSE)."""
1189
+ agent = self._get_or_create_a2a_agent()
1190
+ if not agent:
1191
+ return jsonify({"error": "A2A agent not enabled"}), 501
1192
+
1193
+ try:
1194
+ data = request.get_json()
1195
+ if not data:
1196
+ def error_generator():
1197
+ yield "data: {\"error\": \"Parse error: Invalid JSON\"}\n\n"
1198
+
1199
+ return Response(error_generator(), content_type='text/event-stream')
1200
+
1201
+ # Create async generator for SSE
1202
+ async def sse_generator():
1203
+ async for chunk in agent.handle_task_send_subscribe(data):
1204
+ yield chunk
1205
+
1206
+ def sync_generator():
1207
+ loop = asyncio.new_event_loop()
1208
+ asyncio.set_event_loop(loop)
1209
+ try:
1210
+ async_gen = sse_generator()
1211
+ while True:
1212
+ try:
1213
+ chunk = loop.run_until_complete(async_gen.__anext__())
1214
+ yield chunk
1215
+ except StopAsyncIteration:
1216
+ break
1217
+ finally:
1218
+ loop.close()
1219
+
1220
+ return Response(sync_generator(), content_type='text/event-stream')
1221
+
1222
+ except Exception as e:
1223
+ log.error(f"A2A task send subscribe error: {e}")
1224
+ def error_generator(err):
1225
+ yield f"data: {{\"error\": \"Internal error: {str(err)}\"}}\n\n"
1226
+
1227
+ return Response(error_generator(e), content_type='text/event-stream')
1228
+
1229
+ def handle_a2a_task_get(self):
1230
+ """Handle A2A task get request."""
1231
+ agent = self._get_or_create_a2a_agent()
1232
+ if not agent:
1233
+ return jsonify({"error": "A2A agent not enabled"}), 501
1234
+
1235
+ try:
1236
+ data = request.get_json()
1237
+ if not data:
1238
+ return jsonify({
1239
+ "jsonrpc": "2.0",
1240
+ "error": {
1241
+ "code": -32700,
1242
+ "message": "Parse error: Invalid JSON"
1243
+ },
1244
+ "id": None
1245
+ }), 400
1246
+
1247
+ # Run async handler
1248
+ loop = asyncio.new_event_loop()
1249
+ asyncio.set_event_loop(loop)
1250
+ try:
1251
+ response = loop.run_until_complete(agent.handle_task_get(data))
1252
+ return jsonify(response)
1253
+ finally:
1254
+ loop.close()
1255
+
1256
+ except Exception as e:
1257
+ log.error(f"A2A task get error: {e}")
1258
+ return jsonify({
1259
+ "jsonrpc": "2.0",
1260
+ "error": {
1261
+ "code": -32603,
1262
+ "message": f"Internal error: {str(e)}"
1263
+ },
1264
+ "id": data.get("id") if 'data' in locals() else None
1265
+ }), 500
1266
+
1267
+ def handle_a2a_task_cancel(self):
1268
+ """Handle A2A task cancel request."""
1269
+ agent = self._get_or_create_a2a_agent()
1270
+ if not agent:
1271
+ return jsonify({"error": "A2A agent not enabled"}), 501
1272
+
1273
+ try:
1274
+ data = request.get_json()
1275
+ if not data:
1276
+ return jsonify({
1277
+ "jsonrpc": "2.0",
1278
+ "error": {
1279
+ "code": -32700,
1280
+ "message": "Parse error: Invalid JSON"
1281
+ },
1282
+ "id": None
1283
+ }), 400
1284
+
1285
+ # Run async handler
1286
+ loop = asyncio.new_event_loop()
1287
+ asyncio.set_event_loop(loop)
1288
+ try:
1289
+ response = loop.run_until_complete(agent.handle_task_cancel(data))
1290
+ return jsonify(response)
1291
+ finally:
1292
+ loop.close()
1293
+
1294
+ except Exception as e:
1295
+ log.error(f"A2A task cancel error: {e}")
1296
+ return jsonify({
1297
+ "jsonrpc": "2.0",
1298
+ "error": {
1299
+ "code": -32603,
1300
+ "message": f"Internal error: {str(e)}"
1301
+ },
1302
+ "id": data.get("id") if 'data' in locals() else None
1303
+ }), 500
1304
+
1305
+ def handle_a2a_push_notification(self):
1306
+ """Handle A2A push notification settings."""
1307
+ agent = self._get_or_create_a2a_agent()
1308
+ if not agent:
1309
+ return jsonify({"error": "A2A agent not enabled"}), 501
1310
+
1311
+ try:
1312
+ data = request.get_json()
1313
+ if not data:
1314
+ return jsonify({
1315
+ "jsonrpc": "2.0",
1316
+ "error": {
1317
+ "code": -32700,
1318
+ "message": "Parse error: Invalid JSON"
1319
+ },
1320
+ "id": None
1321
+ }), 400
1322
+
1323
+ # Run async handler
1324
+ loop = asyncio.new_event_loop()
1325
+ asyncio.set_event_loop(loop)
1326
+ try:
1327
+ response = loop.run_until_complete(agent.handle_push_notification_set(data))
1328
+ return jsonify(response)
1329
+ finally:
1330
+ loop.close()
1331
+
1332
+ except Exception as e:
1333
+ log.error(f"A2A push notification error: {e}")
1334
+ return jsonify({
1335
+ "jsonrpc": "2.0",
1336
+ "error": {
1337
+ "code": -32603,
1338
+ "message": f"Internal error: {str(e)}"
1339
+ },
1340
+ "id": data.get("id") if 'data' in locals() else None
1341
+ }), 500
sunholo/mcp/__init__.py CHANGED
@@ -14,7 +14,16 @@
14
14
 
15
15
  """MCP (Model Context Protocol) integration for Sunholo."""
16
16
 
17
- from .mcp_manager import MCPClientManager
18
- from .vac_mcp_server import VACMCPServer
17
+ try:
18
+ from .mcp_manager import MCPClientManager
19
+ except ImportError as e:
20
+ print(f"Warning: MCPClientManager not available - {e}")
21
+ MCPClientManager = None
22
+
23
+ try:
24
+ from .vac_mcp_server import VACMCPServer
25
+ except ImportError as e:
26
+ print(f"Warning: VACMCPServer not available - {e}")
27
+ VACMCPServer = None
19
28
 
20
29
  __all__ = ['MCPClientManager', 'VACMCPServer']
@@ -4,11 +4,34 @@ This shows how to integrate MCP servers with your Flask/VACRoutes application.
4
4
  """
5
5
 
6
6
  from typing import Dict, Any, List, Optional
7
+ import asyncio
7
8
 
8
- # Official MCP imports
9
- from mcp import StdioServerParameters, ClientSession
10
- from mcp.client.stdio import stdio_client
11
- from mcp.types import Tool, Resource, TextContent, CallToolResult
9
+ # MCP SDK imports - try different import paths
10
+ try:
11
+ from mcp.client.stdio import StdioClientTransport
12
+ from mcp.client.session import ClientSession
13
+ except ImportError:
14
+ try:
15
+ # Alternative import paths
16
+ from mcp.client import StdioClientTransport, ClientSession
17
+ except ImportError:
18
+ try:
19
+ # Another alternative
20
+ from mcp import StdioClientTransport, ClientSession
21
+ except ImportError:
22
+ StdioClientTransport = None
23
+ ClientSession = None
24
+
25
+ try:
26
+ from mcp.types import Tool, Resource, TextContent, CallToolResult
27
+ except ImportError:
28
+ try:
29
+ from mcp import Tool, Resource, TextContent, CallToolResult
30
+ except ImportError:
31
+ Tool = None
32
+ Resource = None
33
+ TextContent = None
34
+ CallToolResult = None
12
35
 
13
36
 
14
37
  class MCPClientManager:
@@ -23,41 +46,42 @@ class MCPClientManager:
23
46
  if server_name in self.sessions:
24
47
  return self.sessions[server_name]
25
48
 
26
- # Create server parameters
27
- server_params = StdioServerParameters(
49
+ if not StdioClientTransport or not ClientSession:
50
+ raise ImportError("MCP client dependencies not available")
51
+
52
+ # Create transport and session
53
+ transport = StdioClientTransport(
28
54
  command=command,
29
55
  args=args or []
30
56
  )
57
+ session = ClientSession(transport)
58
+ await session.initialize()
31
59
 
32
- # Connect to the server
33
- async with stdio_client(server_params) as (read, write):
34
- # Create and initialize client session directly
35
- session = ClientSession(read, write)
36
- await session.initialize()
37
- self.sessions[server_name] = session
38
- self.server_configs[server_name] = {
39
- "command": command,
40
- "args": args
41
- }
42
- return session
60
+ self.sessions[server_name] = session
61
+ self.server_configs[server_name] = {
62
+ "command": command,
63
+ "args": args
64
+ }
65
+ return session
43
66
 
44
67
  async def list_tools(self, server_name: Optional[str] = None) -> List[Tool]:
45
68
  """List available tools from one or all connected servers."""
46
69
  if server_name:
47
70
  session = self.sessions.get(server_name)
48
71
  if session:
49
- return await session.list_tools()
72
+ result = await session.list_tools()
73
+ return result.tools
50
74
  return []
51
75
 
52
76
  # List from all servers
53
77
  all_tools = []
54
78
  for name, session in self.sessions.items():
55
- tools = await session.list_tools()
79
+ result = await session.list_tools()
56
80
  # Add server name to tool metadata
57
- for tool in tools:
81
+ for tool in result.tools:
58
82
  tool.metadata = tool.metadata or {}
59
83
  tool.metadata["server"] = name
60
- all_tools.extend(tools)
84
+ all_tools.extend(result.tools)
61
85
  return all_tools
62
86
 
63
87
  async def call_tool(self, server_name: str, tool_name: str, arguments: Dict[str, Any]) -> CallToolResult:
@@ -67,7 +91,13 @@ class MCPClientManager:
67
91
  raise ValueError(f"Not connected to server: {server_name}")
68
92
 
69
93
  # Call the tool
70
- result = await session.call_tool(tool_name, arguments)
94
+ try:
95
+ from mcp.types import CallToolRequest
96
+ request = CallToolRequest(name=tool_name, arguments=arguments)
97
+ result = await session.call_tool(request)
98
+ except ImportError:
99
+ # Try direct call if Request types not available
100
+ result = await session.call_tool(tool_name, arguments)
71
101
  return result
72
102
 
73
103
  async def list_resources(self, server_name: Optional[str] = None) -> List[Resource]:
@@ -75,17 +105,18 @@ class MCPClientManager:
75
105
  if server_name:
76
106
  session = self.sessions.get(server_name)
77
107
  if session:
78
- return await session.list_resources()
108
+ result = await session.list_resources()
109
+ return result.resources
79
110
  return []
80
111
 
81
112
  # List from all servers
82
113
  all_resources = []
83
114
  for name, session in self.sessions.items():
84
- resources = await session.list_resources()
85
- for resource in resources:
115
+ result = await session.list_resources()
116
+ for resource in result.resources:
86
117
  resource.metadata = resource.metadata or {}
87
118
  resource.metadata["server"] = name
88
- all_resources.extend(resources)
119
+ all_resources.extend(result.resources)
89
120
  return all_resources
90
121
 
91
122
  async def read_resource(self, server_name: str, uri: str) -> List[TextContent]:
@@ -94,5 +125,12 @@ class MCPClientManager:
94
125
  if not session:
95
126
  raise ValueError(f"Not connected to server: {server_name}")
96
127
 
97
- result = await session.read_resource(uri)
98
- return result.contents
128
+ try:
129
+ from mcp.types import ReadResourceRequest
130
+ request = ReadResourceRequest(uri=uri)
131
+ result = await session.read_resource(request)
132
+ except ImportError:
133
+ # Try direct call if Request types not available
134
+ result = await session.read_resource(uri)
135
+
136
+ return result.contents if hasattr(result, 'contents') else result
@@ -22,13 +22,8 @@ import json
22
22
  import asyncio
23
23
  from functools import partial
24
24
 
25
- try:
26
- from mcp.server import Server
27
- from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
28
- except ImportError:
29
- Server = None
30
- Tool = None
31
- TextContent = None
25
+ from mcp.server import Server
26
+ from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
32
27
 
33
28
  from ..custom_logging import log
34
29
  from ..streaming import start_streaming_chat_async
@@ -45,8 +40,7 @@ class VACMCPServer:
45
40
  stream_interpreter: The streaming interpreter function
46
41
  vac_interpreter: The static VAC interpreter function (optional)
47
42
  """
48
- if Server is None:
49
- raise ImportError("MCP server requires `pip install sunholo[anthropic]`")
43
+ # MCP server is always available with current SDK
50
44
 
51
45
  self.stream_interpreter = stream_interpreter
52
46
  self.vac_interpreter = vac_interpreter
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.143.1
3
+ Version: 0.143.7
4
4
  Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -15,9 +15,10 @@ Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.10
18
+ Requires-Python: >=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE.txt
21
+ Requires-Dist: a2a-python>=0.0.1
21
22
  Requires-Dist: aiohttp
22
23
  Requires-Dist: flask>=3.1.0
23
24
  Requires-Dist: google-auth
@@ -126,6 +127,7 @@ Requires-Dist: pytesseract; extra == "pipeline"
126
127
  Requires-Dist: tabulate; extra == "pipeline"
127
128
  Requires-Dist: unstructured[all-docs,local-inference]; extra == "pipeline"
128
129
  Provides-Extra: gcp
130
+ Requires-Dist: a2a-python; extra == "gcp"
129
131
  Requires-Dist: aiofiles; extra == "gcp"
130
132
  Requires-Dist: anthropic[vertex]; extra == "gcp"
131
133
  Requires-Dist: google-api-python-client; extra == "gcp"
@@ -1,6 +1,10 @@
1
- sunholo/__init__.py,sha256=InRbX4V0-qdNHo9zYH3GEye7ASLR6LX8-SMvPV4Jsaw,1212
1
+ sunholo/__init__.py,sha256=_zHD01JyUVc-kGqm_bWtWxytec3b8-25fm5vK5j_2VU,1256
2
2
  sunholo/custom_logging.py,sha256=JXZTnXp_DixP3jwYfKw4LYRDS9IuTq7ctCgfZbI2rxA,22023
3
3
  sunholo/langchain_types.py,sha256=uZ4zvgej_f7pLqjtu4YP7qMC_eZD5ym_5x4pyvA1Ih4,1834
4
+ sunholo/a2a/__init__.py,sha256=ohwR-3_toRUdyj3ACNCVPrd_V2lzR5tmL5Hwn1ChSSU,1049
5
+ sunholo/a2a/agent_card.py,sha256=TJOgZ5FZJGgapyFXzBIRnnocUWcfSLah7fjvl0fLYJ4,13387
6
+ sunholo/a2a/task_manager.py,sha256=Ox1oAHarqYdcWku_JFcYDt2pQG3gwfuBTO7WCakE-U0,17670
7
+ sunholo/a2a/vac_a2a_agent.py,sha256=vL-sQceVRBE9d3kjx9m8zGWK1S9J1sHUybAH3tSbVFg,13344
4
8
  sunholo/agents/__init__.py,sha256=AauG3l0y4r5Fzx1zJfZ634M4o-0o7B7J5T8k_gPvNqE,370
5
9
  sunholo/agents/chat_history.py,sha256=gRuIUyU-53A72Q17SmSgf6Ok3YO8hKAZhsc64976018,17782
6
10
  sunholo/agents/dispatch_to_qa.py,sha256=NHihwAoCJ5_Lk11e_jZnucVUGQyZHCB-YpkfMHBCpQk,8882
@@ -14,7 +18,7 @@ sunholo/agents/fastapi/base.py,sha256=W-cyF8ZDUH40rc-c-Apw3-_8IIi2e4Y9qRtnoVnsc1
14
18
  sunholo/agents/fastapi/qna_routes.py,sha256=lKHkXPmwltu9EH3RMwmD153-J6pE7kWQ4BhBlV3to-s,3864
15
19
  sunholo/agents/flask/__init__.py,sha256=dEoByI3gDNUOjpX1uVKP7uPjhfFHJubbiaAv3xLopnk,63
16
20
  sunholo/agents/flask/base.py,sha256=vnpxFEOnCmt9humqj-jYPLfJcdwzsop9NorgkJ-tSaU,1756
17
- sunholo/agents/flask/vac_routes.py,sha256=P_swGAAEee2Z9BdOTQEFza9aI78ezCVtF1MNNi67pug,44599
21
+ sunholo/agents/flask/vac_routes.py,sha256=MvEsIWxrdeuRx-t_RfkodnJyJKP40aKaADWV4Yi3ijw,53375
18
22
  sunholo/archive/__init__.py,sha256=qNHWm5rGPVOlxZBZCpA1wTYPbalizRT7f8X4rs2t290,31
19
23
  sunholo/archive/archive.py,sha256=PxVfDtO2_2ZEEbnhXSCbXLdeoHoQVImo4y3Jr2XkCFY,1204
20
24
  sunholo/auth/__init__.py,sha256=TeP-OY0XGxYV_8AQcVGoh35bvyWhNUcMRfhuD5l44Sk,91
@@ -110,10 +114,10 @@ sunholo/llamaindex/llamaindex_class.py,sha256=PnpPoc7LpP7xvKIXYu-UvI4ehj67pGhE1E
110
114
  sunholo/llamaindex/user_history.py,sha256=ZtkecWuF9ORduyGB8kF8gP66bm9DdvCI-ZiK6Kt-cSE,2265
111
115
  sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
116
  sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
113
- sunholo/mcp/__init__.py,sha256=GmKB8Z0ecXUzpuzYEOXSI0aaZP7LZJc7yTCMEFdAmi4,791
117
+ sunholo/mcp/__init__.py,sha256=Bi0ZYMvWuf1AL_QSrMAREVVdTZFiIokGwrytBXKBJyc,1028
114
118
  sunholo/mcp/cli.py,sha256=d24nnVzhZYz4AWgTqmN-qjKG4rPbf8RhdmEOHZkBHy8,10570
115
- sunholo/mcp/mcp_manager.py,sha256=ooa_2JSNaKIuK9azEV0OaPVf5Mp_tL_DHaYEyFCmgKY,3752
116
- sunholo/mcp/vac_mcp_server.py,sha256=fRcerTqp_pTK8AVITpGPhrez_IaDuUDr4WnVUHPv8GM,10114
119
+ sunholo/mcp/mcp_manager.py,sha256=g75vv6XvM24U7uz366slE-p76Qs4AvVcsarHSF9qIvE,5061
120
+ sunholo/mcp/vac_mcp_server.py,sha256=DSSMsTeLU_1C2hHTvgLjZsg1chyMfb_S9ruCUw2f_dc,9969
117
121
  sunholo/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
122
  sunholo/ollama/ollama_images.py,sha256=H2cpcNu88R4TwyfL_nnqkQhdvBQ2FPCAy4Ok__0yQmo,2351
119
123
  sunholo/pubsub/__init__.py,sha256=DfTEk4zmCfqn6gFxRrqDO0pOrvXTDqH-medpgYO4PGw,117
@@ -171,9 +175,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
171
175
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
172
176
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
173
177
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
174
- sunholo-0.143.1.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
175
- sunholo-0.143.1.dist-info/METADATA,sha256=bvNatmW9CaH2Fyn2AN8np1IeiLF0MNciSg7gjihkYvI,18338
176
- sunholo-0.143.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
177
- sunholo-0.143.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
178
- sunholo-0.143.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
179
- sunholo-0.143.1.dist-info/RECORD,,
178
+ sunholo-0.143.7.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
179
+ sunholo-0.143.7.dist-info/METADATA,sha256=c1IhnVsC3r20UUvBImOmvOIirVrkuNY6Tj-xFuF3jr0,18413
180
+ sunholo-0.143.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
+ sunholo-0.143.7.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
182
+ sunholo-0.143.7.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
183
+ sunholo-0.143.7.dist-info/RECORD,,