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
@@ -1,27 +1,19 @@
1
1
  from omlish import dataclasses as dc
2
2
 
3
- from ...backends.configs import BackendConfig
4
3
  from ...rendering.configs import RenderingConfig
5
- from .chat.ai.configs import AiConfig
6
- from .chat.state.configs import StateConfig
7
- from .chat.user.configs import UserConfig
8
- from .tools.configs import ToolsConfig
9
-
10
-
11
- ##
12
-
13
-
14
- DEFAULT_BACKEND = 'openai'
4
+ from ..configs import SessionConfig
5
+ from .drivers.configs import DriverConfig
6
+ from .facades.configs import FacadeConfig
7
+ from .interfaces.bare.configs import BareInterfaceConfig
8
+ from .interfaces.configs import InterfaceConfig
15
9
 
16
10
 
17
11
  ##
18
12
 
19
13
 
20
14
  @dc.dataclass(frozen=True, kw_only=True)
21
- class ChatConfig:
22
- backend: BackendConfig = BackendConfig()
23
- ai: AiConfig = AiConfig()
24
- state: StateConfig = StateConfig()
25
- user: UserConfig = UserConfig()
15
+ class ChatConfig(SessionConfig):
16
+ driver: DriverConfig = DriverConfig()
17
+ facade: FacadeConfig = FacadeConfig()
18
+ interface: InterfaceConfig = BareInterfaceConfig()
26
19
  rendering: RenderingConfig = RenderingConfig()
27
- tools: ToolsConfig = ToolsConfig()
@@ -7,5 +7,7 @@ from omlish import dataclasses as dc
7
7
  @dc.dataclass(frozen=True, kw_only=True)
8
8
  class AiConfig:
9
9
  stream: bool = False
10
- silent: bool = False
10
+
11
+ verbose: bool = False
12
+
11
13
  enable_tools: bool = False
@@ -0,0 +1,57 @@
1
+ import typing as ta
2
+
3
+ from ...... import minichain as mc
4
+ from ..events.manager import ChatEventsManager
5
+ from ..events.types import AiDeltaChatEvent
6
+ from ..events.types import AiMessagesChatEvent
7
+ from .types import AiChatGenerator
8
+ from .types import StreamAiChatGenerator
9
+
10
+
11
+ ##
12
+
13
+
14
+ class EventEmittingAiChatGenerator(AiChatGenerator):
15
+ def __init__(
16
+ self,
17
+ *,
18
+ wrapped: AiChatGenerator,
19
+ events: ChatEventsManager,
20
+ ) -> None:
21
+ super().__init__()
22
+
23
+ self._wrapped = wrapped
24
+ self._events = events
25
+
26
+ async def get_next_ai_messages(self, chat: 'mc.Chat') -> 'mc.Chat':
27
+ out = await self._wrapped.get_next_ai_messages(chat)
28
+
29
+ await self._events.emit_event(AiMessagesChatEvent(out))
30
+
31
+ return out
32
+
33
+
34
+ class EventEmittingStreamAiChatGenerator(StreamAiChatGenerator):
35
+ def __init__(
36
+ self,
37
+ *,
38
+ wrapped: StreamAiChatGenerator,
39
+ events: ChatEventsManager,
40
+ ) -> None:
41
+ super().__init__()
42
+
43
+ self._wrapped = wrapped
44
+ self._events = events
45
+
46
+ async def get_next_ai_messages_streamed(
47
+ self,
48
+ chat: 'mc.Chat',
49
+ delta_callback: ta.Callable[['mc.AiDelta'], ta.Awaitable[None]] | None = None,
50
+ ) -> 'mc.Chat':
51
+ async def inner(delta: mc.AiDelta) -> None:
52
+ await self._events.emit_event(AiDeltaChatEvent(delta))
53
+
54
+ if delta_callback is not None:
55
+ await delta_callback(delta)
56
+
57
+ return await self._wrapped.get_next_ai_messages_streamed(chat, delta_callback=inner)
@@ -7,6 +7,7 @@ from .injection import chat_options_providers
7
7
 
8
8
 
9
9
  with lang.auto_proxy_import(globals()):
10
+ from . import events as _events
10
11
  from . import rendering as _rendering
11
12
  from . import services as _services
12
13
  from . import tools as _tools
@@ -39,9 +40,11 @@ def bind_ai(cfg: AiConfig = AiConfig()) -> inj.Elements:
39
40
 
40
41
  els.append(stream_ai_stack.push_bind(to_ctor=_services.ChatChoicesStreamServiceStreamAiChatGenerator, singleton=True)) # noqa
41
42
 
42
- if not cfg.silent:
43
+ if cfg.verbose:
43
44
  els.append(stream_ai_stack.push_bind(to_ctor=_rendering.RenderingStreamAiChatGenerator, singleton=True))
44
45
 
46
+ els.append(stream_ai_stack.push_bind(to_ctor=_events.EventEmittingStreamAiChatGenerator, singleton=True))
47
+
45
48
  els.extend([
46
49
  inj.bind(_types.StreamAiChatGenerator, to_key=stream_ai_stack.top),
47
50
  ai_stack.push_bind(to_key=_types.StreamAiChatGenerator),
@@ -50,9 +53,11 @@ def bind_ai(cfg: AiConfig = AiConfig()) -> inj.Elements:
50
53
  else:
51
54
  els.append(ai_stack.push_bind(to_ctor=_services.ChatChoicesServiceAiChatGenerator, singleton=True))
52
55
 
53
- if not cfg.silent:
56
+ if cfg.verbose:
54
57
  els.append(ai_stack.push_bind(to_ctor=_rendering.RenderingAiChatGenerator, singleton=True))
55
58
 
59
+ els.append(ai_stack.push_bind(to_ctor=_events.EventEmittingAiChatGenerator, singleton=True))
60
+
56
61
  if cfg.enable_tools:
57
62
  els.append(ai_stack.push_bind(to_ctor=_tools.ToolExecutingAiChatGenerator, singleton=True))
58
63
 
@@ -61,7 +66,9 @@ def bind_ai(cfg: AiConfig = AiConfig()) -> inj.Elements:
61
66
  #
62
67
 
63
68
  if cfg.enable_tools:
64
- def _provide_tools_chat_choices_options_provider(tc: mc.ToolCatalog) -> _services.ChatChoicesServiceOptionsProvider: # noqa
69
+ def _provide_tools_chat_choices_options_provider(
70
+ tc: mc.ToolCatalog,
71
+ ) -> _services.ChatChoicesServiceOptionsProvider:
65
72
  return _services.ChatChoicesServiceOptionsProvider(lambda: [
66
73
  mc.Tool(tce.spec)
67
74
  for tce in tc.by_name.values()
@@ -58,7 +58,7 @@ class RenderingStreamAiChatGenerator(StreamAiChatGenerator):
58
58
  self,
59
59
  chat: 'mc.Chat',
60
60
  delta_callback: ta.Callable[['mc.AiDelta'], ta.Awaitable[None]] | None = None,
61
- ) -> mc.Chat:
61
+ ) -> 'mc.Chat':
62
62
  async with self._renderer.create_context() as renderer:
63
63
  async def inner(delta: mc.AiDelta) -> None:
64
64
  if isinstance(delta, mc.ContentAiDelta):
@@ -60,7 +60,7 @@ class ChatChoicesStreamServiceStreamAiChatGenerator(StreamAiChatGenerator):
60
60
  self,
61
61
  chat: 'mc.Chat',
62
62
  delta_callback: ta.Callable[['mc.AiDelta'], ta.Awaitable[None]] | None = None,
63
- ) -> mc.AiChat:
63
+ ) -> 'mc.AiChat':
64
64
  opts = self._options() if self._options is not None else []
65
65
 
66
66
  async with self._service_provider.provide_backend() as service:
@@ -1,5 +1,5 @@
1
1
  from ...... import minichain as mc
2
- from ...tools.execution import ToolUseExecutor
2
+ from ..tools.execution import ToolUseExecutor
3
3
  from .types import AiChatGenerator
4
4
 
5
5
 
@@ -24,17 +24,13 @@ class ToolExecutingAiChatGenerator(AiChatGenerator):
24
24
  while True:
25
25
  new = await self._wrapped.get_next_ai_messages([*chat, *out])
26
26
 
27
- out.extend(new)
28
-
29
27
  cont = False
30
28
 
31
29
  for msg in new:
30
+ out.append(msg)
31
+
32
32
  if isinstance(msg, mc.ToolUseMessage):
33
- trm = await self._executor.execute_tool_use(
34
- msg.tu,
35
- # fs_tool_context,
36
- # todo_tool_context, # noqa
37
- )
33
+ trm = await self._executor.execute_tool_use(msg.tu)
38
34
 
39
35
  out.append(trm)
40
36
 
@@ -26,3 +26,12 @@ class StreamAiChatGenerator(AiChatGenerator, lang.Abstract):
26
26
  delta_callback: ta.Callable[['mc.AiDelta'], ta.Awaitable[None]] | None = None,
27
27
  ) -> ta.Awaitable['mc.Chat']:
28
28
  raise NotImplementedError
29
+
30
+
31
+ ##
32
+
33
+
34
+ class AiChatOutput(lang.Abstract):
35
+ @abc.abstractmethod
36
+ def output_ai_chat(self, chat: 'mc.Chat') -> ta.Awaitable[None]:
37
+ raise NotImplementedError
@@ -0,0 +1,25 @@
1
+ from omlish import dataclasses as dc
2
+
3
+ from ....backends.configs import BackendConfig
4
+ from .ai.configs import AiConfig
5
+ from .state.configs import StateConfig
6
+ from .tools.configs import ToolsConfig
7
+ from .user.configs import UserConfig
8
+
9
+
10
+ ##
11
+
12
+
13
+ DEFAULT_BACKEND = 'openai'
14
+
15
+
16
+ ##
17
+
18
+
19
+ @dc.dataclass(frozen=True, kw_only=True)
20
+ class DriverConfig:
21
+ ai: AiConfig = AiConfig()
22
+ backend: BackendConfig = BackendConfig()
23
+ state: StateConfig = StateConfig()
24
+ tools: ToolsConfig = ToolsConfig()
25
+ user: UserConfig = UserConfig()
@@ -0,0 +1,27 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+ from .injection import event_callbacks
5
+
6
+
7
+ with lang.auto_proxy_import(globals()):
8
+ from . import manager as _manager
9
+
10
+
11
+ ##
12
+
13
+
14
+ def bind_events() -> inj.Elements:
15
+ els: list[inj.Elemental] = []
16
+
17
+ #
18
+
19
+ els.append(event_callbacks().bind_items_provider(singleton=True))
20
+
21
+ #
22
+
23
+ els.append(inj.bind(_manager.ChatEventsManager, singleton=True))
24
+
25
+ #
26
+
27
+ return inj.as_elements(*els)
@@ -0,0 +1,14 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+
5
+ with lang.auto_proxy_import(globals()):
6
+ from . import types as _types
7
+
8
+
9
+ ##
10
+
11
+
12
+ @lang.cached_function
13
+ def event_callbacks() -> 'inj.ItemsBinderHelper[_types.ChatEventCallback]':
14
+ return inj.items_binder_helper[_types.ChatEventCallback](_types.ChatEventCallbacks)
@@ -0,0 +1,16 @@
1
+ from .types import ChatEvent
2
+ from .types import ChatEventCallbacks
3
+
4
+
5
+ ##
6
+
7
+
8
+ class ChatEventsManager:
9
+ def __init__(self, callbacks: ChatEventCallbacks) -> None:
10
+ super().__init__()
11
+
12
+ self._callbacks = callbacks
13
+
14
+ async def emit_event(self, event: ChatEvent) -> None:
15
+ for cb in self._callbacks:
16
+ await cb(event)
@@ -0,0 +1,38 @@
1
+ import typing as ta
2
+
3
+ from omlish import dataclasses as dc
4
+ from omlish import lang
5
+
6
+ from ...... import minichain as mc
7
+
8
+
9
+ ##
10
+
11
+
12
+ class ChatEvent(lang.Abstract, lang.Sealed):
13
+ pass
14
+
15
+
16
+ class ChatEventCallback(lang.Func1[ChatEvent, ta.Awaitable[None]]):
17
+ pass
18
+
19
+
20
+ ChatEventCallbacks = ta.NewType('ChatEventCallbacks', ta.Sequence[ChatEventCallback])
21
+
22
+
23
+ ##
24
+
25
+
26
+ @dc.dataclass(frozen=True)
27
+ class UserMessagesChatEvent(ChatEvent, lang.Final):
28
+ chat: 'mc.UserChat'
29
+
30
+
31
+ @dc.dataclass(frozen=True)
32
+ class AiMessagesChatEvent(ChatEvent, lang.Final):
33
+ chat: 'mc.Chat'
34
+
35
+
36
+ @dc.dataclass(frozen=True)
37
+ class AiDeltaChatEvent(ChatEvent, lang.Final):
38
+ delta: 'mc.AiDelta'
@@ -0,0 +1,50 @@
1
+ """
2
+ TODO:
3
+ - lifecycles
4
+ - StreamService
5
+ """
6
+ from ..... import minichain as mc
7
+ from .ai.types import AiChatGenerator
8
+ from .events.manager import ChatEventsManager
9
+ from .events.types import UserMessagesChatEvent
10
+ from .phases.manager import ChatPhaseManager
11
+ from .phases.types import ChatPhase
12
+ from .state.types import ChatStateManager
13
+ from .types import ChatDriver
14
+
15
+
16
+ ##
17
+
18
+
19
+ class ChatDriverImpl(ChatDriver):
20
+ def __init__(
21
+ self,
22
+ *,
23
+ phases: ChatPhaseManager,
24
+ ai_chat_generator: AiChatGenerator,
25
+ chat_state_manager: ChatStateManager,
26
+ events: ChatEventsManager,
27
+ ) -> None:
28
+ super().__init__()
29
+
30
+ self._phases = phases
31
+ self._ai_chat_generator = ai_chat_generator
32
+ self._chat_state_manager = chat_state_manager
33
+ self._events = events
34
+
35
+ async def start(self) -> None:
36
+ await self._phases.set_phase(ChatPhase.STARTING)
37
+ await self._phases.set_phase(ChatPhase.STARTED)
38
+
39
+ async def stop(self) -> None:
40
+ await self._phases.set_phase(ChatPhase.STOPPING)
41
+ await self._phases.set_phase(ChatPhase.STOPPED)
42
+
43
+ async def send_user_messages(self, next_user_chat: 'mc.UserChat') -> None:
44
+ await self._events.emit_event(UserMessagesChatEvent(next_user_chat))
45
+
46
+ prev_user_chat = (await self._chat_state_manager.get_state()).chat
47
+
48
+ next_ai_chat = await self._ai_chat_generator.get_next_ai_messages([*prev_user_chat, *next_user_chat])
49
+
50
+ await self._chat_state_manager.extend_chat([*next_user_chat, *next_ai_chat])
@@ -0,0 +1,70 @@
1
+ """
2
+ TODO:
3
+ - private + expose(ChatDriver)
4
+ """
5
+ import uuid
6
+
7
+ from omlish import inject as inj
8
+ from omlish import lang
9
+
10
+ from ....backends.types import DefaultBackendName
11
+ from .configs import DEFAULT_BACKEND
12
+ from .configs import DriverConfig
13
+
14
+
15
+ with lang.auto_proxy_import(globals()):
16
+ from ....backends import inject as _backends
17
+ from . import impl as _impl
18
+ from . import types as _types
19
+ from .ai import inject as _ai
20
+ from .events import inject as _events
21
+ from .phases import inject as _phases
22
+ from .state import inject as _state
23
+ from .tools import inject as _tools
24
+ from .user import inject as _user
25
+
26
+
27
+ ##
28
+
29
+
30
+ def bind_driver(cfg: DriverConfig = DriverConfig()) -> inj.Elements:
31
+ els: list[inj.Elemental] = []
32
+
33
+ #
34
+
35
+ els.extend([
36
+ _backends.bind_backends(cfg.backend),
37
+
38
+ _ai.bind_ai(cfg.ai),
39
+
40
+ _events.bind_events(),
41
+
42
+ _phases.bind_phases(),
43
+
44
+ _state.bind_state(cfg.state),
45
+
46
+ _tools.bind_tools(cfg.tools),
47
+
48
+ _user.bind_user(cfg.user),
49
+ ])
50
+
51
+ #
52
+
53
+ els.extend([
54
+ inj.bind(_impl.ChatDriverImpl, singleton=True),
55
+ inj.bind(_types.ChatDriver, to_key=_impl.ChatDriverImpl),
56
+
57
+ inj.bind_async_late(_types.ChatDriver, _types.ChatDriverGetter),
58
+ ])
59
+
60
+ #
61
+
62
+ els.append(inj.bind(DefaultBackendName, to_const=DEFAULT_BACKEND))
63
+
64
+ #
65
+
66
+ els.append(inj.bind(_types.ChatDriverId(uuid.uuid4())))
67
+
68
+ #
69
+
70
+ return inj.as_elements(*els)
@@ -9,3 +9,5 @@ from omlish import dataclasses as dc
9
9
  @dc.dataclass(frozen=True, kw_only=True)
10
10
  class StateConfig:
11
11
  state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue'
12
+
13
+ chat_id: str | None = None
@@ -0,0 +1,25 @@
1
+ from .storage import StateStorage
2
+ from .types import ChatId
3
+
4
+
5
+ ##
6
+
7
+
8
+ _LAST_CHAT_ID_STATE_KEY: str = 'last_chat_id'
9
+
10
+
11
+ class LastChatIdManager:
12
+ def __init__(
13
+ self,
14
+ *,
15
+ state_storage: StateStorage,
16
+ ) -> None:
17
+ super().__init__()
18
+
19
+ self._state_storage = state_storage
20
+
21
+ async def get_last_chat_id(self) -> ChatId | None:
22
+ return await self._state_storage.load_state(_LAST_CHAT_ID_STATE_KEY, ChatId)
23
+
24
+ async def set_last_chat_id(self, chat_id: ChatId | None) -> None:
25
+ await self._state_storage.save_state(_LAST_CHAT_ID_STATE_KEY, chat_id, ChatId)
@@ -0,0 +1,83 @@
1
+ """
2
+ TODO:
3
+ - given chat-id + ephemeral = continue in mem
4
+ - fork chats
5
+ """
6
+ import uuid
7
+
8
+ from omlish import inject as inj
9
+ from omlish import lang
10
+
11
+ from ..phases.injection import phase_callbacks
12
+ from ..phases.types import ChatPhase
13
+ from ..phases.types import ChatPhaseCallback
14
+ from .configs import StateConfig
15
+
16
+
17
+ with lang.auto_proxy_import(globals()):
18
+ from . import ids as _ids
19
+ from . import inmemory as _inmemory
20
+ from . import storage as _storage
21
+ from . import types as _types
22
+
23
+
24
+ ##
25
+
26
+
27
+ def _new_chat_id() -> '_types.ChatId':
28
+ return _types.ChatId(uuid.uuid4())
29
+
30
+
31
+ async def _get_last_or_new_chat_id(lcim: '_ids.LastChatIdManager') -> '_types.ChatId':
32
+ return lcid if (lcid := await lcim.get_last_chat_id()) is not None else _new_chat_id()
33
+
34
+
35
+ ##
36
+
37
+
38
+ def bind_state(cfg: StateConfig = StateConfig()) -> inj.Elements:
39
+ els: list[inj.Elemental] = []
40
+
41
+ if cfg.state in ('continue', 'new'):
42
+ if cfg.state == 'new':
43
+ els.append(inj.bind(_types.ChatId(uuid.UUID(cfg.chat_id)) if cfg.chat_id is not None else _new_chat_id()))
44
+
45
+ elif cfg.chat_id is not None:
46
+ els.append(inj.bind(_types.ChatId(uuid.UUID(cfg.chat_id))))
47
+
48
+ else:
49
+ els.append(inj.bind(_types.ChatId, to_async_fn=_get_last_or_new_chat_id, singleton=True))
50
+
51
+ els.extend([
52
+ inj.bind(_storage.build_chat_storage_key),
53
+
54
+ inj.bind(_types.ChatStateManager, to_ctor=_storage.StateStorageChatStateManager, singleton=True),
55
+ ])
56
+
57
+ #
58
+
59
+ els.extend([
60
+ inj.bind(_ids.LastChatIdManager, singleton=True),
61
+
62
+ phase_callbacks().bind_item(to_fn=inj.target(
63
+ lcim=_ids.LastChatIdManager,
64
+ cid=_types.ChatId,
65
+ )(lambda lcim, cid: ChatPhaseCallback(ChatPhase.STARTED, lambda: lcim.set_last_chat_id(cid)))),
66
+ ])
67
+
68
+ elif cfg.state == 'ephemeral':
69
+ if cfg.chat_id is not None:
70
+ raise ValueError('chat-id is not allowed for ephemeral state')
71
+
72
+ els.extend([
73
+ inj.bind(_new_chat_id()),
74
+
75
+ inj.bind(_types.ChatStateManager, to_ctor=_inmemory.InMemoryChatStateManager, singleton=True),
76
+ ])
77
+
78
+ else:
79
+ raise TypeError(cfg.state)
80
+
81
+ #
82
+
83
+ return inj.as_elements(*els)
@@ -20,10 +20,6 @@ class InMemoryChatStateManager(ChatStateManager):
20
20
  async def get_state(self) -> ChatState:
21
21
  return self._state
22
22
 
23
- async def clear_state(self) -> ChatState:
24
- self._state = ChatState()
25
- return self._state
26
-
27
23
  async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
28
24
  self._state = dc.replace(
29
25
  self._state,
@@ -1,9 +1,11 @@
1
1
  from omlish import check
2
2
  from omlish import dataclasses as dc
3
3
  from omlish import lang
4
+ from omlish import typedvalues as tv
4
5
 
5
6
  from ...... import minichain as mc
6
7
  from .....state.storage import StateStorage
8
+ from .types import ChatId
7
9
  from .types import ChatState
8
10
  from .types import ChatStateManager
9
11
 
@@ -11,35 +13,40 @@ from .types import ChatStateManager
11
13
  ##
12
14
 
13
15
 
16
+ class ChatStateStorageKey(tv.UniqueScalarTypedValue[str]):
17
+ pass
18
+
19
+
20
+ def build_chat_storage_key(chat_id: ChatId) -> ChatStateStorageKey:
21
+ return ChatStateStorageKey(f'chat:{chat_id.v}')
22
+
23
+
24
+ ##
25
+
26
+
14
27
  class StateStorageChatStateManager(ChatStateManager):
15
28
  def __init__(
16
29
  self,
17
30
  *,
18
31
  storage: StateStorage,
19
- key: str = 'chat',
32
+ key: ChatStateStorageKey,
20
33
  ) -> None:
21
34
  super().__init__()
22
35
 
23
36
  self._storage = storage
24
- self._key = check.non_empty_str(key)
37
+ self._key = check.isinstance(key, ChatStateStorageKey)
25
38
 
26
39
  self._state: ChatState | None = None
27
40
 
28
41
  async def get_state(self) -> ChatState:
29
42
  if self._state is not None:
30
43
  return self._state
31
- state: ChatState | None = await self._storage.load_state(self._key, ChatState)
44
+ state: ChatState | None = await self._storage.load_state(self._key.v, ChatState)
32
45
  if state is None:
33
46
  state = ChatState()
34
47
  self._state = state
35
48
  return state
36
49
 
37
- async def clear_state(self) -> ChatState:
38
- state = ChatState()
39
- await self._storage.save_state(self._key, state, ChatState)
40
- self._state = state
41
- return state
42
-
43
50
  async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
44
51
  state = await self.get_state()
45
52
  state = dc.replace(
@@ -47,6 +54,6 @@ class StateStorageChatStateManager(ChatStateManager):
47
54
  chat=[*state.chat, *chat_additions],
48
55
  updated_at=lang.utcnow(),
49
56
  )
50
- await self._storage.save_state(self._key, state, ChatState)
57
+ await self._storage.save_state(self._key.v, state, ChatState)
51
58
  self._state = state
52
59
  return state