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.
- 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 +33 -24
- ommlds/cli/sessions/base.py +1 -10
- ommlds/cli/sessions/chat/configs.py +6 -8
- ommlds/cli/sessions/chat/drivers/driver.py +3 -1
- 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 +3 -3
- 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 +4 -2
- ommlds/cli/sessions/chat/interfaces/configs.py +2 -14
- ommlds/cli/sessions/chat/interfaces/inject.py +8 -3
- ommlds/cli/sessions/chat/interfaces/textual/app.py +53 -30
- ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
- ommlds/cli/sessions/chat/interfaces/textual/inject.py +29 -6
- ommlds/cli/sessions/chat/interfaces/textual/interface.py +2 -82
- ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +8 -5
- ommlds/cli/sessions/chat/interfaces/textual/tools.py +4 -3
- ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +11 -3
- 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 +2 -2
- {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/METADATA +6 -6
- {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/RECORD +52 -44
- {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
|
@@ -40,7 +42,7 @@ class InteractiveBareChatInterface(ChatInterface):
|
|
|
40
42
|
print('<')
|
|
41
43
|
print()
|
|
42
44
|
|
|
43
|
-
await self.
|
|
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 =
|
|
17
|
+
def bind_interface(cfg: InterfaceConfig = BareInterfaceConfig()) -> inj.Elements:
|
|
16
18
|
els: list[inj.Elemental] = []
|
|
17
19
|
|
|
18
|
-
if cfg
|
|
20
|
+
if isinstance(cfg, TextualInterfaceConfig):
|
|
19
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,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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
63
|
+
self._chat_event_queue = chat_event_queue
|
|
64
|
+
self._backend_name = backend_name
|
|
55
65
|
|
|
56
|
-
self.
|
|
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
|
-
|
|
157
|
+
_chat_event_task: asyncio.Task[None] | None = None
|
|
148
158
|
|
|
149
159
|
@logs.async_exception_logging(alog)
|
|
150
|
-
async def
|
|
160
|
+
async def _chat_event_task_main(self) -> None:
|
|
151
161
|
while True:
|
|
152
|
-
ev = await self.
|
|
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
|
|
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
|
-
|
|
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
|
|
201
|
+
async def _chat_action_task_main(self) -> None:
|
|
188
202
|
while True:
|
|
189
|
-
ac = await self.
|
|
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
|
|
207
|
+
await alog.debug(lambda: f'Got chat action: {ac!r}')
|
|
194
208
|
|
|
195
|
-
if isinstance(ac,
|
|
209
|
+
if isinstance(ac, ChatApp.UserInput):
|
|
196
210
|
try:
|
|
197
|
-
await self.
|
|
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.
|
|
208
|
-
self.
|
|
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.
|
|
213
|
-
self.
|
|
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
|
-
'
|
|
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.
|
|
225
|
-
await self.
|
|
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.
|
|
231
|
-
await self.
|
|
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.
|
|
261
|
+
await self._chat_action_queue.put(ChatApp.UserInput(event.text))
|
|
247
262
|
|
|
248
263
|
#
|
|
249
264
|
|
|
250
|
-
async def confirm_tool_use(
|
|
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(
|
|
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)
|
|
@@ -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
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
157
|
+
margin: 1;
|
|
155
158
|
}
|
|
@@ -31,7 +31,8 @@ class ChatAppToolExecutionConfirmation(ToolExecutionConfirmation):
|
|
|
31
31
|
# spec=msh.marshal(tce.spec),
|
|
32
32
|
)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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__(
|
|
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.
|
|
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.
|
|
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
|
|
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__(
|
|
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(
|
|
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
|
|
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:
|
|
15
|
+
config: CompletionConfig,
|
|
21
16
|
*,
|
|
22
17
|
service_provider: CompletionServiceBackendProvider,
|
|
23
18
|
) -> None:
|
|
24
|
-
super().__init__(
|
|
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:
|