ommlds 0.0.0.dev479__py3-none-any.whl → 0.0.0.dev481__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.
Files changed (43) hide show
  1. ommlds/.omlish-manifests.json +40 -23
  2. ommlds/__about__.py +1 -1
  3. ommlds/backends/llamacpp/logging.py +4 -1
  4. ommlds/backends/mlx/caching.py +7 -3
  5. ommlds/backends/mlx/cli.py +10 -7
  6. ommlds/backends/mlx/generation.py +18 -16
  7. ommlds/backends/mlx/limits.py +10 -6
  8. ommlds/backends/mlx/loading.py +7 -4
  9. ommlds/backends/tavily/__init__.py +0 -0
  10. ommlds/backends/tavily/protocol.py +301 -0
  11. ommlds/backends/transformers/__init__.py +14 -0
  12. ommlds/minichain/__init__.py +1 -0
  13. ommlds/minichain/_dataclasses.py +46282 -0
  14. ommlds/minichain/backends/impls/anthropic/chat.py +23 -4
  15. ommlds/minichain/backends/impls/duckduckgo/search.py +5 -1
  16. ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
  17. ommlds/minichain/backends/impls/llamacpp/chat.py +6 -3
  18. ommlds/minichain/backends/impls/llamacpp/completion.py +7 -3
  19. ommlds/minichain/backends/impls/llamacpp/stream.py +6 -3
  20. ommlds/minichain/backends/impls/mlx/chat.py +6 -3
  21. ommlds/minichain/backends/impls/openai/format.py +2 -0
  22. ommlds/minichain/backends/impls/openai/names.py +3 -1
  23. ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
  24. ommlds/minichain/backends/impls/tavily.py +66 -0
  25. ommlds/minichain/backends/impls/tinygrad/chat.py +7 -4
  26. ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
  27. ommlds/minichain/backends/impls/transformers/sentence.py +5 -2
  28. ommlds/minichain/backends/impls/transformers/tokens.py +9 -6
  29. ommlds/minichain/backends/impls/transformers/transformers.py +10 -8
  30. ommlds/minichain/llms/types.py +4 -0
  31. ommlds/minichain/search.py +1 -1
  32. ommlds/minichain/standard.py +1 -0
  33. ommlds/specs/__init__.py +0 -0
  34. ommlds/specs/mcp/__init__.py +0 -0
  35. ommlds/specs/mcp/_marshal.py +23 -0
  36. ommlds/specs/mcp/clients.py +146 -0
  37. ommlds/specs/mcp/protocol.py +371 -0
  38. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/METADATA +5 -5
  39. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/RECORD +43 -34
  40. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/WHEEL +0 -0
  41. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/entry_points.txt +0 -0
  42. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/licenses/LICENSE +0 -0
  43. {ommlds-0.0.0.dev479.dist-info → ommlds-0.0.0.dev481.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,146 @@
1
+ import contextlib
2
+ import subprocess
3
+ import typing as ta
4
+
5
+ import anyio.abc
6
+
7
+ from omlish import check
8
+ from omlish import dataclasses as dc
9
+ from omlish import marshal as msh
10
+ from omlish.asyncs import anyio as aiu
11
+ from omlish.specs import jsonrpc as jr
12
+
13
+ from . import protocol as pt
14
+
15
+
16
+ ##
17
+
18
+
19
+ class McpServerConnection:
20
+ def __init__(
21
+ self,
22
+ tg: anyio.abc.TaskGroup,
23
+ stream: anyio.abc.ByteStream,
24
+ *,
25
+ default_timeout: float | None = 30.,
26
+ ) -> None:
27
+ super().__init__()
28
+
29
+ self._conn = jr.Connection(
30
+ tg,
31
+ stream,
32
+ request_handler=self._handle_client_request,
33
+ notification_handler=self._handle_client_notification,
34
+ default_timeout=default_timeout,
35
+ )
36
+
37
+ #
38
+
39
+ @classmethod
40
+ def from_process(
41
+ cls,
42
+ tg: anyio.abc.TaskGroup,
43
+ proc: anyio.abc.Process,
44
+ **kwargs: ta.Any,
45
+ ) -> 'McpServerConnection':
46
+ return cls(
47
+ tg,
48
+ aiu.StapledByteStream(
49
+ check.not_none(proc.stdin),
50
+ check.not_none(proc.stdout),
51
+ ),
52
+ **kwargs,
53
+ )
54
+
55
+ @classmethod
56
+ def open_process(
57
+ cls,
58
+ tg: anyio.abc.TaskGroup,
59
+ cmd: ta.Sequence[str],
60
+ open_kwargs: ta.Mapping[str, ta.Any] | None = None,
61
+ **kwargs: ta.Any,
62
+ ) -> ta.AsyncContextManager[tuple[anyio.abc.Process, 'McpServerConnection']]:
63
+ @contextlib.asynccontextmanager
64
+ async def inner():
65
+ async with await anyio.open_process(
66
+ cmd,
67
+ stdin=subprocess.PIPE,
68
+ stdout=subprocess.PIPE,
69
+ **open_kwargs or {},
70
+ ) as proc:
71
+ async with cls.from_process(
72
+ tg,
73
+ proc,
74
+ **kwargs,
75
+ ) as client:
76
+ yield (proc, client)
77
+
78
+ return inner()
79
+
80
+ #
81
+
82
+ async def __aenter__(self) -> 'McpServerConnection':
83
+ await self._conn.__aenter__()
84
+ return self
85
+
86
+ async def __aexit__(self, et, e, tb) -> None:
87
+ await self._conn.__aexit__(et, e, tb)
88
+
89
+ #
90
+
91
+ async def _handle_client_request(self, _client: jr.Connection, req: jr.Request) -> None:
92
+ pass
93
+
94
+ async def _handle_client_notification(self, _client: jr.Connection, no: jr.Request) -> None:
95
+ pass
96
+
97
+ #
98
+
99
+ async def request(self, req: pt.ClientRequest[pt.ClientResultT]) -> pt.ClientResultT:
100
+ res_cls = pt.MESSAGE_TYPES_BY_JSON_RPC_METHOD_NAME[pt.ClientResult][req.json_rpc_method_name] # type: ignore[type-abstract] # noqa
101
+ req_mv = msh.marshal(req)
102
+ res_mv = await self._conn.request(req.json_rpc_method_name, req_mv) # type: ignore[arg-type]
103
+ res = msh.unmarshal(res_mv, res_cls)
104
+ return ta.cast(pt.ClientResultT, res)
105
+
106
+ async def notify(self, no: pt.Notification) -> None:
107
+ no_mv = msh.marshal(no)
108
+ await self._conn.notify(no.json_rpc_method_name, no_mv) # type: ignore[arg-type]
109
+
110
+ #
111
+
112
+ async def yield_cursor_request(
113
+ self,
114
+ req: pt.CursorClientRequest[pt.CursorClientResultT],
115
+ ) -> ta.AsyncGenerator[pt.CursorClientResultT]:
116
+ check.none(req.cursor)
117
+
118
+ cursor: str | None = None
119
+ while True:
120
+ res = await self.request(dc.replace(req, cursor=cursor)) # noqa
121
+ yield res
122
+
123
+ if (cursor := res.next_cursor) is None:
124
+ break
125
+
126
+ async def list_cursor_request(
127
+ self,
128
+ req: pt.CursorClientRequest[pt.CursorClientResultT],
129
+ ) -> list[pt.CursorClientResultT]:
130
+ return [res async for res in self.yield_cursor_request(req)]
131
+
132
+ #
133
+
134
+ async def list_tools(self) -> list[pt.Tool]:
135
+ return [
136
+ tool
137
+ async for res in self.yield_cursor_request(pt.ListToolsRequest())
138
+ for tool in res.tools
139
+ ]
140
+
141
+ async def list_prompts(self) -> list[pt.Prompt]:
142
+ return [
143
+ prompt
144
+ async for res in self.yield_cursor_request(pt.ListPromptsRequest())
145
+ for prompt in res.prompts
146
+ ]
@@ -0,0 +1,371 @@
1
+ """
2
+ https://modelcontextprotocol.io/specification/2025-06-18
3
+ https://modelcontextprotocol.io/specification/2025-06-18/schema
4
+
5
+ TODO:
6
+ - https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation
7
+ - https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress
8
+ - https://modelcontextprotocol.io/specification/2025-06-18/client/sampling
9
+ - https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation
10
+ - https://modelcontextprotocol.io/specification/2025-06-18/server/prompts
11
+ - https://modelcontextprotocol.io/specification/2025-06-18/server/resources
12
+ - https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging
13
+ """
14
+ import abc
15
+ import typing as ta
16
+
17
+ from omlish import collections as col
18
+ from omlish import dataclasses as dc
19
+ from omlish import lang
20
+ from omlish import marshal as msh
21
+
22
+
23
+ ClientRequestT = ta.TypeVar('ClientRequestT', bound='ClientRequest')
24
+ ClientResultT = ta.TypeVar('ClientResultT', bound='ClientResult')
25
+
26
+ ServerRequestT = ta.TypeVar('ServerRequestT', bound='ServerRequest')
27
+ ServerResultT = ta.TypeVar('ServerResultT', bound='ServerResult')
28
+
29
+ CursorClientRequestT = ta.TypeVar('CursorClientRequestT', bound='CursorClientRequest')
30
+ CursorClientResultT = ta.TypeVar('CursorClientResultT', bound='CursorClientResult')
31
+
32
+
33
+ msh.register_global_module_import('._marshal', __package__)
34
+
35
+
36
+ ##
37
+
38
+
39
+ def _set_class_marshal_options(cls):
40
+ msh.update_object_metadata(
41
+ cls,
42
+ field_naming=msh.Naming.LOW_CAMEL,
43
+ field_defaults=msh.FieldMetadata(
44
+ options=msh.FieldOptions(
45
+ omit_if=lang.is_none,
46
+ ),
47
+ ),
48
+ )
49
+ return cls
50
+
51
+
52
+ ##
53
+
54
+
55
+ @dc.dataclass(frozen=True, kw_only=True)
56
+ class Message(lang.Sealed, lang.Abstract):
57
+ @property
58
+ @abc.abstractmethod
59
+ def json_rpc_method_name(self) -> str:
60
+ raise NotImplementedError
61
+
62
+
63
+ #
64
+
65
+
66
+ class ClientRequest(Message, lang.Abstract, ta.Generic[ClientResultT]):
67
+ pass
68
+
69
+
70
+ class ClientResult(Message, lang.Abstract, ta.Generic[ClientRequestT]):
71
+ pass
72
+
73
+
74
+ #
75
+
76
+
77
+ class ServerRequest(Message, lang.Abstract, ta.Generic[ServerResultT]):
78
+ pass
79
+
80
+
81
+ class ServerResult(Message, lang.Abstract, ta.Generic[ServerRequestT]):
82
+ pass
83
+
84
+
85
+ #
86
+
87
+
88
+ class Notification(Message, lang.Abstract):
89
+ pass
90
+
91
+
92
+ ##
93
+
94
+
95
+ @dc.dataclass(frozen=True, kw_only=True)
96
+ class WithMeta(lang.Sealed, lang.Abstract):
97
+ meta_: ta.Mapping[str, ta.Any] | None = dc.field(default=None) | msh.with_field_metadata(name='_meta')
98
+
99
+
100
+ ##
101
+
102
+
103
+ @dc.dataclass(frozen=True, kw_only=True)
104
+ @_set_class_marshal_options
105
+ class Implementation(lang.Final):
106
+ name: str
107
+ version: str
108
+
109
+
110
+ DEFAULT_PROTOCOL_VERSION = '2025-06-18'
111
+
112
+
113
+ @dc.dataclass(frozen=True, kw_only=True)
114
+ @_set_class_marshal_options
115
+ class ClientCapabilities(lang.Final):
116
+ elicitation: ta.Any | None = None
117
+
118
+ experimental: ta.Mapping[str, ta.Any] | None = None
119
+
120
+ @dc.dataclass(frozen=True, kw_only=True)
121
+ @_set_class_marshal_options
122
+ class Roots:
123
+ list_changed: bool | None = None
124
+
125
+ roots: Roots | None = None
126
+
127
+ sampling: ta.Any | None = None
128
+
129
+
130
+ @dc.dataclass(frozen=True, kw_only=True)
131
+ @_set_class_marshal_options
132
+ class InitializeRequest(ClientRequest['InitializeResult']):
133
+ json_rpc_method_name: ta.ClassVar[str] = 'initialize'
134
+
135
+ client_info: Implementation
136
+ protocol_version: str = DEFAULT_PROTOCOL_VERSION
137
+ capabilities: ClientCapabilities = ClientCapabilities()
138
+
139
+
140
+ @dc.dataclass(frozen=True, kw_only=True)
141
+ @_set_class_marshal_options
142
+ class ServerCapabilities:
143
+ completions: ta.Any | None = None
144
+
145
+ experimental: ta.Mapping[str, ta.Any] | None = None
146
+
147
+ logging: ta.Any | None = None
148
+
149
+ @dc.dataclass(frozen=True, kw_only=True)
150
+ @_set_class_marshal_options
151
+ class Prompts:
152
+ list_changed: bool | None = None
153
+
154
+ prompts: Prompts | None = None
155
+
156
+ @dc.dataclass(frozen=True, kw_only=True)
157
+ @_set_class_marshal_options
158
+ class Resources:
159
+ list_changed: bool | None = None
160
+ subscribe: bool | None = None
161
+
162
+ resources: Resources | None = None
163
+
164
+ @dc.dataclass(frozen=True, kw_only=True)
165
+ @_set_class_marshal_options
166
+ class Tools:
167
+ list_changed: bool | None = None
168
+
169
+ tools: Tools | None = None
170
+
171
+
172
+ @dc.dataclass(frozen=True, kw_only=True)
173
+ @_set_class_marshal_options
174
+ class InitializeResult(ClientResult[InitializeRequest], WithMeta):
175
+ json_rpc_method_name: ta.ClassVar[str] = 'initialize'
176
+
177
+ server_info: Implementation
178
+ protocol_version: str
179
+ capabilities: ServerCapabilities
180
+ instructions: str | None = None
181
+
182
+
183
+ @dc.dataclass(frozen=True, kw_only=True)
184
+ @_set_class_marshal_options
185
+ class InitializedNotification(Notification):
186
+ json_rpc_method_name: ta.ClassVar[str] = 'initialized'
187
+
188
+
189
+ ##
190
+
191
+
192
+ @dc.dataclass(frozen=True, kw_only=True)
193
+ class CursorClientRequest(ClientRequest[CursorClientResultT], lang.Abstract):
194
+ cursor: str | None = None
195
+
196
+
197
+ @dc.dataclass(frozen=True, kw_only=True)
198
+ class CursorClientResult(ClientResult[CursorClientRequestT], lang.Abstract):
199
+ next_cursor: str | None = None
200
+
201
+
202
+ ##
203
+
204
+
205
+ @dc.dataclass(frozen=True, kw_only=True)
206
+ @_set_class_marshal_options
207
+ class ToolAnnotations(lang.Final):
208
+ destructive_hint: bool | None = None
209
+ idempotent_hint: bool | None = None
210
+ open_world_hint: bool | None = None
211
+ read_only_hint: bool | None = None
212
+ title: str | None = None
213
+
214
+
215
+ @dc.dataclass(frozen=True, kw_only=True)
216
+ @_set_class_marshal_options
217
+ class Tool(WithMeta, lang.Final):
218
+ name: str
219
+ title: str | None = None
220
+
221
+ description: str | None = None
222
+
223
+ annotations: ToolAnnotations | None = None
224
+
225
+ input_schema: ta.Any
226
+ output_schema: ta.Any | None = None
227
+
228
+
229
+ @dc.dataclass(frozen=True, kw_only=True)
230
+ @_set_class_marshal_options
231
+ class ListToolsRequest(CursorClientRequest['ListToolsResult']):
232
+ json_rpc_method_name: ta.ClassVar[str] = 'tools/list'
233
+
234
+
235
+ @dc.dataclass(frozen=True, kw_only=True)
236
+ @_set_class_marshal_options
237
+ class ListToolsResult(CursorClientResult[ListToolsRequest], WithMeta):
238
+ json_rpc_method_name: ta.ClassVar[str] = 'tools/list'
239
+
240
+ tools: ta.Sequence[Tool]
241
+
242
+
243
+ ##
244
+
245
+
246
+ class ContentBlock(lang.Abstract):
247
+ pass
248
+
249
+
250
+ @dc.dataclass(frozen=True, kw_only=True)
251
+ @_set_class_marshal_options
252
+ class TextContentBlock(ContentBlock, lang.Final):
253
+ text: str
254
+
255
+
256
+ ##
257
+
258
+
259
+ @dc.dataclass(frozen=True, kw_only=True)
260
+ @_set_class_marshal_options
261
+ class CallToolRequest(ClientRequest['CallToolResult']):
262
+ json_rpc_method_name: ta.ClassVar[str] = 'tools/call'
263
+
264
+ name: str
265
+ arguments: ta.Mapping[str, ta.Any] | None = None
266
+
267
+
268
+ @dc.dataclass(frozen=True, kw_only=True)
269
+ @_set_class_marshal_options
270
+ class CallToolResult(ClientResult[CallToolRequest], WithMeta):
271
+ json_rpc_method_name: ta.ClassVar[str] = 'tools/call'
272
+
273
+ content: ta.Sequence[ContentBlock]
274
+ is_error: bool | None = None
275
+ structured_content: ta.Mapping[str, ta.Any] | None = None
276
+
277
+
278
+ ##
279
+
280
+
281
+ @dc.dataclass(frozen=True, kw_only=True)
282
+ @_set_class_marshal_options
283
+ class PingClientRequest(ClientRequest['PingClientResult'], WithMeta):
284
+ json_rpc_method_name: ta.ClassVar[str] = 'ping'
285
+
286
+
287
+ @dc.dataclass(frozen=True, kw_only=True)
288
+ @_set_class_marshal_options
289
+ class PingClientResult(ClientResult[PingClientRequest]):
290
+ json_rpc_method_name: ta.ClassVar[str] = 'ping'
291
+
292
+
293
+ @dc.dataclass(frozen=True, kw_only=True)
294
+ @_set_class_marshal_options
295
+ class PingServerRequest(ServerRequest['PingServerResult'], WithMeta):
296
+ json_rpc_method_name: ta.ClassVar[str] = 'ping'
297
+
298
+
299
+ @dc.dataclass(frozen=True, kw_only=True)
300
+ @_set_class_marshal_options
301
+ class PingServerResult(ServerResult[PingServerRequest]):
302
+ json_rpc_method_name: ta.ClassVar[str] = 'ping'
303
+
304
+
305
+ ##
306
+
307
+
308
+ @dc.dataclass(frozen=True, kw_only=True)
309
+ @_set_class_marshal_options
310
+ class PromptArgument(lang.Final):
311
+ name: str
312
+ title: str | None = None
313
+
314
+ description: str | None = None
315
+
316
+ required: bool | None = None
317
+
318
+
319
+ @dc.dataclass(frozen=True, kw_only=True)
320
+ @_set_class_marshal_options
321
+ class Prompt(WithMeta, lang.Final):
322
+ name: str
323
+ title: str | None = None
324
+
325
+ description: str | None = None
326
+
327
+ arguments: ta.Sequence[PromptArgument] | None = None
328
+
329
+
330
+ @dc.dataclass(frozen=True, kw_only=True)
331
+ @_set_class_marshal_options
332
+ class ListPromptsRequest(CursorClientRequest['ListPromptsResult']):
333
+ json_rpc_method_name: ta.ClassVar[str] = 'prompts/list'
334
+
335
+
336
+ @dc.dataclass(frozen=True, kw_only=True)
337
+ @_set_class_marshal_options
338
+ class ListPromptsResult(CursorClientResult[ListPromptsRequest], WithMeta):
339
+ json_rpc_method_name: ta.ClassVar[str] = 'prompts/list'
340
+
341
+ prompts: ta.Sequence[Prompt]
342
+
343
+
344
+ ##
345
+
346
+
347
+ LoggingLevel: ta.TypeAlias = ta.Literal[
348
+ 'debug',
349
+ 'info',
350
+ 'notice',
351
+ 'warning',
352
+ 'error',
353
+ 'critical',
354
+ 'alert',
355
+ 'emergency',
356
+ ]
357
+
358
+
359
+ ##
360
+
361
+
362
+ MESSAGE_TYPES_BY_JSON_RPC_METHOD_NAME: ta.Mapping[type[Message], ta.Mapping[str, type[Message]]] = {
363
+ bty: col.make_map_by(lambda mty: mty.json_rpc_method_name, lang.deep_subclasses(bty, concrete_only=True)) # type: ignore # noqa
364
+ for bty in [
365
+ ClientRequest,
366
+ ClientResult,
367
+ ServerRequest,
368
+ ServerResult,
369
+ Notification,
370
+ ]
371
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev479
3
+ Version: 0.0.0.dev481
4
4
  Summary: ommlds
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,11 +14,11 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omdev==0.0.0.dev479
18
- Requires-Dist: omlish==0.0.0.dev479
17
+ Requires-Dist: omdev==0.0.0.dev481
18
+ Requires-Dist: omlish==0.0.0.dev481
19
19
  Provides-Extra: all
20
20
  Requires-Dist: llama-cpp-python~=0.3; extra == "all"
21
- Requires-Dist: mlx~=0.29; extra == "all"
21
+ Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "all"
22
22
  Requires-Dist: mlx-lm~=0.28; sys_platform == "darwin" and extra == "all"
23
23
  Requires-Dist: tiktoken~=0.12; extra == "all"
24
24
  Requires-Dist: tinygrad~=0.11; extra == "all"
@@ -39,7 +39,7 @@ Requires-Dist: wikitextparser~=0.56; extra == "all"
39
39
  Requires-Dist: lxml>=5.3; python_version < "3.13" and extra == "all"
40
40
  Provides-Extra: backends
41
41
  Requires-Dist: llama-cpp-python~=0.3; extra == "backends"
42
- Requires-Dist: mlx~=0.29; extra == "backends"
42
+ Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "backends"
43
43
  Requires-Dist: mlx-lm~=0.28; sys_platform == "darwin" and extra == "backends"
44
44
  Requires-Dist: tiktoken~=0.12; extra == "backends"
45
45
  Requires-Dist: tinygrad~=0.11; extra == "backends"