ommlds 0.0.0.dev488__py3-none-any.whl → 0.0.0.dev489__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.
@@ -10,7 +10,6 @@ from .configs import UserConfig
10
10
 
11
11
  with lang.auto_proxy_import(globals()):
12
12
  from ..state import types as _state
13
- from . import interactive as _interactive
14
13
  from . import oneshot as _oneshot
15
14
  from . import types as _types
16
15
 
@@ -41,8 +40,6 @@ def bind_user(cfg: UserConfig = UserConfig()) -> inj.Elements:
41
40
 
42
41
  raise NotImplementedError
43
42
 
44
- els.append(inj.bind(_types.UserChatInput, to_ctor=_interactive.InteractiveUserChatInput, singleton=True))
45
-
46
43
  else:
47
44
  if cfg.initial_user_content is None:
48
45
  raise ValueError('Initial user content is required for non-interactive chat')
@@ -9,6 +9,8 @@ from .interface import BareChatInterface
9
9
  with lang.auto_proxy_import(globals()):
10
10
  from .....inputs import asyncs as _inputs_asyncs
11
11
  from .....inputs import sync as _inputs_sync
12
+ from ...chat.user import types as _user_types
13
+ from . import user as _user
12
14
 
13
15
 
14
16
  ##
@@ -20,6 +22,8 @@ def bind_bare(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
20
22
  ]
21
23
 
22
24
  if cfg.interactive:
25
+ els.append(inj.bind(_user_types.UserChatInput, to_ctor=_user.InteractiveUserChatInput, singleton=True))
26
+
23
27
  els.extend([
24
28
  inj.bind(_inputs_sync.SyncStringInput, to_const=_inputs_sync.InputSyncStringInput(use_readline=cfg.use_readline)), # noqa
25
29
  inj.bind(_inputs_asyncs.AsyncStringInput, to_ctor=_inputs_asyncs.ThreadAsyncStringInput, singleton=True),
@@ -4,7 +4,7 @@ from ...... import minichain as mc
4
4
  from .....inputs.asyncs import AsyncStringInput
5
5
  from .....inputs.asyncs import SyncAsyncStringInput
6
6
  from .....inputs.sync import InputSyncStringInput
7
- from .types import UserChatInput
7
+ from ...chat.user.types import UserChatInput
8
8
 
9
9
 
10
10
  ##
@@ -1,4 +1,47 @@
1
+ import asyncio
2
+ import dataclasses as dc
3
+ import typing as ta
4
+
1
5
  from omdev.tui import textual as tx
6
+ from omlish import check
7
+
8
+ from ...... import minichain as mc
9
+ from ...driver import ChatDriver
10
+ from .user import QueueUserChatInput
11
+
12
+
13
+ ##
14
+
15
+
16
+ class UserMessage(tx.Static):
17
+ pass
18
+
19
+
20
+ class AiMessage(tx.Static):
21
+ pass
22
+
23
+
24
+ ##
25
+
26
+
27
+ class InputTextArea(tx.TextArea):
28
+ @dc.dataclass()
29
+ class Submitted(tx.Message):
30
+ text: str
31
+
32
+ def __init__(self, **kwargs: ta.Any) -> None:
33
+ super().__init__(**kwargs)
34
+
35
+ async def _on_key(self, event: tx.Key) -> None:
36
+ if event.key == 'enter':
37
+ event.prevent_default()
38
+ event.stop()
39
+
40
+ if text := self.text.strip():
41
+ self.post_message(self.Submitted(text))
42
+
43
+ else:
44
+ await super()._on_key(event)
2
45
 
3
46
 
4
47
  ##
@@ -7,8 +50,142 @@ from omdev.tui import textual as tx
7
50
  class ChatApp(tx.App):
8
51
  def __init__(
9
52
  self,
53
+ *,
54
+ chat_driver: ChatDriver,
55
+ queue_user_chat_input: QueueUserChatInput,
10
56
  ) -> None:
11
57
  super().__init__()
12
58
 
59
+ self._chat_driver = chat_driver
60
+ self._queue_user_chat_input = queue_user_chat_input
61
+
62
+ CSS: ta.ClassVar[str] = """
63
+ #messages-scroll {
64
+ width: 100%;
65
+ height: 1fr;
66
+
67
+ padding: 0 2 0 2;
68
+ }
69
+
70
+ #messages-container {
71
+ height: auto;
72
+ width: 100%;
73
+
74
+ margin-top: 1;
75
+ margin-bottom: 0;
76
+
77
+ layout: stream;
78
+ text-align: left;
79
+ }
80
+
81
+ #input-outer {
82
+ width: 100%;
83
+ height: auto;
84
+ }
85
+
86
+ #input-vertical {
87
+ width: 100%;
88
+ height: auto;
89
+
90
+ margin: 0 2 1 2;
91
+
92
+ padding: 0;
93
+ }
94
+
95
+ #input-vertical2 {
96
+ width: 100%;
97
+ height: auto;
98
+
99
+ border: round $foreground-muted;
100
+
101
+ padding: 0 1;
102
+ }
103
+
104
+ #input-horizontal {
105
+ width: 100%;
106
+ height: auto;
107
+ }
108
+
109
+ #input-glyph {
110
+ width: auto;
111
+
112
+ padding: 0 1 0 0;
113
+
114
+ background: transparent;
115
+ color: $primary;
116
+
117
+ text-style: bold;
118
+ }
119
+
120
+ #input {
121
+ width: 1fr;
122
+ height: auto;
123
+ max-height: 16;
124
+
125
+ border: none;
126
+
127
+ padding: 0;
128
+
129
+ background: transparent;
130
+ color: $text;
131
+ }
132
+ """
133
+
134
+ ENABLE_COMMAND_PALETTE: ta.ClassVar[bool] = False
135
+
136
+ #
137
+
13
138
  def compose(self) -> tx.ComposeResult:
14
- yield tx.Static('app goes here lol')
139
+ with tx.VerticalScroll(id='messages-scroll'):
140
+ yield tx.Static(id='messages-container')
141
+
142
+ with tx.Static(id='input-outer'):
143
+ with tx.Vertical(id='input-vertical'):
144
+ with tx.Vertical(id='input-vertical2'):
145
+ with tx.Horizontal(id='input-horizontal'):
146
+ yield tx.Static('>', id='input-glyph')
147
+ yield InputTextArea(placeholder='...', id='input')
148
+
149
+ #
150
+
151
+ def _get_input_text_area(self) -> InputTextArea:
152
+ return self.query_one('#input', InputTextArea)
153
+
154
+ def _get_messages_container(self) -> tx.Static:
155
+ return self.query_one('#messages-container', tx.Static)
156
+
157
+ #
158
+
159
+ async def _mount_message(self, *messages: tx.Widget) -> None:
160
+ msg_ctr = self._get_messages_container()
161
+
162
+ for msg in messages:
163
+ await msg_ctr.mount(msg)
164
+
165
+ self.call_after_refresh(lambda: msg_ctr.scroll_end(animate=False))
166
+
167
+ #
168
+
169
+ _chat_driver_task: asyncio.Task | None = None
170
+
171
+ async def on_mount(self) -> None:
172
+ check.none(self._chat_driver_task)
173
+ self._chat_driver_task = asyncio.create_task(self._chat_driver.run())
174
+
175
+ self._get_input_text_area().focus()
176
+
177
+ await self._mount_message(UserMessage('Hello!'))
178
+
179
+ async def on_unmount(self) -> None:
180
+ await self._queue_user_chat_input.push_next_user_messages([])
181
+ await check.not_none(self._chat_driver_task)
182
+
183
+ async def on_input_text_area_submitted(self, event: InputTextArea.Submitted) -> None:
184
+ self._get_input_text_area().clear()
185
+
186
+ await self._mount_message(
187
+ UserMessage(event.text),
188
+ # AiMessage(f'You said: {event.text}!'),
189
+ )
190
+
191
+ await self._queue_user_chat_input.push_next_user_messages([mc.UserMessage(event.text)])
@@ -1,8 +1,10 @@
1
1
  from omlish import inject as inj
2
2
 
3
+ from ...chat.user.types import UserChatInput
3
4
  from ..base import ChatInterface
4
5
  from .app import ChatApp
5
6
  from .interface import TextualChatInterface
7
+ from .user import QueueUserChatInput
6
8
 
7
9
 
8
10
  ##
@@ -17,4 +19,9 @@ def bind_textual() -> inj.Elements:
17
19
  inj.bind(ChatApp, singleton=True),
18
20
  ])
19
21
 
22
+ els.extend([
23
+ inj.bind(QueueUserChatInput, singleton=True),
24
+ inj.bind(UserChatInput, to_key=QueueUserChatInput),
25
+ ])
26
+
20
27
  return inj.as_elements(*els)
@@ -0,0 +1,20 @@
1
+ import asyncio
2
+
3
+ from ...... import minichain as mc
4
+ from ...chat.user.types import UserChatInput
5
+
6
+
7
+ ##
8
+
9
+
10
+ class QueueUserChatInput(UserChatInput):
11
+ def __init__(self) -> None:
12
+ super().__init__()
13
+
14
+ self._queue: asyncio.Queue[mc.UserChat] = asyncio.Queue()
15
+
16
+ async def push_next_user_messages(self, chat: 'mc.UserChat') -> None:
17
+ await self._queue.put(chat)
18
+
19
+ async def get_next_user_messages(self) -> 'mc.UserChat':
20
+ return await self._queue.get()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev488
3
+ Version: 0.0.0.dev489
4
4
  Summary: ommlds
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,9 +14,9 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev488
17
+ Requires-Dist: omlish==0.0.0.dev489
18
18
  Provides-Extra: all
19
- Requires-Dist: omdev==0.0.0.dev488; extra == "all"
19
+ Requires-Dist: omdev==0.0.0.dev489; extra == "all"
20
20
  Requires-Dist: llama-cpp-python~=0.3; extra == "all"
21
21
  Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "all"
22
22
  Requires-Dist: mlx-lm~=0.28; sys_platform == "darwin" and extra == "all"
@@ -38,7 +38,7 @@ Requires-Dist: mwparserfromhell~=0.7; extra == "all"
38
38
  Requires-Dist: wikitextparser~=0.56; extra == "all"
39
39
  Requires-Dist: lxml>=5.3; python_version < "3.13" and extra == "all"
40
40
  Provides-Extra: omdev
41
- Requires-Dist: omdev==0.0.0.dev488; extra == "omdev"
41
+ Requires-Dist: omdev==0.0.0.dev489; extra == "omdev"
42
42
  Provides-Extra: backends
43
43
  Requires-Dist: llama-cpp-python~=0.3; extra == "backends"
44
44
  Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "backends"
@@ -148,8 +148,7 @@ ommlds/cli/sessions/chat/chat/state/storage.py,sha256=gKUNsNj3TtH67-FOuHXrQlDsES
148
148
  ommlds/cli/sessions/chat/chat/state/types.py,sha256=NsHCZNeBfLW1BMVB2rn-DUqA9C5w2vp-_3UjjKl4QRo,806
149
149
  ommlds/cli/sessions/chat/chat/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
150
150
  ommlds/cli/sessions/chat/chat/user/configs.py,sha256=qsiua5CkTRPhO4prORK-GXauUhWRZ3psfpoHvuj8EyA,311
151
- ommlds/cli/sessions/chat/chat/user/inject.py,sha256=cnG04z35Svy-6sle2_Z_2a9rlbBefi6syi-MZyiXJa8,2056
152
- ommlds/cli/sessions/chat/chat/user/interactive.py,sha256=SHeiuhkCkM0SM0dTph-W-qk9VLBMuwL1nTE-Moex2qA,862
151
+ ommlds/cli/sessions/chat/chat/user/inject.py,sha256=guy8eQLXlUtXKG9jNnJSCwwT6_jPJ0oxPIGNI2KaT6A,1895
153
152
  ommlds/cli/sessions/chat/chat/user/oneshot.py,sha256=jPrZBBuf-olBfPF7CPTYK7-Dr7EvSriU7L0nORHfbv4,588
154
153
  ommlds/cli/sessions/chat/chat/user/types.py,sha256=MNlhMxlLtxVod9rUZlSPvRaippPAXEdX_GHh73QLeSg,262
155
154
  ommlds/cli/sessions/chat/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -157,12 +156,14 @@ ommlds/cli/sessions/chat/interface/base.py,sha256=3z7yI-ow7037_n6VanQYZjOgKL_iRZ
157
156
  ommlds/cli/sessions/chat/interface/configs.py,sha256=xu9nUAI9opNR7X4m92JKr5HhXPNNflHUHQ4UbOPmVMg,244
158
157
  ommlds/cli/sessions/chat/interface/inject.py,sha256=Ip-srA7ONGF-YgbLikwfTd3O1UoxeDTMI6CnPpc6A80,493
159
158
  ommlds/cli/sessions/chat/interface/bare/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
- ommlds/cli/sessions/chat/interface/bare/inject.py,sha256=edyEuvNSdxG3EAb0aDku_QEVPftc_I7e_EJMLNa7Stw,853
159
+ ommlds/cli/sessions/chat/interface/bare/inject.py,sha256=rJPZnntuELdjxovU2yDo_RZl5qBhUfadMb2Dg1e50Pw,1048
161
160
  ommlds/cli/sessions/chat/interface/bare/interface.py,sha256=X-M0fyyY6FlJbY_ToJOMGJrPwbx68Hi1y9GfkhdPLKA,336
161
+ ommlds/cli/sessions/chat/interface/bare/user.py,sha256=DIRX8-yWl0U9AD1qr5og0XbFhSZxpCUgnwOxIJloK1Y,874
162
162
  ommlds/cli/sessions/chat/interface/textual/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
- ommlds/cli/sessions/chat/interface/textual/app.py,sha256=MZGjY8d8KfV58zhT_0bCRn3-2-ole3FCa-rTVY9EE_A,233
164
- ommlds/cli/sessions/chat/interface/textual/inject.py,sha256=Gqlm7yem3exApLnt_ylSke7Uw-POanno-otdQFxdiSw,399
163
+ ommlds/cli/sessions/chat/interface/textual/app.py,sha256=r25t-0Gvh5O_oS-mKKR5xHSI6d-ibHkpH6ZYKxAFDIk,4303
164
+ ommlds/cli/sessions/chat/interface/textual/inject.py,sha256=MwtFeF3SXfidsxCihcgk3Bp888Ba5CwEj2ZYugkJRtM,620
165
165
  ommlds/cli/sessions/chat/interface/textual/interface.py,sha256=SISY0RardWhYdXfpVMTTjNlaeUIu9lgFIxKkDn0_SwA,417
166
+ ommlds/cli/sessions/chat/interface/textual/user.py,sha256=ywXycpaHX7beD1EAv6PtCYNC5vQL6CLIfWM9iYzH3ts,482
166
167
  ommlds/cli/sessions/chat/phases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
168
  ommlds/cli/sessions/chat/phases/inject.py,sha256=m9GcCVcp1GnNKN1qwDSgTkfp3e107wXgPeEHDLbfG8s,448
168
169
  ommlds/cli/sessions/chat/phases/injection.py,sha256=ZUFRRLp3HNhKaPBW6aiw5XFEGNkqTj8WVSlXM0kg0y0,326
@@ -448,9 +449,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
448
449
  ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
449
450
  ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
450
451
  ommlds/wiki/utils/xml.py,sha256=sNJNkZ9rT8B-kJMO6bRz8J1USy4fyPx0m2PwTX7vxYY,3846
451
- ommlds-0.0.0.dev488.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
452
- ommlds-0.0.0.dev488.dist-info/METADATA,sha256=q5zXFLRuFUGT23QClBchanPGY_rKEPUHSRZxpsRAWLA,3493
453
- ommlds-0.0.0.dev488.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
454
- ommlds-0.0.0.dev488.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
455
- ommlds-0.0.0.dev488.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
456
- ommlds-0.0.0.dev488.dist-info/RECORD,,
452
+ ommlds-0.0.0.dev489.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
453
+ ommlds-0.0.0.dev489.dist-info/METADATA,sha256=3ZTS2XgARBEwxODOmnG5PcDx16y_4TNAy5_ibrVeOO0,3493
454
+ ommlds-0.0.0.dev489.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
455
+ ommlds-0.0.0.dev489.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
456
+ ommlds-0.0.0.dev489.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
457
+ ommlds-0.0.0.dev489.dist-info/RECORD,,