sunholo 0.143.16__py3-none-any.whl → 0.144.1__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.
@@ -13,254 +13,11 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """
16
- MCP Server wrapper for VAC functionality.
16
+ MCP Server wrapper for VAC functionality using FastMCP.
17
17
  This module exposes VAC streaming capabilities as MCP tools.
18
18
  """
19
19
 
20
- from typing import Any, Sequence, Dict, List, Optional, Callable
21
- import json
22
- import asyncio
23
- from functools import partial
20
+ # Import the FastMCP implementation
21
+ from .vac_mcp_server_fastmcp import VACMCPServer
24
22
 
25
- from mcp.server import Server
26
- from mcp.types import Tool, TextContent, ImageContent, EmbeddedResource
27
-
28
- from ..custom_logging import log
29
- from ..streaming import start_streaming_chat_async
30
-
31
-
32
- class VACMCPServer:
33
- """MCP Server that exposes VAC functionality as tools."""
34
-
35
- def __init__(self, stream_interpreter: Callable, vac_interpreter: Callable = None):
36
- """
37
- Initialize the VAC MCP Server.
38
-
39
- Args:
40
- stream_interpreter: The streaming interpreter function
41
- vac_interpreter: The static VAC interpreter function (optional)
42
- """
43
- # MCP server is always available with current SDK
44
-
45
- self.stream_interpreter = stream_interpreter
46
- self.vac_interpreter = vac_interpreter
47
- self.server = Server("sunholo-vac-server")
48
-
49
- # Set up handlers
50
- self._setup_handlers()
51
-
52
- def _setup_handlers(self):
53
- """Set up MCP protocol handlers."""
54
-
55
- @self.server.list_tools()
56
- async def list_tools() -> List[Tool]:
57
- """List available VAC tools."""
58
- tools = [
59
- Tool(
60
- name="vac_stream",
61
- description="Stream responses from a Sunholo VAC (Virtual Agent Computer)",
62
- inputSchema={
63
- "type": "object",
64
- "properties": {
65
- "vector_name": {
66
- "type": "string",
67
- "description": "Name of the VAC to interact with"
68
- },
69
- "user_input": {
70
- "type": "string",
71
- "description": "The user's question or input"
72
- },
73
- "chat_history": {
74
- "type": "array",
75
- "description": "Previous conversation history",
76
- "items": {
77
- "type": "object",
78
- "properties": {
79
- "human": {"type": "string"},
80
- "ai": {"type": "string"}
81
- }
82
- },
83
- "default": []
84
- },
85
- "stream_wait_time": {
86
- "type": "number",
87
- "description": "Time to wait between stream chunks",
88
- "default": 7
89
- },
90
- "stream_timeout": {
91
- "type": "number",
92
- "description": "Maximum time to wait for response",
93
- "default": 120
94
- }
95
- },
96
- "required": ["vector_name", "user_input"]
97
- }
98
- )
99
- ]
100
-
101
- # Add static VAC tool if interpreter is provided
102
- if self.vac_interpreter:
103
- tools.append(
104
- Tool(
105
- name="vac_query",
106
- description="Query a Sunholo VAC (non-streaming)",
107
- inputSchema={
108
- "type": "object",
109
- "properties": {
110
- "vector_name": {
111
- "type": "string",
112
- "description": "Name of the VAC to interact with"
113
- },
114
- "user_input": {
115
- "type": "string",
116
- "description": "The user's question or input"
117
- },
118
- "chat_history": {
119
- "type": "array",
120
- "description": "Previous conversation history",
121
- "items": {
122
- "type": "object",
123
- "properties": {
124
- "human": {"type": "string"},
125
- "ai": {"type": "string"}
126
- }
127
- },
128
- "default": []
129
- }
130
- },
131
- "required": ["vector_name", "user_input"]
132
- }
133
- )
134
- )
135
-
136
- return tools
137
-
138
- @self.server.call_tool()
139
- async def call_tool(
140
- name: str,
141
- arguments: Any
142
- ) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
143
- """Handle tool calls for VAC interactions."""
144
-
145
- if name == "vac_stream":
146
- return await self._handle_vac_stream(arguments)
147
- elif name == "vac_query" and self.vac_interpreter:
148
- return await self._handle_vac_query(arguments)
149
- else:
150
- raise ValueError(f"Unknown tool: {name}")
151
-
152
- async def _handle_vac_stream(self, arguments: Dict[str, Any]) -> Sequence[TextContent]:
153
- """Handle streaming VAC requests."""
154
- vector_name = arguments.get("vector_name")
155
- user_input = arguments.get("user_input")
156
- chat_history = arguments.get("chat_history", [])
157
- stream_wait_time = arguments.get("stream_wait_time", 7)
158
- stream_timeout = arguments.get("stream_timeout", 120)
159
-
160
- if not vector_name or not user_input:
161
- raise ValueError("Missing required arguments: vector_name and user_input")
162
-
163
- log.info(f"MCP streaming request for VAC '{vector_name}': {user_input}")
164
-
165
- try:
166
- # Collect streaming responses
167
- full_response = ""
168
-
169
- # Check if stream_interpreter is async
170
- if asyncio.iscoroutinefunction(self.stream_interpreter):
171
- async for chunk in start_streaming_chat_async(
172
- question=user_input,
173
- vector_name=vector_name,
174
- qna_func_async=self.stream_interpreter,
175
- chat_history=chat_history,
176
- wait_time=stream_wait_time,
177
- timeout=stream_timeout
178
- ):
179
- if isinstance(chunk, dict) and 'answer' in chunk:
180
- full_response = chunk['answer']
181
- elif isinstance(chunk, str):
182
- full_response += chunk
183
- else:
184
- # Fall back to sync version for non-async interpreters
185
- result = self.stream_interpreter(
186
- question=user_input,
187
- vector_name=vector_name,
188
- chat_history=chat_history
189
- )
190
- if isinstance(result, dict):
191
- full_response = result.get("answer", str(result))
192
- else:
193
- full_response = str(result)
194
-
195
- return [
196
- TextContent(
197
- type="text",
198
- text=full_response or "No response generated"
199
- )
200
- ]
201
-
202
- except Exception as e:
203
- log.error(f"Error in MCP VAC stream: {str(e)}")
204
- return [
205
- TextContent(
206
- type="text",
207
- text=f"Error: {str(e)}"
208
- )
209
- ]
210
-
211
- async def _handle_vac_query(self, arguments: Dict[str, Any]) -> Sequence[TextContent]:
212
- """Handle non-streaming VAC requests."""
213
- vector_name = arguments.get("vector_name")
214
- user_input = arguments.get("user_input")
215
- chat_history = arguments.get("chat_history", [])
216
-
217
- if not vector_name or not user_input:
218
- raise ValueError("Missing required arguments: vector_name and user_input")
219
-
220
- log.info(f"MCP query request for VAC '{vector_name}': {user_input}")
221
-
222
- try:
223
- # Run in executor if not async
224
- if asyncio.iscoroutinefunction(self.vac_interpreter):
225
- result = await self.vac_interpreter(
226
- question=user_input,
227
- vector_name=vector_name,
228
- chat_history=chat_history
229
- )
230
- else:
231
- loop = asyncio.get_event_loop()
232
- result = await loop.run_in_executor(
233
- None,
234
- partial(
235
- self.vac_interpreter,
236
- question=user_input,
237
- vector_name=vector_name,
238
- chat_history=chat_history
239
- )
240
- )
241
-
242
- # Extract answer from result
243
- if isinstance(result, dict):
244
- answer = result.get("answer", str(result))
245
- else:
246
- answer = str(result)
247
-
248
- return [
249
- TextContent(
250
- type="text",
251
- text=answer
252
- )
253
- ]
254
-
255
- except Exception as e:
256
- log.error(f"Error in MCP VAC query: {str(e)}")
257
- return [
258
- TextContent(
259
- type="text",
260
- text=f"Error: {str(e)}"
261
- )
262
- ]
263
-
264
- def get_server(self) -> Server:
265
- """Get the underlying MCP server instance."""
266
- return self.server
23
+ __all__ = ['VACMCPServer']
@@ -0,0 +1,193 @@
1
+ # Copyright [2024] [Holosun ApS]
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ FastMCP-based MCP Server wrapper for VAC functionality.
17
+ This module exposes VAC streaming capabilities as MCP tools using FastMCP.
18
+ """
19
+
20
+ from typing import Any, Callable, Dict, List, Optional
21
+ import asyncio
22
+ from functools import partial
23
+
24
+ from fastmcp import FastMCP
25
+
26
+ from ..custom_logging import log
27
+ from ..streaming import start_streaming_chat_async
28
+
29
+
30
+ class VACMCPServer:
31
+ """FastMCP Server that exposes VAC functionality as tools."""
32
+
33
+ def __init__(self, stream_interpreter: Callable, vac_interpreter: Callable = None):
34
+ """
35
+ Initialize the VAC MCP Server using FastMCP.
36
+
37
+ Args:
38
+ stream_interpreter: The streaming interpreter function
39
+ vac_interpreter: The static VAC interpreter function (optional)
40
+ """
41
+ self.stream_interpreter = stream_interpreter
42
+ self.vac_interpreter = vac_interpreter
43
+
44
+ # Initialize FastMCP server
45
+ self.server = FastMCP("sunholo-vac-server")
46
+
47
+ # Register tools
48
+ self._register_tools()
49
+
50
+ def _register_tools(self):
51
+ """Register VAC tools with FastMCP."""
52
+
53
+ @self.server.tool
54
+ async def vac_stream(
55
+ vector_name: str,
56
+ user_input: str,
57
+ chat_history: List[Dict[str, str]] = None,
58
+ stream_wait_time: float = 7,
59
+ stream_timeout: float = 120
60
+ ) -> str:
61
+ """
62
+ Stream responses from a Sunholo VAC (Virtual Agent Computer).
63
+
64
+ Args:
65
+ vector_name: Name of the VAC to interact with
66
+ user_input: The user's question or input
67
+ chat_history: Previous conversation history
68
+ stream_wait_time: Time to wait between stream chunks
69
+ stream_timeout: Maximum time to wait for response
70
+
71
+ Returns:
72
+ The streamed response from the VAC
73
+ """
74
+ if chat_history is None:
75
+ chat_history = []
76
+
77
+ log.info(f"MCP streaming request for VAC '{vector_name}': {user_input}")
78
+
79
+ try:
80
+ # Collect streaming responses
81
+ full_response = ""
82
+
83
+ # Check if stream_interpreter is async
84
+ if asyncio.iscoroutinefunction(self.stream_interpreter):
85
+ async for chunk in start_streaming_chat_async(
86
+ question=user_input,
87
+ vector_name=vector_name,
88
+ qna_func_async=self.stream_interpreter,
89
+ chat_history=chat_history,
90
+ wait_time=stream_wait_time,
91
+ timeout=stream_timeout
92
+ ):
93
+ if isinstance(chunk, dict) and 'answer' in chunk:
94
+ full_response = chunk['answer']
95
+ elif isinstance(chunk, str):
96
+ full_response += chunk
97
+ else:
98
+ # Fall back to sync version for non-async interpreters
99
+ result = self.stream_interpreter(
100
+ question=user_input,
101
+ vector_name=vector_name,
102
+ chat_history=chat_history
103
+ )
104
+ if isinstance(result, dict):
105
+ full_response = result.get("answer", str(result))
106
+ else:
107
+ full_response = str(result)
108
+
109
+ return full_response or "No response generated"
110
+
111
+ except Exception as e:
112
+ log.error(f"Error in MCP VAC stream: {str(e)}")
113
+ return f"Error: {str(e)}"
114
+
115
+ # Register non-streaming tool if interpreter is provided
116
+ if self.vac_interpreter:
117
+ @self.server.tool
118
+ async def vac_query(
119
+ vector_name: str,
120
+ user_input: str,
121
+ chat_history: List[Dict[str, str]] = None
122
+ ) -> str:
123
+ """
124
+ Query a Sunholo VAC (non-streaming).
125
+
126
+ Args:
127
+ vector_name: Name of the VAC to interact with
128
+ user_input: The user's question or input
129
+ chat_history: Previous conversation history
130
+
131
+ Returns:
132
+ The response from the VAC
133
+ """
134
+ if chat_history is None:
135
+ chat_history = []
136
+
137
+ log.info(f"MCP query request for VAC '{vector_name}': {user_input}")
138
+
139
+ try:
140
+ # Run in executor if not async
141
+ if asyncio.iscoroutinefunction(self.vac_interpreter):
142
+ result = await self.vac_interpreter(
143
+ question=user_input,
144
+ vector_name=vector_name,
145
+ chat_history=chat_history
146
+ )
147
+ else:
148
+ loop = asyncio.get_event_loop()
149
+ result = await loop.run_in_executor(
150
+ None,
151
+ partial(
152
+ self.vac_interpreter,
153
+ question=user_input,
154
+ vector_name=vector_name,
155
+ chat_history=chat_history
156
+ )
157
+ )
158
+
159
+ # Extract answer from result
160
+ if isinstance(result, dict):
161
+ answer = result.get("answer", str(result))
162
+ else:
163
+ answer = str(result)
164
+
165
+ return answer
166
+
167
+ except Exception as e:
168
+ log.error(f"Error in MCP VAC query: {str(e)}")
169
+ return f"Error: {str(e)}"
170
+
171
+ def get_server(self) -> FastMCP:
172
+ """Get the underlying FastMCP server instance."""
173
+ return self.server
174
+
175
+ def run(self, transport: str = "stdio", **kwargs):
176
+ """
177
+ Run the MCP server.
178
+
179
+ Args:
180
+ transport: Transport type ("stdio" or "http")
181
+ **kwargs: Additional arguments for the transport
182
+ """
183
+ self.server.run(transport=transport, **kwargs)
184
+
185
+ async def run_async(self, transport: str = "stdio", **kwargs):
186
+ """
187
+ Run the MCP server asynchronously.
188
+
189
+ Args:
190
+ transport: Transport type ("stdio" or "http")
191
+ **kwargs: Additional arguments for the transport
192
+ """
193
+ await self.server.run_async(transport=transport, **kwargs)
@@ -191,12 +191,22 @@ async def start_streaming_chat_async(question, vector_name, qna_func_async, chat
191
191
  yield content_to_send
192
192
  await content_buffer.async_clear()
193
193
 
194
- while not chat_callback_handler.stream_finished.is_set() and not stop_event.is_set():
194
+ # Continue streaming until timeout or stop event
195
+ start_time = asyncio.get_event_loop().time()
196
+ while not stop_event.is_set():
195
197
  try:
196
- await asyncio.wait_for(content_buffer.content_available.wait(), timeout=timeout)
198
+ # Wait for content with a short timeout to check conditions periodically
199
+ await asyncio.wait_for(content_buffer.content_available.wait(), timeout=1.0)
197
200
  except asyncio.TimeoutError:
198
- log.warning(f"Content production has timed out after {timeout} seconds")
199
- break
201
+ # Check if we should continue waiting
202
+ if chat_callback_handler.stream_finished.is_set() and chat_task.done():
203
+ # Stream is finished and task is complete, exit loop
204
+ break
205
+ elapsed = asyncio.get_event_loop().time() - start_time
206
+ if elapsed > timeout:
207
+ log.warning(f"Content production has timed out after {timeout} seconds")
208
+ break
209
+ continue
200
210
 
201
211
  content_to_send = await content_buffer.async_read()
202
212
  if content_to_send:
@@ -223,13 +233,23 @@ async def start_streaming_chat_async(question, vector_name, qna_func_async, chat
223
233
  else:
224
234
  log.info("Sending final full message plus sources...")
225
235
  try:
226
- final_result = result_queue.get_nowait()
236
+ # Use await with timeout instead of get_nowait to ensure we get the result
237
+ final_result = await asyncio.wait_for(result_queue.get(), timeout=5.0)
227
238
  final_yield = parse_output(final_result)
239
+ log.info(f"Got final result: {type(final_yield)}")
240
+ except asyncio.TimeoutError:
241
+ log.warning("Timeout waiting for final result from queue")
242
+ final_yield = ""
228
243
  except asyncio.QueueEmpty:
244
+ log.warning("Queue empty when trying to get final result")
229
245
  final_yield = ""
230
246
 
231
247
  # Match the non-async behavior - yield the parsed output directly, not as JSON
232
- yield final_yield
248
+ if final_yield: # Only yield if we have actual content
249
+ log.info(f"Yielding final_yield: type={type(final_yield)}, is_dict={isinstance(final_yield, dict)}, keys={list(final_yield.keys()) if isinstance(final_yield, dict) else 'N/A'}")
250
+ yield final_yield
251
+ else:
252
+ log.info("Final yield was empty, not yielding")
233
253
 
234
254
 
235
255
  def generate_proxy_stream(stream_to_f, user_input, vector_name, chat_history, generate_f_output, **kwargs):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.143.16
3
+ Version: 0.144.1
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
@@ -12,26 +12,22 @@ Classifier: Intended Audience :: Developers
12
12
  Classifier: Topic :: Software Development :: Build Tools
13
13
  Classifier: License :: OSI Approved :: Apache Software License
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.10
16
15
  Classifier: Programming Language :: Python :: 3.11
17
16
  Classifier: Programming Language :: Python :: 3.12
18
17
  Requires-Python: >=3.11
19
18
  Description-Content-Type: text/markdown
20
19
  License-File: LICENSE.txt
21
- Requires-Dist: a2a-python>=0.0.1
22
- Requires-Dist: aiohttp
23
- Requires-Dist: flask>=3.1.0
24
20
  Requires-Dist: google-auth
25
- Requires-Dist: mcp>=1.1.1
26
21
  Requires-Dist: pydantic
27
- Requires-Dist: pytest-asyncio>=1.0.0
28
22
  Requires-Dist: requests
29
23
  Requires-Dist: ruamel.yaml
30
24
  Requires-Dist: tenacity
31
25
  Provides-Extra: test
32
26
  Requires-Dist: pytest; extra == "test"
27
+ Requires-Dist: pytest-asyncio>=1.0.0; extra == "test"
33
28
  Requires-Dist: pytest-cov; extra == "test"
34
29
  Provides-Extra: all
30
+ Requires-Dist: a2a-python>=0.0.1; extra == "all"
35
31
  Requires-Dist: aiofiles; extra == "all"
36
32
  Requires-Dist: aiohttp; extra == "all"
37
33
  Requires-Dist: anthropic[vertex]; extra == "all"
@@ -39,7 +35,8 @@ Requires-Dist: asyncpg; extra == "all"
39
35
  Requires-Dist: azure-identity; extra == "all"
40
36
  Requires-Dist: azure-storage-blob; extra == "all"
41
37
  Requires-Dist: fastapi; extra == "all"
42
- Requires-Dist: flask; extra == "all"
38
+ Requires-Dist: fastmcp>=2.12.0; extra == "all"
39
+ Requires-Dist: flask>=3.1.0; extra == "all"
43
40
  Requires-Dist: google-auth; extra == "all"
44
41
  Requires-Dist: google-auth-httplib2; extra == "all"
45
42
  Requires-Dist: google-auth-oauthlib; extra == "all"
@@ -82,6 +79,9 @@ Requires-Dist: psutil; extra == "all"
82
79
  Requires-Dist: psycopg2-binary; extra == "all"
83
80
  Requires-Dist: pydantic; extra == "all"
84
81
  Requires-Dist: pypdf; extra == "all"
82
+ Requires-Dist: pytest; extra == "all"
83
+ Requires-Dist: pytest-asyncio>=1.0.0; extra == "all"
84
+ Requires-Dist: pytest-cov; extra == "all"
85
85
  Requires-Dist: python-hcl2; extra == "all"
86
86
  Requires-Dist: python-socketio; extra == "all"
87
87
  Requires-Dist: pytesseract; extra == "all"
@@ -127,7 +127,7 @@ Requires-Dist: pytesseract; extra == "pipeline"
127
127
  Requires-Dist: tabulate; extra == "pipeline"
128
128
  Requires-Dist: unstructured[all-docs,local-inference]; extra == "pipeline"
129
129
  Provides-Extra: gcp
130
- Requires-Dist: a2a-python; extra == "gcp"
130
+ Requires-Dist: a2a-python>=0.0.1; extra == "gcp"
131
131
  Requires-Dist: aiofiles; extra == "gcp"
132
132
  Requires-Dist: anthropic[vertex]; extra == "gcp"
133
133
  Requires-Dist: google-api-python-client; extra == "gcp"
@@ -157,14 +157,16 @@ Provides-Extra: openai
157
157
  Requires-Dist: langchain-openai>=0.3.2; extra == "openai"
158
158
  Requires-Dist: tiktoken; extra == "openai"
159
159
  Provides-Extra: anthropic
160
+ Requires-Dist: fastmcp>=2.12.0; extra == "anthropic"
160
161
  Requires-Dist: langchain-anthropic>=0.1.23; extra == "anthropic"
161
162
  Requires-Dist: mcp>=1.1.1; extra == "anthropic"
162
163
  Provides-Extra: tools
163
164
  Requires-Dist: openapi-spec-validator; extra == "tools"
164
165
  Requires-Dist: playwright; extra == "tools"
165
166
  Provides-Extra: http
167
+ Requires-Dist: aiohttp; extra == "http"
166
168
  Requires-Dist: fastapi; extra == "http"
167
- Requires-Dist: flask; extra == "http"
169
+ Requires-Dist: flask>=3.1.0; extra == "http"
168
170
  Requires-Dist: gunicorn; extra == "http"
169
171
  Requires-Dist: httpcore; extra == "http"
170
172
  Requires-Dist: httpx; extra == "http"
@@ -7,15 +7,16 @@ sunholo/a2a/task_manager.py,sha256=Ox1oAHarqYdcWku_JFcYDt2pQG3gwfuBTO7WCakE-U0,1
7
7
  sunholo/a2a/vac_a2a_agent.py,sha256=vL-sQceVRBE9d3kjx9m8zGWK1S9J1sHUybAH3tSbVFg,13344
8
8
  sunholo/agents/__init__.py,sha256=AauG3l0y4r5Fzx1zJfZ634M4o-0o7B7J5T8k_gPvNqE,370
9
9
  sunholo/agents/chat_history.py,sha256=gRuIUyU-53A72Q17SmSgf6Ok3YO8hKAZhsc64976018,17782
10
- sunholo/agents/dispatch_to_qa.py,sha256=NHihwAoCJ5_Lk11e_jZnucVUGQyZHCB-YpkfMHBCpQk,8882
10
+ sunholo/agents/dispatch_to_qa.py,sha256=Q5i3S1GMRN_zbT15WLhkiZzGn2ebbGQJv5oqRGhmyBI,9243
11
11
  sunholo/agents/langserve.py,sha256=C46ph2mnygr6bdHijYWYyfQDI9ylAF0_9Kx2PfcCJpU,4414
12
12
  sunholo/agents/pubsub.py,sha256=TscZN_6am6DfaQkC-Yl18ZIBOoLE-0nDSiil6GpQEh4,1344
13
13
  sunholo/agents/route.py,sha256=mV8tGABbSqcg3PQL02MgQOs41gKEHLMyIJJJcTuFdbE,2988
14
14
  sunholo/agents/special_commands.py,sha256=-9reLRZtTFMuivY4JLk6FDLi5Pf6lphn4SJlSPuMVsE,6492
15
15
  sunholo/agents/swagger.py,sha256=2tzGmpveUMmTREykZvVnDj3j295wyOMu7mUFDnXdY3c,10671
16
- sunholo/agents/fastapi/__init__.py,sha256=S_pj4_bTUmDGoq_exaREHlOKThi0zTuGT0VZY0YfODQ,88
16
+ sunholo/agents/fastapi/__init__.py,sha256=f7x7kiEjaNyBiOwJHLJ4vdOiePqkXdI52sIAAHtS-ms,141
17
17
  sunholo/agents/fastapi/base.py,sha256=W-cyF8ZDUH40rc-c-Apw3-_8IIi2e4Y9qRtnoVnsc1Q,2521
18
18
  sunholo/agents/fastapi/qna_routes.py,sha256=lKHkXPmwltu9EH3RMwmD153-J6pE7kWQ4BhBlV3to-s,3864
19
+ sunholo/agents/fastapi/vac_routes.py,sha256=sK2FuJsOBgc8sIKtTOYuyhNql5JX63U_hMpQQ-nLcug,42985
19
20
  sunholo/agents/flask/__init__.py,sha256=dEoByI3gDNUOjpX1uVKP7uPjhfFHJubbiaAv3xLopnk,63
20
21
  sunholo/agents/flask/base.py,sha256=vnpxFEOnCmt9humqj-jYPLfJcdwzsop9NorgkJ-tSaU,1756
21
22
  sunholo/agents/flask/vac_routes.py,sha256=kaPUDyIH5KhCgeCEtag97qErGVZfqpY1ZEiX3y1_r-s,57505
@@ -115,10 +116,12 @@ sunholo/llamaindex/user_history.py,sha256=ZtkecWuF9ORduyGB8kF8gP66bm9DdvCI-ZiK6K
115
116
  sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
117
  sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
117
118
  sunholo/mcp/__init__.py,sha256=Bi0ZYMvWuf1AL_QSrMAREVVdTZFiIokGwrytBXKBJyc,1028
118
- sunholo/mcp/cli.py,sha256=zVbUDrOS1kvSMYJvq7PqzYnEwwzP1sGE0YWwhpVd_VM,12255
119
+ sunholo/mcp/cli.py,sha256=RyTrTBQMUaNMAZ1Nyh-XKb9qGnCA5hMxpKp5-9lqfrI,821
120
+ sunholo/mcp/cli_fastmcp.py,sha256=MWx7kJ4RHX0tTygWs247aOYr4bCKOwjnmccOPjcTVnc,6104
119
121
  sunholo/mcp/mcp_manager.py,sha256=g75vv6XvM24U7uz366slE-p76Qs4AvVcsarHSF9qIvE,5061
120
122
  sunholo/mcp/stdio_http_bridge.py,sha256=IunHOtnjKAkRWef3SJnqnAL2r2qBRpCH2k_Q_y0Tdf8,3237
121
- sunholo/mcp/vac_mcp_server.py,sha256=WcFOgN2_lyp1vfn-KcW4GewrBidkRYDx9gzEORV6rV8,10611
123
+ sunholo/mcp/vac_mcp_server.py,sha256=MotoCw5lDsxCeVtwh1499yGFku9w-78xXhGkIHTUo3w,838
124
+ sunholo/mcp/vac_mcp_server_fastmcp.py,sha256=Po8Uvs1iSYKNfB5Qz5B7URU9i4jEW70wPpJZDEbAO1U,7430
122
125
  sunholo/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
126
  sunholo/ollama/ollama_images.py,sha256=H2cpcNu88R4TwyfL_nnqkQhdvBQ2FPCAy4Ok__0yQmo,2351
124
127
  sunholo/pubsub/__init__.py,sha256=DfTEk4zmCfqn6gFxRrqDO0pOrvXTDqH-medpgYO4PGw,117
@@ -133,7 +136,7 @@ sunholo/streaming/__init__.py,sha256=MpbydI2UYo_adttPQFkxNM33b-QRyNEbrKJx0C2AGPc
133
136
  sunholo/streaming/content_buffer.py,sha256=bqPta3Q1tXI88Ngyj1kgPC-v4phhGm1nZURcuqQSGIQ,12537
134
137
  sunholo/streaming/langserve.py,sha256=hi7q8WY8DPKrALl9m_dOMxWOdE-iEuk7YW05SVDFIX8,6514
135
138
  sunholo/streaming/stream_lookup.py,sha256=hYg1DbdSE_QNJ8ZB-ynXJlWgvFjrGvwoUsGJu_E0pRQ,360
136
- sunholo/streaming/streaming.py,sha256=2KgiiCZl0nGZQDF1oO7gti3e3_cPlomLRkQGGMNj0qQ,17203
139
+ sunholo/streaming/streaming.py,sha256=P0zQoTIjOrW5-lNwZ1mrxtEqAVgFdINZ7tnwNGHQnwY,18390
137
140
  sunholo/summarise/__init__.py,sha256=MZk3dblUMODcPb1crq4v-Z508NrFIpkSWNf9FIO8BcU,38
138
141
  sunholo/summarise/summarise.py,sha256=UnycBVLLEXK1HitCOG2zW3XIyxMrw47xoVf6e2OC9A0,4150
139
142
  sunholo/templates/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -176,9 +179,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
176
179
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
177
180
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
178
181
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
179
- sunholo-0.143.16.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
180
- sunholo-0.143.16.dist-info/METADATA,sha256=U3OPOEIEC-Dvnjni6sM10RgsnYk5sczbbQD2cP252Yc,18502
181
- sunholo-0.143.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
182
- sunholo-0.143.16.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
183
- sunholo-0.143.16.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
184
- sunholo-0.143.16.dist-info/RECORD,,
182
+ sunholo-0.144.1.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
183
+ sunholo-0.144.1.dist-info/METADATA,sha256=ia-s14a5R5yxApJZGGqqeT2ebAlxEyc5AJhniD9xtxg,18700
184
+ sunholo-0.144.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
185
+ sunholo-0.144.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
186
+ sunholo-0.144.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
187
+ sunholo-0.144.1.dist-info/RECORD,,