ommlds 0.0.0.dev480__py3-none-any.whl → 0.0.0.dev503__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 (277) hide show
  1. ommlds/.omlish-manifests.json +100 -33
  2. ommlds/README.md +11 -0
  3. ommlds/__about__.py +9 -6
  4. ommlds/backends/anthropic/protocol/__init__.py +13 -1
  5. ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
  6. ommlds/backends/anthropic/protocol/sse/events.py +2 -0
  7. ommlds/backends/cerebras/__init__.py +7 -0
  8. ommlds/backends/cerebras/_dataclasses.py +4254 -0
  9. ommlds/backends/cerebras/_marshal.py +24 -0
  10. ommlds/backends/cerebras/protocol.py +312 -0
  11. ommlds/backends/google/protocol/__init__.py +13 -0
  12. ommlds/backends/google/protocol/_dataclasses.py +5997 -0
  13. ommlds/backends/groq/__init__.py +7 -0
  14. ommlds/backends/groq/_dataclasses.py +3901 -0
  15. ommlds/backends/groq/clients.py +9 -0
  16. ommlds/backends/llamacpp/logging.py +4 -1
  17. ommlds/backends/mlx/caching.py +7 -3
  18. ommlds/backends/mlx/cli.py +10 -7
  19. ommlds/backends/mlx/generation.py +18 -16
  20. ommlds/backends/mlx/limits.py +10 -6
  21. ommlds/backends/mlx/loading.py +7 -4
  22. ommlds/backends/ollama/__init__.py +7 -0
  23. ommlds/backends/ollama/_dataclasses.py +3488 -0
  24. ommlds/backends/ollama/protocol.py +3 -0
  25. ommlds/backends/openai/protocol/__init__.py +15 -1
  26. ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
  27. ommlds/backends/tavily/__init__.py +7 -0
  28. ommlds/backends/tavily/_dataclasses.py +1734 -0
  29. ommlds/backends/transformers/__init__.py +14 -0
  30. ommlds/cli/__init__.py +7 -0
  31. ommlds/cli/_dataclasses.py +3515 -0
  32. ommlds/cli/backends/catalog.py +0 -5
  33. ommlds/cli/backends/inject.py +70 -7
  34. ommlds/cli/backends/meta.py +82 -0
  35. ommlds/cli/content/messages.py +1 -1
  36. ommlds/cli/inject.py +11 -3
  37. ommlds/cli/main.py +137 -68
  38. ommlds/cli/rendering/types.py +6 -0
  39. ommlds/cli/secrets.py +2 -1
  40. ommlds/cli/sessions/base.py +1 -10
  41. ommlds/cli/sessions/chat/configs.py +9 -17
  42. ommlds/cli/sessions/chat/{chat → drivers}/ai/configs.py +3 -1
  43. ommlds/cli/sessions/chat/drivers/ai/events.py +57 -0
  44. ommlds/cli/sessions/chat/{chat → drivers}/ai/inject.py +10 -3
  45. ommlds/cli/sessions/chat/{chat → drivers}/ai/rendering.py +1 -1
  46. ommlds/cli/sessions/chat/{chat → drivers}/ai/services.py +1 -1
  47. ommlds/cli/sessions/chat/{chat → drivers}/ai/tools.py +4 -8
  48. ommlds/cli/sessions/chat/{chat → drivers}/ai/types.py +9 -0
  49. ommlds/cli/sessions/chat/drivers/configs.py +25 -0
  50. ommlds/cli/sessions/chat/drivers/events/inject.py +27 -0
  51. ommlds/cli/sessions/chat/drivers/events/injection.py +14 -0
  52. ommlds/cli/sessions/chat/drivers/events/manager.py +16 -0
  53. ommlds/cli/sessions/chat/drivers/events/types.py +38 -0
  54. ommlds/cli/sessions/chat/drivers/impl.py +50 -0
  55. ommlds/cli/sessions/chat/drivers/inject.py +70 -0
  56. ommlds/cli/sessions/chat/{chat → drivers}/state/configs.py +2 -0
  57. ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
  58. ommlds/cli/sessions/chat/drivers/state/inject.py +83 -0
  59. ommlds/cli/sessions/chat/{chat → drivers}/state/inmemory.py +0 -4
  60. ommlds/cli/sessions/chat/{chat → drivers}/state/storage.py +17 -10
  61. ommlds/cli/sessions/chat/{chat → drivers}/state/types.py +10 -5
  62. ommlds/cli/sessions/chat/{tools → drivers/tools}/configs.py +2 -2
  63. ommlds/cli/sessions/chat/drivers/tools/confirmation.py +44 -0
  64. ommlds/cli/sessions/chat/drivers/tools/errorhandling.py +39 -0
  65. ommlds/cli/sessions/chat/{tools → drivers/tools}/execution.py +3 -4
  66. ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/inject.py +3 -3
  67. ommlds/cli/sessions/chat/{tools → drivers/tools}/inject.py +7 -12
  68. ommlds/cli/sessions/chat/{tools → drivers/tools}/injection.py +5 -5
  69. ommlds/cli/sessions/chat/{tools → drivers/tools}/rendering.py +3 -3
  70. ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/inject.py +3 -3
  71. ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/tools.py +1 -1
  72. ommlds/cli/sessions/chat/drivers/types.py +31 -0
  73. ommlds/cli/sessions/chat/{chat → drivers}/user/configs.py +0 -3
  74. ommlds/cli/sessions/chat/drivers/user/inject.py +41 -0
  75. ommlds/cli/sessions/chat/facades/__init__.py +0 -0
  76. ommlds/cli/sessions/chat/facades/commands/__init__.py +0 -0
  77. ommlds/cli/sessions/chat/facades/commands/base.py +83 -0
  78. ommlds/cli/sessions/chat/facades/commands/configs.py +9 -0
  79. ommlds/cli/sessions/chat/facades/commands/inject.py +41 -0
  80. ommlds/cli/sessions/chat/facades/commands/injection.py +15 -0
  81. ommlds/cli/sessions/chat/facades/commands/manager.py +59 -0
  82. ommlds/cli/sessions/chat/facades/commands/simple.py +34 -0
  83. ommlds/cli/sessions/chat/facades/commands/types.py +13 -0
  84. ommlds/cli/sessions/chat/facades/configs.py +11 -0
  85. ommlds/cli/sessions/chat/facades/facade.py +26 -0
  86. ommlds/cli/sessions/chat/facades/inject.py +35 -0
  87. ommlds/cli/sessions/chat/facades/ui.py +34 -0
  88. ommlds/cli/sessions/chat/inject.py +8 -31
  89. ommlds/cli/sessions/chat/interfaces/__init__.py +0 -0
  90. ommlds/cli/sessions/chat/interfaces/bare/__init__.py +0 -0
  91. ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
  92. ommlds/cli/sessions/chat/interfaces/bare/inject.py +69 -0
  93. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +49 -0
  94. ommlds/cli/sessions/chat/interfaces/bare/oneshot.py +21 -0
  95. ommlds/cli/sessions/chat/{tools/confirmation.py → interfaces/bare/tools.py} +3 -22
  96. ommlds/cli/sessions/chat/interfaces/base.py +13 -0
  97. ommlds/cli/sessions/chat/interfaces/configs.py +11 -0
  98. ommlds/cli/sessions/chat/interfaces/inject.py +29 -0
  99. ommlds/cli/sessions/chat/interfaces/textual/__init__.py +0 -0
  100. ommlds/cli/sessions/chat/interfaces/textual/app.py +310 -0
  101. ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
  102. ommlds/cli/sessions/chat/interfaces/textual/facades.py +19 -0
  103. ommlds/cli/sessions/chat/interfaces/textual/inject.py +97 -0
  104. ommlds/cli/sessions/chat/interfaces/textual/interface.py +24 -0
  105. ommlds/cli/sessions/chat/interfaces/textual/styles/__init__.py +29 -0
  106. ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +53 -0
  107. ommlds/cli/sessions/chat/interfaces/textual/styles/markdown.tcss +7 -0
  108. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +157 -0
  109. ommlds/cli/sessions/chat/interfaces/textual/tools.py +38 -0
  110. ommlds/cli/sessions/chat/interfaces/textual/widgets/__init__.py +0 -0
  111. ommlds/cli/sessions/chat/interfaces/textual/widgets/input.py +36 -0
  112. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +197 -0
  113. ommlds/cli/sessions/chat/session.py +8 -13
  114. ommlds/cli/sessions/completion/configs.py +3 -4
  115. ommlds/cli/sessions/completion/inject.py +1 -2
  116. ommlds/cli/sessions/completion/session.py +4 -8
  117. ommlds/cli/sessions/configs.py +10 -0
  118. ommlds/cli/sessions/embedding/configs.py +3 -4
  119. ommlds/cli/sessions/embedding/inject.py +1 -2
  120. ommlds/cli/sessions/embedding/session.py +4 -8
  121. ommlds/cli/sessions/inject.py +15 -15
  122. ommlds/cli/state/storage.py +7 -1
  123. ommlds/minichain/__init__.py +161 -38
  124. ommlds/minichain/_dataclasses.py +20452 -0
  125. ommlds/minichain/_typedvalues.py +11 -4
  126. ommlds/minichain/backends/impls/anthropic/names.py +3 -3
  127. ommlds/minichain/backends/impls/anthropic/protocol.py +2 -2
  128. ommlds/minichain/backends/impls/anthropic/stream.py +1 -1
  129. ommlds/minichain/backends/impls/cerebras/__init__.py +0 -0
  130. ommlds/minichain/backends/impls/cerebras/chat.py +80 -0
  131. ommlds/minichain/backends/impls/cerebras/names.py +45 -0
  132. ommlds/minichain/backends/impls/cerebras/protocol.py +143 -0
  133. ommlds/minichain/backends/impls/cerebras/stream.py +125 -0
  134. ommlds/minichain/backends/impls/duckduckgo/search.py +5 -1
  135. ommlds/minichain/backends/impls/google/names.py +6 -0
  136. ommlds/minichain/backends/impls/google/stream.py +1 -1
  137. ommlds/minichain/backends/impls/google/tools.py +2 -2
  138. ommlds/minichain/backends/impls/groq/chat.py +2 -0
  139. ommlds/minichain/backends/impls/groq/protocol.py +2 -2
  140. ommlds/minichain/backends/impls/groq/stream.py +3 -1
  141. ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
  142. ommlds/minichain/backends/impls/llamacpp/chat.py +6 -3
  143. ommlds/minichain/backends/impls/llamacpp/completion.py +7 -3
  144. ommlds/minichain/backends/impls/llamacpp/stream.py +6 -3
  145. ommlds/minichain/backends/impls/mlx/chat.py +6 -3
  146. ommlds/minichain/backends/impls/ollama/chat.py +51 -57
  147. ommlds/minichain/backends/impls/ollama/protocol.py +144 -0
  148. ommlds/minichain/backends/impls/openai/format.py +4 -3
  149. ommlds/minichain/backends/impls/openai/names.py +3 -1
  150. ommlds/minichain/backends/impls/openai/stream.py +33 -1
  151. ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
  152. ommlds/minichain/backends/impls/tinygrad/chat.py +7 -4
  153. ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
  154. ommlds/minichain/backends/impls/transformers/sentence.py +5 -2
  155. ommlds/minichain/backends/impls/transformers/tokens.py +9 -6
  156. ommlds/minichain/backends/impls/transformers/transformers.py +10 -8
  157. ommlds/minichain/backends/strings/resolving.py +1 -1
  158. ommlds/minichain/chat/content.py +42 -0
  159. ommlds/minichain/chat/messages.py +43 -39
  160. ommlds/minichain/chat/stream/joining.py +36 -12
  161. ommlds/minichain/chat/stream/types.py +1 -1
  162. ommlds/minichain/chat/templating.py +3 -3
  163. ommlds/minichain/content/__init__.py +19 -3
  164. ommlds/minichain/content/_marshal.py +181 -55
  165. ommlds/minichain/content/code.py +26 -0
  166. ommlds/minichain/content/composite.py +28 -0
  167. ommlds/minichain/content/content.py +27 -0
  168. ommlds/minichain/content/dynamic.py +12 -0
  169. ommlds/minichain/content/emphasis.py +27 -0
  170. ommlds/minichain/content/images.py +2 -2
  171. ommlds/minichain/content/json.py +2 -2
  172. ommlds/minichain/content/link.py +13 -0
  173. ommlds/minichain/content/markdown.py +12 -0
  174. ommlds/minichain/content/metadata.py +10 -0
  175. ommlds/minichain/content/namespaces.py +8 -0
  176. ommlds/minichain/content/placeholders.py +10 -9
  177. ommlds/minichain/content/quote.py +26 -0
  178. ommlds/minichain/content/raw.py +49 -0
  179. ommlds/minichain/content/recursive.py +12 -0
  180. ommlds/minichain/content/section.py +26 -0
  181. ommlds/minichain/content/sequence.py +17 -3
  182. ommlds/minichain/content/standard.py +32 -0
  183. ommlds/minichain/content/tag.py +28 -0
  184. ommlds/minichain/content/templates.py +13 -0
  185. ommlds/minichain/content/text.py +2 -2
  186. ommlds/minichain/content/transform/__init__.py +0 -0
  187. ommlds/minichain/content/transform/json.py +55 -0
  188. ommlds/minichain/content/transform/markdown.py +8 -0
  189. ommlds/minichain/content/transform/materialize.py +51 -0
  190. ommlds/minichain/content/transform/metadata.py +16 -0
  191. ommlds/minichain/content/{prepare.py → transform/prepare.py} +10 -15
  192. ommlds/minichain/content/transform/recursive.py +97 -0
  193. ommlds/minichain/content/transform/standard.py +43 -0
  194. ommlds/minichain/content/{transforms → transform}/stringify.py +1 -7
  195. ommlds/minichain/content/transform/strings.py +33 -0
  196. ommlds/minichain/content/transform/templates.py +25 -0
  197. ommlds/minichain/content/visitors.py +231 -0
  198. ommlds/minichain/lib/fs/tools/read.py +1 -1
  199. ommlds/minichain/lib/fs/tools/recursivels/rendering.py +1 -1
  200. ommlds/minichain/lib/fs/tools/recursivels/running.py +1 -1
  201. ommlds/minichain/lib/todo/tools/write.py +2 -1
  202. ommlds/minichain/lib/todo/types.py +1 -1
  203. ommlds/minichain/metadata.py +56 -2
  204. ommlds/minichain/resources.py +22 -1
  205. ommlds/minichain/services/README.md +154 -0
  206. ommlds/minichain/services/__init__.py +6 -2
  207. ommlds/minichain/services/_marshal.py +46 -10
  208. ommlds/minichain/services/_origclasses.py +11 -0
  209. ommlds/minichain/services/_typedvalues.py +8 -3
  210. ommlds/minichain/services/requests.py +73 -3
  211. ommlds/minichain/services/responses.py +73 -3
  212. ommlds/minichain/services/services.py +9 -0
  213. ommlds/minichain/stream/services.py +24 -1
  214. ommlds/minichain/text/applypatch.py +2 -1
  215. ommlds/minichain/text/toolparsing/llamacpp/types.py +1 -1
  216. ommlds/minichain/tokens/specials.py +1 -1
  217. ommlds/minichain/tools/execution/catalog.py +1 -1
  218. ommlds/minichain/tools/execution/errorhandling.py +36 -0
  219. ommlds/minichain/tools/execution/errors.py +2 -2
  220. ommlds/minichain/tools/execution/executors.py +1 -1
  221. ommlds/minichain/tools/fns.py +1 -1
  222. ommlds/minichain/tools/jsonschema.py +2 -2
  223. ommlds/minichain/tools/reflect.py +6 -6
  224. ommlds/minichain/tools/types.py +12 -15
  225. ommlds/minichain/vectors/_marshal.py +1 -1
  226. ommlds/minichain/vectors/embeddings.py +1 -1
  227. ommlds/minichain/wrappers/__init__.py +7 -0
  228. ommlds/minichain/wrappers/firstinwins.py +144 -0
  229. ommlds/minichain/wrappers/instrument.py +146 -0
  230. ommlds/minichain/wrappers/retry.py +168 -0
  231. ommlds/minichain/wrappers/services.py +98 -0
  232. ommlds/minichain/wrappers/stream.py +57 -0
  233. ommlds/nanochat/rustbpe/README.md +9 -0
  234. ommlds/nanochat/tokenizers.py +40 -6
  235. ommlds/specs/mcp/clients.py +146 -0
  236. ommlds/specs/mcp/protocol.py +123 -18
  237. ommlds/tools/git.py +82 -65
  238. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/METADATA +13 -11
  239. ommlds-0.0.0.dev503.dist-info/RECORD +520 -0
  240. ommlds/cli/sessions/chat/chat/state/inject.py +0 -36
  241. ommlds/cli/sessions/chat/chat/user/inject.py +0 -62
  242. ommlds/cli/sessions/chat/chat/user/interactive.py +0 -31
  243. ommlds/cli/sessions/chat/chat/user/oneshot.py +0 -25
  244. ommlds/cli/sessions/chat/chat/user/types.py +0 -15
  245. ommlds/cli/sessions/chat/driver.py +0 -43
  246. ommlds/minichain/content/materialize.py +0 -196
  247. ommlds/minichain/content/simple.py +0 -47
  248. ommlds/minichain/content/transforms/base.py +0 -46
  249. ommlds/minichain/content/transforms/interleave.py +0 -70
  250. ommlds/minichain/content/transforms/squeeze.py +0 -72
  251. ommlds/minichain/content/transforms/strings.py +0 -24
  252. ommlds/minichain/content/types.py +0 -43
  253. ommlds/minichain/stream/wrap.py +0 -62
  254. ommlds-0.0.0.dev480.dist-info/RECORD +0 -427
  255. /ommlds/cli/sessions/chat/{chat → drivers}/__init__.py +0 -0
  256. /ommlds/cli/sessions/chat/{chat → drivers}/ai/__init__.py +0 -0
  257. /ommlds/cli/sessions/chat/{chat → drivers}/ai/injection.py +0 -0
  258. /ommlds/cli/sessions/chat/{chat/state → drivers/events}/__init__.py +0 -0
  259. /ommlds/cli/sessions/chat/{chat/user → drivers/phases}/__init__.py +0 -0
  260. /ommlds/cli/sessions/chat/{phases → drivers/phases}/inject.py +0 -0
  261. /ommlds/cli/sessions/chat/{phases → drivers/phases}/injection.py +0 -0
  262. /ommlds/cli/sessions/chat/{phases → drivers/phases}/manager.py +0 -0
  263. /ommlds/cli/sessions/chat/{phases → drivers/phases}/types.py +0 -0
  264. /ommlds/cli/sessions/chat/{phases → drivers/state}/__init__.py +0 -0
  265. /ommlds/cli/sessions/chat/{tools → drivers/tools}/__init__.py +0 -0
  266. /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/__init__.py +0 -0
  267. /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/configs.py +0 -0
  268. /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/__init__.py +0 -0
  269. /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/configs.py +0 -0
  270. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/__init__.py +0 -0
  271. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/configs.py +0 -0
  272. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/inject.py +0 -0
  273. /ommlds/{minichain/content/transforms → cli/sessions/chat/drivers/user}/__init__.py +0 -0
  274. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/WHEEL +0 -0
  275. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/entry_points.txt +0 -0
  276. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/licenses/LICENSE +0 -0
  277. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,6 @@ from .types import BackendProvider
10
10
  from .types import ChatChoicesServiceBackendProvider
11
11
  from .types import ChatChoicesStreamServiceBackendProvider
12
12
  from .types import CompletionServiceBackendProvider
13
- from .types import DefaultBackendName
14
13
  from .types import EmbeddingServiceBackendProvider
15
14
  from .types import ServiceT
16
15
 
@@ -26,7 +25,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
26
25
  self,
27
26
  *,
28
27
  name: BackendName | None = None,
29
- default_name: DefaultBackendName | None = None,
30
28
  catalog: 'mc.BackendCatalog',
31
29
  configs: BackendConfigs | None = None,
32
30
  instantiator: Instantiator | None = None,
@@ -34,7 +32,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
34
32
  super().__init__()
35
33
 
36
34
  self._name = name
37
- self._default_name = default_name
38
35
  self._catalog = catalog
39
36
  self._configs = configs
40
37
  if instantiator is None:
@@ -46,8 +43,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
46
43
  name: str
47
44
  if self._name is not None:
48
45
  name = self._name
49
- elif self._default_name is not None:
50
- name = self._default_name
51
46
  else:
52
47
  raise RuntimeError('No backend name specified')
53
48
 
@@ -12,6 +12,7 @@ from .injection import backend_configs
12
12
  with lang.auto_proxy_import(globals()):
13
13
  from ...minichain.backends.impls.huggingface import repos as hf_repos
14
14
  from . import catalog as _catalog
15
+ from . import meta as _meta
15
16
  from . import types as _types
16
17
 
17
18
 
@@ -42,13 +43,75 @@ def bind_backends(cfg: BackendConfig = BackendConfig()) -> inj.Elements:
42
43
 
43
44
  if cfg.backend is not None:
44
45
  lst.append(inj.bind(_types.BackendName, to_const=cfg.backend))
45
-
46
- lst.extend([
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
51
- ])
46
+ else:
47
+ lst.append(inj.bind(_types.BackendName, to_fn=inj.target(dbn=_types.DefaultBackendName)(lambda dbn: dbn)))
48
+
49
+ backend_provider_pairs: list = [
50
+ (_types.ChatChoicesServiceBackendProvider, _catalog.CatalogChatChoicesServiceBackendProvider),
51
+ (_types.ChatChoicesStreamServiceBackendProvider, _catalog.CatalogChatChoicesStreamServiceBackendProvider),
52
+ (_types.CompletionServiceBackendProvider, _catalog.CatalogCompletionServiceBackendProvider),
53
+ (_types.EmbeddingServiceBackendProvider, _catalog.CatalogEmbeddingServiceBackendProvider),
54
+ ]
55
+
56
+ for bp_iface, bp_impl in backend_provider_pairs:
57
+ bp_stack = inj.wrapper_binder_helper(bp_iface)
58
+
59
+ if bp_iface is _types.ChatChoicesServiceBackendProvider:
60
+ fiw_ben_lst: list[str] = [
61
+ # 'openai',
62
+ # 'anthropic',
63
+ # 'groq',
64
+ # 'cerebras',
65
+ ]
66
+
67
+ if fiw_ben_lst:
68
+ for ben in fiw_ben_lst:
69
+ ben_bp_key: inj.Key = inj.Key(bp_impl, tag=ben)
70
+ lst.extend([
71
+ inj.private(
72
+ inj.bind(_types.BackendName, to_const=ben),
73
+ inj.bind(ben_bp_key, to_ctor=bp_impl, singleton=True, expose=True),
74
+ inj.bind(bp_iface, to_key=ben_bp_key),
75
+ ),
76
+ bp_stack.push_bind(to_key=ben_bp_key),
77
+ ])
78
+
79
+ fiw_key: inj.Key = inj.Key(_meta.FirstInWinsBackendProvider, tag=bp_iface)
80
+ lst.extend([
81
+ inj.private(
82
+ inj.set_binder[_types.BackendProvider]().bind(bp_stack.top),
83
+ inj.bind(fiw_key, to_ctor=_meta.FirstInWinsBackendProvider, singleton=True, expose=True),
84
+ ),
85
+ bp_stack.push_bind(to_key=fiw_key),
86
+ ])
87
+
88
+ else:
89
+ lst.append(bp_stack.push_bind(to_ctor=bp_impl, singleton=True))
90
+
91
+ else:
92
+ lst.append(bp_stack.push_bind(to_ctor=bp_impl, singleton=True))
93
+
94
+ if bp_iface is _types.ChatChoicesServiceBackendProvider:
95
+ rt_key: inj.Key = inj.Key(_meta.RetryBackendProvider, tag=bp_iface)
96
+ lst.extend([
97
+ inj.private(
98
+ inj.bind(_types.BackendProvider, to_key=bp_stack.top),
99
+ inj.bind(rt_key, to_ctor=_meta.RetryBackendProvider, singleton=True, expose=True),
100
+ ),
101
+ bp_stack.push_bind(to_key=rt_key),
102
+ ])
103
+
104
+ elif bp_iface is _types.ChatChoicesStreamServiceBackendProvider:
105
+ rts_key: inj.Key = inj.Key(_meta.RetryStreamBackendProvider, tag=bp_iface)
106
+ lst.extend([
107
+ inj.private(
108
+ inj.bind(_types.BackendProvider, to_key=bp_stack.top),
109
+ inj.bind(rts_key, to_ctor=_meta.RetryStreamBackendProvider, singleton=True, expose=True),
110
+ ),
111
+ bp_stack.push_bind(to_key=rts_key),
112
+ ])
113
+
114
+ lst.append(inj.bind(bp_iface, to_key=bp_stack.top))
52
115
 
53
116
  #
54
117
 
@@ -0,0 +1,82 @@
1
+ import contextlib
2
+ import typing as ta
3
+
4
+ from ... import minichain as mc
5
+ from .types import BackendProvider
6
+
7
+
8
+ ServiceT = ta.TypeVar('ServiceT', bound=mc.Service)
9
+ StreamServiceT = ta.TypeVar('StreamServiceT', bound=mc.Service)
10
+
11
+
12
+ ##
13
+
14
+
15
+ class FirstInWinsBackendProvider(BackendProvider[ServiceT]):
16
+ def __init__(
17
+ self,
18
+ backend_providers: ta.AbstractSet[BackendProvider],
19
+ ) -> None:
20
+ super().__init__()
21
+
22
+ self._backend_providers = [ta.cast(BackendProvider[ServiceT], bp) for bp in backend_providers]
23
+
24
+ def provide_backend(self) -> ta.AsyncContextManager[ServiceT]:
25
+ @contextlib.asynccontextmanager
26
+ async def inner():
27
+ from ...minichain.wrappers.firstinwins import AsyncioFirstInWinsService
28
+
29
+ async with contextlib.AsyncExitStack() as aes:
30
+ svcs = [
31
+ await aes.enter_async_context(bp.provide_backend())
32
+ for bp in self._backend_providers
33
+ ]
34
+ yield AsyncioFirstInWinsService(*svcs)
35
+
36
+ return inner()
37
+
38
+
39
+ ##
40
+
41
+
42
+ class RetryBackendProvider(BackendProvider):
43
+ def __init__(
44
+ self,
45
+ backend_provider: BackendProvider,
46
+ ) -> None:
47
+ super().__init__()
48
+
49
+ self._backend_provider = backend_provider
50
+
51
+ def provide_backend(self) -> ta.AsyncContextManager:
52
+ @contextlib.asynccontextmanager
53
+ async def inner():
54
+ async with contextlib.AsyncExitStack() as aes:
55
+ yield mc.RetryService(
56
+ await aes.enter_async_context(self._backend_provider.provide_backend()),
57
+ )
58
+
59
+ return inner()
60
+
61
+
62
+ ##
63
+
64
+
65
+ class RetryStreamBackendProvider(BackendProvider):
66
+ def __init__(
67
+ self,
68
+ backend_provider: BackendProvider,
69
+ ) -> None:
70
+ super().__init__()
71
+
72
+ self._backend_provider = backend_provider
73
+
74
+ def provide_backend(self) -> ta.AsyncContextManager:
75
+ @contextlib.asynccontextmanager
76
+ async def inner():
77
+ async with contextlib.AsyncExitStack() as aes:
78
+ yield mc.RetryStreamService(
79
+ await aes.enter_async_context(self._backend_provider.provide_backend()),
80
+ )
81
+
82
+ return inner()
@@ -19,7 +19,7 @@ class MessageContentExtractor(lang.Abstract):
19
19
  class MessageContentExtractorImpl(MessageContentExtractor):
20
20
  def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
21
21
  if isinstance(message, (mc.SystemMessage, mc.UserMessage, mc.AiMessage)):
22
- if message.c is not None:
22
+ if message.c:
23
23
  return check.isinstance(message.c, str)
24
24
  else:
25
25
  return None
ommlds/cli/inject.py CHANGED
@@ -1,7 +1,8 @@
1
- import typing as ta
2
-
3
1
  from omlish import inject as inj
4
2
  from omlish import lang
3
+ from omlish import lifecycles as lc
4
+
5
+ from .sessions.configs import SessionConfig
5
6
 
6
7
 
7
8
  with lang.auto_proxy_import(globals()):
@@ -15,12 +16,19 @@ with lang.auto_proxy_import(globals()):
15
16
 
16
17
  def bind_main(
17
18
  *,
18
- session_cfg: ta.Any,
19
+ session_cfg: SessionConfig,
19
20
  ) -> inj.Elements:
20
21
  els: list[inj.Elemental] = []
21
22
 
22
23
  #
23
24
 
25
+ els.extend([
26
+ lc.bind_async_lifecycle_registrar(),
27
+ lc.bind_async_managed_lifecycle_manager(eager=True),
28
+ ])
29
+
30
+ #
31
+
24
32
  els.extend([
25
33
  _sessions.bind_sessions(session_cfg),
26
34
 
ommlds/cli/main.py CHANGED
@@ -16,10 +16,14 @@ from omlish.argparse import all as ap
16
16
  from omlish.logs import all as logs
17
17
 
18
18
  from .inject import bind_main
19
- from .secrets import install_secrets
19
+ from .secrets import install_env_secrets
20
20
  from .sessions.base import Session
21
21
  from .sessions.chat.configs import ChatConfig
22
+ from .sessions.chat.interfaces.bare.configs import BareInterfaceConfig
23
+ from .sessions.chat.interfaces.configs import InterfaceConfig
24
+ from .sessions.chat.interfaces.textual.configs import TextualInterfaceConfig
22
25
  from .sessions.completion.configs import CompletionConfig
26
+ from .sessions.configs import SessionConfig
23
27
  from .sessions.embedding.configs import EmbeddingConfig
24
28
 
25
29
 
@@ -36,7 +40,8 @@ def _process_main_extra_args(args: ap.Namespace) -> None:
36
40
  logs.configure_standard_logging('DEBUG')
37
41
  else:
38
42
  logs.configure_standard_logging('INFO')
39
- logs.silence_noisy_loggers()
43
+
44
+ logs.silence_noisy_loggers()
40
45
 
41
46
 
42
47
  ##
@@ -44,7 +49,7 @@ def _process_main_extra_args(args: ap.Namespace) -> None:
44
49
 
45
50
  class Profile(lang.Abstract):
46
51
  @abc.abstractmethod
47
- def run(self, argv: ta.Sequence[str]) -> ta.Awaitable[None]:
52
+ def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
48
53
  raise NotImplementedError
49
54
 
50
55
 
@@ -69,49 +74,85 @@ class ChatProfile(Profile):
69
74
  def configure_backend(self, cfg: ChatConfig) -> ChatConfig:
70
75
  return dc.replace(
71
76
  cfg,
72
- backend=dc.replace(
73
- cfg.backend,
74
- backend=self._args.backend,
77
+ driver=dc.replace(
78
+ cfg.driver,
79
+ backend=dc.replace(
80
+ cfg.driver.backend,
81
+ backend=self._args.backend,
82
+ ),
75
83
  ),
76
84
  )
77
85
 
78
86
  #
79
87
 
80
- INPUT_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
81
- ap.arg('message', nargs='*', group='input'),
82
- ap.arg('-i', '--interactive', action='store_true', group='input'),
83
- ap.arg('-e', '--editor', action='store_true', group='input'),
88
+ INTERFACE_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
89
+ ap.arg('-i', '--interactive', action='store_true', group='interface'),
90
+ ap.arg('-T', '--textual', action='store_true', group='interface'),
91
+ ap.arg('-e', '--editor', action='store_true', group='interface'),
84
92
  ]
85
93
 
86
- def configure_input(self, cfg: ChatConfig) -> ChatConfig:
94
+ def configure_interface(self, cfg: ChatConfig) -> ChatConfig:
87
95
  if self._args.editor:
88
96
  check.arg(not self._args.interactive)
89
97
  check.arg(not self._args.message)
90
98
  raise NotImplementedError
91
99
 
92
- elif self._args.interactive:
93
- check.arg(not self._args.message)
94
- return dc.replace(
100
+ if self._args.textual:
101
+ check.isinstance(cfg.interface, BareInterfaceConfig)
102
+ cfg = dc.replace(
103
+ cfg,
104
+ interface=TextualInterfaceConfig(**{
105
+ f.name: getattr(cfg.interface, f.name)
106
+ for f in dc.fields(InterfaceConfig)
107
+ }),
108
+ )
109
+
110
+ else:
111
+ cfg = dc.replace(
95
112
  cfg,
96
- user=dc.replace(
97
- cfg.user,
98
- interactive=True,
113
+ driver=dc.replace(
114
+ cfg.driver,
115
+ ai=dc.replace(
116
+ cfg.driver.ai,
117
+ verbose=True,
118
+ ),
119
+ ),
120
+ interface=dc.replace(
121
+ check.isinstance(cfg.interface, BareInterfaceConfig),
122
+ interactive=self._args.interactive,
99
123
  ),
100
124
  )
101
125
 
126
+ return cfg
127
+
128
+ #
129
+
130
+ INPUT_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
131
+ ap.arg('message', nargs='*', group='input'),
132
+ ]
133
+
134
+ def configure_input(self, cfg: ChatConfig) -> ChatConfig:
135
+ if self._args.interactive or self._args.textual:
136
+ check.arg(not self._args.message)
137
+
102
138
  elif self._args.message:
103
139
  # TODO: '-' -> stdin
104
- return dc.replace(
140
+ cfg = dc.replace(
105
141
  cfg,
106
- user=dc.replace(
107
- cfg.user,
108
- initial_user_content=' '.join(self._args.message),
142
+ driver=dc.replace(
143
+ cfg.driver,
144
+ user=dc.replace(
145
+ cfg.driver.user,
146
+ initial_user_content=' '.join(self._args.message),
147
+ ),
109
148
  ),
110
149
  )
111
150
 
112
151
  else:
113
152
  raise ValueError('Must specify input')
114
153
 
154
+ return cfg
155
+
115
156
  #
116
157
 
117
158
  STATE_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
@@ -122,9 +163,12 @@ class ChatProfile(Profile):
122
163
  def configure_state(self, cfg: ChatConfig) -> ChatConfig:
123
164
  return dc.replace(
124
165
  cfg,
125
- state=dc.replace(
126
- cfg.state,
127
- state='ephemeral' if self._args.ephemeral else 'new' if self._args.new else 'continue',
166
+ driver=dc.replace(
167
+ cfg.driver,
168
+ state=dc.replace(
169
+ cfg.driver.state,
170
+ state='ephemeral' if self._args.ephemeral else 'new' if self._args.new else 'continue',
171
+ ),
128
172
  ),
129
173
  )
130
174
 
@@ -138,9 +182,12 @@ class ChatProfile(Profile):
138
182
  def configure_output(self, cfg: ChatConfig) -> ChatConfig:
139
183
  return dc.replace(
140
184
  cfg,
141
- ai=dc.replace(
142
- cfg.ai,
143
- stream=bool(self._args.stream),
185
+ driver=dc.replace(
186
+ cfg.driver,
187
+ ai=dc.replace(
188
+ cfg.driver.ai,
189
+ stream=bool(self._args.stream),
190
+ ),
144
191
  ),
145
192
  rendering=dc.replace(
146
193
  cfg.rendering,
@@ -158,26 +205,36 @@ class ChatProfile(Profile):
158
205
  ]
159
206
 
160
207
  def configure_tools(self, cfg: ChatConfig) -> ChatConfig:
208
+ if not (
209
+ self._args.enable_fs_tools or
210
+ self._args.enable_todo_tools or
211
+ # self._args.enable_unsafe_tools_do_not_use_lol or
212
+ self._args.enable_test_weather_tool or
213
+ self._args.code
214
+ ):
215
+ return cfg
216
+
161
217
  return dc.replace(
162
218
  cfg,
163
- ai=dc.replace(
164
- cfg.ai,
165
- enable_tools=(
166
- self._args.enable_fs_tools or
167
- self._args.enable_todo_tools or
168
- # self._args.enable_unsafe_tools_do_not_use_lol or
169
- self._args.enable_test_weather_tool or
170
- self._args.code
219
+ driver=dc.replace(
220
+ cfg.driver,
221
+ ai=dc.replace(
222
+ cfg.driver.ai,
223
+ enable_tools=True,
224
+ ),
225
+ tools=dc.replace(
226
+ cfg.driver.tools,
227
+ enabled_tools={ # noqa
228
+ *(cfg.driver.tools.enabled_tools or []),
229
+ *(['fs'] if self._args.enable_fs_tools else []),
230
+ *(['todo'] if self._args.enable_todo_tools else []),
231
+ *(['weather'] if self._args.enable_test_weather_tool else []),
232
+ },
171
233
  ),
172
234
  ),
173
- tools=dc.replace(
174
- cfg.tools,
175
- enabled_tools={ # noqa
176
- *(cfg.tools.enabled_tools or []),
177
- *(['fs'] if self._args.enable_fs_tools else []),
178
- *(['todo'] if self._args.enable_todo_tools else []),
179
- *(['weather'] if self._args.enable_test_weather_tool else []),
180
- },
235
+ interface=dc.replace(
236
+ cfg.interface,
237
+ enable_tools=True,
181
238
  ),
182
239
  )
183
240
 
@@ -193,9 +250,12 @@ class ChatProfile(Profile):
193
250
 
194
251
  cfg = dc.replace(
195
252
  cfg,
196
- ai=dc.replace(
197
- cfg.ai,
198
- enable_tools=True,
253
+ driver=dc.replace(
254
+ cfg.driver,
255
+ ai=dc.replace(
256
+ cfg.driver.ai,
257
+ enable_tools=True,
258
+ ),
199
259
  ),
200
260
  )
201
261
 
@@ -205,9 +265,12 @@ class ChatProfile(Profile):
205
265
 
206
266
  cfg = dc.replace(
207
267
  cfg,
208
- user=dc.replace(
209
- cfg.user,
210
- initial_system_content=system_content,
268
+ driver=dc.replace(
269
+ cfg.driver,
270
+ user=dc.replace(
271
+ cfg.driver.user,
272
+ initial_system_content=system_content,
273
+ ),
211
274
  ),
212
275
  )
213
276
 
@@ -215,11 +278,12 @@ class ChatProfile(Profile):
215
278
 
216
279
  #
217
280
 
218
- async def run(self, argv: ta.Sequence[str]) -> None:
281
+ def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
219
282
  parser = ap.ArgumentParser()
220
283
 
221
284
  for grp_name, grp_args in [
222
285
  ('backend', self.BACKEND_ARGS),
286
+ ('interface', self.INTERFACE_ARGS),
223
287
  ('input', self.INPUT_ARGS),
224
288
  ('state', self.STATE_ARGS),
225
289
  ('output', self.OUTPUT_ARGS),
@@ -234,23 +298,21 @@ class ChatProfile(Profile):
234
298
 
235
299
  cfg = ChatConfig()
236
300
  cfg = self.configure_backend(cfg)
301
+ cfg = self.configure_interface(cfg)
237
302
  cfg = self.configure_input(cfg)
238
303
  cfg = self.configure_state(cfg)
239
304
  cfg = self.configure_output(cfg)
240
305
  cfg = self.configure_tools(cfg)
241
306
  cfg = self.configure_code(cfg)
242
307
 
243
- with inj.create_managed_injector(bind_main(
244
- session_cfg=cfg,
245
- )) as injector:
246
- await injector[Session].run()
308
+ return cfg
247
309
 
248
310
 
249
311
  ##
250
312
 
251
313
 
252
314
  class CompletionProfile(Profile):
253
- async def run(self, argv: ta.Sequence[str]) -> None:
315
+ def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
254
316
  parser = ap.ArgumentParser()
255
317
  parser.add_argument('prompt', nargs='*')
256
318
  parser.add_argument('-b', '--backend', default='openai')
@@ -259,21 +321,18 @@ class CompletionProfile(Profile):
259
321
  content = ' '.join(args.prompt)
260
322
 
261
323
  cfg = CompletionConfig(
262
- check.non_empty_str(content),
324
+ content=check.non_empty_str(content),
263
325
  backend=args.backend,
264
326
  )
265
327
 
266
- with inj.create_managed_injector(bind_main(
267
- session_cfg=cfg,
268
- )) as injector:
269
- await injector[Session].run()
328
+ return cfg
270
329
 
271
330
 
272
331
  ##
273
332
 
274
333
 
275
334
  class EmbedProfile(Profile):
276
- async def run(self, argv: ta.Sequence[str]) -> None:
335
+ def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
277
336
  parser = ap.ArgumentParser()
278
337
  parser.add_argument('prompt', nargs='*')
279
338
  parser.add_argument('-b', '--backend', default='openai')
@@ -282,14 +341,11 @@ class EmbedProfile(Profile):
282
341
  content = ' '.join(args.prompt)
283
342
 
284
343
  cfg = EmbeddingConfig(
285
- check.non_empty_str(content),
344
+ content=check.non_empty_str(content),
286
345
  backend=args.backend,
287
346
  )
288
347
 
289
- with inj.create_managed_injector(bind_main(
290
- session_cfg=cfg,
291
- )) as injector:
292
- await injector[Session].run()
348
+ return cfg
293
349
 
294
350
 
295
351
  ##
@@ -305,6 +361,16 @@ PROFILE_TYPES: ta.Mapping[str, type[Profile]] = {
305
361
  ##
306
362
 
307
363
 
364
+ async def _run_session_cfg(session_cfg: SessionConfig) -> None:
365
+ async with inj.create_async_managed_injector(bind_main(
366
+ session_cfg=session_cfg,
367
+ )) as injector:
368
+ await (await injector[Session]).run()
369
+
370
+
371
+ ##
372
+
373
+
308
374
  MAIN_PROFILE_ARGS: ta.Sequence[ap.Arg] = [
309
375
  ap.arg('-p', '--profile', default='chat'),
310
376
  ap.arg('args', nargs=ap.REMAINDER),
@@ -321,11 +387,14 @@ async def _a_main(argv: ta.Any = None) -> None:
321
387
 
322
388
  _process_main_extra_args(args)
323
389
 
324
- install_secrets()
390
+ install_env_secrets()
325
391
 
326
392
  profile_cls = PROFILE_TYPES[args.profile]
327
393
  profile = profile_cls()
328
- await profile.run([*unk_args, *args.args])
394
+
395
+ session_cfg = profile.configure([*unk_args, *args.args])
396
+
397
+ await _run_session_cfg(session_cfg)
329
398
 
330
399
 
331
400
  def _main(args: ta.Any = None) -> None:
@@ -1,3 +1,9 @@
1
+ """
2
+ TODO:
3
+ - html?
4
+ - terminal?
5
+ - rich? textual.Content?
6
+ """
1
7
  import abc
2
8
  import typing as ta
3
9
 
ommlds/cli/secrets.py CHANGED
@@ -6,10 +6,11 @@ from omdev.home.secrets import load_secrets
6
6
  ##
7
7
 
8
8
 
9
- def install_secrets() -> None:
9
+ def install_env_secrets() -> None:
10
10
  # FIXME: lol garbage
11
11
  for key in [
12
12
  'ANTHROPIC_API_KEY',
13
+ 'CEREBRAS_API_KEY',
13
14
  'GEMINI_API_KEY',
14
15
  'GROQ_API_KEY',
15
16
  'HUGGINGFACE_TOKEN',
@@ -1,22 +1,13 @@
1
1
  import abc
2
2
  import typing as ta
3
3
 
4
- from omlish import dataclasses as dc
5
4
  from omlish import lang
6
- from omlish.configs import all as cfgs
7
-
8
-
9
- SessionConfigT = ta.TypeVar('SessionConfigT', bound='Session.Config')
10
5
 
11
6
 
12
7
  ##
13
8
 
14
9
 
15
- class Session(cfgs.Configurable[SessionConfigT], lang.Abstract):
16
- @dc.dataclass(frozen=True)
17
- class Config(cfgs.Configurable.Config):
18
- pass
19
-
10
+ class Session(lang.Abstract):
20
11
  @abc.abstractmethod
21
12
  def run(self) -> ta.Awaitable[None]:
22
13
  raise NotImplementedError