sunholo 0.143.15__py3-none-any.whl → 0.144.0__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.
- sunholo/agents/dispatch_to_qa.py +16 -2
- sunholo/agents/fastapi/__init__.py +2 -1
- sunholo/agents/fastapi/vac_routes.py +1017 -0
- sunholo/mcp/cli.py +22 -338
- sunholo/mcp/cli_fastmcp.py +201 -0
- sunholo/mcp/stdio_http_bridge.py +7 -7
- sunholo/mcp/vac_mcp_server.py +4 -247
- sunholo/mcp/vac_mcp_server_fastmcp.py +193 -0
- sunholo/streaming/streaming.py +26 -6
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/METADATA +12 -10
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/RECORD +15 -12
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/WHEEL +0 -0
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/entry_points.txt +0 -0
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/licenses/LICENSE.txt +0 -0
- {sunholo-0.143.15.dist-info → sunholo-0.144.0.dist-info}/top_level.txt +0 -0
sunholo/mcp/vac_mcp_server.py
CHANGED
@@ -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
|
-
|
21
|
-
import
|
22
|
-
import asyncio
|
23
|
-
from functools import partial
|
20
|
+
# Import the FastMCP implementation
|
21
|
+
from .vac_mcp_server_fastmcp import VACMCPServer
|
24
22
|
|
25
|
-
|
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)
|
sunholo/streaming/streaming.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
199
|
-
|
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
|
-
|
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
|
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.
|
3
|
+
Version: 0.144.0
|
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:
|
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=
|
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=
|
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=cU5_t7VCbu1_6zrUBbyekEC3N7LUQCpJzr8nQGKInPM,42143
|
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=
|
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
|
-
sunholo/mcp/stdio_http_bridge.py,sha256=
|
121
|
-
sunholo/mcp/vac_mcp_server.py,sha256=
|
122
|
+
sunholo/mcp/stdio_http_bridge.py,sha256=IunHOtnjKAkRWef3SJnqnAL2r2qBRpCH2k_Q_y0Tdf8,3237
|
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=
|
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.
|
180
|
-
sunholo-0.
|
181
|
-
sunholo-0.
|
182
|
-
sunholo-0.
|
183
|
-
sunholo-0.
|
184
|
-
sunholo-0.
|
182
|
+
sunholo-0.144.0.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
183
|
+
sunholo-0.144.0.dist-info/METADATA,sha256=SMIB3kckTnKrsx4HfMgnhaMiEl3MOKZzguwC4tFdLec,18700
|
184
|
+
sunholo-0.144.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
185
|
+
sunholo-0.144.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
186
|
+
sunholo-0.144.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
187
|
+
sunholo-0.144.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|