sunholo 0.144.2__py3-none-any.whl → 0.144.4__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 +251 -14
- sunholo/mcp/__init__.py +10 -1
- sunholo/mcp/sse_utils.py +105 -0
- sunholo/mcp/vac_mcp_server_fastmcp.py +2 -1
- sunholo/mcp/vac_tools.py +7 -9
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/METADATA +1 -1
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/RECORD +11 -10
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/WHEEL +0 -0
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/entry_points.txt +0 -0
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/licenses/LICENSE.txt +0 -0
- {sunholo-0.144.2.dist-info → sunholo-0.144.4.dist-info}/top_level.txt +0 -0
@@ -113,31 +113,92 @@ class VACRoutesFastAPI:
|
|
113
113
|
|
114
114
|
## Basic Usage
|
115
115
|
|
116
|
+
### Simplified Setup (Recommended)
|
117
|
+
|
118
|
+
Use the helper method for automatic lifespan management:
|
119
|
+
|
116
120
|
```python
|
117
|
-
from fastapi import FastAPI
|
118
121
|
from sunholo.agents.fastapi import VACRoutesFastAPI
|
119
122
|
|
120
|
-
app = FastAPI()
|
121
|
-
|
122
123
|
async def my_stream_interpreter(question, vector_name, chat_history, callback, **kwargs):
|
123
124
|
# Your streaming VAC logic here
|
124
125
|
# Use callback.async_on_llm_new_token(token) for streaming
|
125
|
-
# Return final result with sources
|
126
126
|
return {"answer": "Response", "sources": []}
|
127
127
|
|
128
|
-
#
|
128
|
+
# Single call sets up everything with MCP server and proper lifespan management
|
129
|
+
app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(
|
130
|
+
title="My VAC Application",
|
131
|
+
stream_interpreter=my_stream_interpreter
|
132
|
+
# MCP server is automatically enabled when using this method
|
133
|
+
)
|
134
|
+
|
135
|
+
# Add custom endpoints if needed
|
136
|
+
@app.get("/custom")
|
137
|
+
async def custom_endpoint():
|
138
|
+
return {"message": "Hello"}
|
139
|
+
|
140
|
+
# Run the app
|
141
|
+
if __name__ == "__main__":
|
142
|
+
import uvicorn
|
143
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
144
|
+
```
|
145
|
+
|
146
|
+
### Manual Setup (Advanced)
|
147
|
+
|
148
|
+
For more control over lifespan management:
|
149
|
+
|
150
|
+
```python
|
151
|
+
from contextlib import asynccontextmanager
|
152
|
+
from fastapi import FastAPI
|
153
|
+
from sunholo.agents.fastapi import VACRoutesFastAPI
|
154
|
+
|
155
|
+
async def my_stream_interpreter(question, vector_name, chat_history, callback, **kwargs):
|
156
|
+
return {"answer": "Response", "sources": []}
|
157
|
+
|
158
|
+
# Define your app's lifespan
|
159
|
+
@asynccontextmanager
|
160
|
+
async def app_lifespan(app: FastAPI):
|
161
|
+
print("Starting up...")
|
162
|
+
yield
|
163
|
+
print("Shutting down...")
|
164
|
+
|
165
|
+
# Create temp app to get MCP lifespan
|
166
|
+
temp_app = FastAPI()
|
167
|
+
vac_routes_temp = VACRoutesFastAPI(
|
168
|
+
temp_app,
|
169
|
+
stream_interpreter=my_stream_interpreter,
|
170
|
+
enable_mcp_server=True
|
171
|
+
)
|
172
|
+
|
173
|
+
# Get MCP lifespan
|
174
|
+
mcp_lifespan = vac_routes_temp.get_mcp_lifespan()
|
175
|
+
|
176
|
+
# Combine lifespans
|
177
|
+
@asynccontextmanager
|
178
|
+
async def combined_lifespan(app: FastAPI):
|
179
|
+
async with app_lifespan(app):
|
180
|
+
if mcp_lifespan:
|
181
|
+
async with mcp_lifespan(app):
|
182
|
+
yield
|
183
|
+
else:
|
184
|
+
yield
|
185
|
+
|
186
|
+
# Create app with combined lifespan
|
187
|
+
app = FastAPI(title="My VAC Application", lifespan=combined_lifespan)
|
188
|
+
|
189
|
+
# Initialize VAC routes
|
129
190
|
vac_routes = VACRoutesFastAPI(
|
130
191
|
app=app,
|
131
192
|
stream_interpreter=my_stream_interpreter,
|
132
|
-
enable_mcp_server=True
|
193
|
+
enable_mcp_server=True
|
133
194
|
)
|
134
|
-
|
135
|
-
# Your FastAPI app now includes:
|
136
|
-
# - All VAC endpoints
|
137
|
-
# - MCP server at /mcp (for Claude Desktop/Code to connect)
|
138
|
-
# - Built-in VAC tools: vac_stream, vac_query, list_available_vacs, get_vac_info
|
139
195
|
```
|
140
196
|
|
197
|
+
Your FastAPI app now includes:
|
198
|
+
- All VAC endpoints
|
199
|
+
- MCP server at /mcp (for Claude Desktop/Code to connect)
|
200
|
+
- Built-in VAC tools: vac_stream, vac_query, list_available_vacs, get_vac_info
|
201
|
+
|
141
202
|
## Adding Custom MCP Tools
|
142
203
|
|
143
204
|
### Method 1: Using Decorators
|
@@ -416,6 +477,158 @@ class VACRoutesFastAPI:
|
|
416
477
|
|
417
478
|
self.register_routes()
|
418
479
|
|
480
|
+
@staticmethod
|
481
|
+
def create_app_with_mcp(
|
482
|
+
title: str = "VAC Application",
|
483
|
+
stream_interpreter: Optional[callable] = None,
|
484
|
+
vac_interpreter: Optional[callable] = None,
|
485
|
+
app_lifespan: Optional[callable] = None,
|
486
|
+
**kwargs
|
487
|
+
) -> tuple[FastAPI, 'VACRoutesFastAPI']:
|
488
|
+
"""
|
489
|
+
Helper method to create a FastAPI app with proper MCP lifespan management.
|
490
|
+
|
491
|
+
This method simplifies the setup process by handling the lifespan combination
|
492
|
+
automatically, avoiding the need for the double initialization pattern.
|
493
|
+
MCP server is automatically enabled when using this method.
|
494
|
+
|
495
|
+
Args:
|
496
|
+
title: Title for the FastAPI app
|
497
|
+
stream_interpreter: Streaming interpreter function
|
498
|
+
vac_interpreter: Non-streaming interpreter function
|
499
|
+
app_lifespan: Optional app lifespan context manager
|
500
|
+
**kwargs: Additional arguments passed to VACRoutesFastAPI (except enable_mcp_server)
|
501
|
+
|
502
|
+
Returns:
|
503
|
+
Tuple of (FastAPI app, VACRoutesFastAPI instance)
|
504
|
+
|
505
|
+
Example:
|
506
|
+
```python
|
507
|
+
from sunholo.agents.fastapi import VACRoutesFastAPI
|
508
|
+
|
509
|
+
async def my_interpreter(question, vector_name, chat_history, callback, **kwargs):
|
510
|
+
# Your logic here
|
511
|
+
return {"answer": "response", "sources": []}
|
512
|
+
|
513
|
+
# Single call to set up everything (MCP is automatically enabled)
|
514
|
+
app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(
|
515
|
+
title="My VAC App",
|
516
|
+
stream_interpreter=my_interpreter
|
517
|
+
)
|
518
|
+
|
519
|
+
# Add custom endpoints
|
520
|
+
@app.get("/custom")
|
521
|
+
async def custom_endpoint():
|
522
|
+
return {"message": "Custom endpoint"}
|
523
|
+
|
524
|
+
if __name__ == "__main__":
|
525
|
+
import uvicorn
|
526
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
527
|
+
```
|
528
|
+
"""
|
529
|
+
from contextlib import asynccontextmanager
|
530
|
+
|
531
|
+
# Default app lifespan if not provided
|
532
|
+
if app_lifespan is None:
|
533
|
+
@asynccontextmanager
|
534
|
+
async def app_lifespan(app: FastAPI):
|
535
|
+
yield
|
536
|
+
|
537
|
+
# Import here to avoid circular imports
|
538
|
+
if VACMCPServer:
|
539
|
+
from fastmcp import FastMCP
|
540
|
+
|
541
|
+
# Create MCP server directly to get its lifespan
|
542
|
+
mcp_server = FastMCP("sunholo-vac-fastapi-server")
|
543
|
+
|
544
|
+
# Register built-in VAC tools directly
|
545
|
+
from sunholo.mcp.vac_tools import register_vac_tools
|
546
|
+
register_vac_tools(mcp_server, None)
|
547
|
+
|
548
|
+
# Get the MCP app with path="" so when mounted at /mcp it's accessible at /mcp
|
549
|
+
mcp_app = mcp_server.http_app(path="", stateless_http=True)
|
550
|
+
|
551
|
+
# Create combined lifespan
|
552
|
+
@asynccontextmanager
|
553
|
+
async def combined_lifespan(app: FastAPI):
|
554
|
+
async with app_lifespan(app):
|
555
|
+
async with mcp_app.lifespan(app):
|
556
|
+
yield
|
557
|
+
|
558
|
+
# Create the actual app with combined lifespan
|
559
|
+
app = FastAPI(
|
560
|
+
title=title,
|
561
|
+
lifespan=combined_lifespan
|
562
|
+
)
|
563
|
+
|
564
|
+
# Mount the MCP app at /mcp
|
565
|
+
app.mount("/mcp", mcp_app)
|
566
|
+
|
567
|
+
# Now create VAC routes WITHOUT MCP (since we already mounted it)
|
568
|
+
vac_routes = VACRoutesFastAPI(
|
569
|
+
app,
|
570
|
+
stream_interpreter=stream_interpreter,
|
571
|
+
vac_interpreter=vac_interpreter,
|
572
|
+
enable_mcp_server=False, # Don't enable again since we manually mounted
|
573
|
+
**kwargs
|
574
|
+
)
|
575
|
+
|
576
|
+
# Store reference to MCP server for tool registration
|
577
|
+
vac_routes.vac_mcp_server = type('MockMCPServer', (), {
|
578
|
+
'add_tool': lambda self, func, name=None, desc=None: mcp_server.tool(func) if name is None else mcp_server.tool(name=name)(func),
|
579
|
+
'server': mcp_server
|
580
|
+
})()
|
581
|
+
else:
|
582
|
+
# No MCP support available
|
583
|
+
app = FastAPI(
|
584
|
+
title=title,
|
585
|
+
lifespan=app_lifespan
|
586
|
+
)
|
587
|
+
|
588
|
+
vac_routes = VACRoutesFastAPI(
|
589
|
+
app,
|
590
|
+
stream_interpreter=stream_interpreter,
|
591
|
+
vac_interpreter=vac_interpreter,
|
592
|
+
enable_mcp_server=False,
|
593
|
+
**kwargs
|
594
|
+
)
|
595
|
+
|
596
|
+
return app, vac_routes
|
597
|
+
|
598
|
+
def get_mcp_lifespan(self):
|
599
|
+
"""
|
600
|
+
Get the MCP app's lifespan for manual lifespan management.
|
601
|
+
|
602
|
+
Returns:
|
603
|
+
The MCP app's lifespan if MCP server is enabled, None otherwise.
|
604
|
+
|
605
|
+
Example:
|
606
|
+
```python
|
607
|
+
from contextlib import asynccontextmanager
|
608
|
+
|
609
|
+
# Create temp app to get MCP lifespan
|
610
|
+
temp_app = FastAPI()
|
611
|
+
vac_routes = VACRoutesFastAPI(temp_app, ..., enable_mcp_server=True)
|
612
|
+
mcp_lifespan = vac_routes.get_mcp_lifespan()
|
613
|
+
|
614
|
+
# Combine with your app's lifespan
|
615
|
+
@asynccontextmanager
|
616
|
+
async def combined_lifespan(app: FastAPI):
|
617
|
+
async with my_app_lifespan(app):
|
618
|
+
if mcp_lifespan:
|
619
|
+
async with mcp_lifespan(app):
|
620
|
+
yield
|
621
|
+
else:
|
622
|
+
yield
|
623
|
+
|
624
|
+
app = FastAPI(lifespan=combined_lifespan)
|
625
|
+
```
|
626
|
+
"""
|
627
|
+
if self.vac_mcp_server:
|
628
|
+
mcp_app = self.vac_mcp_server.get_http_app()
|
629
|
+
return mcp_app.lifespan
|
630
|
+
return None
|
631
|
+
|
419
632
|
async def vac_interpreter_default(self, question: str, vector_name: str, chat_history=None, **kwargs):
|
420
633
|
"""Default VAC interpreter that uses the stream interpreter without streaming."""
|
421
634
|
class NoOpCallback:
|
@@ -484,11 +697,35 @@ class VACRoutesFastAPI:
|
|
484
697
|
if self.enable_mcp_server and self.vac_mcp_server:
|
485
698
|
try:
|
486
699
|
mcp_app = self.vac_mcp_server.get_http_app()
|
487
|
-
|
488
|
-
|
700
|
+
|
701
|
+
# Note: FastAPI doesn't expose lifespan as a public attribute,
|
702
|
+
# so we can't easily check if it's configured. The error will be
|
703
|
+
# caught below if lifespan is missing.
|
704
|
+
|
705
|
+
# Mount at root - the MCP app already has /mcp path configured
|
706
|
+
self.app.mount("", mcp_app)
|
707
|
+
log.info("✅ MCP server mounted at /mcp endpoint")
|
708
|
+
|
709
|
+
except RuntimeError as e:
|
710
|
+
if "Task group is not initialized" in str(e):
|
711
|
+
error_msg = (
|
712
|
+
"MCP server initialization failed: Lifespan not configured properly.\n"
|
713
|
+
"The FastAPI app must be created with the MCP lifespan.\n\n"
|
714
|
+
"Quick fix: Use the helper method:\n"
|
715
|
+
" app, vac_routes = VACRoutesFastAPI.create_app_with_mcp(\n"
|
716
|
+
" stream_interpreter=your_interpreter,\n"
|
717
|
+
" enable_mcp_server=True\n"
|
718
|
+
" )\n\n"
|
719
|
+
"Or manually configure the lifespan - see documentation for details."
|
720
|
+
)
|
721
|
+
log.error(error_msg)
|
722
|
+
raise RuntimeError(error_msg) from e
|
723
|
+
else:
|
724
|
+
log.error(f"Failed to mount MCP server: {e}")
|
725
|
+
raise RuntimeError(f"MCP server initialization failed: {e}") from e
|
489
726
|
except Exception as e:
|
490
727
|
log.error(f"Failed to mount MCP server: {e}")
|
491
|
-
raise RuntimeError(f"MCP server initialization failed: {e}")
|
728
|
+
raise RuntimeError(f"MCP server initialization failed: {e}") from e
|
492
729
|
|
493
730
|
# A2A agent endpoints
|
494
731
|
if self.enable_a2a_agent:
|
sunholo/mcp/__init__.py
CHANGED
@@ -26,4 +26,13 @@ except ImportError as e:
|
|
26
26
|
print(f"Warning: VACMCPServer not available - {e}")
|
27
27
|
VACMCPServer = None
|
28
28
|
|
29
|
-
|
29
|
+
# SSE utilities are always available
|
30
|
+
from .sse_utils import parse_sse_response, is_sse_response, extract_sse_data
|
31
|
+
|
32
|
+
__all__ = [
|
33
|
+
'MCPClientManager',
|
34
|
+
'VACMCPServer',
|
35
|
+
'parse_sse_response',
|
36
|
+
'is_sse_response',
|
37
|
+
'extract_sse_data'
|
38
|
+
]
|
sunholo/mcp/sse_utils.py
ADDED
@@ -0,0 +1,105 @@
|
|
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
|
+
Utilities for parsing Server-Sent Events (SSE) responses from MCP servers.
|
17
|
+
"""
|
18
|
+
|
19
|
+
import json
|
20
|
+
from typing import Any, Dict, Optional
|
21
|
+
|
22
|
+
|
23
|
+
def parse_sse_response(text: str) -> Dict[str, Any]:
|
24
|
+
"""
|
25
|
+
Parse SSE-formatted response from MCP server.
|
26
|
+
|
27
|
+
FastMCP returns responses in SSE format when using HTTP transport,
|
28
|
+
even with stateless_http=True. This function extracts the JSON data
|
29
|
+
from the SSE format.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
text: Raw response text from MCP server
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Parsed JSON data from the SSE response
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
ValueError: If the response cannot be parsed
|
39
|
+
|
40
|
+
Example:
|
41
|
+
>>> response_text = 'event: message\\ndata: {"jsonrpc": "2.0", "id": 1, "result": {...}}'
|
42
|
+
>>> data = parse_sse_response(response_text)
|
43
|
+
>>> print(data['result'])
|
44
|
+
"""
|
45
|
+
# Check if it's SSE format
|
46
|
+
if text.startswith('event:') or text.startswith('data:'):
|
47
|
+
# Parse SSE format - extract JSON from data: line
|
48
|
+
lines = text.split('\n')
|
49
|
+
|
50
|
+
# Find the data line
|
51
|
+
data_line = None
|
52
|
+
for line in lines:
|
53
|
+
if line.startswith('data:'):
|
54
|
+
data_line = line
|
55
|
+
break
|
56
|
+
|
57
|
+
if data_line:
|
58
|
+
# Remove 'data:' prefix and parse JSON
|
59
|
+
json_str = data_line[5:].strip()
|
60
|
+
try:
|
61
|
+
return json.loads(json_str)
|
62
|
+
except json.JSONDecodeError as e:
|
63
|
+
raise ValueError(f"Failed to parse JSON from SSE data: {e}")
|
64
|
+
else:
|
65
|
+
raise ValueError("No data line found in SSE response")
|
66
|
+
else:
|
67
|
+
# Try parsing as regular JSON
|
68
|
+
try:
|
69
|
+
return json.loads(text)
|
70
|
+
except json.JSONDecodeError as e:
|
71
|
+
raise ValueError(f"Response is not valid JSON or SSE format: {e}")
|
72
|
+
|
73
|
+
|
74
|
+
def is_sse_response(text: str) -> bool:
|
75
|
+
"""
|
76
|
+
Check if a response is in SSE format.
|
77
|
+
|
78
|
+
Args:
|
79
|
+
text: Response text to check
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
True if the response appears to be SSE format
|
83
|
+
"""
|
84
|
+
return text.startswith('event:') or text.startswith('data:')
|
85
|
+
|
86
|
+
|
87
|
+
def extract_sse_data(text: str) -> Optional[str]:
|
88
|
+
"""
|
89
|
+
Extract the data portion from an SSE response.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
text: SSE-formatted response text
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
The extracted data string, or None if not found
|
96
|
+
"""
|
97
|
+
if not is_sse_response(text):
|
98
|
+
return None
|
99
|
+
|
100
|
+
lines = text.split('\n')
|
101
|
+
for line in lines:
|
102
|
+
if line.startswith('data:'):
|
103
|
+
return line[5:].strip()
|
104
|
+
|
105
|
+
return None
|
@@ -73,7 +73,8 @@ class VACMCPServer:
|
|
73
73
|
|
74
74
|
def get_http_app(self):
|
75
75
|
"""Get the HTTP app for mounting in FastAPI."""
|
76
|
-
|
76
|
+
# Following FastMCP docs: when mounted at root "", path="/mcp" gives us /mcp endpoint
|
77
|
+
return self.server.http_app(path="/mcp")
|
77
78
|
|
78
79
|
def add_tool(self, func: Callable, name: str = None, description: str = None):
|
79
80
|
"""
|
sunholo/mcp/vac_tools.py
CHANGED
@@ -54,12 +54,9 @@ def get_vac_config(vector_name: str = None) -> 'ConfigManager':
|
|
54
54
|
|
55
55
|
default_vac = os.getenv("DEFAULT_VAC_NAME", "demo")
|
56
56
|
vac_name = vector_name or default_vac
|
57
|
-
vac_config_folder = os.getenv("VAC_CONFIG_FOLDER")
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
else:
|
62
|
-
return ConfigManager(vac_name)
|
58
|
+
# ConfigManager uses VAC_CONFIG_FOLDER env var automatically
|
59
|
+
return ConfigManager(vac_name)
|
63
60
|
|
64
61
|
|
65
62
|
async def call_vac_async(question: str, vector_name: str, chat_history: List[Dict[str, str]] = None) -> str:
|
@@ -241,9 +238,10 @@ def register_vac_tools(server: 'FastMCP', registry: 'MCPToolRegistry' = None):
|
|
241
238
|
|
242
239
|
# Register tools in registry if provided
|
243
240
|
if registry:
|
244
|
-
|
245
|
-
registry.register_tool("
|
246
|
-
registry.register_tool("
|
247
|
-
registry.register_tool("
|
241
|
+
# Extract the underlying function from FunctionTool objects
|
242
|
+
registry.register_tool("vac_stream", vac_stream.fn if hasattr(vac_stream, 'fn') else vac_stream)
|
243
|
+
registry.register_tool("vac_query", vac_query.fn if hasattr(vac_query, 'fn') else vac_query)
|
244
|
+
registry.register_tool("list_available_vacs", list_available_vacs.fn if hasattr(list_available_vacs, 'fn') else list_available_vacs)
|
245
|
+
registry.register_tool("get_vac_info", get_vac_info.fn if hasattr(get_vac_info, 'fn') else get_vac_info)
|
248
246
|
|
249
247
|
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=tDZ-2U6UAHlloFpH-5HDd2Ob_80GiOr5CMO6XZPDGXM,60877
|
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
|
@@ -115,15 +115,16 @@ sunholo/llamaindex/llamaindex_class.py,sha256=PnpPoc7LpP7xvKIXYu-UvI4ehj67pGhE1E
|
|
115
115
|
sunholo/llamaindex/user_history.py,sha256=ZtkecWuF9ORduyGB8kF8gP66bm9DdvCI-ZiK6Kt-cSE,2265
|
116
116
|
sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
117
|
sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
|
118
|
-
sunholo/mcp/__init__.py,sha256=
|
118
|
+
sunholo/mcp/__init__.py,sha256=hvVdBeXrYZq1saa57xlN88e7KIi4oA0VFEpgLJttTcs,1228
|
119
119
|
sunholo/mcp/cli.py,sha256=RyTrTBQMUaNMAZ1Nyh-XKb9qGnCA5hMxpKp5-9lqfrI,821
|
120
120
|
sunholo/mcp/cli_fastmcp.py,sha256=MWx7kJ4RHX0tTygWs247aOYr4bCKOwjnmccOPjcTVnc,6104
|
121
121
|
sunholo/mcp/extensible_mcp_server.py,sha256=docJT800-wJLApU6kEa3lwu9FHyy1yvtJIk8JI05Z3o,8960
|
122
122
|
sunholo/mcp/mcp_manager.py,sha256=g75vv6XvM24U7uz366slE-p76Qs4AvVcsarHSF9qIvE,5061
|
123
|
+
sunholo/mcp/sse_utils.py,sha256=LBugTxAIccQmcU2ueKIcvVlR2GjhVajwqHDnVn2s6e8,3173
|
123
124
|
sunholo/mcp/stdio_http_bridge.py,sha256=IunHOtnjKAkRWef3SJnqnAL2r2qBRpCH2k_Q_y0Tdf8,3237
|
124
125
|
sunholo/mcp/vac_mcp_server.py,sha256=MotoCw5lDsxCeVtwh1499yGFku9w-78xXhGkIHTUo3w,838
|
125
|
-
sunholo/mcp/vac_mcp_server_fastmcp.py,sha256=
|
126
|
-
sunholo/mcp/vac_tools.py,sha256=
|
126
|
+
sunholo/mcp/vac_mcp_server_fastmcp.py,sha256=Xvcwl3-kqqap-yRvOrxYHmNPQi1RBAznoZKXk8b3ydQ,4556
|
127
|
+
sunholo/mcp/vac_tools.py,sha256=T7JmN4rAWYGmXbnXdpJlgE91DZGi4byzK9uWENndROA,8642
|
127
128
|
sunholo/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
128
129
|
sunholo/ollama/ollama_images.py,sha256=H2cpcNu88R4TwyfL_nnqkQhdvBQ2FPCAy4Ok__0yQmo,2351
|
129
130
|
sunholo/pubsub/__init__.py,sha256=DfTEk4zmCfqn6gFxRrqDO0pOrvXTDqH-medpgYO4PGw,117
|
@@ -181,9 +182,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
|
|
181
182
|
sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
|
182
183
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
183
184
|
sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
|
184
|
-
sunholo-0.144.
|
185
|
-
sunholo-0.144.
|
186
|
-
sunholo-0.144.
|
187
|
-
sunholo-0.144.
|
188
|
-
sunholo-0.144.
|
189
|
-
sunholo-0.144.
|
185
|
+
sunholo-0.144.4.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
186
|
+
sunholo-0.144.4.dist-info/METADATA,sha256=anqykTQweoYFxojD32NfblATFycMbT3x8-SYQ4uHwys,18700
|
187
|
+
sunholo-0.144.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
188
|
+
sunholo-0.144.4.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
189
|
+
sunholo-0.144.4.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
190
|
+
sunholo-0.144.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|