ommlds 0.0.0.dev489__py3-none-any.whl → 0.0.0.dev491__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 (99) hide show
  1. ommlds/.omlish-manifests.json +3 -3
  2. ommlds/README.md +11 -0
  3. ommlds/__about__.py +1 -1
  4. ommlds/backends/ollama/_dataclasses.py +53 -23
  5. ommlds/backends/ollama/protocol.py +3 -0
  6. ommlds/cli/_dataclasses.py +835 -356
  7. ommlds/cli/main.py +80 -41
  8. ommlds/cli/rendering/types.py +6 -0
  9. ommlds/cli/sessions/chat/configs.py +4 -12
  10. ommlds/cli/sessions/chat/{chat → drivers}/ai/configs.py +3 -1
  11. ommlds/cli/sessions/chat/drivers/ai/events.py +57 -0
  12. ommlds/cli/sessions/chat/{chat → drivers}/ai/inject.py +10 -3
  13. ommlds/cli/sessions/chat/{chat → drivers}/ai/rendering.py +1 -1
  14. ommlds/cli/sessions/chat/{chat → drivers}/ai/services.py +1 -1
  15. ommlds/cli/sessions/chat/{chat → drivers}/ai/tools.py +1 -1
  16. ommlds/cli/sessions/chat/{chat → drivers}/ai/types.py +9 -0
  17. ommlds/cli/sessions/chat/drivers/configs.py +25 -0
  18. ommlds/cli/sessions/chat/drivers/driver.py +49 -0
  19. ommlds/cli/sessions/chat/drivers/events/inject.py +27 -0
  20. ommlds/cli/sessions/chat/drivers/events/injection.py +14 -0
  21. ommlds/cli/sessions/chat/drivers/events/manager.py +16 -0
  22. ommlds/cli/sessions/chat/drivers/events/types.py +38 -0
  23. ommlds/cli/sessions/chat/drivers/inject.py +69 -0
  24. ommlds/cli/sessions/chat/{chat → drivers}/state/inject.py +3 -3
  25. ommlds/cli/sessions/chat/{chat → drivers}/state/types.py +1 -1
  26. ommlds/cli/sessions/chat/{tools → drivers/tools}/configs.py +2 -2
  27. ommlds/cli/sessions/chat/drivers/tools/confirmation.py +44 -0
  28. ommlds/cli/sessions/chat/{tools → drivers/tools}/execution.py +3 -4
  29. ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/inject.py +3 -3
  30. ommlds/cli/sessions/chat/{tools → drivers/tools}/inject.py +4 -12
  31. ommlds/cli/sessions/chat/{tools → drivers/tools}/injection.py +1 -1
  32. ommlds/cli/sessions/chat/{tools → drivers/tools}/rendering.py +3 -3
  33. ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/inject.py +3 -3
  34. ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/tools.py +1 -1
  35. ommlds/cli/sessions/chat/drivers/types.py +10 -0
  36. ommlds/cli/sessions/chat/{chat → drivers}/user/configs.py +0 -2
  37. ommlds/cli/sessions/chat/drivers/user/inject.py +40 -0
  38. ommlds/cli/sessions/chat/inject.py +4 -32
  39. ommlds/cli/sessions/chat/interfaces/bare/inject.py +61 -0
  40. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +41 -0
  41. ommlds/cli/sessions/chat/{interface/bare/interface.py → interfaces/bare/oneshot.py} +5 -3
  42. ommlds/cli/sessions/chat/{tools/confirmation.py → interfaces/bare/tools.py} +3 -22
  43. ommlds/cli/sessions/chat/{interface → interfaces}/bare/user.py +1 -1
  44. ommlds/cli/sessions/chat/{interface → interfaces}/configs.py +8 -0
  45. ommlds/cli/sessions/chat/interfaces/textual/__init__.py +0 -0
  46. ommlds/cli/sessions/chat/interfaces/textual/app.py +217 -0
  47. ommlds/cli/sessions/chat/interfaces/textual/inject.py +67 -0
  48. ommlds/cli/sessions/chat/{interface → interfaces}/textual/interface.py +0 -3
  49. ommlds/cli/sessions/chat/interfaces/textual/styles/__init__.py +29 -0
  50. ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +51 -0
  51. ommlds/cli/sessions/chat/interfaces/textual/styles/markdown.tcss +7 -0
  52. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +104 -0
  53. ommlds/cli/sessions/chat/interfaces/textual/widgets/__init__.py +0 -0
  54. ommlds/cli/sessions/chat/interfaces/textual/widgets/input.py +36 -0
  55. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +114 -0
  56. ommlds/cli/sessions/chat/session.py +1 -1
  57. ommlds/minichain/backends/impls/ollama/chat.py +24 -56
  58. ommlds/minichain/backends/impls/ollama/protocol.py +144 -0
  59. ommlds/nanochat/rustbpe/README.md +9 -0
  60. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/METADATA +6 -6
  61. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/RECORD +91 -73
  62. ommlds/cli/sessions/chat/chat/user/inject.py +0 -52
  63. ommlds/cli/sessions/chat/chat/user/oneshot.py +0 -25
  64. ommlds/cli/sessions/chat/chat/user/types.py +0 -15
  65. ommlds/cli/sessions/chat/driver.py +0 -43
  66. ommlds/cli/sessions/chat/interface/bare/inject.py +0 -32
  67. ommlds/cli/sessions/chat/interface/textual/app.py +0 -191
  68. ommlds/cli/sessions/chat/interface/textual/inject.py +0 -27
  69. ommlds/cli/sessions/chat/interface/textual/user.py +0 -20
  70. /ommlds/cli/sessions/chat/{chat → drivers}/__init__.py +0 -0
  71. /ommlds/cli/sessions/chat/{chat → drivers}/ai/__init__.py +0 -0
  72. /ommlds/cli/sessions/chat/{chat → drivers}/ai/injection.py +0 -0
  73. /ommlds/cli/sessions/chat/{chat/state → drivers/events}/__init__.py +0 -0
  74. /ommlds/cli/sessions/chat/{chat/user → drivers/phases}/__init__.py +0 -0
  75. /ommlds/cli/sessions/chat/{phases → drivers/phases}/inject.py +0 -0
  76. /ommlds/cli/sessions/chat/{phases → drivers/phases}/injection.py +0 -0
  77. /ommlds/cli/sessions/chat/{phases → drivers/phases}/manager.py +0 -0
  78. /ommlds/cli/sessions/chat/{phases → drivers/phases}/types.py +0 -0
  79. /ommlds/cli/sessions/chat/{interface → drivers/state}/__init__.py +0 -0
  80. /ommlds/cli/sessions/chat/{chat → drivers}/state/configs.py +0 -0
  81. /ommlds/cli/sessions/chat/{chat → drivers}/state/inmemory.py +0 -0
  82. /ommlds/cli/sessions/chat/{chat → drivers}/state/storage.py +0 -0
  83. /ommlds/cli/sessions/chat/{interface/bare → drivers/tools}/__init__.py +0 -0
  84. /ommlds/cli/sessions/chat/{interface/textual → drivers/tools/fs}/__init__.py +0 -0
  85. /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/configs.py +0 -0
  86. /ommlds/cli/sessions/chat/{phases → drivers/tools/todo}/__init__.py +0 -0
  87. /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/configs.py +0 -0
  88. /ommlds/cli/sessions/chat/{tools → drivers/tools/weather}/__init__.py +0 -0
  89. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/configs.py +0 -0
  90. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/inject.py +0 -0
  91. /ommlds/cli/sessions/chat/{tools/fs → drivers/user}/__init__.py +0 -0
  92. /ommlds/cli/sessions/chat/{tools/todo → interfaces}/__init__.py +0 -0
  93. /ommlds/cli/sessions/chat/{tools/weather → interfaces/bare}/__init__.py +0 -0
  94. /ommlds/cli/sessions/chat/{interface → interfaces}/base.py +0 -0
  95. /ommlds/cli/sessions/chat/{interface → interfaces}/inject.py +0 -0
  96. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/WHEEL +0 -0
  97. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/entry_points.txt +0 -0
  98. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/licenses/LICENSE +0 -0
  99. {ommlds-0.0.0.dev489.dist-info → ommlds-0.0.0.dev491.dist-info}/top_level.txt +0 -0
ommlds/cli/main.py CHANGED
@@ -69,9 +69,12 @@ class ChatProfile(Profile):
69
69
  def configure_backend(self, cfg: ChatConfig) -> ChatConfig:
70
70
  return dc.replace(
71
71
  cfg,
72
- backend=dc.replace(
73
- cfg.backend,
74
- backend=self._args.backend,
72
+ driver=dc.replace(
73
+ cfg.driver,
74
+ backend=dc.replace(
75
+ cfg.driver.backend,
76
+ backend=self._args.backend,
77
+ ),
75
78
  ),
76
79
  )
77
80
 
@@ -89,17 +92,28 @@ class ChatProfile(Profile):
89
92
  check.arg(not self._args.message)
90
93
  raise NotImplementedError
91
94
 
92
- if self._args.interactive:
95
+ if self._args.textual:
93
96
  cfg = dc.replace(
94
97
  cfg,
95
98
  interface=dc.replace(
96
99
  cfg.interface,
97
- interactive=True,
98
- use_textual=self._args.textual,
100
+ use_textual=True,
99
101
  ),
100
- user=dc.replace(
101
- cfg.user,
102
- interactive=True,
102
+ )
103
+
104
+ else:
105
+ cfg = dc.replace(
106
+ cfg,
107
+ driver=dc.replace(
108
+ cfg.driver,
109
+ ai=dc.replace(
110
+ cfg.driver.ai,
111
+ verbose=True,
112
+ ),
113
+ ),
114
+ interface=dc.replace(
115
+ cfg.interface,
116
+ interactive=self._args.interactive,
103
117
  ),
104
118
  )
105
119
 
@@ -112,16 +126,19 @@ class ChatProfile(Profile):
112
126
  ]
113
127
 
114
128
  def configure_input(self, cfg: ChatConfig) -> ChatConfig:
115
- if self._args.interactive:
129
+ if self._args.interactive or self._args.textual:
116
130
  check.arg(not self._args.message)
117
131
 
118
132
  elif self._args.message:
119
133
  # TODO: '-' -> stdin
120
134
  cfg = dc.replace(
121
135
  cfg,
122
- user=dc.replace(
123
- cfg.user,
124
- initial_user_content=' '.join(self._args.message),
136
+ driver=dc.replace(
137
+ cfg.driver,
138
+ user=dc.replace(
139
+ cfg.driver.user,
140
+ initial_user_content=' '.join(self._args.message),
141
+ ),
125
142
  ),
126
143
  )
127
144
 
@@ -140,9 +157,12 @@ class ChatProfile(Profile):
140
157
  def configure_state(self, cfg: ChatConfig) -> ChatConfig:
141
158
  return dc.replace(
142
159
  cfg,
143
- state=dc.replace(
144
- cfg.state,
145
- state='ephemeral' if self._args.ephemeral else 'new' if self._args.new else 'continue',
160
+ driver=dc.replace(
161
+ cfg.driver,
162
+ state=dc.replace(
163
+ cfg.driver.state,
164
+ state='ephemeral' if self._args.ephemeral else 'new' if self._args.new else 'continue',
165
+ ),
146
166
  ),
147
167
  )
148
168
 
@@ -156,9 +176,12 @@ class ChatProfile(Profile):
156
176
  def configure_output(self, cfg: ChatConfig) -> ChatConfig:
157
177
  return dc.replace(
158
178
  cfg,
159
- ai=dc.replace(
160
- cfg.ai,
161
- stream=bool(self._args.stream),
179
+ driver=dc.replace(
180
+ cfg.driver,
181
+ ai=dc.replace(
182
+ cfg.driver.ai,
183
+ stream=bool(self._args.stream),
184
+ ),
162
185
  ),
163
186
  rendering=dc.replace(
164
187
  cfg.rendering,
@@ -176,26 +199,36 @@ class ChatProfile(Profile):
176
199
  ]
177
200
 
178
201
  def configure_tools(self, cfg: ChatConfig) -> ChatConfig:
202
+ if not (
203
+ self._args.enable_fs_tools or
204
+ self._args.enable_todo_tools or
205
+ # self._args.enable_unsafe_tools_do_not_use_lol or
206
+ self._args.enable_test_weather_tool or
207
+ self._args.code
208
+ ):
209
+ return cfg
210
+
179
211
  return dc.replace(
180
212
  cfg,
181
- ai=dc.replace(
182
- cfg.ai,
183
- enable_tools=(
184
- self._args.enable_fs_tools or
185
- self._args.enable_todo_tools or
186
- # self._args.enable_unsafe_tools_do_not_use_lol or
187
- self._args.enable_test_weather_tool or
188
- self._args.code
213
+ driver=dc.replace(
214
+ cfg.driver,
215
+ ai=dc.replace(
216
+ cfg.driver.ai,
217
+ enable_tools=True,
218
+ ),
219
+ tools=dc.replace(
220
+ cfg.driver.tools,
221
+ enabled_tools={ # noqa
222
+ *(cfg.driver.tools.enabled_tools or []),
223
+ *(['fs'] if self._args.enable_fs_tools else []),
224
+ *(['todo'] if self._args.enable_todo_tools else []),
225
+ *(['weather'] if self._args.enable_test_weather_tool else []),
226
+ },
189
227
  ),
190
228
  ),
191
- tools=dc.replace(
192
- cfg.tools,
193
- enabled_tools={ # noqa
194
- *(cfg.tools.enabled_tools or []),
195
- *(['fs'] if self._args.enable_fs_tools else []),
196
- *(['todo'] if self._args.enable_todo_tools else []),
197
- *(['weather'] if self._args.enable_test_weather_tool else []),
198
- },
229
+ interface=dc.replace(
230
+ cfg.interface,
231
+ enable_tools=True,
199
232
  ),
200
233
  )
201
234
 
@@ -211,9 +244,12 @@ class ChatProfile(Profile):
211
244
 
212
245
  cfg = dc.replace(
213
246
  cfg,
214
- ai=dc.replace(
215
- cfg.ai,
216
- enable_tools=True,
247
+ driver=dc.replace(
248
+ cfg.driver,
249
+ ai=dc.replace(
250
+ cfg.driver.ai,
251
+ enable_tools=True,
252
+ ),
217
253
  ),
218
254
  )
219
255
 
@@ -223,9 +259,12 @@ class ChatProfile(Profile):
223
259
 
224
260
  cfg = dc.replace(
225
261
  cfg,
226
- user=dc.replace(
227
- cfg.user,
228
- initial_system_content=system_content,
262
+ driver=dc.replace(
263
+ cfg.driver,
264
+ user=dc.replace(
265
+ cfg.driver.user,
266
+ initial_system_content=system_content,
267
+ ),
229
268
  ),
230
269
  )
231
270
 
@@ -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
 
@@ -1,12 +1,8 @@
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 .interface.configs import InterfaceConfig
9
- from .tools.configs import ToolsConfig
4
+ from .drivers.configs import DriverConfig
5
+ from .interfaces.configs import InterfaceConfig
10
6
 
11
7
 
12
8
  ##
@@ -20,10 +16,6 @@ DEFAULT_BACKEND = 'openai'
20
16
 
21
17
  @dc.dataclass(frozen=True, kw_only=True)
22
18
  class ChatConfig:
23
- backend: BackendConfig = BackendConfig()
24
- ai: AiConfig = AiConfig()
25
- state: StateConfig = StateConfig()
26
- user: UserConfig = UserConfig()
27
- rendering: RenderingConfig = RenderingConfig()
19
+ driver: DriverConfig = DriverConfig()
28
20
  interface: InterfaceConfig = InterfaceConfig()
29
- tools: ToolsConfig = ToolsConfig()
21
+ rendering: RenderingConfig = RenderingConfig()
@@ -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
 
@@ -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,49 @@
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
+
14
+
15
+ ##
16
+
17
+
18
+ class ChatDriver:
19
+ def __init__(
20
+ self,
21
+ *,
22
+ phases: ChatPhaseManager,
23
+ ai_chat_generator: AiChatGenerator,
24
+ chat_state_manager: ChatStateManager,
25
+ events: ChatEventsManager,
26
+ ) -> None:
27
+ super().__init__()
28
+
29
+ self._phases = phases
30
+ self._ai_chat_generator = ai_chat_generator
31
+ self._chat_state_manager = chat_state_manager
32
+ self._events = events
33
+
34
+ async def start(self) -> None:
35
+ await self._phases.set_phase(ChatPhase.STARTING)
36
+ await self._phases.set_phase(ChatPhase.STARTED)
37
+
38
+ async def stop(self) -> None:
39
+ await self._phases.set_phase(ChatPhase.STOPPING)
40
+ await self._phases.set_phase(ChatPhase.STOPPED)
41
+
42
+ async def send_user_messages(self, next_user_chat: 'mc.UserChat') -> None:
43
+ await self._events.emit_event(UserMessagesChatEvent(next_user_chat))
44
+
45
+ prev_user_chat = (await self._chat_state_manager.get_state()).chat
46
+
47
+ next_ai_chat = await self._ai_chat_generator.get_next_ai_messages([*prev_user_chat, *next_user_chat])
48
+
49
+ await self._chat_state_manager.extend_chat([*next_user_chat, *next_ai_chat])
@@ -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,69 @@
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 driver as _driver
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) -> 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(_driver.ChatDriver, singleton=True),
55
+
56
+ inj.bind_late(_driver.ChatDriver),
57
+ ])
58
+
59
+ #
60
+
61
+ els.append(inj.bind(DefaultBackendName, to_const=DEFAULT_BACKEND))
62
+
63
+ #
64
+
65
+ els.append(inj.bind(_types.ChatDriverId(uuid.uuid4())))
66
+
67
+ #
68
+
69
+ return inj.as_elements(*els)
@@ -1,9 +1,9 @@
1
1
  from omlish import inject as inj
2
2
  from omlish import lang
3
3
 
4
- from ...phases.injection import phase_callbacks
5
- from ...phases.types import ChatPhase
6
- from ...phases.types import ChatPhaseCallback
4
+ from ..phases.injection import phase_callbacks
5
+ from ..phases.types import ChatPhase
6
+ from ..phases.types import ChatPhaseCallback
7
7
  from .configs import StateConfig
8
8
 
9
9
 
@@ -18,7 +18,7 @@ class ChatState:
18
18
  created_at: datetime.datetime = dc.field(default_factory=lang.utcnow)
19
19
  updated_at: datetime.datetime = dc.field(default_factory=lang.utcnow)
20
20
 
21
- chat: mc.Chat = ()
21
+ chat: 'mc.Chat' = ()
22
22
 
23
23
 
24
24
  ##
@@ -9,10 +9,10 @@ from omlish import lang
9
9
 
10
10
  @dc.dataclass(frozen=True, kw_only=True)
11
11
  class ToolsConfig:
12
- silent: bool = False
13
- dangerous_no_confirmation: bool = False
14
12
  enabled_tools: ta.Iterable[str] | None = None
15
13
 
14
+ verbose: bool = False
15
+
16
16
 
17
17
  ##
18
18