ommlds 0.0.0.dev492__py3-none-any.whl → 0.0.0.dev493__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 (52) hide show
  1. ommlds/__about__.py +1 -1
  2. ommlds/cli/_dataclasses.py +617 -724
  3. ommlds/cli/backends/catalog.py +0 -5
  4. ommlds/cli/backends/inject.py +2 -0
  5. ommlds/cli/inject.py +11 -3
  6. ommlds/cli/main.py +33 -24
  7. ommlds/cli/sessions/base.py +1 -10
  8. ommlds/cli/sessions/chat/configs.py +6 -8
  9. ommlds/cli/sessions/chat/drivers/driver.py +3 -1
  10. ommlds/cli/sessions/chat/drivers/inject.py +2 -3
  11. ommlds/cli/sessions/chat/drivers/state/configs.py +2 -0
  12. ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
  13. ommlds/cli/sessions/chat/drivers/state/inject.py +54 -6
  14. ommlds/cli/sessions/chat/drivers/state/inmemory.py +0 -4
  15. ommlds/cli/sessions/chat/drivers/state/storage.py +17 -10
  16. ommlds/cli/sessions/chat/drivers/state/types.py +9 -4
  17. ommlds/cli/sessions/chat/drivers/user/inject.py +3 -3
  18. ommlds/cli/sessions/chat/facades/__init__.py +0 -0
  19. ommlds/cli/sessions/chat/facades/configs.py +9 -0
  20. ommlds/cli/sessions/chat/facades/facade.py +19 -0
  21. ommlds/cli/sessions/chat/facades/inject.py +23 -0
  22. ommlds/cli/sessions/chat/inject.py +5 -3
  23. ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
  24. ommlds/cli/sessions/chat/interfaces/bare/inject.py +2 -2
  25. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +4 -2
  26. ommlds/cli/sessions/chat/interfaces/configs.py +2 -14
  27. ommlds/cli/sessions/chat/interfaces/inject.py +8 -3
  28. ommlds/cli/sessions/chat/interfaces/textual/app.py +53 -30
  29. ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
  30. ommlds/cli/sessions/chat/interfaces/textual/inject.py +29 -6
  31. ommlds/cli/sessions/chat/interfaces/textual/interface.py +2 -82
  32. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +8 -5
  33. ommlds/cli/sessions/chat/interfaces/textual/tools.py +4 -3
  34. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +11 -3
  35. ommlds/cli/sessions/chat/session.py +2 -15
  36. ommlds/cli/sessions/completion/configs.py +3 -4
  37. ommlds/cli/sessions/completion/inject.py +1 -2
  38. ommlds/cli/sessions/completion/session.py +4 -8
  39. ommlds/cli/sessions/configs.py +10 -0
  40. ommlds/cli/sessions/embedding/configs.py +3 -4
  41. ommlds/cli/sessions/embedding/inject.py +1 -2
  42. ommlds/cli/sessions/embedding/session.py +4 -8
  43. ommlds/cli/sessions/inject.py +15 -15
  44. ommlds/cli/state/storage.py +7 -1
  45. ommlds/minichain/backends/impls/cerebras/protocol.py +4 -4
  46. ommlds/minichain/backends/impls/ollama/chat.py +2 -2
  47. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/METADATA +6 -6
  48. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/RECORD +52 -44
  49. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/WHEEL +0 -0
  50. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  51. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  52. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,7 @@ from omlish import inject as inj
2
2
  from omlish import lang
3
3
 
4
4
  from ..base import ChatInterface
5
- from ..configs import InterfaceConfig
5
+ from .configs import BareInterfaceConfig
6
6
 
7
7
 
8
8
  with lang.auto_proxy_import(globals()):
@@ -17,7 +17,7 @@ with lang.auto_proxy_import(globals()):
17
17
  ##
18
18
 
19
19
 
20
- def bind_bare(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
20
+ def bind_bare(cfg: BareInterfaceConfig = BareInterfaceConfig()) -> inj.Elements:
21
21
  els: list[inj.Elemental] = []
22
22
 
23
23
  #
@@ -1,10 +1,10 @@
1
1
  import typing as ta
2
2
 
3
- from ...... import minichain as mc
4
3
  from .....inputs.asyncs import AsyncStringInput
5
4
  from .....inputs.asyncs import SyncAsyncStringInput
6
5
  from .....inputs.sync import InputSyncStringInput
7
6
  from ...drivers.driver import ChatDriver
7
+ from ...facades.facade import ChatFacade
8
8
  from ..base import ChatInterface
9
9
 
10
10
 
@@ -18,11 +18,13 @@ class InteractiveBareChatInterface(ChatInterface):
18
18
  self,
19
19
  *,
20
20
  driver: ChatDriver,
21
+ facade: ChatFacade,
21
22
  string_input: AsyncStringInput | None = None,
22
23
  ) -> None:
23
24
  super().__init__()
24
25
 
25
26
  self._driver = driver
27
+ self._facade = facade
26
28
  if string_input is None:
27
29
  string_input = self.DEFAULT_STRING_INPUT
28
30
  self._string_input = string_input
@@ -40,7 +42,7 @@ class InteractiveBareChatInterface(ChatInterface):
40
42
  print('<')
41
43
  print()
42
44
 
43
- await self._driver.send_user_messages([mc.UserMessage(s)])
45
+ await self._facade.handle_user_input(s)
44
46
 
45
47
  print()
46
48
 
@@ -1,23 +1,11 @@
1
- """
2
- TODO:
3
- - obviously, subclasses of InterfaceConfig
4
- - this is really just another instance of the whole `argparse -> config -> inject` flow
5
- """
6
- import typing as ta
7
-
8
1
  from omlish import dataclasses as dc
2
+ from omlish import lang
9
3
 
10
4
 
11
5
  ##
12
6
 
13
7
 
14
8
  @dc.dataclass(frozen=True, kw_only=True)
15
- class InterfaceConfig:
16
- interactive: bool = False
17
-
18
- use_textual: bool = False
19
-
20
- use_readline: bool | ta.Literal['auto'] = 'auto'
21
-
9
+ class InterfaceConfig(lang.Abstract):
22
10
  enable_tools: bool = False
23
11
  dangerous_no_tool_confirmation: bool = False
@@ -1,7 +1,9 @@
1
1
  from omlish import inject as inj
2
2
  from omlish import lang
3
3
 
4
+ from .bare.configs import BareInterfaceConfig
4
5
  from .configs import InterfaceConfig
6
+ from .textual.configs import TextualInterfaceConfig
5
7
 
6
8
 
7
9
  with lang.auto_proxy_import(globals()):
@@ -12,13 +14,16 @@ with lang.auto_proxy_import(globals()):
12
14
  ##
13
15
 
14
16
 
15
- def bind_interface(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
17
+ def bind_interface(cfg: InterfaceConfig = BareInterfaceConfig()) -> inj.Elements:
16
18
  els: list[inj.Elemental] = []
17
19
 
18
- if cfg.use_textual:
20
+ if isinstance(cfg, TextualInterfaceConfig):
19
21
  els.append(_textual.bind_textual(cfg))
20
22
 
21
- else:
23
+ elif isinstance(cfg, BareInterfaceConfig):
22
24
  els.append(_bare.bind_bare(cfg))
23
25
 
26
+ else:
27
+ raise TypeError(cfg)
28
+
24
29
  return inj.as_elements(*els)
@@ -1,15 +1,19 @@
1
1
  import asyncio
2
+ import os
2
3
  import typing as ta
3
4
 
4
5
  from omdev.tui import textual as tx
5
6
  from omlish import check
7
+ from omlish import dataclasses as dc
6
8
  from omlish import lang
7
9
  from omlish.logs import all as logs
8
10
 
9
11
  from ...... import minichain as mc
12
+ from .....backends.types import BackendName
10
13
  from ...drivers.driver import ChatDriver
11
14
  from ...drivers.events.types import AiDeltaChatEvent
12
15
  from ...drivers.events.types import AiMessagesChatEvent
16
+ from ...facades.facade import ChatFacade
13
17
  from .styles import read_app_css
14
18
  from .widgets.input import InputOuter
15
19
  from .widgets.input import InputTextArea
@@ -27,13 +31,13 @@ log, alog = logs.get_module_loggers(globals())
27
31
  ##
28
32
 
29
33
 
30
- ChatDriverEventQueue = ta.NewType('ChatDriverEventQueue', asyncio.Queue)
34
+ ChatEventQueue = ta.NewType('ChatEventQueue', asyncio.Queue)
31
35
 
32
36
 
33
37
  ##
34
38
 
35
39
 
36
- class ChatAppGetter(lang.CachedFunc0['ChatApp']):
40
+ class ChatAppGetter(lang.CachedFunc0[ta.Awaitable['ChatApp']]):
37
41
  pass
38
42
 
39
43
 
@@ -43,17 +47,23 @@ class ChatApp(tx.App):
43
47
  def __init__(
44
48
  self,
45
49
  *,
50
+ chat_facade: ChatFacade,
46
51
  chat_driver: ChatDriver,
47
- chat_driver_event_queue: ChatDriverEventQueue,
52
+ chat_event_queue: ChatEventQueue,
53
+ backend_name: BackendName | None = None,
54
+ devtools_setup: tx.DevtoolsSetup | None = None,
48
55
  ) -> None:
49
56
  super().__init__()
50
57
 
51
- tx.setup_app_devtools(self, port=41932)
58
+ if devtools_setup is not None:
59
+ devtools_setup(self)
52
60
 
61
+ self._chat_facade = chat_facade
53
62
  self._chat_driver = chat_driver
54
- self._chat_driver_event_queue = chat_driver_event_queue
63
+ self._chat_event_queue = chat_event_queue
64
+ self._backend_name = backend_name
55
65
 
56
- self._chat_driver_action_queue: asyncio.Queue[ta.Any] = asyncio.Queue()
66
+ self._chat_action_queue: asyncio.Queue[ta.Any] = asyncio.Queue()
57
67
 
58
68
  def get_driver_class(self) -> type[tx.Driver]:
59
69
  return tx.get_pending_writes_driver_class(super().get_driver_class())
@@ -144,16 +154,16 @@ class ChatApp(tx.App):
144
154
 
145
155
  #
146
156
 
147
- _chat_driver_event_task: asyncio.Task[None] | None = None
157
+ _chat_event_task: asyncio.Task[None] | None = None
148
158
 
149
159
  @logs.async_exception_logging(alog)
150
- async def _chat_driver_event_task_main(self) -> None:
160
+ async def _chat_event_task_main(self) -> None:
151
161
  while True:
152
- ev = await self._chat_driver_event_queue.get()
162
+ ev = await self._chat_event_queue.get()
153
163
  if ev is None:
154
164
  break
155
165
 
156
- await alog.debug(lambda: f'Got chat driver event: {ev!r}')
166
+ await alog.debug(lambda: f'Got chat event: {ev!r}')
157
167
 
158
168
  if isinstance(ev, AiMessagesChatEvent):
159
169
  wx: list[tx.Widget] = []
@@ -181,20 +191,24 @@ class ChatApp(tx.App):
181
191
 
182
192
  #
183
193
 
184
- _chat_driver_action_task: asyncio.Task[None] | None = None
194
+ @dc.dataclass(frozen=True)
195
+ class UserInput:
196
+ text: str
197
+
198
+ _chat_action_task: asyncio.Task[None] | None = None
185
199
 
186
200
  @logs.async_exception_logging(alog)
187
- async def _chat_driver_action_task_main(self) -> None:
201
+ async def _chat_action_task_main(self) -> None:
188
202
  while True:
189
- ac = await self._chat_driver_action_queue.get()
203
+ ac = await self._chat_action_queue.get()
190
204
  if ac is None:
191
205
  break
192
206
 
193
- await alog.debug(lambda: f'Got chat driver action: {ac!r}')
207
+ await alog.debug(lambda: f'Got chat action: {ac!r}')
194
208
 
195
- if isinstance(ac, mc.UserMessage):
209
+ if isinstance(ac, ChatApp.UserInput):
196
210
  try:
197
- await self._chat_driver.send_user_messages([ac])
211
+ await self._chat_facade.handle_user_input(ac.text)
198
212
  except Exception as e: # noqa
199
213
  raise
200
214
 
@@ -204,31 +218,32 @@ class ChatApp(tx.App):
204
218
  #
205
219
 
206
220
  async def on_mount(self) -> None:
207
- check.state(self._chat_driver_event_task is None)
208
- self._chat_driver_event_task = asyncio.create_task(self._chat_driver_event_task_main())
221
+ check.state(self._chat_event_task is None)
222
+ self._chat_event_task = asyncio.create_task(self._chat_event_task_main())
209
223
 
210
224
  await self._chat_driver.start()
211
225
 
212
- check.state(self._chat_driver_action_task is None)
213
- self._chat_driver_action_task = asyncio.create_task(self._chat_driver_action_task_main())
226
+ check.state(self._chat_action_task is None)
227
+ self._chat_action_task = asyncio.create_task(self._chat_action_task_main())
214
228
 
215
229
  self._get_input_text_area().focus()
216
230
 
217
231
  await self._mount_messages(
218
- WelcomeMessage(
219
- 'Hello!',
220
- ),
232
+ WelcomeMessage('\n'.join([
233
+ f'Backend: {self._backend_name or "?"}',
234
+ f'Dir: {os.getcwd()}',
235
+ ])),
221
236
  )
222
237
 
223
238
  async def on_unmount(self) -> None:
224
- if (cdt := self._chat_driver_event_task) is not None:
225
- await self._chat_driver_event_queue.put(None)
239
+ if (cdt := self._chat_event_task) is not None:
240
+ await self._chat_event_queue.put(None)
226
241
  await cdt
227
242
 
228
243
  await self._chat_driver.stop()
229
244
 
230
- if (cet := self._chat_driver_event_task) is not None:
231
- await self._chat_driver_event_queue.put(None)
245
+ if (cet := self._chat_event_task) is not None:
246
+ await self._chat_event_queue.put(None)
232
247
  await cet
233
248
 
234
249
  @tx.on(InputTextArea.Submitted)
@@ -243,14 +258,22 @@ class ChatApp(tx.App):
243
258
  ),
244
259
  )
245
260
 
246
- await self._chat_driver_action_queue.put(mc.UserMessage(event.text))
261
+ await self._chat_action_queue.put(ChatApp.UserInput(event.text))
247
262
 
248
263
  #
249
264
 
250
- async def confirm_tool_use(self, message: str) -> bool:
265
+ async def confirm_tool_use(
266
+ self,
267
+ outer_message: str,
268
+ inner_message: str,
269
+ ) -> bool:
251
270
  fut: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
252
271
 
253
- tcm = ToolConfirmationMessage(message, fut)
272
+ tcm = ToolConfirmationMessage(
273
+ outer_message,
274
+ inner_message,
275
+ fut,
276
+ )
254
277
 
255
278
  async def inner() -> None:
256
279
  await self._mount_messages(tcm)
@@ -0,0 +1,11 @@
1
+ from omlish import dataclasses as dc
2
+
3
+ from ..configs import InterfaceConfig
4
+
5
+
6
+ ##
7
+
8
+
9
+ @dc.dataclass(frozen=True, kw_only=True)
10
+ class TextualInterfaceConfig(InterfaceConfig):
11
+ pass
@@ -3,16 +3,19 @@ FIXME:
3
3
  - too lazy to lazy import guts like every other proper inject module lol >_<
4
4
  """
5
5
  import asyncio
6
+ import contextlib
6
7
 
7
8
  from omlish import inject as inj
8
9
  from omlish import lang
9
10
 
10
11
  from ...drivers.events.injection import event_callbacks
11
12
  from ..base import ChatInterface
12
- from ..configs import InterfaceConfig
13
+ from .configs import TextualInterfaceConfig
13
14
 
14
15
 
15
16
  with lang.auto_proxy_import(globals()):
17
+ from omdev.tui import textual as tx
18
+
16
19
  from ...drivers.tools import confirmation as _tools_confirmation
17
20
  from . import app as _app
18
21
  from . import interface as _interface
@@ -22,7 +25,7 @@ with lang.auto_proxy_import(globals()):
22
25
  ##
23
26
 
24
27
 
25
- def bind_textual(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
28
+ def bind_textual(cfg: TextualInterfaceConfig = TextualInterfaceConfig()) -> inj.Elements:
26
29
  els: list[inj.Elemental] = [
27
30
  inj.bind(ChatInterface, to_ctor=_interface.TextualChatInterface, singleton=True),
28
31
  ]
@@ -31,18 +34,17 @@ def bind_textual(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
31
34
 
32
35
  els.extend([
33
36
  inj.bind(_app.ChatApp, singleton=True),
34
-
35
- inj.bind_late(_app.ChatApp, _app.ChatAppGetter),
37
+ inj.bind_async_late(_app.ChatApp, _app.ChatAppGetter),
36
38
  ])
37
39
 
38
40
  #
39
41
 
40
42
  els.extend([
41
- inj.bind(_app.ChatDriverEventQueue, to_const=asyncio.Queue()),
43
+ inj.bind(_app.ChatEventQueue, to_const=asyncio.Queue()),
42
44
 
43
45
  event_callbacks().bind_item(to_fn=inj.KwargsTarget.of(
44
46
  lambda eq: lambda ev: eq.put(ev),
45
- eq=_app.ChatDriverEventQueue,
47
+ eq=_app.ChatEventQueue,
46
48
  )),
47
49
  ])
48
50
 
@@ -65,4 +67,25 @@ def bind_textual(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
65
67
 
66
68
  #
67
69
 
70
+ els.extend([
71
+ inj.bind(tx.DevtoolsConfig(port=41932)), # FIXME: lol
72
+
73
+ inj.bind(
74
+ tx.DevtoolsManager,
75
+ singleton=True,
76
+ to_async_fn=inj.make_async_managed_provider(
77
+ tx.DevtoolsManager,
78
+ contextlib.aclosing,
79
+ ),
80
+ ),
81
+
82
+ inj.bind(
83
+ tx.DevtoolsSetup,
84
+ to_async_fn=inj.KwargsTarget.of(lambda mgr: mgr.get_setup(), mgr=tx.DevtoolsManager),
85
+ singleton=True,
86
+ ),
87
+ ])
88
+
89
+ #
90
+
68
91
  return inj.as_elements(*els)
@@ -1,90 +1,9 @@
1
- import inspect
2
- import logging
3
- import typing as ta
4
-
5
1
  from omdev.tui import textual as tx
6
2
 
7
3
  from ..base import ChatInterface
8
4
  from .app import ChatApp
9
5
 
10
6
 
11
- if ta.TYPE_CHECKING:
12
- from textual_dev.client import DevtoolsClient
13
-
14
-
15
- ##
16
-
17
-
18
- def _translate_log_level(level: int) -> tuple['tx.LogGroup', 'tx.LogVerbosity']:
19
- if level >= logging.ERROR:
20
- return (tx.LogGroup.ERROR, tx.LogVerbosity.HIGH)
21
- elif level >= logging.WARNING:
22
- return (tx.LogGroup.ERROR, tx.LogVerbosity.HIGH)
23
- elif level >= logging.INFO:
24
- return (tx.LogGroup.INFO, tx.LogVerbosity.NORMAL)
25
- elif level >= logging.DEBUG:
26
- return (tx.LogGroup.DEBUG, tx.LogVerbosity.NORMAL)
27
- else:
28
- return (tx.LogGroup.UNDEFINED, tx.LogVerbosity.NORMAL)
29
-
30
-
31
- class _HackLoggingHandler(logging.Handler):
32
- """
33
- TODO:
34
- - reify caller from LogContextInfos
35
- - queue worker, this blocks the asyncio thread lol
36
- - move to omdev.tui.textual obviously
37
- """
38
-
39
- def __init__(self, devtools: ta.Optional['DevtoolsClient']) -> None:
40
- super().__init__()
41
-
42
- self._devtools = devtools
43
-
44
- def emit(self, record: logging.LogRecord) -> None:
45
- if (devtools := self._devtools) is not None and devtools.is_connected:
46
- from textual_dev.client import DevtoolsLog
47
-
48
- msg = self.format(record)
49
-
50
- caller = inspect.Traceback(
51
- filename=record.filename,
52
- lineno=record.lineno,
53
- function=record.funcName,
54
- code_context=None,
55
- index=None,
56
- )
57
-
58
- group, verbosity = _translate_log_level(record.levelno)
59
-
60
- devtools.log(
61
- DevtoolsLog(
62
- msg,
63
- caller=caller,
64
- ),
65
- group=group,
66
- verbosity=verbosity,
67
- )
68
-
69
-
70
- def _hack_loggers(devtools: ta.Optional['DevtoolsClient']) -> None:
71
- from omlish.logs.std.standard import _locking_logging_module_lock # noqa
72
- from omlish.logs.std.standard import StandardConfiguredLoggingHandler
73
-
74
- with _locking_logging_module_lock():
75
- std_handler = next((h for h in logging.root.handlers if isinstance(h, StandardConfiguredLoggingHandler)), None)
76
-
77
- hack_handler = _HackLoggingHandler(devtools)
78
-
79
- if std_handler is not None:
80
- hack_handler.setFormatter(std_handler.formatter)
81
-
82
- for std_filter in std_handler.filters:
83
- hack_handler.addFilter(std_filter)
84
-
85
- logging.root.handlers = [hack_handler]
86
-
87
-
88
7
  ##
89
8
 
90
9
 
@@ -99,6 +18,7 @@ class TextualChatInterface(ChatInterface):
99
18
  self._app = app
100
19
 
101
20
  async def run(self) -> None:
102
- _hack_loggers(self._app.devtools)
21
+ # FIXME: move lol
22
+ tx.set_root_logger_to_devtools(self._app.devtools)
103
23
 
104
24
  await self._app.run_async()
@@ -42,7 +42,6 @@
42
42
  }
43
43
 
44
44
  .welcome-message-content {
45
- text-align: center;
46
45
  }
47
46
 
48
47
 
@@ -135,8 +134,6 @@
135
134
  .tool-confirmation-message-inner {
136
135
  width: 1fr;
137
136
  height: auto;
138
-
139
- padding: 1;
140
137
  }
141
138
 
142
139
  .tool-confirmation-message-inner-open {
@@ -146,10 +143,16 @@
146
143
  .tool-confirmation-message-inner-closed {
147
144
  }
148
145
 
149
- .tool-confirmation-message-content {
146
+ .tool-confirmation-message-outer-content {
147
+ }
148
+
149
+ .tool-confirmation-message-inner-content {
150
150
  background: $background;
151
+
152
+ margin: 1;
153
+ padding: 1;
151
154
  }
152
155
 
153
156
  .tool-confirmation-message-controls {
154
- margin-top: 1;
157
+ margin: 1;
155
158
  }
@@ -31,7 +31,8 @@ class ChatAppToolExecutionConfirmation(ToolExecutionConfirmation):
31
31
  # spec=msh.marshal(tce.spec),
32
32
  )
33
33
 
34
- msg = f'Execute requested tool?\n\n{json.dumps_pretty(tr_dct)}' # noqa
35
-
36
- if not await self._app().confirm_tool_use(msg):
34
+ if not await (await self._app()).confirm_tool_use(
35
+ 'Execute requested tool?',
36
+ json.dumps_pretty(tr_dct),
37
+ ):
37
38
  raise ToolExecutionRequestDeniedError
@@ -139,19 +139,26 @@ class ToolConfirmationControls(tx.Static):
139
139
 
140
140
 
141
141
  class ToolConfirmationMessage(Message):
142
- def __init__(self, content: str, fut: asyncio.Future[bool]) -> None:
142
+ def __init__(
143
+ self,
144
+ outer_content: str,
145
+ inner_content: str,
146
+ fut: asyncio.Future[bool],
147
+ ) -> None:
143
148
  super().__init__()
144
149
 
145
150
  self.add_class('tool-confirmation-message')
146
151
 
147
- self._content = content
152
+ self._outer_content = outer_content
153
+ self._inner_content = inner_content
148
154
  self._fut = fut
149
155
 
150
156
  def compose(self) -> tx.ComposeResult:
151
157
  with tx.Horizontal(classes='tool-confirmation-message-outer'):
152
158
  yield tx.Static('? ', classes='tool-confirmation-message-glyph')
153
159
  with tx.Vertical(classes='tool-confirmation-message-inner tool-confirmation-message-inner-open'):
154
- yield tx.Static(self._content, classes='tool-confirmation-message-content')
160
+ yield tx.Static(self._outer_content, classes='tool-confirmation-message-outer-content')
161
+ yield tx.Static(self._inner_content, classes='tool-confirmation-message-inner-content')
155
162
  yield ToolConfirmationControls(classes='tool-confirmation-message-controls')
156
163
 
157
164
  @tx.on(ToolConfirmationControls.Allowed)
@@ -160,5 +167,6 @@ class ToolConfirmationMessage(Message):
160
167
  await inner.query_one(ToolConfirmationControls).remove()
161
168
  inner.remove_class('tool-confirmation-message-inner-open')
162
169
  inner.add_class('tool-confirmation-message-inner-closed')
170
+ inner.query_one('.tool-confirmation-message-outer-content', tx.Static).update('Tool use confirmed.')
163
171
 
164
172
  self._fut.set_result(True)
@@ -1,9 +1,6 @@
1
1
  import typing as ta
2
2
 
3
- from omlish import dataclasses as dc
4
-
5
3
  from ..base import Session
6
- from .configs import ChatConfig
7
4
  from .interfaces.base import ChatInterface
8
5
 
9
6
 
@@ -11,23 +8,13 @@ from .interfaces.base import ChatInterface
11
8
 
12
9
 
13
10
  @ta.final
14
- class ChatSession(Session['ChatSession.Config']):
15
- """
16
- An adapter to the lower level, dumber, non-chat-specific cli 'session' layer. Nothing else takes the kitchen-sink
17
- 'ChatConfig' object, it's only here for type dispatch in lower layers.
18
- """
19
-
20
- @dc.dataclass(frozen=True)
21
- class Config(Session.Config, ChatConfig):
22
- pass
23
-
11
+ class ChatSession(Session):
24
12
  def __init__(
25
13
  self,
26
- config: Config,
27
14
  *,
28
15
  interface: ChatInterface,
29
16
  ) -> None:
30
- super().__init__(config)
17
+ super().__init__()
31
18
 
32
19
  self._interface = interface
33
20
 
@@ -1,6 +1,7 @@
1
1
  from omlish import dataclasses as dc
2
2
 
3
3
  from .... import minichain as mc
4
+ from ..configs import SessionConfig
4
5
 
5
6
 
6
7
  ##
@@ -12,10 +13,8 @@ DEFAULT_BACKEND = 'openai'
12
13
  ##
13
14
 
14
15
 
15
- @dc.dataclass(frozen=True)
16
- class CompletionConfig:
16
+ @dc.dataclass(frozen=True, kw_only=True)
17
+ class CompletionConfig(SessionConfig):
17
18
  content: 'mc.Content'
18
19
 
19
- _: dc.KW_ONLY
20
-
21
20
  backend: str | None = None
@@ -1,4 +1,3 @@
1
- from omlish import dataclasses as dc
2
1
  from omlish import inject as inj
3
2
  from omlish import lang
4
3
 
@@ -23,7 +22,7 @@ def bind_completion(cfg: CompletionConfig) -> inj.Elements:
23
22
  #
24
23
 
25
24
  els.extend([
26
- inj.bind(_session.CompletionSession.Config(**dc.asdict(cfg))),
25
+ inj.bind(cfg),
27
26
  inj.bind(Session, to_ctor=_session.CompletionSession, singleton=True),
28
27
  ])
29
28
 
@@ -1,5 +1,4 @@
1
1
  from omlish import check
2
- from omlish import dataclasses as dc
3
2
 
4
3
  from .... import minichain as mc
5
4
  from ...backends.types import CompletionServiceBackendProvider
@@ -10,19 +9,16 @@ from .configs import CompletionConfig
10
9
  ##
11
10
 
12
11
 
13
- class CompletionSession(Session['CompletionSession.Config']):
14
- @dc.dataclass(frozen=True)
15
- class Config(Session.Config, CompletionConfig):
16
- pass
17
-
12
+ class CompletionSession(Session):
18
13
  def __init__(
19
14
  self,
20
- config: Config,
15
+ config: CompletionConfig,
21
16
  *,
22
17
  service_provider: CompletionServiceBackendProvider,
23
18
  ) -> None:
24
- super().__init__(config)
19
+ super().__init__()
25
20
 
21
+ self._config = config
26
22
  self._service_provider = service_provider
27
23
 
28
24
  async def run(self) -> None:
@@ -0,0 +1,10 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+
5
+ ##
6
+
7
+
8
+ @dc.dataclass(frozen=True, kw_only=True)
9
+ class SessionConfig(lang.Abstract):
10
+ pass