sunholo 0.144.1__py3-none-any.whl → 0.144.3__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/fastapi/vac_routes.py +590 -134
- sunholo/mcp/extensible_mcp_server.py +271 -0
- sunholo/mcp/vac_mcp_server_fastmcp.py +74 -137
- sunholo/mcp/vac_tools.py +250 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/METADATA +1 -1
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/RECORD +10 -8
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/WHEEL +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/entry_points.txt +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/licenses/LICENSE.txt +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.3.dist-info}/top_level.txt +0 -0
sunholo/mcp/vac_tools.py
ADDED
@@ -0,0 +1,250 @@
|
|
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
|
+
Built-in VAC tools for MCP server integration.
|
17
|
+
Provides core Sunholo VAC functionality as MCP tools.
|
18
|
+
"""
|
19
|
+
|
20
|
+
import asyncio
|
21
|
+
import os
|
22
|
+
import sys
|
23
|
+
from typing import Dict, List, Optional, Any
|
24
|
+
|
25
|
+
try:
|
26
|
+
from fastmcp import FastMCP
|
27
|
+
FASTMCP_AVAILABLE = True
|
28
|
+
except ImportError:
|
29
|
+
FastMCP = None
|
30
|
+
FASTMCP_AVAILABLE = False
|
31
|
+
|
32
|
+
from ..custom_logging import log
|
33
|
+
|
34
|
+
# Import with fallbacks for optional dependencies
|
35
|
+
try:
|
36
|
+
from ..utils import ConfigManager
|
37
|
+
CONFIG_AVAILABLE = True
|
38
|
+
except ImportError:
|
39
|
+
ConfigManager = None
|
40
|
+
CONFIG_AVAILABLE = False
|
41
|
+
|
42
|
+
try:
|
43
|
+
from ..streaming import start_streaming_chat_async
|
44
|
+
STREAMING_AVAILABLE = True
|
45
|
+
except ImportError:
|
46
|
+
start_streaming_chat_async = None
|
47
|
+
STREAMING_AVAILABLE = False
|
48
|
+
|
49
|
+
|
50
|
+
def get_vac_config(vector_name: str = None) -> 'ConfigManager':
|
51
|
+
"""Get VAC configuration for the specified vector name."""
|
52
|
+
if not CONFIG_AVAILABLE:
|
53
|
+
raise ImportError("ConfigManager not available. Install sunholo with appropriate extras.")
|
54
|
+
|
55
|
+
default_vac = os.getenv("DEFAULT_VAC_NAME", "demo")
|
56
|
+
vac_name = vector_name or default_vac
|
57
|
+
vac_config_folder = os.getenv("VAC_CONFIG_FOLDER")
|
58
|
+
|
59
|
+
if vac_config_folder:
|
60
|
+
return ConfigManager(vac_name, config_folder=vac_config_folder)
|
61
|
+
else:
|
62
|
+
return ConfigManager(vac_name)
|
63
|
+
|
64
|
+
|
65
|
+
async def call_vac_async(question: str, vector_name: str, chat_history: List[Dict[str, str]] = None) -> str:
|
66
|
+
"""
|
67
|
+
Call VAC asynchronously using the streaming interface.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
question: The user's question
|
71
|
+
vector_name: Name of the VAC to query
|
72
|
+
chat_history: Previous conversation history
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
The VAC's response
|
76
|
+
"""
|
77
|
+
if not STREAMING_AVAILABLE:
|
78
|
+
raise ImportError("Streaming functionality not available. Install sunholo with streaming support.")
|
79
|
+
|
80
|
+
if chat_history is None:
|
81
|
+
chat_history = []
|
82
|
+
|
83
|
+
try:
|
84
|
+
config = get_vac_config(vector_name)
|
85
|
+
|
86
|
+
# Import the appropriate QNA function based on configuration
|
87
|
+
llm_str = config.vacConfig('llm')
|
88
|
+
|
89
|
+
if llm_str and 'anthropic' in llm_str.lower():
|
90
|
+
try:
|
91
|
+
from ..agents.langchain.vertex_genai2 import qna_async
|
92
|
+
except ImportError:
|
93
|
+
log.warning("Anthropic integration not available, falling back to default")
|
94
|
+
from ..agents.langchain.vertex_genai2 import qna_async
|
95
|
+
elif llm_str and 'openai' in llm_str.lower():
|
96
|
+
try:
|
97
|
+
from ..agents.langchain.vertex_genai2 import qna_async
|
98
|
+
except ImportError:
|
99
|
+
log.warning("OpenAI integration not available, falling back to default")
|
100
|
+
from ..agents.langchain.vertex_genai2 import qna_async
|
101
|
+
else:
|
102
|
+
# Default to vertex AI
|
103
|
+
from ..agents.langchain.vertex_genai2 import qna_async
|
104
|
+
|
105
|
+
# Use streaming interface to get response
|
106
|
+
full_response = ""
|
107
|
+
async for chunk in start_streaming_chat_async(
|
108
|
+
question=question,
|
109
|
+
vector_name=vector_name,
|
110
|
+
qna_func_async=qna_async,
|
111
|
+
chat_history=chat_history
|
112
|
+
):
|
113
|
+
if isinstance(chunk, dict) and 'answer' in chunk:
|
114
|
+
full_response = chunk['answer']
|
115
|
+
elif isinstance(chunk, str):
|
116
|
+
full_response += chunk
|
117
|
+
|
118
|
+
return full_response or "No response generated"
|
119
|
+
|
120
|
+
except Exception as e:
|
121
|
+
log.error(f"Error calling VAC {vector_name}: {str(e)}")
|
122
|
+
return f"Error: {str(e)}"
|
123
|
+
|
124
|
+
|
125
|
+
def register_vac_tools(server: 'FastMCP', registry: 'MCPToolRegistry' = None):
|
126
|
+
"""
|
127
|
+
Register built-in VAC tools with a FastMCP server.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
server: FastMCP server instance
|
131
|
+
registry: Optional registry to track tools
|
132
|
+
"""
|
133
|
+
|
134
|
+
@server.tool
|
135
|
+
async def vac_stream(
|
136
|
+
vector_name: str,
|
137
|
+
user_input: str,
|
138
|
+
chat_history: Optional[List[Dict[str, str]]] = None,
|
139
|
+
stream_wait_time: float = 7,
|
140
|
+
stream_timeout: float = 120
|
141
|
+
) -> str:
|
142
|
+
"""
|
143
|
+
Stream responses from a Sunholo VAC (Virtual Agent Computer).
|
144
|
+
|
145
|
+
Args:
|
146
|
+
vector_name: Name of the VAC to interact with
|
147
|
+
user_input: The user's question or input
|
148
|
+
chat_history: Previous conversation history
|
149
|
+
stream_wait_time: Time to wait between stream chunks (default: 7)
|
150
|
+
stream_timeout: Maximum time to wait for response (default: 120)
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
The streamed response from the VAC
|
154
|
+
"""
|
155
|
+
if chat_history is None:
|
156
|
+
chat_history = []
|
157
|
+
|
158
|
+
log.info(f"MCP streaming request for VAC '{vector_name}': {user_input}")
|
159
|
+
|
160
|
+
try:
|
161
|
+
return await call_vac_async(user_input, vector_name, chat_history)
|
162
|
+
except Exception as e:
|
163
|
+
log.error(f"Error in MCP VAC stream: {str(e)}")
|
164
|
+
return f"Error: {str(e)}"
|
165
|
+
|
166
|
+
@server.tool
|
167
|
+
async def vac_query(
|
168
|
+
vector_name: str,
|
169
|
+
user_input: str,
|
170
|
+
chat_history: Optional[List[Dict[str, str]]] = None
|
171
|
+
) -> str:
|
172
|
+
"""
|
173
|
+
Query a Sunholo VAC (non-streaming).
|
174
|
+
|
175
|
+
Args:
|
176
|
+
vector_name: Name of the VAC to interact with
|
177
|
+
user_input: The user's question or input
|
178
|
+
chat_history: Previous conversation history
|
179
|
+
|
180
|
+
Returns:
|
181
|
+
The response from the VAC
|
182
|
+
"""
|
183
|
+
if chat_history is None:
|
184
|
+
chat_history = []
|
185
|
+
|
186
|
+
log.info(f"MCP query request for VAC '{vector_name}': {user_input}")
|
187
|
+
|
188
|
+
try:
|
189
|
+
return await call_vac_async(user_input, vector_name, chat_history)
|
190
|
+
except Exception as e:
|
191
|
+
log.error(f"Error in MCP VAC query: {str(e)}")
|
192
|
+
return f"Error: {str(e)}"
|
193
|
+
|
194
|
+
@server.tool
|
195
|
+
def list_available_vacs() -> List[str]:
|
196
|
+
"""
|
197
|
+
List all available VAC configurations.
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
List of available VAC names
|
201
|
+
"""
|
202
|
+
try:
|
203
|
+
config = get_vac_config()
|
204
|
+
# Try to get available VACs from config
|
205
|
+
all_configs = config.load_all_configs()
|
206
|
+
if 'vacConfig' in all_configs and 'vac' in all_configs['vacConfig']:
|
207
|
+
return list(all_configs['vacConfig']['vac'].keys())
|
208
|
+
else:
|
209
|
+
return [os.getenv("DEFAULT_VAC_NAME", "demo")]
|
210
|
+
except Exception as e:
|
211
|
+
log.error(f"Error listing VACs: {str(e)}")
|
212
|
+
return [os.getenv("DEFAULT_VAC_NAME", "demo")]
|
213
|
+
|
214
|
+
@server.tool
|
215
|
+
def get_vac_info(vector_name: str) -> Dict[str, Any]:
|
216
|
+
"""
|
217
|
+
Get information about a specific VAC configuration.
|
218
|
+
|
219
|
+
Args:
|
220
|
+
vector_name: Name of the VAC to get info for
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
Dictionary containing VAC configuration details
|
224
|
+
"""
|
225
|
+
try:
|
226
|
+
config = get_vac_config(vector_name)
|
227
|
+
return {
|
228
|
+
"name": vector_name,
|
229
|
+
"llm": config.vacConfig('llm'),
|
230
|
+
"model": config.vacConfig('model'),
|
231
|
+
"description": f"VAC configuration for {vector_name}",
|
232
|
+
"available": True
|
233
|
+
}
|
234
|
+
except Exception as e:
|
235
|
+
log.error(f"Error getting VAC info for {vector_name}: {str(e)}")
|
236
|
+
return {
|
237
|
+
"name": vector_name,
|
238
|
+
"error": str(e),
|
239
|
+
"available": False
|
240
|
+
}
|
241
|
+
|
242
|
+
# Register tools in registry if provided
|
243
|
+
if registry:
|
244
|
+
# Extract the underlying function from FunctionTool objects
|
245
|
+
registry.register_tool("vac_stream", vac_stream.fn if hasattr(vac_stream, 'fn') else vac_stream)
|
246
|
+
registry.register_tool("vac_query", vac_query.fn if hasattr(vac_query, 'fn') else vac_query)
|
247
|
+
registry.register_tool("list_available_vacs", list_available_vacs.fn if hasattr(list_available_vacs, 'fn') else list_available_vacs)
|
248
|
+
registry.register_tool("get_vac_info", get_vac_info.fn if hasattr(get_vac_info, 'fn') else get_vac_info)
|
249
|
+
|
250
|
+
log.info("Registered built-in VAC tools with MCP server")
|
@@ -16,7 +16,7 @@ sunholo/agents/swagger.py,sha256=2tzGmpveUMmTREykZvVnDj3j295wyOMu7mUFDnXdY3c,106
|
|
16
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=
|
19
|
+
sunholo/agents/fastapi/vac_routes.py,sha256=s0wzGupaIsfQqJlDA1f8MXoVY8LGsHr0VP2xeRQOzM0,59860
|
20
20
|
sunholo/agents/flask/__init__.py,sha256=dEoByI3gDNUOjpX1uVKP7uPjhfFHJubbiaAv3xLopnk,63
|
21
21
|
sunholo/agents/flask/base.py,sha256=vnpxFEOnCmt9humqj-jYPLfJcdwzsop9NorgkJ-tSaU,1756
|
22
22
|
sunholo/agents/flask/vac_routes.py,sha256=kaPUDyIH5KhCgeCEtag97qErGVZfqpY1ZEiX3y1_r-s,57505
|
@@ -118,10 +118,12 @@ sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZB
|
|
118
118
|
sunholo/mcp/__init__.py,sha256=Bi0ZYMvWuf1AL_QSrMAREVVdTZFiIokGwrytBXKBJyc,1028
|
119
119
|
sunholo/mcp/cli.py,sha256=RyTrTBQMUaNMAZ1Nyh-XKb9qGnCA5hMxpKp5-9lqfrI,821
|
120
120
|
sunholo/mcp/cli_fastmcp.py,sha256=MWx7kJ4RHX0tTygWs247aOYr4bCKOwjnmccOPjcTVnc,6104
|
121
|
+
sunholo/mcp/extensible_mcp_server.py,sha256=docJT800-wJLApU6kEa3lwu9FHyy1yvtJIk8JI05Z3o,8960
|
121
122
|
sunholo/mcp/mcp_manager.py,sha256=g75vv6XvM24U7uz366slE-p76Qs4AvVcsarHSF9qIvE,5061
|
122
123
|
sunholo/mcp/stdio_http_bridge.py,sha256=IunHOtnjKAkRWef3SJnqnAL2r2qBRpCH2k_Q_y0Tdf8,3237
|
123
124
|
sunholo/mcp/vac_mcp_server.py,sha256=MotoCw5lDsxCeVtwh1499yGFku9w-78xXhGkIHTUo3w,838
|
124
|
-
sunholo/mcp/vac_mcp_server_fastmcp.py,sha256=
|
125
|
+
sunholo/mcp/vac_mcp_server_fastmcp.py,sha256=R95GDWRbVyAVqVhWVkJv9wd5gH1bWKiz69IU5rWPnIc,4451
|
126
|
+
sunholo/mcp/vac_tools.py,sha256=pTtHxPHD5k80wRnmJw1-RJK8L8IOOCWpGyTL1W2M934,8744
|
125
127
|
sunholo/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
126
128
|
sunholo/ollama/ollama_images.py,sha256=H2cpcNu88R4TwyfL_nnqkQhdvBQ2FPCAy4Ok__0yQmo,2351
|
127
129
|
sunholo/pubsub/__init__.py,sha256=DfTEk4zmCfqn6gFxRrqDO0pOrvXTDqH-medpgYO4PGw,117
|
@@ -179,9 +181,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
|
|
179
181
|
sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
|
180
182
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
181
183
|
sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
|
182
|
-
sunholo-0.144.
|
183
|
-
sunholo-0.144.
|
184
|
-
sunholo-0.144.
|
185
|
-
sunholo-0.144.
|
186
|
-
sunholo-0.144.
|
187
|
-
sunholo-0.144.
|
184
|
+
sunholo-0.144.3.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
185
|
+
sunholo-0.144.3.dist-info/METADATA,sha256=RObZwvRVw98mi-1RzJMaNtiLKZ0mui7wnNsaqf-oYmI,18700
|
186
|
+
sunholo-0.144.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
187
|
+
sunholo-0.144.3.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
188
|
+
sunholo-0.144.3.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
189
|
+
sunholo-0.144.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|