ommlds 0.0.0.dev426__py3-none-any.whl → 0.0.0.dev485__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 (295) hide show
  1. ommlds/.omlish-manifests.json +336 -39
  2. ommlds/__about__.py +16 -10
  3. ommlds/_hacks/__init__.py +4 -0
  4. ommlds/_hacks/funcs.py +110 -0
  5. ommlds/_hacks/names.py +158 -0
  6. ommlds/_hacks/params.py +73 -0
  7. ommlds/_hacks/patches.py +0 -3
  8. ommlds/backends/anthropic/protocol/__init__.py +13 -1
  9. ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
  10. ommlds/backends/anthropic/protocol/_marshal.py +2 -2
  11. ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
  12. ommlds/backends/anthropic/protocol/sse/assemble.py +23 -7
  13. ommlds/backends/anthropic/protocol/sse/events.py +13 -0
  14. ommlds/backends/anthropic/protocol/types.py +40 -8
  15. ommlds/backends/google/protocol/__init__.py +16 -0
  16. ommlds/backends/google/protocol/_dataclasses.py +5997 -0
  17. ommlds/backends/google/protocol/_marshal.py +16 -0
  18. ommlds/backends/google/protocol/types.py +626 -0
  19. ommlds/backends/groq/__init__.py +7 -0
  20. ommlds/backends/groq/_dataclasses.py +3901 -0
  21. ommlds/backends/groq/_marshal.py +23 -0
  22. ommlds/backends/groq/protocol.py +249 -0
  23. ommlds/backends/llamacpp/logging.py +4 -1
  24. ommlds/backends/mlx/caching.py +7 -3
  25. ommlds/backends/mlx/cli.py +10 -7
  26. ommlds/backends/mlx/generation.py +19 -17
  27. ommlds/backends/mlx/limits.py +10 -6
  28. ommlds/backends/mlx/loading.py +65 -5
  29. ommlds/backends/ollama/__init__.py +7 -0
  30. ommlds/backends/ollama/_dataclasses.py +3458 -0
  31. ommlds/backends/ollama/protocol.py +170 -0
  32. ommlds/backends/openai/protocol/__init__.py +24 -29
  33. ommlds/backends/openai/protocol/_common.py +18 -0
  34. ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
  35. ommlds/backends/openai/protocol/_marshal.py +27 -0
  36. ommlds/backends/openai/protocol/chatcompletion/chunk.py +58 -31
  37. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +49 -44
  38. ommlds/backends/openai/protocol/chatcompletion/message.py +55 -43
  39. ommlds/backends/openai/protocol/chatcompletion/request.py +114 -66
  40. ommlds/backends/openai/protocol/chatcompletion/response.py +71 -45
  41. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +27 -20
  42. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +16 -7
  43. ommlds/backends/openai/protocol/completionusage.py +24 -15
  44. ommlds/backends/tavily/__init__.py +7 -0
  45. ommlds/backends/tavily/_dataclasses.py +1734 -0
  46. ommlds/backends/tavily/protocol.py +301 -0
  47. ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
  48. ommlds/backends/transformers/__init__.py +14 -0
  49. ommlds/backends/transformers/filecache.py +109 -0
  50. ommlds/backends/transformers/streamers.py +73 -0
  51. ommlds/cli/__init__.py +7 -0
  52. ommlds/cli/_dataclasses.py +2562 -0
  53. ommlds/cli/asyncs.py +30 -0
  54. ommlds/cli/backends/catalog.py +93 -0
  55. ommlds/cli/backends/configs.py +9 -0
  56. ommlds/cli/backends/inject.py +31 -36
  57. ommlds/cli/backends/injection.py +16 -0
  58. ommlds/cli/backends/types.py +46 -0
  59. ommlds/cli/content/messages.py +34 -0
  60. ommlds/cli/content/strings.py +42 -0
  61. ommlds/cli/inject.py +17 -32
  62. ommlds/cli/inputs/__init__.py +0 -0
  63. ommlds/cli/inputs/asyncs.py +32 -0
  64. ommlds/cli/inputs/sync.py +75 -0
  65. ommlds/cli/main.py +270 -110
  66. ommlds/cli/rendering/__init__.py +0 -0
  67. ommlds/cli/rendering/configs.py +9 -0
  68. ommlds/cli/rendering/inject.py +31 -0
  69. ommlds/cli/rendering/markdown.py +52 -0
  70. ommlds/cli/rendering/raw.py +73 -0
  71. ommlds/cli/rendering/types.py +21 -0
  72. ommlds/cli/secrets.py +21 -0
  73. ommlds/cli/sessions/base.py +1 -1
  74. ommlds/cli/sessions/chat/chat/__init__.py +0 -0
  75. ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
  76. ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
  77. ommlds/cli/sessions/chat/chat/ai/inject.py +74 -0
  78. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  79. ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
  80. ommlds/cli/sessions/chat/chat/ai/services.py +79 -0
  81. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  82. ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
  83. ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
  84. ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
  85. ommlds/cli/sessions/chat/chat/state/inject.py +36 -0
  86. ommlds/cli/sessions/chat/chat/state/inmemory.py +33 -0
  87. ommlds/cli/sessions/chat/chat/state/storage.py +52 -0
  88. ommlds/cli/sessions/chat/chat/state/types.py +38 -0
  89. ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
  90. ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
  91. ommlds/cli/sessions/chat/chat/user/inject.py +62 -0
  92. ommlds/cli/sessions/chat/chat/user/interactive.py +31 -0
  93. ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
  94. ommlds/cli/sessions/chat/chat/user/types.py +15 -0
  95. ommlds/cli/sessions/chat/configs.py +27 -0
  96. ommlds/cli/sessions/chat/driver.py +43 -0
  97. ommlds/cli/sessions/chat/inject.py +33 -65
  98. ommlds/cli/sessions/chat/phases/__init__.py +0 -0
  99. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  100. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  101. ommlds/cli/sessions/chat/phases/manager.py +29 -0
  102. ommlds/cli/sessions/chat/phases/types.py +29 -0
  103. ommlds/cli/sessions/chat/session.py +27 -0
  104. ommlds/cli/sessions/chat/tools/__init__.py +0 -0
  105. ommlds/cli/sessions/chat/tools/configs.py +22 -0
  106. ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
  107. ommlds/cli/sessions/chat/tools/execution.py +66 -0
  108. ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
  109. ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
  110. ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
  111. ommlds/cli/sessions/chat/tools/inject.py +88 -0
  112. ommlds/cli/sessions/chat/tools/injection.py +44 -0
  113. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  114. ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
  115. ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
  116. ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
  117. ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
  118. ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
  119. ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
  120. ommlds/cli/{tools/weather.py → sessions/chat/tools/weather/tools.py} +1 -1
  121. ommlds/cli/sessions/completion/configs.py +21 -0
  122. ommlds/cli/sessions/completion/inject.py +42 -0
  123. ommlds/cli/sessions/completion/session.py +35 -0
  124. ommlds/cli/sessions/embedding/configs.py +21 -0
  125. ommlds/cli/sessions/embedding/inject.py +42 -0
  126. ommlds/cli/sessions/embedding/session.py +33 -0
  127. ommlds/cli/sessions/inject.py +28 -11
  128. ommlds/cli/state/__init__.py +0 -0
  129. ommlds/cli/state/inject.py +28 -0
  130. ommlds/cli/{state.py → state/storage.py} +41 -24
  131. ommlds/minichain/__init__.py +84 -24
  132. ommlds/minichain/_dataclasses.py +15401 -0
  133. ommlds/minichain/_marshal.py +49 -9
  134. ommlds/minichain/_typedvalues.py +2 -4
  135. ommlds/minichain/backends/catalogs/base.py +20 -1
  136. ommlds/minichain/backends/catalogs/simple.py +2 -2
  137. ommlds/minichain/backends/catalogs/strings.py +10 -8
  138. ommlds/minichain/backends/impls/anthropic/chat.py +65 -27
  139. ommlds/minichain/backends/impls/anthropic/names.py +10 -8
  140. ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
  141. ommlds/minichain/backends/impls/anthropic/stream.py +111 -43
  142. ommlds/minichain/backends/impls/duckduckgo/search.py +6 -2
  143. ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
  144. ommlds/minichain/backends/impls/dummy/chat.py +69 -0
  145. ommlds/minichain/backends/impls/google/chat.py +114 -22
  146. ommlds/minichain/backends/impls/google/search.py +7 -2
  147. ommlds/minichain/backends/impls/google/stream.py +219 -0
  148. ommlds/minichain/backends/impls/google/tools.py +149 -0
  149. ommlds/minichain/backends/impls/groq/__init__.py +0 -0
  150. ommlds/minichain/backends/impls/groq/chat.py +75 -0
  151. ommlds/minichain/backends/impls/groq/names.py +48 -0
  152. ommlds/minichain/backends/impls/groq/protocol.py +143 -0
  153. ommlds/minichain/backends/impls/groq/stream.py +125 -0
  154. ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
  155. ommlds/minichain/backends/impls/llamacpp/chat.py +40 -22
  156. ommlds/minichain/backends/impls/llamacpp/completion.py +9 -5
  157. ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
  158. ommlds/minichain/backends/impls/llamacpp/stream.py +43 -23
  159. ommlds/minichain/backends/impls/mistral.py +20 -5
  160. ommlds/minichain/backends/impls/mlx/chat.py +101 -24
  161. ommlds/minichain/backends/impls/ollama/__init__.py +0 -0
  162. ommlds/minichain/backends/impls/ollama/chat.py +199 -0
  163. ommlds/minichain/backends/impls/openai/chat.py +18 -8
  164. ommlds/minichain/backends/impls/openai/completion.py +10 -3
  165. ommlds/minichain/backends/impls/openai/embedding.py +10 -3
  166. ommlds/minichain/backends/impls/openai/format.py +131 -106
  167. ommlds/minichain/backends/impls/openai/names.py +31 -5
  168. ommlds/minichain/backends/impls/openai/stream.py +43 -25
  169. ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
  170. ommlds/minichain/backends/impls/tavily.py +66 -0
  171. ommlds/minichain/backends/impls/tinygrad/chat.py +30 -20
  172. ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
  173. ommlds/minichain/backends/impls/transformers/sentence.py +6 -3
  174. ommlds/minichain/backends/impls/transformers/tokens.py +10 -7
  175. ommlds/minichain/backends/impls/transformers/transformers.py +160 -37
  176. ommlds/minichain/backends/strings/parsing.py +1 -1
  177. ommlds/minichain/backends/strings/resolving.py +4 -1
  178. ommlds/minichain/chat/_marshal.py +16 -9
  179. ommlds/minichain/chat/choices/adapters.py +4 -4
  180. ommlds/minichain/chat/choices/services.py +1 -1
  181. ommlds/minichain/chat/choices/stream/__init__.py +0 -0
  182. ommlds/minichain/chat/choices/stream/adapters.py +35 -0
  183. ommlds/minichain/chat/choices/stream/joining.py +31 -0
  184. ommlds/minichain/chat/choices/stream/services.py +45 -0
  185. ommlds/minichain/chat/choices/stream/types.py +43 -0
  186. ommlds/minichain/chat/choices/types.py +2 -2
  187. ommlds/minichain/chat/history.py +3 -3
  188. ommlds/minichain/chat/messages.py +55 -19
  189. ommlds/minichain/chat/services.py +3 -3
  190. ommlds/minichain/chat/stream/_marshal.py +16 -0
  191. ommlds/minichain/chat/stream/joining.py +85 -0
  192. ommlds/minichain/chat/stream/services.py +15 -21
  193. ommlds/minichain/chat/stream/types.py +32 -19
  194. ommlds/minichain/chat/tools/execution.py +8 -7
  195. ommlds/minichain/chat/tools/ids.py +9 -15
  196. ommlds/minichain/chat/tools/parsing.py +17 -26
  197. ommlds/minichain/chat/transforms/base.py +29 -38
  198. ommlds/minichain/chat/transforms/metadata.py +30 -4
  199. ommlds/minichain/chat/transforms/services.py +9 -11
  200. ommlds/minichain/content/_marshal.py +44 -20
  201. ommlds/minichain/content/json.py +13 -0
  202. ommlds/minichain/content/materialize.py +14 -21
  203. ommlds/minichain/content/prepare.py +4 -0
  204. ommlds/minichain/content/transforms/interleave.py +1 -1
  205. ommlds/minichain/content/transforms/squeeze.py +1 -1
  206. ommlds/minichain/content/transforms/stringify.py +1 -1
  207. ommlds/minichain/json.py +20 -0
  208. ommlds/minichain/lib/code/__init__.py +0 -0
  209. ommlds/minichain/lib/code/prompts.py +6 -0
  210. ommlds/minichain/lib/fs/binfiles.py +108 -0
  211. ommlds/minichain/lib/fs/context.py +126 -0
  212. ommlds/minichain/lib/fs/errors.py +101 -0
  213. ommlds/minichain/lib/fs/suggestions.py +36 -0
  214. ommlds/minichain/lib/fs/tools/__init__.py +0 -0
  215. ommlds/minichain/lib/fs/tools/edit.py +104 -0
  216. ommlds/minichain/lib/fs/tools/ls.py +38 -0
  217. ommlds/minichain/lib/fs/tools/read.py +115 -0
  218. ommlds/minichain/lib/fs/tools/recursivels/__init__.py +0 -0
  219. ommlds/minichain/lib/fs/tools/recursivels/execution.py +40 -0
  220. ommlds/minichain/lib/todo/__init__.py +0 -0
  221. ommlds/minichain/lib/todo/context.py +54 -0
  222. ommlds/minichain/lib/todo/tools/__init__.py +0 -0
  223. ommlds/minichain/lib/todo/tools/read.py +44 -0
  224. ommlds/minichain/lib/todo/tools/write.py +335 -0
  225. ommlds/minichain/lib/todo/types.py +60 -0
  226. ommlds/minichain/llms/_marshal.py +25 -17
  227. ommlds/minichain/llms/types.py +4 -0
  228. ommlds/minichain/registries/globals.py +18 -4
  229. ommlds/minichain/resources.py +68 -45
  230. ommlds/minichain/search.py +1 -1
  231. ommlds/minichain/services/_marshal.py +46 -39
  232. ommlds/minichain/services/facades.py +3 -3
  233. ommlds/minichain/services/services.py +1 -1
  234. ommlds/minichain/standard.py +8 -0
  235. ommlds/minichain/stream/services.py +152 -38
  236. ommlds/minichain/stream/wrap.py +22 -24
  237. ommlds/minichain/text/toolparsing/llamacpp/hermes2.py +3 -2
  238. ommlds/minichain/text/toolparsing/llamacpp/llama31.py +3 -2
  239. ommlds/minichain/text/toolparsing/llamacpp/utils.py +3 -2
  240. ommlds/minichain/tools/_marshal.py +1 -1
  241. ommlds/minichain/tools/execution/catalog.py +2 -1
  242. ommlds/minichain/tools/execution/context.py +34 -14
  243. ommlds/minichain/tools/execution/errors.py +15 -0
  244. ommlds/minichain/tools/execution/executors.py +8 -3
  245. ommlds/minichain/tools/execution/reflect.py +40 -5
  246. ommlds/minichain/tools/fns.py +46 -9
  247. ommlds/minichain/tools/jsonschema.py +14 -5
  248. ommlds/minichain/tools/reflect.py +54 -18
  249. ommlds/minichain/tools/types.py +33 -1
  250. ommlds/minichain/utils.py +27 -0
  251. ommlds/minichain/vectors/_marshal.py +11 -10
  252. ommlds/minichain/vectors/types.py +1 -1
  253. ommlds/nanochat/LICENSE +21 -0
  254. ommlds/nanochat/__init__.py +0 -0
  255. ommlds/nanochat/rustbpe/LICENSE +21 -0
  256. ommlds/nanochat/tokenizers.py +406 -0
  257. ommlds/server/cli.py +1 -2
  258. ommlds/server/server.py +5 -5
  259. ommlds/server/service.py +1 -1
  260. ommlds/specs/__init__.py +0 -0
  261. ommlds/specs/mcp/__init__.py +0 -0
  262. ommlds/specs/mcp/_marshal.py +23 -0
  263. ommlds/specs/mcp/clients.py +146 -0
  264. ommlds/specs/mcp/protocol.py +371 -0
  265. ommlds/tools/git.py +35 -12
  266. ommlds/tools/ocr.py +8 -9
  267. ommlds/wiki/analyze.py +6 -7
  268. ommlds/wiki/text/mfh.py +1 -5
  269. ommlds/wiki/text/wtp.py +1 -3
  270. ommlds/wiki/utils/xml.py +5 -5
  271. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/METADATA +24 -21
  272. ommlds-0.0.0.dev485.dist-info/RECORD +436 -0
  273. ommlds/cli/backends/standard.py +0 -20
  274. ommlds/cli/sessions/chat/base.py +0 -42
  275. ommlds/cli/sessions/chat/interactive.py +0 -73
  276. ommlds/cli/sessions/chat/printing.py +0 -96
  277. ommlds/cli/sessions/chat/prompt.py +0 -143
  278. ommlds/cli/sessions/chat/state.py +0 -109
  279. ommlds/cli/sessions/chat/tools.py +0 -91
  280. ommlds/cli/sessions/completion/completion.py +0 -44
  281. ommlds/cli/sessions/embedding/embedding.py +0 -42
  282. ommlds/cli/tools/config.py +0 -13
  283. ommlds/cli/tools/inject.py +0 -64
  284. ommlds/minichain/chat/stream/adapters.py +0 -69
  285. ommlds/minichain/lib/fs/ls/execution.py +0 -32
  286. ommlds-0.0.0.dev426.dist-info/RECORD +0 -303
  287. /ommlds/{cli/tools → backends/google}/__init__.py +0 -0
  288. /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
  289. /ommlds/{minichain/lib/fs/ls → cli/content}/__init__.py +0 -0
  290. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/rendering.py +0 -0
  291. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/running.py +0 -0
  292. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/WHEEL +0 -0
  293. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/entry_points.txt +0 -0
  294. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/licenses/LICENSE +0 -0
  295. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,23 @@
1
+ from omlish import lang
2
+ from omlish import marshal as msh
3
+
4
+ from .protocol import ContentBlock
5
+
6
+
7
+ ##
8
+
9
+
10
+ @lang.static_init
11
+ def _install_standard_marshaling() -> None:
12
+ for root_cls, tag_field in [
13
+ (ContentBlock, 'type'),
14
+ ]:
15
+ msh.install_standard_factories(*msh.standard_polymorphism_factories(
16
+ msh.polymorphism_from_subclasses(
17
+ root_cls,
18
+ naming=msh.Naming.SNAKE,
19
+ strip_suffix=msh.AutoStripSuffix,
20
+ ),
21
+ msh.FieldTypeTagging(tag_field),
22
+ unions='partial',
23
+ ))
@@ -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
+ }
ommlds/tools/git.py CHANGED
@@ -12,10 +12,12 @@ import urllib.request
12
12
 
13
13
  from omdev.git.magic import GIT_DIFF_OMIT_MAGIC_COMMENT
14
14
  from omdev.home.secrets import load_secrets
15
+ from omdev.py.srcheaders import get_py_header_lines
15
16
  from omdev.tools.git.messages import GitMessageGenerator
16
17
  from omlish import check
17
18
  from omlish import lang
18
19
  from omlish.configs.classes import Configurable
20
+ from omlish.http import all as http
19
21
  from omlish.subprocesses.sync import subprocesses
20
22
 
21
23
  from .. import minichain as mc
@@ -23,10 +25,8 @@ from ..minichain.backends.impls.openai.chat import OpenaiChatChoicesService
23
25
  from ..server.client import McServerClient
24
26
 
25
27
 
26
- if ta.TYPE_CHECKING:
28
+ with lang.auto_proxy_import(globals()):
27
29
  from ..minichain.backends.impls.mlx import chat as mc_mlx_chat
28
- else:
29
- mc_mlx_chat = lang.proxy_import('..minichain.backends.impls.mlx.chat', __package__)
30
30
 
31
31
 
32
32
  GitAiBackendConfigT = ta.TypeVar('GitAiBackendConfigT', bound='GitAiBackend.Config')
@@ -45,6 +45,22 @@ class GitAiBackend(Configurable[GitAiBackendConfigT], lang.Abstract):
45
45
  raise NotImplementedError
46
46
 
47
47
 
48
+ def _get_single_ai_message_str(resp: mc.ChatChoicesResponse) -> str:
49
+ return check.not_empty(
50
+ check.isinstance(
51
+ check.isinstance(
52
+ check.single(
53
+ check.single(
54
+ resp.v,
55
+ ).ms,
56
+ ),
57
+ mc.AiMessage,
58
+ ).c,
59
+ str,
60
+ ),
61
+ )
62
+
63
+
48
64
  #
49
65
 
50
66
 
@@ -62,13 +78,15 @@ class OpenaiGitAiBackend(GitAiBackend['OpenaiGitAiBackend.Config']):
62
78
  if (sec := load_secrets().try_get(key.lower())) is not None:
63
79
  os.environ[key] = sec.reveal()
64
80
 
65
- llm = OpenaiChatChoicesService()
81
+ llm = OpenaiChatChoicesService(
82
+ http_client=http.SyncAsyncHttpClient(http.client()),
83
+ )
66
84
 
67
- resp = llm.invoke(mc.ChatChoicesRequest(
85
+ resp = lang.sync_await(llm.invoke(mc.ChatChoicesRequest(
68
86
  [mc.UserMessage(prompt)],
69
87
  # FIXME: *((MaxTokens(self._config.max_tokens),) if self._config.max_tokens is not None else ()),
70
- ))
71
- return check.not_empty(check.isinstance(resp.v[0].m.c, str))
88
+ )))
89
+ return _get_single_ai_message_str(resp)
72
90
 
73
91
 
74
92
  #
@@ -93,11 +111,11 @@ class MlxGitAiBackend(GitAiBackend['MlxGitAiBackend.Config']):
93
111
 
94
112
  def _run_prompt(self, prompt: str) -> str:
95
113
  with mc_mlx_chat.MlxChatChoicesService(mc.ModelRepo.parse(self._config.model)) as llm:
96
- resp = llm.invoke(mc.ChatChoicesRequest(
114
+ resp = lang.sync_await(llm.invoke(mc.ChatChoicesRequest(
97
115
  [mc.UserMessage(prompt)],
98
116
  # FIXME: *((MaxTokens(self._config.max_tokens),) if self._config.max_tokens is not None else ()),
99
- ))
100
- text = check.not_empty(check.isinstance(resp.v[0].m.c, str))
117
+ )))
118
+ text = _get_single_ai_message_str(resp)
101
119
 
102
120
  text = _strip_markdown_code_block(text)
103
121
 
@@ -199,9 +217,14 @@ class AiGitMessageGenerator(GitMessageGenerator):
199
217
  except FileNotFoundError:
200
218
  return False
201
219
 
220
+ hls = get_py_header_lines(f_src)
221
+
202
222
  return any(
203
- l.startswith(GIT_DIFF_OMIT_MAGIC_COMMENT)
204
- for l in f_src.splitlines()
223
+ hl.src.strip() in (
224
+ '# @omlish-generated',
225
+ GIT_DIFF_OMIT_MAGIC_COMMENT,
226
+ )
227
+ for hl in hls
205
228
  )
206
229
 
207
230
  # TODO: configurable
ommlds/tools/ocr.py CHANGED
@@ -13,20 +13,13 @@ from omlish import check
13
13
  from omlish import lang
14
14
 
15
15
 
16
- if ta.TYPE_CHECKING:
16
+ with lang.auto_proxy_import(globals()):
17
17
  import pytesseract
18
18
  import rapidocr_onnxruntime as rapidocr
19
19
  from PIL import Image
20
20
 
21
21
  from omdev.clipboard import darwin_cf as darwin_clipboard
22
22
 
23
- else:
24
- pytesseract = lang.proxy_import('pytesseract')
25
- rapidocr = lang.proxy_import('rapidocr_onnxruntime')
26
- Image = lang.proxy_import('PIL.Image')
27
-
28
- darwin_clipboard = lang.proxy_import('omdev.clipboard.darwin_cf')
29
-
30
23
 
31
24
  ##
32
25
 
@@ -34,7 +27,7 @@ else:
34
27
  Ocr: ta.TypeAlias = ta.Callable[['Image.Image'], str]
35
28
 
36
29
  OCR_BACKENDS: ta.Mapping[str, Ocr] = {
37
- 'rapidocr': lambda img: '\n'.join(text[1] for text in rapidocr.RapidOCR()(img)[0] or []),
30
+ 'rapidocr': lambda img: '\n'.join(text[1] for text in rapidocr.RapidOCR()(_get_img_png_bytes(img))[0] or []),
38
31
  'tesseract': lambda img: pytesseract.image_to_string(img),
39
32
  }
40
33
 
@@ -62,6 +55,12 @@ def _get_img_data(file: str | None) -> ta.Any:
62
55
  return sys.stdin.buffer
63
56
 
64
57
 
58
+ def _get_img_png_bytes(img: 'Image.Image') -> bytes:
59
+ out = io.BytesIO()
60
+ img.save(out, format='PNG')
61
+ return out.getvalue()
62
+
63
+
65
64
  def _main() -> None:
66
65
  parser = argparse.ArgumentParser()
67
66
  parser.add_argument('file', nargs='?')