ommlds 0.0.0.dev440__py3-none-any.whl → 0.0.0.dev480__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 (271) hide show
  1. ommlds/.omlish-manifests.json +332 -35
  2. ommlds/__about__.py +15 -9
  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/_marshal.py +2 -2
  9. ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
  10. ommlds/backends/anthropic/protocol/sse/assemble.py +23 -7
  11. ommlds/backends/anthropic/protocol/sse/events.py +13 -0
  12. ommlds/backends/anthropic/protocol/types.py +30 -9
  13. ommlds/backends/google/protocol/__init__.py +3 -0
  14. ommlds/backends/google/protocol/_marshal.py +16 -0
  15. ommlds/backends/google/protocol/types.py +626 -0
  16. ommlds/backends/groq/_marshal.py +23 -0
  17. ommlds/backends/groq/protocol.py +249 -0
  18. ommlds/backends/mlx/generation.py +1 -1
  19. ommlds/backends/mlx/loading.py +58 -1
  20. ommlds/backends/ollama/__init__.py +0 -0
  21. ommlds/backends/ollama/protocol.py +170 -0
  22. ommlds/backends/openai/protocol/__init__.py +9 -28
  23. ommlds/backends/openai/protocol/_common.py +18 -0
  24. ommlds/backends/openai/protocol/_marshal.py +27 -0
  25. ommlds/backends/openai/protocol/chatcompletion/chunk.py +58 -31
  26. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +49 -44
  27. ommlds/backends/openai/protocol/chatcompletion/message.py +55 -43
  28. ommlds/backends/openai/protocol/chatcompletion/request.py +114 -66
  29. ommlds/backends/openai/protocol/chatcompletion/response.py +71 -45
  30. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +27 -20
  31. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +16 -7
  32. ommlds/backends/openai/protocol/completionusage.py +24 -15
  33. ommlds/backends/tavily/__init__.py +0 -0
  34. ommlds/backends/tavily/protocol.py +301 -0
  35. ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
  36. ommlds/backends/transformers/__init__.py +0 -0
  37. ommlds/backends/transformers/filecache.py +109 -0
  38. ommlds/backends/transformers/streamers.py +73 -0
  39. ommlds/cli/asyncs.py +30 -0
  40. ommlds/cli/backends/catalog.py +93 -0
  41. ommlds/cli/backends/configs.py +9 -0
  42. ommlds/cli/backends/inject.py +31 -36
  43. ommlds/cli/backends/injection.py +16 -0
  44. ommlds/cli/backends/types.py +46 -0
  45. ommlds/cli/content/__init__.py +0 -0
  46. ommlds/cli/content/messages.py +34 -0
  47. ommlds/cli/content/strings.py +42 -0
  48. ommlds/cli/inject.py +15 -32
  49. ommlds/cli/inputs/__init__.py +0 -0
  50. ommlds/cli/inputs/asyncs.py +32 -0
  51. ommlds/cli/inputs/sync.py +75 -0
  52. ommlds/cli/main.py +270 -110
  53. ommlds/cli/rendering/__init__.py +0 -0
  54. ommlds/cli/rendering/configs.py +9 -0
  55. ommlds/cli/rendering/inject.py +31 -0
  56. ommlds/cli/rendering/markdown.py +52 -0
  57. ommlds/cli/rendering/raw.py +73 -0
  58. ommlds/cli/rendering/types.py +21 -0
  59. ommlds/cli/secrets.py +21 -0
  60. ommlds/cli/sessions/base.py +1 -1
  61. ommlds/cli/sessions/chat/chat/__init__.py +0 -0
  62. ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
  63. ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
  64. ommlds/cli/sessions/chat/chat/ai/inject.py +74 -0
  65. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  66. ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
  67. ommlds/cli/sessions/chat/chat/ai/services.py +79 -0
  68. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  69. ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
  70. ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
  71. ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
  72. ommlds/cli/sessions/chat/chat/state/inject.py +36 -0
  73. ommlds/cli/sessions/chat/chat/state/inmemory.py +33 -0
  74. ommlds/cli/sessions/chat/chat/state/storage.py +52 -0
  75. ommlds/cli/sessions/chat/chat/state/types.py +38 -0
  76. ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
  77. ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
  78. ommlds/cli/sessions/chat/chat/user/inject.py +62 -0
  79. ommlds/cli/sessions/chat/chat/user/interactive.py +31 -0
  80. ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
  81. ommlds/cli/sessions/chat/chat/user/types.py +15 -0
  82. ommlds/cli/sessions/chat/configs.py +27 -0
  83. ommlds/cli/sessions/chat/driver.py +43 -0
  84. ommlds/cli/sessions/chat/inject.py +33 -65
  85. ommlds/cli/sessions/chat/phases/__init__.py +0 -0
  86. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  87. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  88. ommlds/cli/sessions/chat/phases/manager.py +29 -0
  89. ommlds/cli/sessions/chat/phases/types.py +29 -0
  90. ommlds/cli/sessions/chat/session.py +27 -0
  91. ommlds/cli/sessions/chat/tools/__init__.py +0 -0
  92. ommlds/cli/sessions/chat/tools/configs.py +22 -0
  93. ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
  94. ommlds/cli/sessions/chat/tools/execution.py +66 -0
  95. ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
  96. ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
  97. ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
  98. ommlds/cli/sessions/chat/tools/inject.py +88 -0
  99. ommlds/cli/sessions/chat/tools/injection.py +44 -0
  100. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  101. ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
  102. ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
  103. ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
  104. ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
  105. ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
  106. ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
  107. ommlds/cli/{tools/weather.py → sessions/chat/tools/weather/tools.py} +1 -1
  108. ommlds/cli/sessions/completion/configs.py +21 -0
  109. ommlds/cli/sessions/completion/inject.py +42 -0
  110. ommlds/cli/sessions/completion/session.py +35 -0
  111. ommlds/cli/sessions/embedding/configs.py +21 -0
  112. ommlds/cli/sessions/embedding/inject.py +42 -0
  113. ommlds/cli/sessions/embedding/session.py +33 -0
  114. ommlds/cli/sessions/inject.py +28 -11
  115. ommlds/cli/state/__init__.py +0 -0
  116. ommlds/cli/state/inject.py +28 -0
  117. ommlds/cli/{state.py → state/storage.py} +41 -24
  118. ommlds/minichain/__init__.py +84 -24
  119. ommlds/minichain/_marshal.py +49 -9
  120. ommlds/minichain/_typedvalues.py +2 -4
  121. ommlds/minichain/backends/catalogs/base.py +20 -1
  122. ommlds/minichain/backends/catalogs/simple.py +2 -2
  123. ommlds/minichain/backends/catalogs/strings.py +10 -8
  124. ommlds/minichain/backends/impls/anthropic/chat.py +65 -27
  125. ommlds/minichain/backends/impls/anthropic/names.py +10 -8
  126. ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
  127. ommlds/minichain/backends/impls/anthropic/stream.py +111 -43
  128. ommlds/minichain/backends/impls/duckduckgo/search.py +1 -1
  129. ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
  130. ommlds/minichain/backends/impls/dummy/chat.py +69 -0
  131. ommlds/minichain/backends/impls/google/chat.py +114 -22
  132. ommlds/minichain/backends/impls/google/search.py +7 -2
  133. ommlds/minichain/backends/impls/google/stream.py +219 -0
  134. ommlds/minichain/backends/impls/google/tools.py +149 -0
  135. ommlds/minichain/backends/impls/groq/__init__.py +0 -0
  136. ommlds/minichain/backends/impls/groq/chat.py +75 -0
  137. ommlds/minichain/backends/impls/groq/names.py +48 -0
  138. ommlds/minichain/backends/impls/groq/protocol.py +143 -0
  139. ommlds/minichain/backends/impls/groq/stream.py +125 -0
  140. ommlds/minichain/backends/impls/llamacpp/chat.py +33 -18
  141. ommlds/minichain/backends/impls/llamacpp/completion.py +1 -1
  142. ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
  143. ommlds/minichain/backends/impls/llamacpp/stream.py +37 -20
  144. ommlds/minichain/backends/impls/mistral.py +20 -5
  145. ommlds/minichain/backends/impls/mlx/chat.py +96 -22
  146. ommlds/minichain/backends/impls/ollama/__init__.py +0 -0
  147. ommlds/minichain/backends/impls/ollama/chat.py +199 -0
  148. ommlds/minichain/backends/impls/openai/chat.py +18 -8
  149. ommlds/minichain/backends/impls/openai/completion.py +10 -3
  150. ommlds/minichain/backends/impls/openai/embedding.py +10 -3
  151. ommlds/minichain/backends/impls/openai/format.py +131 -106
  152. ommlds/minichain/backends/impls/openai/names.py +31 -5
  153. ommlds/minichain/backends/impls/openai/stream.py +43 -25
  154. ommlds/minichain/backends/impls/tavily.py +66 -0
  155. ommlds/minichain/backends/impls/tinygrad/chat.py +23 -16
  156. ommlds/minichain/backends/impls/transformers/sentence.py +1 -1
  157. ommlds/minichain/backends/impls/transformers/tokens.py +1 -1
  158. ommlds/minichain/backends/impls/transformers/transformers.py +155 -34
  159. ommlds/minichain/backends/strings/parsing.py +1 -1
  160. ommlds/minichain/backends/strings/resolving.py +4 -1
  161. ommlds/minichain/chat/_marshal.py +16 -9
  162. ommlds/minichain/chat/choices/adapters.py +4 -4
  163. ommlds/minichain/chat/choices/services.py +1 -1
  164. ommlds/minichain/chat/choices/stream/__init__.py +0 -0
  165. ommlds/minichain/chat/choices/stream/adapters.py +35 -0
  166. ommlds/minichain/chat/choices/stream/joining.py +31 -0
  167. ommlds/minichain/chat/choices/stream/services.py +45 -0
  168. ommlds/minichain/chat/choices/stream/types.py +43 -0
  169. ommlds/minichain/chat/choices/types.py +2 -2
  170. ommlds/minichain/chat/history.py +3 -3
  171. ommlds/minichain/chat/messages.py +55 -19
  172. ommlds/minichain/chat/services.py +3 -3
  173. ommlds/minichain/chat/stream/_marshal.py +16 -0
  174. ommlds/minichain/chat/stream/joining.py +85 -0
  175. ommlds/minichain/chat/stream/services.py +15 -21
  176. ommlds/minichain/chat/stream/types.py +32 -19
  177. ommlds/minichain/chat/tools/execution.py +8 -7
  178. ommlds/minichain/chat/tools/ids.py +9 -15
  179. ommlds/minichain/chat/tools/parsing.py +17 -26
  180. ommlds/minichain/chat/transforms/base.py +29 -38
  181. ommlds/minichain/chat/transforms/metadata.py +30 -4
  182. ommlds/minichain/chat/transforms/services.py +9 -11
  183. ommlds/minichain/content/_marshal.py +44 -20
  184. ommlds/minichain/content/json.py +13 -0
  185. ommlds/minichain/content/materialize.py +14 -21
  186. ommlds/minichain/content/prepare.py +4 -0
  187. ommlds/minichain/content/transforms/interleave.py +1 -1
  188. ommlds/minichain/content/transforms/squeeze.py +1 -1
  189. ommlds/minichain/content/transforms/stringify.py +1 -1
  190. ommlds/minichain/json.py +20 -0
  191. ommlds/minichain/lib/code/__init__.py +0 -0
  192. ommlds/minichain/lib/code/prompts.py +6 -0
  193. ommlds/minichain/lib/fs/binfiles.py +108 -0
  194. ommlds/minichain/lib/fs/context.py +126 -0
  195. ommlds/minichain/lib/fs/errors.py +101 -0
  196. ommlds/minichain/lib/fs/suggestions.py +36 -0
  197. ommlds/minichain/lib/fs/tools/__init__.py +0 -0
  198. ommlds/minichain/lib/fs/tools/edit.py +104 -0
  199. ommlds/minichain/lib/fs/tools/ls.py +38 -0
  200. ommlds/minichain/lib/fs/tools/read.py +115 -0
  201. ommlds/minichain/lib/fs/tools/recursivels/__init__.py +0 -0
  202. ommlds/minichain/lib/fs/tools/recursivels/execution.py +40 -0
  203. ommlds/minichain/lib/todo/__init__.py +0 -0
  204. ommlds/minichain/lib/todo/context.py +54 -0
  205. ommlds/minichain/lib/todo/tools/__init__.py +0 -0
  206. ommlds/minichain/lib/todo/tools/read.py +44 -0
  207. ommlds/minichain/lib/todo/tools/write.py +335 -0
  208. ommlds/minichain/lib/todo/types.py +60 -0
  209. ommlds/minichain/llms/_marshal.py +25 -17
  210. ommlds/minichain/llms/types.py +4 -0
  211. ommlds/minichain/registries/globals.py +18 -4
  212. ommlds/minichain/resources.py +66 -43
  213. ommlds/minichain/search.py +1 -1
  214. ommlds/minichain/services/_marshal.py +46 -39
  215. ommlds/minichain/services/facades.py +3 -3
  216. ommlds/minichain/services/services.py +1 -1
  217. ommlds/minichain/standard.py +8 -0
  218. ommlds/minichain/stream/services.py +152 -38
  219. ommlds/minichain/stream/wrap.py +22 -24
  220. ommlds/minichain/tools/_marshal.py +1 -1
  221. ommlds/minichain/tools/execution/catalog.py +2 -1
  222. ommlds/minichain/tools/execution/context.py +34 -14
  223. ommlds/minichain/tools/execution/errors.py +15 -0
  224. ommlds/minichain/tools/execution/executors.py +8 -3
  225. ommlds/minichain/tools/execution/reflect.py +40 -5
  226. ommlds/minichain/tools/fns.py +46 -9
  227. ommlds/minichain/tools/jsonschema.py +14 -5
  228. ommlds/minichain/tools/reflect.py +54 -18
  229. ommlds/minichain/tools/types.py +33 -1
  230. ommlds/minichain/utils.py +27 -0
  231. ommlds/minichain/vectors/_marshal.py +11 -10
  232. ommlds/nanochat/LICENSE +21 -0
  233. ommlds/nanochat/__init__.py +0 -0
  234. ommlds/nanochat/rustbpe/LICENSE +21 -0
  235. ommlds/nanochat/tokenizers.py +406 -0
  236. ommlds/server/server.py +3 -3
  237. ommlds/specs/__init__.py +0 -0
  238. ommlds/specs/mcp/__init__.py +0 -0
  239. ommlds/specs/mcp/_marshal.py +23 -0
  240. ommlds/specs/mcp/protocol.py +266 -0
  241. ommlds/tools/git.py +27 -10
  242. ommlds/tools/ocr.py +8 -9
  243. ommlds/wiki/analyze.py +2 -2
  244. ommlds/wiki/text/mfh.py +1 -5
  245. ommlds/wiki/text/wtp.py +1 -3
  246. ommlds/wiki/utils/xml.py +5 -5
  247. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/METADATA +24 -21
  248. ommlds-0.0.0.dev480.dist-info/RECORD +427 -0
  249. ommlds/cli/backends/standard.py +0 -20
  250. ommlds/cli/sessions/chat/base.py +0 -42
  251. ommlds/cli/sessions/chat/interactive.py +0 -73
  252. ommlds/cli/sessions/chat/printing.py +0 -96
  253. ommlds/cli/sessions/chat/prompt.py +0 -143
  254. ommlds/cli/sessions/chat/state.py +0 -109
  255. ommlds/cli/sessions/chat/tools.py +0 -91
  256. ommlds/cli/sessions/completion/completion.py +0 -44
  257. ommlds/cli/sessions/embedding/embedding.py +0 -42
  258. ommlds/cli/tools/config.py +0 -13
  259. ommlds/cli/tools/inject.py +0 -64
  260. ommlds/minichain/chat/stream/adapters.py +0 -69
  261. ommlds/minichain/lib/fs/ls/execution.py +0 -32
  262. ommlds-0.0.0.dev440.dist-info/RECORD +0 -303
  263. /ommlds/{cli/tools → backends/google}/__init__.py +0 -0
  264. /ommlds/{minichain/lib/fs/ls → backends/groq}/__init__.py +0 -0
  265. /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
  266. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/rendering.py +0 -0
  267. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/running.py +0 -0
  268. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/WHEEL +0 -0
  269. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/entry_points.txt +0 -0
  270. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/licenses/LICENSE +0 -0
  271. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,109 @@
1
+ import contextlib
2
+ import dataclasses as dc
3
+ import os
4
+ import threading
5
+ import typing as ta
6
+
7
+ import transformers as tfm
8
+
9
+ from omlish import lang
10
+
11
+ from ..._hacks.funcs import create_detour
12
+
13
+
14
+ ##
15
+
16
+
17
+ @dc.dataclass(frozen=True, kw_only=True)
18
+ class _FileCachePatchContext:
19
+ local_first: bool = False
20
+ local_config_present_is_authoritative: bool = False
21
+
22
+
23
+ _FILE_CACHE_PATCH_CONTEXT_TLS = threading.local()
24
+
25
+
26
+ def _get_file_cache_patch_context() -> _FileCachePatchContext:
27
+ try:
28
+ return _FILE_CACHE_PATCH_CONTEXT_TLS.context
29
+ except AttributeError:
30
+ ctx = _FILE_CACHE_PATCH_CONTEXT_TLS.context = _FileCachePatchContext()
31
+ return ctx
32
+
33
+
34
+ _FILE_CACHE_PATCH_LOCK = threading.Lock()
35
+
36
+
37
+ @lang.cached_function(lock=_FILE_CACHE_PATCH_LOCK)
38
+ def patch_file_cache() -> None:
39
+ """
40
+ I tried to make a `local_first_pipeline` function to be called instead of `tfm.pipeline`, I really did, but the
41
+ transformers code is such a disgusting rat's nest full of direct static calls to the caching code strewn about at
42
+ every layer with no concern whatsoever for forwarding kwargs where they need to go.
43
+ """
44
+
45
+ from transformers.utils.hub import cached_files
46
+
47
+ orig_cached_files: ta.Callable[..., str | None] = lang.copy_function(cached_files) # type: ignore
48
+
49
+ get_file_cache_patch_context = _get_file_cache_patch_context
50
+
51
+ def new_cached_files(
52
+ path_or_repo_id: str | os.PathLike,
53
+ filenames: list[str],
54
+ **kwargs: ta.Any,
55
+ ) -> str | None:
56
+ ctx = get_file_cache_patch_context()
57
+
58
+ if ctx.local_first and not kwargs.get('local_files_only'):
59
+ try:
60
+ local = orig_cached_files(
61
+ path_or_repo_id,
62
+ filenames,
63
+ **{**kwargs, 'local_files_only': True},
64
+ )
65
+ except OSError as e: # noqa
66
+ pass
67
+ else:
68
+ return local
69
+
70
+ if ctx.local_config_present_is_authoritative:
71
+ try:
72
+ local_config = orig_cached_files(
73
+ path_or_repo_id,
74
+ [tfm.CONFIG_NAME],
75
+ **{**kwargs, 'local_files_only': True},
76
+ )
77
+ except OSError as e: # noqa
78
+ pass
79
+ else:
80
+ raise OSError(
81
+ f'Files {filenames!r} requested under local_first '
82
+ f'but local_config present at {local_config!r}, '
83
+ f'assuming files do not exist.',
84
+ )
85
+
86
+ return orig_cached_files(path_or_repo_id, filenames, **kwargs)
87
+
88
+ cached_files.__code__ = create_detour(cached_files, new_cached_files, as_kwargs=True)
89
+
90
+
91
+ @contextlib.contextmanager
92
+ def file_cache_patch_context(
93
+ *,
94
+ local_first: bool = False,
95
+ local_config_present_is_authoritative: bool = False,
96
+ ) -> ta.Generator[None]:
97
+ patch_file_cache()
98
+
99
+ new_ctx = dc.replace(
100
+ old_ctx := _get_file_cache_patch_context(),
101
+ local_first=local_first,
102
+ local_config_present_is_authoritative=local_config_present_is_authoritative,
103
+ )
104
+
105
+ _FILE_CACHE_PATCH_CONTEXT_TLS.context = new_ctx
106
+ try:
107
+ yield
108
+ finally:
109
+ _FILE_CACHE_PATCH_CONTEXT_TLS.context = old_ctx
@@ -0,0 +1,73 @@
1
+ import functools
2
+ import typing as ta
3
+
4
+ import transformers as tfm
5
+
6
+
7
+ T = ta.TypeVar('T')
8
+ P = ta.ParamSpec('P')
9
+
10
+
11
+ ##
12
+
13
+
14
+ class CancellableTextStreamer(tfm.TextStreamer):
15
+ class Callback(ta.Protocol):
16
+ def __call__(self, text: str, *, stream_end: bool) -> None: ...
17
+
18
+ def __init__(
19
+ self,
20
+ tokenizer: tfm.AutoTokenizer,
21
+ callback: Callback,
22
+ *,
23
+ skip_prompt: bool = False,
24
+ **decode_kwargs: ta.Any,
25
+ ) -> None:
26
+ super().__init__(
27
+ tokenizer,
28
+ skip_prompt=skip_prompt,
29
+ **decode_kwargs,
30
+ )
31
+
32
+ self.callback = callback
33
+
34
+ _cancelled: bool = False
35
+
36
+ #
37
+
38
+ @property
39
+ def cancelled(self) -> bool:
40
+ return self._cancelled
41
+
42
+ def cancel(self) -> None:
43
+ self._cancelled = True
44
+
45
+ class Cancelled(BaseException): # noqa
46
+ pass
47
+
48
+ @staticmethod
49
+ def ignoring_cancelled(fn: ta.Callable[P, T]) -> ta.Callable[P, T | None]:
50
+ @functools.wraps(fn)
51
+ def inner(*args, **kwargs):
52
+ try:
53
+ return fn(*args, **kwargs)
54
+ except CancellableTextStreamer.Cancelled:
55
+ pass
56
+
57
+ return inner
58
+
59
+ def _maybe_raise_cancelled(self) -> None:
60
+ if self._cancelled:
61
+ raise CancellableTextStreamer.Cancelled
62
+
63
+ #
64
+
65
+ def put(self, value: ta.Any) -> None:
66
+ self._maybe_raise_cancelled()
67
+ super().put(value)
68
+ self._maybe_raise_cancelled()
69
+
70
+ def on_finalized_text(self, text: str, stream_end: bool = False) -> None:
71
+ self._maybe_raise_cancelled()
72
+ self.callback(text, stream_end=stream_end)
73
+ self._maybe_raise_cancelled()
ommlds/cli/asyncs.py ADDED
@@ -0,0 +1,30 @@
1
+ import abc
2
+ import functools
3
+ import typing as ta
4
+
5
+ from omlish import lang
6
+
7
+
8
+ with lang.auto_proxy_import(globals()):
9
+ import anyio
10
+
11
+
12
+ T = ta.TypeVar('T')
13
+ P = ta.ParamSpec('P')
14
+
15
+
16
+ ##
17
+
18
+
19
+ class AsyncThreadRunner(lang.Abstract):
20
+ @abc.abstractmethod
21
+ def run_in_thread(self, fn: ta.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> ta.Awaitable[T]:
22
+ raise NotImplementedError
23
+
24
+
25
+ ##
26
+
27
+
28
+ class AnyioAsyncThreadRunner(AsyncThreadRunner):
29
+ def run_in_thread(self, fn: ta.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> ta.Awaitable[T]:
30
+ return anyio.to_thread.run_sync(functools.partial(fn, *args, **kwargs))
@@ -0,0 +1,93 @@
1
+ import contextlib
2
+ import typing as ta
3
+
4
+ from omlish import lang
5
+
6
+ from ... import minichain as mc
7
+ from .types import BackendConfigs
8
+ from .types import BackendName
9
+ from .types import BackendProvider
10
+ from .types import ChatChoicesServiceBackendProvider
11
+ from .types import ChatChoicesStreamServiceBackendProvider
12
+ from .types import CompletionServiceBackendProvider
13
+ from .types import DefaultBackendName
14
+ from .types import EmbeddingServiceBackendProvider
15
+ from .types import ServiceT
16
+
17
+
18
+ ##
19
+
20
+
21
+ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
22
+ class Instantiator(lang.Func2['mc.BackendCatalog.Backend', BackendConfigs | None, ta.Awaitable[ta.Any]]):
23
+ pass
24
+
25
+ def __init__(
26
+ self,
27
+ *,
28
+ name: BackendName | None = None,
29
+ default_name: DefaultBackendName | None = None,
30
+ catalog: 'mc.BackendCatalog',
31
+ configs: BackendConfigs | None = None,
32
+ instantiator: Instantiator | None = None,
33
+ ) -> None:
34
+ super().__init__()
35
+
36
+ self._name = name
37
+ self._default_name = default_name
38
+ self._catalog = catalog
39
+ self._configs = configs
40
+ if instantiator is None:
41
+ instantiator = CatalogBackendProvider.Instantiator(lang.as_async(lambda be, cfgs: be.factory(*cfgs or [])))
42
+ self._instantiator = instantiator
43
+
44
+ @contextlib.asynccontextmanager
45
+ async def _provide_backend(self, cls: type[ServiceT]) -> ta.AsyncIterator[ServiceT]:
46
+ name: str
47
+ if self._name is not None:
48
+ name = self._name
49
+ elif self._default_name is not None:
50
+ name = self._default_name
51
+ else:
52
+ raise RuntimeError('No backend name specified')
53
+
54
+ be = self._catalog.get_backend(cls, name)
55
+
56
+ service: ServiceT
57
+ async with lang.async_or_sync_maybe_managing(await self._instantiator(be, self._configs)) as service:
58
+ yield service
59
+
60
+
61
+ ##
62
+
63
+
64
+ class CatalogChatChoicesServiceBackendProvider(
65
+ CatalogBackendProvider['mc.ChatChoicesService'],
66
+ ChatChoicesServiceBackendProvider,
67
+ ):
68
+ def provide_backend(self) -> ta.AsyncContextManager['mc.ChatChoicesService']:
69
+ return self._provide_backend(mc.ChatChoicesService) # type: ignore[type-abstract]
70
+
71
+
72
+ class CatalogChatChoicesStreamServiceBackendProvider(
73
+ CatalogBackendProvider['mc.ChatChoicesStreamService'],
74
+ ChatChoicesStreamServiceBackendProvider,
75
+ ):
76
+ def provide_backend(self) -> ta.AsyncContextManager['mc.ChatChoicesStreamService']:
77
+ return self._provide_backend(mc.ChatChoicesStreamService) # type: ignore[type-abstract]
78
+
79
+
80
+ class CatalogCompletionServiceBackendProvider(
81
+ CatalogBackendProvider['mc.CompletionService'],
82
+ CompletionServiceBackendProvider,
83
+ ):
84
+ def provide_backend(self) -> ta.AsyncContextManager['mc.CompletionService']:
85
+ return self._provide_backend(mc.CompletionService) # type: ignore[type-abstract]
86
+
87
+
88
+ class CatalogEmbeddingServiceBackendProvider(
89
+ CatalogBackendProvider['mc.EmbeddingService'],
90
+ EmbeddingServiceBackendProvider,
91
+ ):
92
+ def provide_backend(self) -> ta.AsyncContextManager['mc.EmbeddingService']:
93
+ return self._provide_backend(mc.EmbeddingService) # type: ignore[type-abstract]
@@ -0,0 +1,9 @@
1
+ from omlish import dataclasses as dc
2
+
3
+
4
+ ##
5
+
6
+
7
+ @dc.dataclass(frozen=True, kw_only=True)
8
+ class BackendConfig:
9
+ backend: str | None = None
@@ -2,69 +2,64 @@ import typing as ta
2
2
 
3
3
  from omlish import inject as inj
4
4
  from omlish import lang
5
+ from omlish import typedvalues as tv
5
6
 
6
7
  from ... import minichain as mc
8
+ from .configs import BackendConfig
9
+ from .injection import backend_configs
10
+
11
+
12
+ with lang.auto_proxy_import(globals()):
13
+ from ...minichain.backends.impls.huggingface import repos as hf_repos
14
+ from . import catalog as _catalog
15
+ from . import types as _types
7
16
 
8
17
 
9
18
  ##
10
19
 
11
20
 
12
- def bind_strings_backends() -> inj.Elements:
21
+ def bind_backends(cfg: BackendConfig = BackendConfig()) -> inj.Elements:
13
22
  lst: list[inj.Elemental] = []
14
23
 
24
+ #
25
+
15
26
  lst.extend([
16
27
  inj.bind(mc.BackendStringBackendCatalog, singleton=True),
17
28
  inj.bind(mc.BackendCatalog, to_key=mc.BackendStringBackendCatalog),
18
29
  ])
19
30
 
20
- from ...minichain.backends.impls.huggingface.repos import HuggingfaceModelRepoResolver
21
-
22
31
  lst.extend([
23
- inj.bind(HuggingfaceModelRepoResolver, singleton=True),
24
- inj.bind(mc.ModelRepoResolver, to_key=HuggingfaceModelRepoResolver),
32
+ inj.bind(hf_repos.HuggingfaceModelRepoResolver, singleton=True),
33
+ inj.bind(mc.ModelRepoResolver, to_key=hf_repos.HuggingfaceModelRepoResolver),
25
34
 
26
35
  ])
27
36
 
28
- return inj.as_elements(*lst)
29
-
30
-
31
- def bind_simple_backends() -> inj.Elements:
32
- lst: list[inj.Elemental] = []
37
+ #
33
38
 
34
- lst.extend([
35
- inj.set_binder[mc.SimpleBackendCatalogEntry](),
36
- inj.bind(
37
- lang.typed_lambda(mc.SimpleBackendCatalogEntries, s=ta.AbstractSet[mc.SimpleBackendCatalogEntry])(
38
- lambda s: list(s),
39
- ),
40
- singleton=True,
41
- ),
42
- ])
39
+ lst.append(backend_configs().bind_items_provider(singleton=True))
43
40
 
44
- lst.extend([
45
- inj.bind(mc.SimpleBackendCatalog, singleton=True),
46
- inj.bind(mc.BackendCatalog, to_key=mc.SimpleBackendCatalog),
47
- ])
41
+ #
48
42
 
49
- from .standard import STANDARD_BACKEND_CATALOG_ENTRIES
43
+ if cfg.backend is not None:
44
+ lst.append(inj.bind(_types.BackendName, to_const=cfg.backend))
50
45
 
51
46
  lst.extend([
52
- inj.bind_set_entry_const(ta.AbstractSet[mc.SimpleBackendCatalogEntry], e)
53
- for e in STANDARD_BACKEND_CATALOG_ENTRIES
47
+ inj.bind(_types.ChatChoicesServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesServiceBackendProvider, singleton=True), # noqa
48
+ inj.bind(_types.ChatChoicesStreamServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesStreamServiceBackendProvider, singleton=True), # noqa
49
+ inj.bind(_types.CompletionServiceBackendProvider, to_ctor=_catalog.CatalogCompletionServiceBackendProvider, singleton=True), # noqa
50
+ inj.bind(_types.EmbeddingServiceBackendProvider, to_ctor=_catalog.CatalogEmbeddingServiceBackendProvider, singleton=True), # noqa
54
51
  ])
55
52
 
56
- return inj.as_elements(*lst)
53
+ #
57
54
 
55
+ async def catalog_backend_instantiator_provider(injector: inj.AsyncInjector) -> _catalog.CatalogBackendProvider.Instantiator: # noqa
56
+ async def inner(be: 'mc.BackendCatalog.Backend', cfgs: _types.BackendConfigs | None) -> ta.Any:
57
+ kwt = inj.build_kwargs_target(be.factory, non_strict=True)
58
+ kw = await injector.provide_kwargs(kwt)
59
+ return be.factory(*tv.collect(*(be.configs or []), *(cfgs or []), override=True), **kw)
58
60
 
59
- def bind_backends(
60
- *,
61
- enable_backend_strings: bool = False,
62
- ) -> inj.Elements:
63
- lst: list[inj.Elemental] = []
61
+ return _catalog.CatalogBackendProvider.Instantiator(inner)
64
62
 
65
- if enable_backend_strings:
66
- lst.append(bind_strings_backends())
67
- else:
68
- lst.append(bind_simple_backends())
63
+ lst.append(inj.bind(_catalog.CatalogBackendProvider.Instantiator, to_async_fn=catalog_backend_instantiator_provider)) # noqa
69
64
 
70
65
  return inj.as_elements(*lst)
@@ -0,0 +1,16 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+ from ... import minichain as mc
5
+
6
+
7
+ with lang.auto_proxy_import(globals()):
8
+ from . import types as _types
9
+
10
+
11
+ ##
12
+
13
+
14
+ @lang.cached_function
15
+ def backend_configs() -> 'inj.ItemsBinderHelper[mc.Config]':
16
+ return inj.items_binder_helper[mc.Config](_types.BackendConfigs)
@@ -0,0 +1,46 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import lang
5
+
6
+ from ... import minichain as mc
7
+
8
+
9
+ ServiceT = ta.TypeVar('ServiceT', bound=mc.Service)
10
+
11
+
12
+ ##
13
+
14
+
15
+ BackendName = ta.NewType('BackendName', str)
16
+ DefaultBackendName = ta.NewType('DefaultBackendName', str)
17
+
18
+ BackendConfigs = ta.NewType('BackendConfigs', ta.Sequence['mc.Config'])
19
+
20
+
21
+ ##
22
+
23
+
24
+ class BackendProvider(lang.Abstract, ta.Generic[ServiceT]):
25
+ @abc.abstractmethod
26
+ def provide_backend(self) -> ta.AsyncContextManager[ServiceT]:
27
+ raise NotImplementedError
28
+
29
+
30
+ ##
31
+
32
+
33
+ class ChatChoicesServiceBackendProvider(BackendProvider['mc.ChatChoicesService'], lang.Abstract):
34
+ pass
35
+
36
+
37
+ class ChatChoicesStreamServiceBackendProvider(BackendProvider['mc.ChatChoicesStreamService'], lang.Abstract):
38
+ pass
39
+
40
+
41
+ class CompletionServiceBackendProvider(BackendProvider['mc.CompletionService'], lang.Abstract):
42
+ pass
43
+
44
+
45
+ class EmbeddingServiceBackendProvider(BackendProvider['mc.EmbeddingService'], lang.Abstract):
46
+ pass
File without changes
@@ -0,0 +1,34 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish import lang
6
+
7
+ from ... import minichain as mc
8
+
9
+
10
+ ##
11
+
12
+
13
+ class MessageContentExtractor(lang.Abstract):
14
+ @abc.abstractmethod
15
+ def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
16
+ raise NotImplementedError
17
+
18
+
19
+ class MessageContentExtractorImpl(MessageContentExtractor):
20
+ def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
21
+ if isinstance(message, (mc.SystemMessage, mc.UserMessage, mc.AiMessage)):
22
+ if message.c is not None:
23
+ return check.isinstance(message.c, str)
24
+ else:
25
+ return None
26
+
27
+ elif isinstance(message, mc.ToolUseMessage):
28
+ return None
29
+
30
+ elif isinstance(message, mc.ToolUseResultMessage):
31
+ return check.isinstance(message.tur.c, str)
32
+
33
+ else:
34
+ raise TypeError(message)
@@ -0,0 +1,42 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import lang
5
+ from omlish.formats import json
6
+
7
+ from ... import minichain as mc
8
+
9
+
10
+ ##
11
+
12
+
13
+ class ContentStringifier(lang.Abstract):
14
+ @abc.abstractmethod
15
+ def stringify_content(self, content: 'mc.Content') -> str | None:
16
+ raise NotImplementedError
17
+
18
+
19
+ class ContentStringifierImpl(ContentStringifier):
20
+ def stringify_content(self, content: 'mc.Content') -> str | None:
21
+ if isinstance(content, str):
22
+ return content
23
+
24
+ elif isinstance(content, mc.JsonContent):
25
+ return json.dumps_pretty(content.v)
26
+
27
+ else:
28
+ raise TypeError(content)
29
+
30
+
31
+ class HasContentStringifier(lang.Abstract):
32
+ def __init__(
33
+ self,
34
+ *args: ta.Any,
35
+ content_stringifier: ContentStringifier | None = None,
36
+ **kwargs: ta.Any,
37
+ ) -> None:
38
+ super().__init__(*args, **kwargs)
39
+
40
+ if content_stringifier is None:
41
+ content_stringifier = ContentStringifierImpl()
42
+ self._content_stringifier = content_stringifier
ommlds/cli/inject.py CHANGED
@@ -1,30 +1,13 @@
1
- import os.path
1
+ import typing as ta
2
2
 
3
- from omdev.home.paths import get_home_paths
4
3
  from omlish import inject as inj
5
4
  from omlish import lang
6
5
 
7
6
 
8
7
  with lang.auto_proxy_import(globals()):
9
- from . import state
10
- from .backends import inject as backends_inj
11
- from .sessions import base as sessions_base
12
- from .sessions import inject as sessions_inj
13
- from .tools import config as tools_cfg
14
- from .tools import inject as tools_inj
15
-
16
-
17
- ##
18
-
19
-
20
- def _provide_state_storage() -> 'state.StateStorage':
21
- state_dir = os.path.join(get_home_paths().state_dir, 'minichain', 'cli')
22
- if not os.path.exists(state_dir):
23
- os.makedirs(state_dir, exist_ok=True)
24
- os.chmod(state_dir, 0o770) # noqa
25
-
26
- state_file = os.path.join(state_dir, 'state.json')
27
- return state.JsonFileStateStorage(state_file)
8
+ from . import asyncs
9
+ from .sessions import inject as _sessions
10
+ from .state import inject as _state
28
11
 
29
12
 
30
13
  ##
@@ -32,23 +15,23 @@ def _provide_state_storage() -> 'state.StateStorage':
32
15
 
33
16
  def bind_main(
34
17
  *,
35
- session_cfg: 'sessions_base.Session.Config',
36
- tools_config: 'tools_cfg.ToolsConfig',
37
- enable_backend_strings: bool = False,
18
+ session_cfg: ta.Any,
38
19
  ) -> inj.Elements:
39
- els: list[inj.Elemental] = [
40
- backends_inj.bind_backends(
41
- enable_backend_strings=enable_backend_strings,
42
- ),
20
+ els: list[inj.Elemental] = []
21
+
22
+ #
43
23
 
44
- sessions_inj.bind_sessions(session_cfg),
24
+ els.extend([
25
+ _sessions.bind_sessions(session_cfg),
45
26
 
46
- tools_inj.bind_tools(tools_config),
47
- ]
27
+ _state.bind_state(),
28
+ ])
48
29
 
49
30
  #
50
31
 
51
- els.append(inj.bind(_provide_state_storage, singleton=True))
32
+ els.extend([
33
+ inj.bind(asyncs.AsyncThreadRunner, to_ctor=asyncs.AnyioAsyncThreadRunner),
34
+ ])
52
35
 
53
36
  #
54
37
 
File without changes
@@ -0,0 +1,32 @@
1
+ import typing as ta
2
+
3
+ from .. import asyncs
4
+ from .sync import SyncStringInput
5
+
6
+
7
+ ##
8
+
9
+
10
+ class AsyncStringInput(ta.Protocol):
11
+ def __call__(self) -> ta.Awaitable[str]: ...
12
+
13
+
14
+ class ThreadAsyncStringInput:
15
+ def __init__(self, child: SyncStringInput, runner: asyncs.AsyncThreadRunner) -> None:
16
+ super().__init__()
17
+
18
+ self._child = child
19
+ self._runner = runner
20
+
21
+ async def __call__(self) -> str:
22
+ return await self._runner.run_in_thread(self._child)
23
+
24
+
25
+ class SyncAsyncStringInput:
26
+ def __init__(self, child: SyncStringInput) -> None:
27
+ super().__init__()
28
+
29
+ self._child = child
30
+
31
+ async def __call__(self) -> str:
32
+ return self._child()