ommlds 0.0.0.dev494__py3-none-any.whl → 0.0.0.dev495__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/cli/_dataclasses.py +7 -7
  2. ommlds/cli/backends/inject.py +51 -8
  3. ommlds/cli/backends/meta.py +35 -0
  4. ommlds/cli/inject.py +1 -1
  5. ommlds/cli/sessions/chat/drivers/{driver.py → impl.py} +2 -9
  6. ommlds/cli/sessions/chat/drivers/inject.py +5 -3
  7. ommlds/cli/sessions/chat/drivers/state/inject.py +2 -3
  8. ommlds/cli/sessions/chat/drivers/tools/injection.py +4 -4
  9. ommlds/cli/sessions/chat/drivers/types.py +21 -0
  10. ommlds/cli/sessions/chat/drivers/user/inject.py +1 -1
  11. ommlds/cli/sessions/chat/facades/commands/inject.py +0 -2
  12. ommlds/cli/sessions/chat/facades/commands/simple.py +2 -8
  13. ommlds/cli/sessions/chat/facades/facade.py +1 -1
  14. ommlds/cli/sessions/chat/facades/inject.py +5 -0
  15. ommlds/cli/sessions/chat/facades/ui.py +11 -0
  16. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +1 -1
  17. ommlds/cli/sessions/chat/interfaces/bare/oneshot.py +1 -1
  18. ommlds/cli/sessions/chat/interfaces/textual/app.py +21 -3
  19. ommlds/cli/sessions/chat/interfaces/textual/inject.py +2 -5
  20. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +2 -0
  21. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +12 -5
  22. ommlds/minichain/__init__.py +16 -6
  23. ommlds/minichain/_dataclasses.py +6872 -3644
  24. ommlds/minichain/backends/strings/resolving.py +1 -1
  25. ommlds/minichain/chat/messages.py +1 -1
  26. ommlds/minichain/content/_marshal.py +6 -4
  27. ommlds/minichain/content/cancontent.py +32 -0
  28. ommlds/minichain/content/materialize.py +2 -25
  29. ommlds/minichain/content/prepare.py +3 -3
  30. ommlds/minichain/content/sequence.py +2 -2
  31. ommlds/minichain/content/tag.py +19 -0
  32. ommlds/minichain/content/transforms/interleave.py +10 -3
  33. ommlds/minichain/lib/fs/tools/read.py +1 -1
  34. ommlds/minichain/lib/fs/tools/recursivels/rendering.py +1 -1
  35. ommlds/minichain/lib/fs/tools/recursivels/running.py +1 -1
  36. ommlds/minichain/lib/todo/types.py +1 -1
  37. ommlds/minichain/meta/__init__.py +0 -0
  38. ommlds/minichain/meta/firstinwins.py +131 -0
  39. ommlds/minichain/text/applypatch.py +2 -1
  40. ommlds/minichain/text/toolparsing/llamacpp/types.py +1 -1
  41. ommlds/minichain/tokens/specials.py +1 -1
  42. ommlds/minichain/tools/execution/errorhandling.py +35 -0
  43. ommlds/minichain/tools/execution/errors.py +1 -1
  44. ommlds/minichain/tools/reflect.py +1 -1
  45. ommlds/minichain/tools/types.py +1 -1
  46. ommlds/minichain/vectors/_marshal.py +1 -1
  47. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/METADATA +4 -4
  48. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/RECORD +52 -46
  49. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/WHEEL +0 -0
  50. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/entry_points.txt +0 -0
  51. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/licenses/LICENSE +0 -0
  52. {ommlds-0.0.0.dev494.dist-info → ommlds-0.0.0.dev495.dist-info}/top_level.txt +0 -0
@@ -2062,7 +2062,7 @@ def _process_dataclass__55eb2b38eb7d4e32f3a9306577040632e1c376fb():
2062
2062
  '__dataclass__init__fields__0__annotation',
2063
2063
  ),
2064
2064
  cls_names=(
2065
- ('ommlds.cli.sessions.chat.drivers.driver', 'AiDeltaChatEvent'),
2065
+ ('ommlds.cli.sessions.chat.drivers.impl', 'AiDeltaChatEvent'),
2066
2066
  ),
2067
2067
  )
2068
2068
  def _process_dataclass__aff24d9a92d53ba94dacb7fb303b9eb4ebd0763f():
@@ -2196,8 +2196,8 @@ def _process_dataclass__aff24d9a92d53ba94dacb7fb303b9eb4ebd0763f():
2196
2196
  '__dataclass__init__fields__0__annotation',
2197
2197
  ),
2198
2198
  cls_names=(
2199
- ('ommlds.cli.sessions.chat.drivers.driver', 'AiMessagesChatEvent'),
2200
- ('ommlds.cli.sessions.chat.drivers.driver', 'UserMessagesChatEvent'),
2199
+ ('ommlds.cli.sessions.chat.drivers.impl', 'AiMessagesChatEvent'),
2200
+ ('ommlds.cli.sessions.chat.drivers.impl', 'UserMessagesChatEvent'),
2201
2201
  ),
2202
2202
  )
2203
2203
  def _process_dataclass__b211fde543b7c2c533cdcf9f21b47d2f7f76e5c9():
@@ -2330,9 +2330,9 @@ def _process_dataclass__b211fde543b7c2c533cdcf9f21b47d2f7f76e5c9():
2330
2330
  '__dataclass__init__fields__0__annotation',
2331
2331
  ),
2332
2332
  cls_names=(
2333
- ('ommlds.cli.sessions.chat.drivers.driver', 'ChatId'),
2333
+ ('ommlds.cli.sessions.chat.drivers.impl', 'ChatDriverId'),
2334
+ ('ommlds.cli.sessions.chat.drivers.impl', 'ChatId'),
2334
2335
  ('ommlds.cli.sessions.chat.drivers.state.ids', 'ChatStateStorageKey'),
2335
- ('ommlds.cli.sessions.chat.drivers.types', 'ChatDriverId'),
2336
2336
  ),
2337
2337
  )
2338
2338
  def _process_dataclass__3576262424b3ef8ff20966fa3744e5dba9a2ae7d():
@@ -2471,7 +2471,7 @@ def _process_dataclass__3576262424b3ef8ff20966fa3744e5dba9a2ae7d():
2471
2471
  '__dataclass__init__fields__1__annotation',
2472
2472
  ),
2473
2473
  cls_names=(
2474
- ('ommlds.cli.sessions.chat.drivers.driver', 'ChatPhaseCallback'),
2474
+ ('ommlds.cli.sessions.chat.drivers.impl', 'ChatPhaseCallback'),
2475
2475
  ),
2476
2476
  )
2477
2477
  def _process_dataclass__927265170439340895560333250bc087fa726eff():
@@ -2639,7 +2639,7 @@ def _process_dataclass__927265170439340895560333250bc087fa726eff():
2639
2639
  '__dataclass__init__fields__3__default',
2640
2640
  ),
2641
2641
  cls_names=(
2642
- ('ommlds.cli.sessions.chat.drivers.driver', 'ChatState'),
2642
+ ('ommlds.cli.sessions.chat.drivers.impl', 'ChatState'),
2643
2643
  ),
2644
2644
  )
2645
2645
  def _process_dataclass__9f7e26a87dd163b610f38caa1ce9b3c6356e632a():
@@ -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
 
@@ -43,14 +44,56 @@ def bind_backends(cfg: BackendConfig = BackendConfig()) -> inj.Elements:
43
44
  if cfg.backend is not None:
44
45
  lst.append(inj.bind(_types.BackendName, to_const=cfg.backend))
45
46
  else:
46
- lst.append(inj.bind(_types.BackendName, to_fn=inj.KwargsTarget.of(lambda dbn: dbn, dbn=_types.DefaultBackendName))) # noqa
47
-
48
- lst.extend([
49
- inj.bind(_types.ChatChoicesServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesServiceBackendProvider, singleton=True), # noqa
50
- inj.bind(_types.ChatChoicesStreamServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesStreamServiceBackendProvider, singleton=True), # noqa
51
- inj.bind(_types.CompletionServiceBackendProvider, to_ctor=_catalog.CatalogCompletionServiceBackendProvider, singleton=True), # noqa
52
- inj.bind(_types.EmbeddingServiceBackendProvider, to_ctor=_catalog.CatalogEmbeddingServiceBackendProvider, singleton=True), # noqa
53
- ])
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),
74
+ inj.bind(bp_iface, to_key=ben_bp_key),
75
+ inj.expose(ben_bp_key),
76
+ ),
77
+ bp_stack.push_bind(to_key=ben_bp_key),
78
+ ])
79
+
80
+ fiw_key: inj.Key = inj.Key(_meta.FirstInWinsBackendProvider, tag=bp_iface)
81
+ lst.extend([
82
+ inj.private(
83
+ inj.set_binder[_types.BackendProvider]().bind(bp_stack.top),
84
+ inj.bind(fiw_key, to_ctor=_meta.FirstInWinsBackendProvider, singleton=True),
85
+ inj.expose(fiw_key),
86
+ ),
87
+ bp_stack.push_bind(to_key=fiw_key),
88
+ ])
89
+
90
+ else:
91
+ lst.append(bp_stack.push_bind(to_ctor=bp_impl, singleton=True))
92
+
93
+ else:
94
+ lst.append(bp_stack.push_bind(to_ctor=bp_impl, singleton=True))
95
+
96
+ lst.append(inj.bind(bp_iface, to_key=bp_stack.top))
54
97
 
55
98
  #
56
99
 
@@ -0,0 +1,35 @@
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
+
10
+
11
+ ##
12
+
13
+
14
+ class FirstInWinsBackendProvider(BackendProvider[ServiceT]):
15
+ def __init__(
16
+ self,
17
+ backend_providers: ta.AbstractSet[BackendProvider],
18
+ ) -> None:
19
+ super().__init__()
20
+
21
+ self._backend_providers = [ta.cast(BackendProvider[ServiceT], bp) for bp in backend_providers]
22
+
23
+ def provide_backend(self) -> ta.AsyncContextManager[ServiceT]:
24
+ @contextlib.asynccontextmanager
25
+ async def inner():
26
+ from ...minichain.meta.firstinwins import AsyncioFirstInWinsService
27
+
28
+ async with contextlib.AsyncExitStack() as aes:
29
+ svcs = [
30
+ await aes.enter_async_context(bp.provide_backend())
31
+ for bp in self._backend_providers
32
+ ]
33
+ yield AsyncioFirstInWinsService(*svcs)
34
+
35
+ return inner()
ommlds/cli/inject.py CHANGED
@@ -24,7 +24,7 @@ def bind_main(
24
24
 
25
25
  els.extend([
26
26
  lc.bind_async_lifecycle_registrar(),
27
- lc.bind_async_managed_lifecycle_manager(),
27
+ lc.bind_async_managed_lifecycle_manager(eager=True),
28
28
  ])
29
29
 
30
30
  #
@@ -3,10 +3,6 @@ TODO:
3
3
  - lifecycles
4
4
  - StreamService
5
5
  """
6
- import typing as ta
7
-
8
- from omlish import lang
9
-
10
6
  from ..... import minichain as mc
11
7
  from .ai.types import AiChatGenerator
12
8
  from .events.manager import ChatEventsManager
@@ -14,16 +10,13 @@ from .events.types import UserMessagesChatEvent
14
10
  from .phases.manager import ChatPhaseManager
15
11
  from .phases.types import ChatPhase
16
12
  from .state.types import ChatStateManager
13
+ from .types import ChatDriver
17
14
 
18
15
 
19
16
  ##
20
17
 
21
18
 
22
- class ChatDriverGetter(lang.Func0[ta.Awaitable['ChatDriver']]):
23
- pass
24
-
25
-
26
- class ChatDriver:
19
+ class ChatDriverImpl(ChatDriver):
27
20
  def __init__(
28
21
  self,
29
22
  *,
@@ -14,7 +14,7 @@ from .configs import DriverConfig
14
14
 
15
15
  with lang.auto_proxy_import(globals()):
16
16
  from ....backends import inject as _backends
17
- from . import driver as _driver
17
+ from . import impl as _impl
18
18
  from . import types as _types
19
19
  from .ai import inject as _ai
20
20
  from .events import inject as _events
@@ -51,8 +51,10 @@ def bind_driver(cfg: DriverConfig = DriverConfig()) -> inj.Elements:
51
51
  #
52
52
 
53
53
  els.extend([
54
- inj.bind(_driver.ChatDriver, singleton=True),
55
- inj.bind_async_late(_driver.ChatDriver, _driver.ChatDriverGetter),
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),
56
58
  ])
57
59
 
58
60
  #
@@ -59,11 +59,10 @@ def bind_state(cfg: StateConfig = StateConfig()) -> inj.Elements:
59
59
  els.extend([
60
60
  inj.bind(_ids.LastChatIdManager, singleton=True),
61
61
 
62
- phase_callbacks().bind_item(to_fn=inj.KwargsTarget.of(
63
- lambda lcim, cid: ChatPhaseCallback(ChatPhase.STARTED, lambda: lcim.set_last_chat_id(cid)),
62
+ phase_callbacks().bind_item(to_fn=inj.target(
64
63
  lcim=_ids.LastChatIdManager,
65
64
  cid=_types.ChatId,
66
- )),
65
+ )(lambda lcim, cid: ChatPhaseCallback(ChatPhase.STARTED, lambda: lcim.set_last_chat_id(cid)))),
67
66
  ])
68
67
 
69
68
  elif cfg.state == 'ephemeral':
@@ -29,10 +29,10 @@ def tool_context_providers() -> 'inj.ItemsBinderHelper[_execution.ToolContextPro
29
29
 
30
30
 
31
31
  def bind_tool_context_provider_to_key(key: ta.Any) -> inj.Elements:
32
- return tool_context_providers().bind_item(to_fn=inj.KwargsTarget.of(
33
- lambda v: _execution.ToolContextProvider(lambda: [v]),
34
- v=key,
35
- ), singleton=True)
32
+ return tool_context_providers().bind_item(
33
+ to_fn=inj.target(v=key)(lambda v: _execution.ToolContextProvider(lambda: [v])),
34
+ singleton=True,
35
+ )
36
36
 
37
37
 
38
38
  ##
@@ -1,10 +1,31 @@
1
+ import abc
2
+ import typing as ta
1
3
  import uuid
2
4
 
5
+ from omlish import lang
3
6
  from omlish import typedvalues as tv
4
7
 
8
+ from ..... import minichain as mc
9
+
5
10
 
6
11
  ##
7
12
 
8
13
 
9
14
  class ChatDriverId(tv.UniqueScalarTypedValue[uuid.UUID]):
10
15
  pass
16
+
17
+
18
+ class ChatDriverGetter(lang.Func0[ta.Awaitable['ChatDriver']]):
19
+ pass
20
+
21
+
22
+ class ChatDriver(lang.Abstract):
23
+ async def start(self) -> None:
24
+ pass
25
+
26
+ async def stop(self) -> None:
27
+ pass
28
+
29
+ @abc.abstractmethod
30
+ def send_user_messages(self, next_user_chat: 'mc.UserChat') -> ta.Awaitable[None]:
31
+ raise NotImplementedError
@@ -9,7 +9,7 @@ from .configs import UserConfig
9
9
 
10
10
 
11
11
  with lang.auto_proxy_import(globals()):
12
- from .. import driver as _driver
12
+ from .. import types as _driver
13
13
  from ..state import types as _state
14
14
 
15
15
 
@@ -32,8 +32,6 @@ def bind_commands(cfg: CommandsConfig = CommandsConfig()) -> inj.Elements:
32
32
  inj.bind(_simple.EchoCommand, singleton=True),
33
33
  commands().bind_item(to_key=_simple.EchoCommand),
34
34
 
35
- inj.bind(_simple.QuitSignal, to_const=lang.as_async(lambda: None)), # type: ignore # FIXME: lol
36
-
37
35
  inj.bind(_simple.QuitCommand, singleton=True),
38
36
  commands().bind_item(to_key=_simple.QuitCommand),
39
37
  ])
@@ -1,8 +1,6 @@
1
- import typing as ta
2
-
3
- from omlish import lang
4
1
  from omlish.argparse import all as argparse
5
2
 
3
+ from ..ui import UiQuitSignal
6
4
  from .base import Command
7
5
 
8
6
 
@@ -22,15 +20,11 @@ class EchoCommand(Command):
22
20
  ##
23
21
 
24
22
 
25
- class QuitSignal(lang.Func0[ta.Awaitable[None]]):
26
- pass
27
-
28
-
29
23
  class QuitCommand(Command):
30
24
  def __init__(
31
25
  self,
32
26
  *,
33
- quit_signal: QuitSignal,
27
+ quit_signal: UiQuitSignal,
34
28
  ) -> None:
35
29
  super().__init__()
36
30
 
@@ -1,5 +1,5 @@
1
1
  from ..... import minichain as mc
2
- from ..drivers.driver import ChatDriver
2
+ from ..drivers.types import ChatDriver
3
3
  from .commands.manager import CommandsManager
4
4
 
5
5
 
@@ -6,6 +6,7 @@ from .configs import FacadeConfig
6
6
 
7
7
  with lang.auto_proxy_import(globals()):
8
8
  from . import facade as _facade
9
+ from . import ui as _ui
9
10
  from .commands import inject as _commands
10
11
 
11
12
 
@@ -27,4 +28,8 @@ def bind_facade(cfg: FacadeConfig = FacadeConfig()) -> inj.Elements:
27
28
 
28
29
  #
29
30
 
31
+ els.append(inj.bind(_ui.UiQuitSignal(_ui.raise_system_exit_ui_quit_signal)))
32
+
33
+ #
34
+
30
35
  return inj.as_elements(*els)
@@ -21,3 +21,14 @@ class NopUiMessageDisplayer(UiMessageDisplayer):
21
21
  class PrintMessageDisplayer(UiMessageDisplayer):
22
22
  async def display_ui_message(self, content: str) -> None:
23
23
  print(content)
24
+
25
+
26
+ ##
27
+
28
+
29
+ class UiQuitSignal(lang.Func0[ta.Awaitable[None]]):
30
+ pass
31
+
32
+
33
+ async def raise_system_exit_ui_quit_signal() -> None:
34
+ raise SystemExit
@@ -3,7 +3,7 @@ import typing as ta
3
3
  from .....inputs.asyncs import AsyncStringInput
4
4
  from .....inputs.asyncs import SyncAsyncStringInput
5
5
  from .....inputs.sync import InputSyncStringInput
6
- from ...drivers.driver import ChatDriver
6
+ from ...drivers.types import ChatDriver
7
7
  from ...facades.facade import ChatFacade
8
8
  from ..base import ChatInterface
9
9
 
@@ -1,4 +1,4 @@
1
- from ...drivers.driver import ChatDriver
1
+ from ...drivers.types import ChatDriver
2
2
  from ..base import ChatInterface
3
3
 
4
4
 
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import os
3
3
  import typing as ta
4
+ import weakref
4
5
 
5
6
  from omdev.tui import textual as tx
6
7
  from omlish import check
@@ -10,14 +11,15 @@ from omlish.logs import all as logs
10
11
 
11
12
  from ...... import minichain as mc
12
13
  from .....backends.types import BackendName
13
- from ...drivers.driver import ChatDriver
14
14
  from ...drivers.events.types import AiDeltaChatEvent
15
15
  from ...drivers.events.types import AiMessagesChatEvent
16
+ from ...drivers.types import ChatDriver
16
17
  from ...facades.facade import ChatFacade
17
18
  from .styles import read_app_css
18
19
  from .widgets.input import InputOuter
19
20
  from .widgets.input import InputTextArea
20
21
  from .widgets.messages import AiMessage
22
+ from .widgets.messages import MessagesContainer
21
23
  from .widgets.messages import StaticAiMessage
22
24
  from .widgets.messages import StreamAiMessage
23
25
  from .widgets.messages import ToolConfirmationMessage
@@ -66,6 +68,8 @@ class ChatApp(tx.App):
66
68
 
67
69
  self._chat_action_queue: asyncio.Queue[ta.Any] = asyncio.Queue()
68
70
 
71
+ self._input_focused_key_events: weakref.WeakSet[tx.Key] = weakref.WeakSet()
72
+
69
73
  def get_driver_class(self) -> type[tx.Driver]:
70
74
  return tx.get_pending_writes_driver_class(super().get_driver_class())
71
75
 
@@ -74,7 +78,7 @@ class ChatApp(tx.App):
74
78
  #
75
79
 
76
80
  def compose(self) -> tx.ComposeResult:
77
- yield tx.VerticalScroll(id='messages-container')
81
+ yield MessagesContainer(id='messages-container')
78
82
 
79
83
  yield InputOuter(id='input-outer')
80
84
 
@@ -84,7 +88,7 @@ class ChatApp(tx.App):
84
88
  return self.query_one('#input', InputTextArea)
85
89
 
86
90
  def _get_messages_container(self) -> tx.VerticalScroll:
87
- return self.query_one('#messages-container', tx.VerticalScroll)
91
+ return self.query_one('#messages-container', MessagesContainer)
88
92
 
89
93
  #
90
94
 
@@ -261,6 +265,20 @@ class ChatApp(tx.App):
261
265
 
262
266
  await self._chat_action_queue.put(ChatApp.UserInput(event.text))
263
267
 
268
+ @tx.on(tx.Key)
269
+ async def on_key(self, event: tx.Key) -> None:
270
+ if event in self._input_focused_key_events:
271
+ return
272
+
273
+ chat_input = self._get_input_text_area()
274
+
275
+ if not chat_input.has_focus:
276
+ self._input_focused_key_events.add(event)
277
+
278
+ chat_input.focus()
279
+
280
+ self.screen.post_message(tx.Key(event.key, event.character))
281
+
264
282
  #
265
283
 
266
284
  async def confirm_tool_use(
@@ -44,10 +44,7 @@ def bind_textual(cfg: TextualInterfaceConfig = TextualInterfaceConfig()) -> inj.
44
44
  els.extend([
45
45
  inj.bind(_app.ChatEventQueue, to_const=asyncio.Queue()),
46
46
 
47
- event_callbacks().bind_item(to_fn=inj.KwargsTarget.of(
48
- lambda eq: lambda ev: eq.put(ev),
49
- eq=_app.ChatEventQueue,
50
- )),
47
+ event_callbacks().bind_item(to_fn=inj.target(eq=_app.ChatEventQueue)(lambda eq: lambda ev: eq.put(ev))),
51
48
  ])
52
49
 
53
50
  #
@@ -83,7 +80,7 @@ def bind_textual(cfg: TextualInterfaceConfig = TextualInterfaceConfig()) -> inj.
83
80
 
84
81
  inj.bind(
85
82
  tx.DevtoolsSetup,
86
- to_async_fn=inj.KwargsTarget.of(lambda mgr: mgr.get_setup(), mgr=tx.DevtoolsManager),
83
+ to_async_fn=inj.target(mgr=tx.DevtoolsManager)(lambda mgr: mgr.get_setup()),
87
84
  singleton=True,
88
85
  ),
89
86
  ])
@@ -53,6 +53,8 @@
53
53
  /* Welcome */
54
54
 
55
55
  .welcome-message {
56
+ margin: 0;
57
+
56
58
  padding: 1 2 1 1;
57
59
 
58
60
  border: round;
@@ -9,6 +9,13 @@ from omlish import lang
9
9
  ##
10
10
 
11
11
 
12
+ class MessagesContainer(tx.VerticalScroll):
13
+ pass
14
+
15
+
16
+ ##
17
+
18
+
12
19
  class Message(tx.Static, lang.Abstract):
13
20
  def __init__(self, *args: ta.Any, **kwargs: ta.Any) -> None:
14
21
  super().__init__(*args, **kwargs)
@@ -16,7 +23,7 @@ class Message(tx.Static, lang.Abstract):
16
23
  self.add_class('message')
17
24
 
18
25
 
19
- ##
26
+ #
20
27
 
21
28
 
22
29
  class WelcomeMessage(Message):
@@ -32,7 +39,7 @@ class WelcomeMessage(Message):
32
39
  yield tx.Static(self._content, classes='welcome-message-content')
33
40
 
34
41
 
35
- ##
42
+ #
36
43
 
37
44
 
38
45
  class UserMessage(Message):
@@ -50,7 +57,7 @@ class UserMessage(Message):
50
57
  yield tx.Static(self._content)
51
58
 
52
59
 
53
- ##
60
+ #
54
61
 
55
62
 
56
63
  class AiMessage(Message, lang.Abstract):
@@ -124,7 +131,7 @@ class StreamAiMessage(AiMessage):
124
131
  self._stream_ = None
125
132
 
126
133
 
127
- ##
134
+ #
128
135
 
129
136
 
130
137
  class ToolConfirmationControls(tx.Static):
@@ -172,7 +179,7 @@ class ToolConfirmationMessage(Message):
172
179
  self._fut.set_result(True)
173
180
 
174
181
 
175
- ##
182
+ #
176
183
 
177
184
 
178
185
  class UiMessage(Message):
@@ -227,6 +227,10 @@ with _lang.auto_proxy_init(
227
227
 
228
228
  ##
229
229
 
230
+ from .content.cancontent import ( # noqa
231
+ CanContent,
232
+ )
233
+
230
234
  from .content.images import ( # noqa
231
235
  ImageContent,
232
236
  )
@@ -236,8 +240,6 @@ with _lang.auto_proxy_init(
236
240
  )
237
241
 
238
242
  from .content.materialize import ( # noqa
239
- CanContent,
240
-
241
243
  materialize_content,
242
244
  )
243
245
 
@@ -275,6 +277,10 @@ with _lang.auto_proxy_init(
275
277
  SequenceContent,
276
278
  )
277
279
 
280
+ from .content.tag import ( # noqa
281
+ TagContent,
282
+ )
283
+
278
284
  from .content.text import ( # noqa
279
285
  TextContent,
280
286
  )
@@ -391,6 +397,14 @@ with _lang.auto_proxy_init(
391
397
  tool_context,
392
398
  )
393
399
 
400
+ from .tools.execution.errorhandling import ( # noqa
401
+ ErrorHandlingToolExecutor,
402
+ )
403
+
404
+ from .tools.execution.errors import ( # noqa
405
+ ToolExecutionError,
406
+ )
407
+
394
408
  from .tools.execution.executors import ( # noqa
395
409
  ToolExecutor,
396
410
 
@@ -403,10 +417,6 @@ with _lang.auto_proxy_init(
403
417
  reflect_tool_catalog_entry,
404
418
  )
405
419
 
406
- from .tools.execution.errors import ( # noqa
407
- ToolExecutionError,
408
- )
409
-
410
420
  from .tools.fns import ( # noqa
411
421
  ToolFn,
412
422