ai-microcore 4.0.0.dev14__tar.gz → 4.0.0.dev15__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.dev14 → ai_microcore-4.0.0.dev15}/PKG-INFO +1 -1
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/__init__.py +1 -1
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/configuration.py +8 -1
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/mcp.py +37 -10
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/wrappers/llm_response_wrapper.py +9 -2
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/LICENSE +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/README.md +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/_env.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/_llm_functions.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/_prepare_llm_args.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/ai_func/__init__.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/ai_func/ai-func.json.j2 +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/ai_func/ai-func.pythonic.j2 +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/ai_modules.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/embedding_db/__init__.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/embedding_db/chromadb.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/file_storage.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/interactive_setup.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/json_parsing.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/__init__.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/_openai_llm_v0.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/_openai_llm_v1.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/anthropic.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/google_genai.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/google_vertex_ai.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/local_llm.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/local_transformers.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/openai_llm.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/llm/shared.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/logging.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/message_types.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/metrics.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/python.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/templating/__init__.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/templating/jinja2.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/text2speech/elevenlabs.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/tokenizing.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/types.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/ui.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/utils.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/wrappers/__init__.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/wrappers/prompt_wrapper.py +0 -0
- {ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/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.dev15
|
|
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>
|
|
@@ -112,7 +112,13 @@ class BaseConfig:
|
|
|
112
112
|
|
|
113
113
|
if self.USE_DOT_ENV:
|
|
114
114
|
if self.DOT_ENV_FILE or not _default_dotenv_loaded:
|
|
115
|
-
|
|
115
|
+
fp = self.DOT_ENV_FILE
|
|
116
|
+
if fp and "~" in fp:
|
|
117
|
+
fp = Path(fp).expanduser()
|
|
118
|
+
dotenv.load_dotenv(
|
|
119
|
+
override=True,
|
|
120
|
+
dotenv_path=fp
|
|
121
|
+
)
|
|
116
122
|
if not self.DOT_ENV_FILE:
|
|
117
123
|
_default_dotenv_loaded = True
|
|
118
124
|
|
|
@@ -411,6 +417,7 @@ class Config(LLMConfig):
|
|
|
411
417
|
MCP_SERVERS: list = from_env(dtype=list)
|
|
412
418
|
|
|
413
419
|
INTERACTIVE_SETUP: bool = field(default=False)
|
|
420
|
+
"""Whether to run interactive setup if configuration is not valid."""
|
|
414
421
|
|
|
415
422
|
def __post_init__(self):
|
|
416
423
|
try:
|
|
@@ -6,7 +6,7 @@ from dataclasses import dataclass, field
|
|
|
6
6
|
from mcp.client.streamable_http import streamablehttp_client
|
|
7
7
|
from mcp import ClientSession, types
|
|
8
8
|
|
|
9
|
-
from .utils import ExtendedString
|
|
9
|
+
from .utils import ExtendedString, ConvertableToMessage
|
|
10
10
|
from .ai_func import AiFuncSyntax
|
|
11
11
|
from . import ui
|
|
12
12
|
from .types import BadAIAnswer, BadAIJsonAnswer
|
|
@@ -38,6 +38,10 @@ class ToolsCache:
|
|
|
38
38
|
storage.write_json(ToolsCache.FILE, cached_tools)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
class MCPAnswer(ExtendedString, ConvertableToMessage):
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
41
45
|
@dataclass
|
|
42
46
|
class MCPConnection:
|
|
43
47
|
url: str = None
|
|
@@ -46,8 +50,9 @@ class MCPConnection:
|
|
|
46
50
|
connection: any = None
|
|
47
51
|
context_manager: any = None
|
|
48
52
|
session: ClientSession = field(default=None, init=False)
|
|
49
|
-
del_event: asyncio.Event = field(default=None, init=False)
|
|
50
53
|
tools: Optional["Tools"] = field(default=None, init=False)
|
|
54
|
+
_lifecycle_task: asyncio.Task = field(default=None, init=False)
|
|
55
|
+
_del_event: asyncio.Event = field(default=None, init=False)
|
|
51
56
|
|
|
52
57
|
@staticmethod
|
|
53
58
|
async def init(
|
|
@@ -59,7 +64,7 @@ class MCPConnection:
|
|
|
59
64
|
del_event = asyncio.Event()
|
|
60
65
|
opened_event = asyncio.Event()
|
|
61
66
|
con: MCPConnection = MCPConnection()
|
|
62
|
-
con.
|
|
67
|
+
con._del_event = del_event # pylint: disable=W0212
|
|
63
68
|
|
|
64
69
|
# That's a bit of a hack for closing the async context managers
|
|
65
70
|
async def lifecycle():
|
|
@@ -80,15 +85,23 @@ class MCPConnection:
|
|
|
80
85
|
if fetch_tools:
|
|
81
86
|
await con.fetch_tools(use_cache=use_cache)
|
|
82
87
|
opened_event.set()
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
finally:
|
|
85
|
-
await
|
|
90
|
+
await del_event.wait()
|
|
91
|
+
await con._close() # pylint: disable=W0212
|
|
86
92
|
|
|
87
|
-
asyncio.create_task(lifecycle())
|
|
93
|
+
con._lifecycle_task = asyncio.create_task(lifecycle()) # pylint: disable=W0212
|
|
88
94
|
await opened_event.wait()
|
|
89
95
|
return con
|
|
90
96
|
|
|
91
97
|
async def close(self):
|
|
98
|
+
self._del_event.set()
|
|
99
|
+
if self._lifecycle_task:
|
|
100
|
+
await self._lifecycle_task
|
|
101
|
+
else:
|
|
102
|
+
logging.error(f"Trying to close MCP connection that is not opened ({self.url})")
|
|
103
|
+
|
|
104
|
+
async def _close(self):
|
|
92
105
|
logging.info(f"Closing MCP session ({self.url})...")
|
|
93
106
|
try:
|
|
94
107
|
if self.session:
|
|
@@ -129,7 +142,7 @@ class MCPConnection:
|
|
|
129
142
|
return self.tools
|
|
130
143
|
|
|
131
144
|
def __del__(self):
|
|
132
|
-
self.
|
|
145
|
+
self._del_event.set()
|
|
133
146
|
|
|
134
147
|
async def exec(self, params: dict | LLMResponse):
|
|
135
148
|
if isinstance(params, LLMResponse):
|
|
@@ -150,7 +163,7 @@ class MCPConnection:
|
|
|
150
163
|
result = await self.session.call_tool(name, params)
|
|
151
164
|
content = result.content
|
|
152
165
|
if content and len(content) == 1 and content[0].type == "text":
|
|
153
|
-
return
|
|
166
|
+
return MCPAnswer(content[0].text, result.__dict__)
|
|
154
167
|
return result
|
|
155
168
|
|
|
156
169
|
|
|
@@ -231,10 +244,19 @@ class Tools(dict[str, Tool]):
|
|
|
231
244
|
|
|
232
245
|
@dataclass
|
|
233
246
|
class MCPServer:
|
|
234
|
-
name: str
|
|
235
247
|
url: str
|
|
248
|
+
name: str = field(default="")
|
|
236
249
|
tools: Tools = field(default_factory=Tools)
|
|
237
250
|
|
|
251
|
+
@staticmethod
|
|
252
|
+
def name_from_url(url: str) -> str:
|
|
253
|
+
"""Domain name from URL."""
|
|
254
|
+
return url.split("//")[-1].split("/")[0]
|
|
255
|
+
|
|
256
|
+
def __post_init__(self):
|
|
257
|
+
if not self.name:
|
|
258
|
+
self.name = MCPServer.name_from_url(self.url)
|
|
259
|
+
|
|
238
260
|
async def connect(
|
|
239
261
|
self,
|
|
240
262
|
fetch_tools: bool = True,
|
|
@@ -244,9 +266,14 @@ class MCPServer:
|
|
|
244
266
|
|
|
245
267
|
|
|
246
268
|
class MCPRegistry(dict[str, MCPServer]):
|
|
247
|
-
def __init__(self, server_configs: list[dict]):
|
|
269
|
+
def __init__(self, server_configs: list[dict | str]):
|
|
248
270
|
super().__init__()
|
|
249
271
|
for server_config in server_configs:
|
|
272
|
+
if isinstance(server_config, str):
|
|
273
|
+
server_config = {
|
|
274
|
+
"name": MCPServer.name_from_url(server_config),
|
|
275
|
+
"url": server_config
|
|
276
|
+
}
|
|
250
277
|
self[server_config["name"]] = MCPServer(**server_config)
|
|
251
278
|
|
|
252
279
|
def get(self, server_name: str) -> MCPServer:
|
{ai_microcore-4.0.0.dev14 → ai_microcore-4.0.0.dev15}/microcore/wrappers/llm_response_wrapper.py
RENAMED
|
@@ -6,7 +6,7 @@ from ..utils import ExtendedString, ConvertableToMessage, extract_number
|
|
|
6
6
|
from ..message_types import Role, AssistantMsg
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
|
-
from ..mcp import MCPConnection
|
|
9
|
+
from ..mcp import MCPConnection, MCPAnswer
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class DictFromLLMResponse(dict):
|
|
@@ -90,5 +90,12 @@ class LLMResponse(ExtendedString, ConvertableToMessage):
|
|
|
90
90
|
def as_message(self) -> AssistantMsg:
|
|
91
91
|
return self.as_assistant
|
|
92
92
|
|
|
93
|
-
async def to_mcp(self, mcp: "MCPConnection"):
|
|
93
|
+
async def to_mcp(self, mcp: "MCPConnection") -> "MCPAnswer":
|
|
94
94
|
return await mcp.exec(self)
|
|
95
|
+
|
|
96
|
+
def is_tool_call(self):
|
|
97
|
+
from .._env import env
|
|
98
|
+
return self.parse_json(
|
|
99
|
+
raise_errors=False,
|
|
100
|
+
required_fields=[env().config.AI_SYNTAX_FUNCTION_NAME_FIELD],
|
|
101
|
+
) is not False
|
|
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
|
|
File without changes
|
|
File without changes
|