ommlds 0.0.0.dev491__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.
- ommlds/.omlish-manifests.json +9 -7
- ommlds/__about__.py +1 -1
- ommlds/cli/_dataclasses.py +617 -724
- ommlds/cli/backends/catalog.py +0 -5
- ommlds/cli/backends/inject.py +2 -0
- ommlds/cli/inject.py +11 -3
- ommlds/cli/main.py +35 -25
- ommlds/cli/sessions/base.py +1 -10
- ommlds/cli/sessions/chat/configs.py +6 -8
- ommlds/cli/sessions/chat/drivers/driver.py +8 -0
- ommlds/cli/sessions/chat/drivers/inject.py +2 -3
- ommlds/cli/sessions/chat/drivers/state/configs.py +2 -0
- ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
- ommlds/cli/sessions/chat/drivers/state/inject.py +54 -6
- ommlds/cli/sessions/chat/drivers/state/inmemory.py +0 -4
- ommlds/cli/sessions/chat/drivers/state/storage.py +17 -10
- ommlds/cli/sessions/chat/drivers/state/types.py +9 -4
- ommlds/cli/sessions/chat/drivers/user/inject.py +4 -4
- ommlds/cli/sessions/chat/facades/__init__.py +0 -0
- ommlds/cli/sessions/chat/facades/configs.py +9 -0
- ommlds/cli/sessions/chat/facades/facade.py +19 -0
- ommlds/cli/sessions/chat/facades/inject.py +23 -0
- ommlds/cli/sessions/chat/inject.py +5 -3
- ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
- ommlds/cli/sessions/chat/interfaces/bare/inject.py +2 -2
- ommlds/cli/sessions/chat/interfaces/bare/interactive.py +10 -2
- ommlds/cli/sessions/chat/interfaces/configs.py +2 -14
- ommlds/cli/sessions/chat/interfaces/inject.py +9 -4
- ommlds/cli/sessions/chat/interfaces/textual/app.py +113 -45
- ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
- ommlds/cli/sessions/chat/interfaces/textual/inject.py +39 -15
- ommlds/cli/sessions/chat/interfaces/textual/interface.py +5 -0
- ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +3 -1
- ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +76 -22
- ommlds/cli/sessions/chat/interfaces/textual/tools.py +38 -0
- ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +60 -2
- ommlds/cli/sessions/chat/session.py +2 -15
- ommlds/cli/sessions/completion/configs.py +3 -4
- ommlds/cli/sessions/completion/inject.py +1 -2
- ommlds/cli/sessions/completion/session.py +4 -8
- ommlds/cli/sessions/configs.py +10 -0
- ommlds/cli/sessions/embedding/configs.py +3 -4
- ommlds/cli/sessions/embedding/inject.py +1 -2
- ommlds/cli/sessions/embedding/session.py +4 -8
- ommlds/cli/sessions/inject.py +15 -15
- ommlds/cli/state/storage.py +7 -1
- ommlds/minichain/backends/impls/cerebras/protocol.py +4 -4
- ommlds/minichain/backends/impls/ollama/chat.py +26 -0
- ommlds/minichain/backends/impls/openai/names.py +3 -1
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/METADATA +6 -6
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/RECORD +55 -46
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import dataclasses as dc
|
|
4
|
+
|
|
5
|
+
from ..configs import InterfaceConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
12
|
+
class BareInterfaceConfig(InterfaceConfig):
|
|
13
|
+
interactive: bool = False
|
|
14
|
+
|
|
15
|
+
use_readline: bool | ta.Literal['auto'] = 'auto'
|
|
@@ -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
|
|
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:
|
|
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
|
|
@@ -36,6 +38,12 @@ class InteractiveBareChatInterface(ChatInterface):
|
|
|
36
38
|
except EOFError:
|
|
37
39
|
break
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
print()
|
|
42
|
+
print('<')
|
|
43
|
+
print()
|
|
44
|
+
|
|
45
|
+
await self._facade.handle_user_input(s)
|
|
46
|
+
|
|
47
|
+
print()
|
|
40
48
|
|
|
41
49
|
await self._driver.stop()
|
|
@@ -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 =
|
|
17
|
+
def bind_interface(cfg: InterfaceConfig = BareInterfaceConfig()) -> inj.Elements:
|
|
16
18
|
els: list[inj.Elemental] = []
|
|
17
19
|
|
|
18
|
-
if cfg
|
|
19
|
-
els.append(_textual.bind_textual())
|
|
20
|
+
if isinstance(cfg, TextualInterfaceConfig):
|
|
21
|
+
els.append(_textual.bind_textual(cfg))
|
|
20
22
|
|
|
21
|
-
|
|
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,45 +1,69 @@
|
|
|
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
|
|
8
|
+
from omlish import lang
|
|
9
|
+
from omlish.logs import all as logs
|
|
6
10
|
|
|
7
11
|
from ...... import minichain as mc
|
|
12
|
+
from .....backends.types import BackendName
|
|
8
13
|
from ...drivers.driver import ChatDriver
|
|
9
14
|
from ...drivers.events.types import AiDeltaChatEvent
|
|
10
15
|
from ...drivers.events.types import AiMessagesChatEvent
|
|
16
|
+
from ...facades.facade import ChatFacade
|
|
11
17
|
from .styles import read_app_css
|
|
12
18
|
from .widgets.input import InputOuter
|
|
13
19
|
from .widgets.input import InputTextArea
|
|
14
20
|
from .widgets.messages import AiMessage
|
|
15
21
|
from .widgets.messages import StaticAiMessage
|
|
16
22
|
from .widgets.messages import StreamAiMessage
|
|
23
|
+
from .widgets.messages import ToolConfirmationMessage
|
|
17
24
|
from .widgets.messages import UserMessage
|
|
18
25
|
from .widgets.messages import WelcomeMessage
|
|
19
26
|
|
|
20
27
|
|
|
28
|
+
log, alog = logs.get_module_loggers(globals())
|
|
29
|
+
|
|
30
|
+
|
|
21
31
|
##
|
|
22
32
|
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
ChatEventQueue = ta.NewType('ChatEventQueue', asyncio.Queue)
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
##
|
|
28
38
|
|
|
29
39
|
|
|
40
|
+
class ChatAppGetter(lang.CachedFunc0[ta.Awaitable['ChatApp']]):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
30
44
|
class ChatApp(tx.App):
|
|
31
45
|
ENABLE_COMMAND_PALETTE: ta.ClassVar[bool] = False
|
|
32
46
|
|
|
33
47
|
def __init__(
|
|
34
48
|
self,
|
|
35
49
|
*,
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
chat_facade: ChatFacade,
|
|
51
|
+
chat_driver: ChatDriver,
|
|
52
|
+
chat_event_queue: ChatEventQueue,
|
|
53
|
+
backend_name: BackendName | None = None,
|
|
54
|
+
devtools_setup: tx.DevtoolsSetup | None = None,
|
|
38
55
|
) -> None:
|
|
39
56
|
super().__init__()
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
if devtools_setup is not None:
|
|
59
|
+
devtools_setup(self)
|
|
60
|
+
|
|
61
|
+
self._chat_facade = chat_facade
|
|
62
|
+
self._chat_driver = chat_driver
|
|
63
|
+
self._chat_event_queue = chat_event_queue
|
|
64
|
+
self._backend_name = backend_name
|
|
65
|
+
|
|
66
|
+
self._chat_action_queue: asyncio.Queue[ta.Any] = asyncio.Queue()
|
|
43
67
|
|
|
44
68
|
def get_driver_class(self) -> type[tx.Driver]:
|
|
45
69
|
return tx.get_pending_writes_driver_class(super().get_driver_class())
|
|
@@ -49,8 +73,7 @@ class ChatApp(tx.App):
|
|
|
49
73
|
#
|
|
50
74
|
|
|
51
75
|
def compose(self) -> tx.ComposeResult:
|
|
52
|
-
|
|
53
|
-
yield tx.Static(id='messages-container')
|
|
76
|
+
yield tx.VerticalScroll(id='messages-container')
|
|
54
77
|
|
|
55
78
|
yield InputOuter(id='input-outer')
|
|
56
79
|
|
|
@@ -59,22 +82,19 @@ class ChatApp(tx.App):
|
|
|
59
82
|
def _get_input_text_area(self) -> InputTextArea:
|
|
60
83
|
return self.query_one('#input', InputTextArea)
|
|
61
84
|
|
|
62
|
-
def
|
|
63
|
-
return self.query_one('#messages-
|
|
64
|
-
|
|
65
|
-
def _get_messages_container(self) -> tx.Static:
|
|
66
|
-
return self.query_one('#messages-container', tx.Static)
|
|
85
|
+
def _get_messages_container(self) -> tx.VerticalScroll:
|
|
86
|
+
return self.query_one('#messages-container', tx.VerticalScroll)
|
|
67
87
|
|
|
68
88
|
#
|
|
69
89
|
|
|
70
90
|
def _is_messages_at_bottom(self, threshold: int = 3) -> bool:
|
|
71
|
-
return (ms := self.
|
|
91
|
+
return (ms := self._get_messages_container()).scroll_y >= (ms.max_scroll_y - threshold)
|
|
72
92
|
|
|
73
93
|
def _scroll_messages_to_bottom(self) -> None:
|
|
74
|
-
self.
|
|
94
|
+
self._get_messages_container().scroll_end(animate=False)
|
|
75
95
|
|
|
76
96
|
def _anchor_messages(self) -> None:
|
|
77
|
-
if (ms := self.
|
|
97
|
+
if (ms := self._get_messages_container()).max_scroll_y:
|
|
78
98
|
ms.anchor()
|
|
79
99
|
|
|
80
100
|
#
|
|
@@ -102,7 +122,7 @@ class ChatApp(tx.App):
|
|
|
102
122
|
|
|
103
123
|
await sam.append_content(content)
|
|
104
124
|
|
|
105
|
-
self.call_after_refresh(
|
|
125
|
+
self.call_after_refresh(self._scroll_messages_to_bottom)
|
|
106
126
|
|
|
107
127
|
if was_at_bottom:
|
|
108
128
|
self.call_after_refresh(self._anchor_messages)
|
|
@@ -116,7 +136,7 @@ class ChatApp(tx.App):
|
|
|
116
136
|
msg_ctr = self._get_messages_container()
|
|
117
137
|
|
|
118
138
|
for msg in [*(self._pending_mount_messages or []), *messages]:
|
|
119
|
-
if isinstance(msg, AiMessage):
|
|
139
|
+
if isinstance(msg, (AiMessage, ToolConfirmationMessage)):
|
|
120
140
|
await self._finalize_stream_ai_message()
|
|
121
141
|
|
|
122
142
|
await msg_ctr.mount(msg)
|
|
@@ -127,21 +147,24 @@ class ChatApp(tx.App):
|
|
|
127
147
|
|
|
128
148
|
self._pending_mount_messages = None
|
|
129
149
|
|
|
130
|
-
self.call_after_refresh(
|
|
150
|
+
self.call_after_refresh(self._scroll_messages_to_bottom)
|
|
131
151
|
|
|
132
152
|
if was_at_bottom:
|
|
133
153
|
self.call_after_refresh(self._anchor_messages)
|
|
134
154
|
|
|
135
155
|
#
|
|
136
156
|
|
|
137
|
-
|
|
157
|
+
_chat_event_task: asyncio.Task[None] | None = None
|
|
138
158
|
|
|
139
|
-
|
|
159
|
+
@logs.async_exception_logging(alog)
|
|
160
|
+
async def _chat_event_task_main(self) -> None:
|
|
140
161
|
while True:
|
|
141
|
-
ev = await self.
|
|
162
|
+
ev = await self._chat_event_queue.get()
|
|
142
163
|
if ev is None:
|
|
143
164
|
break
|
|
144
165
|
|
|
166
|
+
await alog.debug(lambda: f'Got chat event: {ev!r}')
|
|
167
|
+
|
|
145
168
|
if isinstance(ev, AiMessagesChatEvent):
|
|
146
169
|
wx: list[tx.Widget] = []
|
|
147
170
|
|
|
@@ -159,50 +182,71 @@ class ChatApp(tx.App):
|
|
|
159
182
|
self.call_later(self._mount_messages)
|
|
160
183
|
|
|
161
184
|
elif isinstance(ev, AiDeltaChatEvent):
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
185
|
+
if isinstance(ev.delta, mc.ContentAiDelta):
|
|
186
|
+
cc = check.isinstance(ev.delta.c, str)
|
|
187
|
+
self.call_later(self._append_stream_ai_message_content, cc)
|
|
188
|
+
|
|
189
|
+
elif isinstance(ev.delta, mc.ToolUseAiDelta):
|
|
190
|
+
pass
|
|
165
191
|
|
|
166
192
|
#
|
|
167
193
|
|
|
168
|
-
|
|
169
|
-
|
|
194
|
+
@dc.dataclass(frozen=True)
|
|
195
|
+
class UserInput:
|
|
196
|
+
text: str
|
|
170
197
|
|
|
171
|
-
|
|
172
|
-
# self.after_repaint()
|
|
173
|
-
#
|
|
174
|
-
# self._schedule_after_refresh()
|
|
198
|
+
_chat_action_task: asyncio.Task[None] | None = None
|
|
175
199
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
200
|
+
@logs.async_exception_logging(alog)
|
|
201
|
+
async def _chat_action_task_main(self) -> None:
|
|
202
|
+
while True:
|
|
203
|
+
ac = await self._chat_action_queue.get()
|
|
204
|
+
if ac is None:
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
await alog.debug(lambda: f'Got chat action: {ac!r}')
|
|
208
|
+
|
|
209
|
+
if isinstance(ac, ChatApp.UserInput):
|
|
210
|
+
try:
|
|
211
|
+
await self._chat_facade.handle_user_input(ac.text)
|
|
212
|
+
except Exception as e: # noqa
|
|
213
|
+
raise
|
|
214
|
+
|
|
215
|
+
else:
|
|
216
|
+
raise TypeError(ac) # noqa
|
|
180
217
|
|
|
181
218
|
#
|
|
182
219
|
|
|
183
220
|
async def on_mount(self) -> None:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
check.state(self._event_queue_task is None)
|
|
187
|
-
self._event_queue_task = asyncio.create_task(self._event_queue_task_main())
|
|
221
|
+
check.state(self._chat_event_task is None)
|
|
222
|
+
self._chat_event_task = asyncio.create_task(self._chat_event_task_main())
|
|
188
223
|
|
|
189
224
|
await self._chat_driver.start()
|
|
190
225
|
|
|
226
|
+
check.state(self._chat_action_task is None)
|
|
227
|
+
self._chat_action_task = asyncio.create_task(self._chat_action_task_main())
|
|
228
|
+
|
|
191
229
|
self._get_input_text_area().focus()
|
|
192
230
|
|
|
193
231
|
await self._mount_messages(
|
|
194
|
-
WelcomeMessage(
|
|
195
|
-
'
|
|
196
|
-
|
|
232
|
+
WelcomeMessage('\n'.join([
|
|
233
|
+
f'Backend: {self._backend_name or "?"}',
|
|
234
|
+
f'Dir: {os.getcwd()}',
|
|
235
|
+
])),
|
|
197
236
|
)
|
|
198
237
|
|
|
199
238
|
async def on_unmount(self) -> None:
|
|
239
|
+
if (cdt := self._chat_event_task) is not None:
|
|
240
|
+
await self._chat_event_queue.put(None)
|
|
241
|
+
await cdt
|
|
242
|
+
|
|
200
243
|
await self._chat_driver.stop()
|
|
201
244
|
|
|
202
|
-
if (
|
|
203
|
-
await self.
|
|
204
|
-
await
|
|
245
|
+
if (cet := self._chat_event_task) is not None:
|
|
246
|
+
await self._chat_event_queue.put(None)
|
|
247
|
+
await cet
|
|
205
248
|
|
|
249
|
+
@tx.on(InputTextArea.Submitted)
|
|
206
250
|
async def on_input_text_area_submitted(self, event: InputTextArea.Submitted) -> None:
|
|
207
251
|
self._get_input_text_area().clear()
|
|
208
252
|
|
|
@@ -214,4 +258,28 @@ class ChatApp(tx.App):
|
|
|
214
258
|
),
|
|
215
259
|
)
|
|
216
260
|
|
|
217
|
-
await self.
|
|
261
|
+
await self._chat_action_queue.put(ChatApp.UserInput(event.text))
|
|
262
|
+
|
|
263
|
+
#
|
|
264
|
+
|
|
265
|
+
async def confirm_tool_use(
|
|
266
|
+
self,
|
|
267
|
+
outer_message: str,
|
|
268
|
+
inner_message: str,
|
|
269
|
+
) -> bool:
|
|
270
|
+
fut: asyncio.Future[bool] = asyncio.get_running_loop().create_future()
|
|
271
|
+
|
|
272
|
+
tcm = ToolConfirmationMessage(
|
|
273
|
+
outer_message,
|
|
274
|
+
inner_message,
|
|
275
|
+
fut,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
async def inner() -> None:
|
|
279
|
+
await self._mount_messages(tcm)
|
|
280
|
+
|
|
281
|
+
self.call_later(inner)
|
|
282
|
+
|
|
283
|
+
ret = await fut
|
|
284
|
+
|
|
285
|
+
return ret
|
|
@@ -3,44 +3,48 @@ 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
|
|
13
|
-
from .app import ChatApp
|
|
14
|
-
from .app import ChatDriverEventQueue
|
|
15
|
-
from .interface import TextualChatInterface
|
|
13
|
+
from .configs import TextualInterfaceConfig
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
with lang.auto_proxy_import(globals()):
|
|
17
|
+
from omdev.tui import textual as tx
|
|
18
|
+
|
|
19
19
|
from ...drivers.tools import confirmation as _tools_confirmation
|
|
20
|
+
from . import app as _app
|
|
21
|
+
from . import interface as _interface
|
|
22
|
+
from . import tools as _tools
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
##
|
|
23
26
|
|
|
24
27
|
|
|
25
|
-
def bind_textual(cfg:
|
|
28
|
+
def bind_textual(cfg: TextualInterfaceConfig = TextualInterfaceConfig()) -> inj.Elements:
|
|
26
29
|
els: list[inj.Elemental] = [
|
|
27
|
-
inj.bind(ChatInterface, to_ctor=TextualChatInterface, singleton=True),
|
|
30
|
+
inj.bind(ChatInterface, to_ctor=_interface.TextualChatInterface, singleton=True),
|
|
28
31
|
]
|
|
29
32
|
|
|
30
33
|
#
|
|
31
34
|
|
|
32
35
|
els.extend([
|
|
33
|
-
inj.bind(ChatApp, singleton=True),
|
|
36
|
+
inj.bind(_app.ChatApp, singleton=True),
|
|
37
|
+
inj.bind_async_late(_app.ChatApp, _app.ChatAppGetter),
|
|
34
38
|
])
|
|
35
39
|
|
|
36
40
|
#
|
|
37
41
|
|
|
38
42
|
els.extend([
|
|
39
|
-
inj.bind(
|
|
43
|
+
inj.bind(_app.ChatEventQueue, to_const=asyncio.Queue()),
|
|
40
44
|
|
|
41
45
|
event_callbacks().bind_item(to_fn=inj.KwargsTarget.of(
|
|
42
46
|
lambda eq: lambda ev: eq.put(ev),
|
|
43
|
-
eq=
|
|
47
|
+
eq=_app.ChatEventQueue,
|
|
44
48
|
)),
|
|
45
49
|
])
|
|
46
50
|
|
|
@@ -55,12 +59,32 @@ def bind_textual(cfg: InterfaceConfig = InterfaceConfig()) -> inj.Elements:
|
|
|
55
59
|
))
|
|
56
60
|
|
|
57
61
|
else:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
els.append(inj.bind(
|
|
63
|
+
_tools_confirmation.ToolExecutionConfirmation,
|
|
64
|
+
to_ctor=_tools.ChatAppToolExecutionConfirmation,
|
|
65
|
+
singleton=True,
|
|
66
|
+
))
|
|
67
|
+
|
|
68
|
+
#
|
|
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
|
+
])
|
|
64
88
|
|
|
65
89
|
#
|
|
66
90
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from omdev.tui import textual as tx
|
|
2
|
+
|
|
1
3
|
from ..base import ChatInterface
|
|
2
4
|
from .app import ChatApp
|
|
3
5
|
|
|
@@ -16,4 +18,7 @@ class TextualChatInterface(ChatInterface):
|
|
|
16
18
|
self._app = app
|
|
17
19
|
|
|
18
20
|
async def run(self) -> None:
|
|
21
|
+
# FIXME: move lol
|
|
22
|
+
tx.set_root_logger_to_devtools(self._app.devtools)
|
|
23
|
+
|
|
19
24
|
await self._app.run_async()
|