ai-microcore 4.0.0.dev17__tar.gz → 4.0.0.dev19__tar.gz
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.
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/PKG-INFO +1 -1
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/__init__.py +3 -1
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/mcp.py +111 -17
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/tokenizing.py +2 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/LICENSE +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/README.md +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/_env.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/_llm_functions.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/_prepare_llm_args.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/ai_func/__init__.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/ai_func/ai-func.json.j2 +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/ai_func/ai-func.pythonic.j2 +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/ai_modules.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/configuration.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/embedding_db/__init__.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/embedding_db/chromadb.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/file_storage.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/interactive_setup.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/json_parsing.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/__init__.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/_openai_llm_v0.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/_openai_llm_v1.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/anthropic.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/google_genai.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/google_vertex_ai.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/local_llm.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/local_transformers.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/openai_llm.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/llm/shared.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/logging.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/message_types.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/metrics.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/python.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/templating/__init__.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/templating/jinja2.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/text2speech/elevenlabs.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/types.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/ui.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/utils.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/wrappers/__init__.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/wrappers/llm_response_wrapper.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/wrappers/prompt_wrapper.py +0 -0
- {ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-microcore
|
|
3
|
-
Version: 4.0.0.
|
|
3
|
+
Version: 4.0.0.dev19
|
|
4
4
|
Summary: # Minimalistic Foundation for AI Applications
|
|
5
5
|
Keywords: llm,large language models,ai,similarity search,ai search,gpt,openai,framework,adapter
|
|
6
6
|
Author-email: Vitalii Stepanenko <mail@vitalii.in>
|
|
@@ -23,6 +23,7 @@ from .wrappers.llm_response_wrapper import LLMResponse
|
|
|
23
23
|
from ._llm_functions import llm, allm, llm_parallel
|
|
24
24
|
from .utils import parse, dedent
|
|
25
25
|
from .metrics import Metrics
|
|
26
|
+
from .interactive_setup import interactive_setup
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def tpl(file: os.PathLike[str] | str, **kwargs) -> str | PromptWrapper:
|
|
@@ -181,7 +182,8 @@ __all__ = [
|
|
|
181
182
|
"mcp_server",
|
|
182
183
|
"tokenizing",
|
|
183
184
|
"Metrics",
|
|
185
|
+
"interactive_setup",
|
|
184
186
|
# "wrappers",
|
|
185
187
|
]
|
|
186
188
|
|
|
187
|
-
__version__ = "4.0.0-
|
|
189
|
+
__version__ = "4.0.0-dev19"
|
|
@@ -2,8 +2,11 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
from typing import Optional
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
+
from enum import Enum
|
|
5
6
|
|
|
7
|
+
import requests
|
|
6
8
|
from mcp.client.streamable_http import streamablehttp_client
|
|
9
|
+
from mcp.client.sse import sse_client
|
|
7
10
|
from mcp import ClientSession, types
|
|
8
11
|
|
|
9
12
|
from .utils import ExtendedString, ConvertableToMessage
|
|
@@ -37,17 +40,32 @@ class ToolsCache:
|
|
|
37
40
|
cached_tools[mcp_url] = tools
|
|
38
41
|
storage.write_json(ToolsCache.FILE, cached_tools)
|
|
39
42
|
|
|
43
|
+
@staticmethod
|
|
44
|
+
def clear():
|
|
45
|
+
logging.info("Clearing MCP tools cache...")
|
|
46
|
+
storage.delete(ToolsCache.FILE)
|
|
47
|
+
|
|
40
48
|
|
|
41
49
|
class MCPAnswer(ExtendedString, ConvertableToMessage):
|
|
42
50
|
...
|
|
43
51
|
|
|
44
52
|
|
|
53
|
+
class McpTransport(str, Enum):
|
|
54
|
+
SSE: str = "sse"
|
|
55
|
+
STREAMABLE_HTTP: str = "streamable_http"
|
|
56
|
+
WS: str = "ws"
|
|
57
|
+
STDIO: str = "stdio"
|
|
58
|
+
|
|
59
|
+
def __str__(self):
|
|
60
|
+
return self.value
|
|
61
|
+
|
|
62
|
+
|
|
45
63
|
@dataclass
|
|
46
64
|
class MCPConnection:
|
|
47
65
|
url: str = None
|
|
66
|
+
transport: McpTransport = field(default=McpTransport.STREAMABLE_HTTP)
|
|
48
67
|
read_stream: any = None
|
|
49
68
|
write_stream: any = None
|
|
50
|
-
connection: any = None
|
|
51
69
|
context_manager: any = None
|
|
52
70
|
session: ClientSession = field(default=None, init=False)
|
|
53
71
|
tools: Optional["Tools"] = field(default=None, init=False)
|
|
@@ -57,8 +75,10 @@ class MCPConnection:
|
|
|
57
75
|
@staticmethod
|
|
58
76
|
async def init(
|
|
59
77
|
url: str,
|
|
78
|
+
transport: McpTransport,
|
|
60
79
|
fetch_tools: bool = True,
|
|
61
80
|
use_cache: bool = True,
|
|
81
|
+
connect_timeout: float = 10,
|
|
62
82
|
) -> "MCPConnection":
|
|
63
83
|
|
|
64
84
|
del_event = asyncio.Event()
|
|
@@ -69,17 +89,26 @@ class MCPConnection:
|
|
|
69
89
|
# That's a bit of a hack for closing the async context managers
|
|
70
90
|
async def lifecycle():
|
|
71
91
|
try:
|
|
72
|
-
logging.info(f"Connecting to MCP {url}...")
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
logging.info(f"Connecting to {transport} MCP {url}...")
|
|
93
|
+
if transport == McpTransport.STREAMABLE_HTTP:
|
|
94
|
+
context_manager = streamablehttp_client(url)
|
|
95
|
+
(
|
|
96
|
+
read_stream,
|
|
97
|
+
write_stream,
|
|
98
|
+
_
|
|
99
|
+
) = await context_manager.__aenter__() # pylint: disable=E1101
|
|
100
|
+
elif transport == McpTransport.SSE:
|
|
101
|
+
context_manager = sse_client(url)
|
|
102
|
+
(
|
|
103
|
+
read_stream,
|
|
104
|
+
write_stream
|
|
105
|
+
) = await context_manager.__aenter__() # pylint: disable=E1101
|
|
106
|
+
else:
|
|
107
|
+
raise ValueError(f"Unsupported transport type: {transport}")
|
|
79
108
|
con.url = url
|
|
109
|
+
con.transport = transport
|
|
80
110
|
con.read_stream = read_stream
|
|
81
111
|
con.write_stream = write_stream
|
|
82
|
-
con.connection = connection
|
|
83
112
|
con.context_manager = context_manager
|
|
84
113
|
await con.init_session()
|
|
85
114
|
if fetch_tools:
|
|
@@ -91,7 +120,15 @@ class MCPConnection:
|
|
|
91
120
|
await con._close() # pylint: disable=W0212
|
|
92
121
|
|
|
93
122
|
con._lifecycle_task = asyncio.create_task(lifecycle()) # pylint: disable=W0212
|
|
94
|
-
|
|
123
|
+
try:
|
|
124
|
+
await asyncio.wait_for(opened_event.wait(), timeout=connect_timeout)
|
|
125
|
+
except Exception as e:
|
|
126
|
+
logging.warning(f"Failed to connect to MCP {url}: {e}")
|
|
127
|
+
try:
|
|
128
|
+
await con._close() # pylint: disable=W0212
|
|
129
|
+
except: # noqa: E722 # pylint: disable=W0702
|
|
130
|
+
pass
|
|
131
|
+
raise
|
|
95
132
|
return con
|
|
96
133
|
|
|
97
134
|
async def close(self):
|
|
@@ -149,6 +186,13 @@ class MCPConnection:
|
|
|
149
186
|
def __del__(self):
|
|
150
187
|
self._del_event.set()
|
|
151
188
|
|
|
189
|
+
async def call(self, name: str, **kwargs):
|
|
190
|
+
assert env().config.AI_SYNTAX_FUNCTION_NAME_FIELD not in kwargs
|
|
191
|
+
params = dict(kwargs)
|
|
192
|
+
params[env().config.AI_SYNTAX_FUNCTION_NAME_FIELD] = name
|
|
193
|
+
return await self.exec(params)
|
|
194
|
+
|
|
195
|
+
|
|
152
196
|
async def exec(self, params: dict | LLMResponse):
|
|
153
197
|
if isinstance(params, LLMResponse):
|
|
154
198
|
try:
|
|
@@ -252,6 +296,32 @@ class MCPServer:
|
|
|
252
296
|
url: str
|
|
253
297
|
name: str = field(default="")
|
|
254
298
|
tools: Tools = field(default_factory=Tools)
|
|
299
|
+
transport: McpTransport = field(default=None)
|
|
300
|
+
|
|
301
|
+
@staticmethod
|
|
302
|
+
def _try_sse_or_streamable_http(url) -> tuple[McpTransport, str]:
|
|
303
|
+
if url.endswith("/"):
|
|
304
|
+
test_sse_url = f"{url}sse"
|
|
305
|
+
else:
|
|
306
|
+
test_sse_url = f"{url}/sse"
|
|
307
|
+
try:
|
|
308
|
+
response = requests.request(method="HEAD", url=test_sse_url, timeout=5)
|
|
309
|
+
if response.status_code == 200:
|
|
310
|
+
return McpTransport.SSE, test_sse_url
|
|
311
|
+
except requests.RequestException:
|
|
312
|
+
# not a SSE endpoint or not reachable
|
|
313
|
+
pass
|
|
314
|
+
return McpTransport.STREAMABLE_HTTP, f"{url}/mcp"
|
|
315
|
+
|
|
316
|
+
@staticmethod
|
|
317
|
+
def _guess_transport_type_by_url(url: str) -> Optional[McpTransport]:
|
|
318
|
+
if url.startswith("ws://") or url.startswith("wss://"):
|
|
319
|
+
return McpTransport.WS
|
|
320
|
+
if url.endswith("/mcp"):
|
|
321
|
+
return McpTransport.STREAMABLE_HTTP
|
|
322
|
+
if url.endswith("/sse"):
|
|
323
|
+
return McpTransport.SSE
|
|
324
|
+
return None
|
|
255
325
|
|
|
256
326
|
@staticmethod
|
|
257
327
|
def name_from_url(url: str) -> str:
|
|
@@ -261,13 +331,26 @@ class MCPServer:
|
|
|
261
331
|
def __post_init__(self):
|
|
262
332
|
if not self.name:
|
|
263
333
|
self.name = MCPServer.name_from_url(self.url)
|
|
334
|
+
if not self.transport:
|
|
335
|
+
self.transport = self._guess_transport_type_by_url(self.url)
|
|
264
336
|
|
|
265
337
|
async def connect(
|
|
266
338
|
self,
|
|
267
339
|
fetch_tools: bool = True,
|
|
268
340
|
use_cache: bool = True,
|
|
341
|
+
connect_timeout: float = 10,
|
|
269
342
|
) -> MCPConnection:
|
|
270
|
-
|
|
343
|
+
if self.transport:
|
|
344
|
+
transport, url = self.transport, self.url
|
|
345
|
+
else:
|
|
346
|
+
transport, url = self._try_sse_or_streamable_http(self.url)
|
|
347
|
+
return await MCPConnection.init(
|
|
348
|
+
url=url,
|
|
349
|
+
transport=transport,
|
|
350
|
+
fetch_tools=fetch_tools,
|
|
351
|
+
use_cache=use_cache,
|
|
352
|
+
connect_timeout=connect_timeout,
|
|
353
|
+
)
|
|
271
354
|
|
|
272
355
|
def get_tools_cache(self) -> Tools | None:
|
|
273
356
|
return ToolsCache.read(self.url)
|
|
@@ -289,16 +372,27 @@ class MCPRegistry(dict[str, MCPServer]):
|
|
|
289
372
|
raise ValueError(f"MCP server '{server_name}' not found in registry")
|
|
290
373
|
return self[server_name]
|
|
291
374
|
|
|
292
|
-
async def precache_tools(
|
|
375
|
+
async def precache_tools(
|
|
376
|
+
self,
|
|
377
|
+
raise_errors: bool = False,
|
|
378
|
+
connect_timeout: int = 10,
|
|
379
|
+
):
|
|
293
380
|
async def precache_server_tools(server_name):
|
|
294
|
-
conn =
|
|
295
|
-
fetch_tools=True,
|
|
296
|
-
use_cache=False
|
|
297
|
-
)
|
|
381
|
+
conn = None
|
|
298
382
|
try:
|
|
383
|
+
conn = await self.get(server_name).connect(
|
|
384
|
+
fetch_tools=True,
|
|
385
|
+
use_cache=False,
|
|
386
|
+
connect_timeout=connect_timeout,
|
|
387
|
+
)
|
|
299
388
|
conn.update_tools_cache()
|
|
389
|
+
except Exception as e: # pylint: disable=W0718
|
|
390
|
+
logging.error("Failed to precache tools for MCP server %s: %s", server_name, e)
|
|
391
|
+
if raise_errors:
|
|
392
|
+
raise
|
|
300
393
|
finally:
|
|
301
|
-
|
|
394
|
+
if conn is not None:
|
|
395
|
+
await conn.close()
|
|
302
396
|
|
|
303
397
|
await asyncio.gather(*[precache_server_tools(srv) for srv in self.keys()])
|
|
304
398
|
|
|
@@ -25,6 +25,8 @@ def _resolve_tiktoken_encoding(
|
|
|
25
25
|
)
|
|
26
26
|
if for_model:
|
|
27
27
|
try:
|
|
28
|
+
if for_model.startswith("gpt-4.1") or for_model.startswith("gpt-4.5"):
|
|
29
|
+
return tiktoken.get_encoding("o200k_base")
|
|
28
30
|
return tiktoken.encoding_for_model(for_model)
|
|
29
31
|
except (KeyError, requests.exceptions.ConnectionError):
|
|
30
32
|
logging.warning(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_microcore-4.0.0.dev17 → ai_microcore-4.0.0.dev19}/microcore/wrappers/llm_response_wrapper.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|