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
ommlds/cli/backends/catalog.py
CHANGED
|
@@ -10,7 +10,6 @@ from .types import BackendProvider
|
|
|
10
10
|
from .types import ChatChoicesServiceBackendProvider
|
|
11
11
|
from .types import ChatChoicesStreamServiceBackendProvider
|
|
12
12
|
from .types import CompletionServiceBackendProvider
|
|
13
|
-
from .types import DefaultBackendName
|
|
14
13
|
from .types import EmbeddingServiceBackendProvider
|
|
15
14
|
from .types import ServiceT
|
|
16
15
|
|
|
@@ -26,7 +25,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
|
|
|
26
25
|
self,
|
|
27
26
|
*,
|
|
28
27
|
name: BackendName | None = None,
|
|
29
|
-
default_name: DefaultBackendName | None = None,
|
|
30
28
|
catalog: 'mc.BackendCatalog',
|
|
31
29
|
configs: BackendConfigs | None = None,
|
|
32
30
|
instantiator: Instantiator | None = None,
|
|
@@ -34,7 +32,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
|
|
|
34
32
|
super().__init__()
|
|
35
33
|
|
|
36
34
|
self._name = name
|
|
37
|
-
self._default_name = default_name
|
|
38
35
|
self._catalog = catalog
|
|
39
36
|
self._configs = configs
|
|
40
37
|
if instantiator is None:
|
|
@@ -46,8 +43,6 @@ class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
|
|
|
46
43
|
name: str
|
|
47
44
|
if self._name is not None:
|
|
48
45
|
name = self._name
|
|
49
|
-
elif self._default_name is not None:
|
|
50
|
-
name = self._default_name
|
|
51
46
|
else:
|
|
52
47
|
raise RuntimeError('No backend name specified')
|
|
53
48
|
|
ommlds/cli/backends/inject.py
CHANGED
|
@@ -42,6 +42,8 @@ def bind_backends(cfg: BackendConfig = BackendConfig()) -> inj.Elements:
|
|
|
42
42
|
|
|
43
43
|
if cfg.backend is not None:
|
|
44
44
|
lst.append(inj.bind(_types.BackendName, to_const=cfg.backend))
|
|
45
|
+
else:
|
|
46
|
+
lst.append(inj.bind(_types.BackendName, to_fn=inj.KwargsTarget.of(lambda dbn: dbn, dbn=_types.DefaultBackendName))) # noqa
|
|
45
47
|
|
|
46
48
|
lst.extend([
|
|
47
49
|
inj.bind(_types.ChatChoicesServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesServiceBackendProvider, singleton=True), # noqa
|
ommlds/cli/inject.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import typing as ta
|
|
2
|
-
|
|
3
1
|
from omlish import inject as inj
|
|
4
2
|
from omlish import lang
|
|
3
|
+
from omlish import lifecycles as lc
|
|
4
|
+
|
|
5
|
+
from .sessions.configs import SessionConfig
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
with lang.auto_proxy_import(globals()):
|
|
@@ -15,12 +16,19 @@ with lang.auto_proxy_import(globals()):
|
|
|
15
16
|
|
|
16
17
|
def bind_main(
|
|
17
18
|
*,
|
|
18
|
-
session_cfg:
|
|
19
|
+
session_cfg: SessionConfig,
|
|
19
20
|
) -> inj.Elements:
|
|
20
21
|
els: list[inj.Elemental] = []
|
|
21
22
|
|
|
22
23
|
#
|
|
23
24
|
|
|
25
|
+
els.extend([
|
|
26
|
+
lc.bind_async_lifecycle_registrar(),
|
|
27
|
+
lc.bind_async_managed_lifecycle_manager(),
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
#
|
|
31
|
+
|
|
24
32
|
els.extend([
|
|
25
33
|
_sessions.bind_sessions(session_cfg),
|
|
26
34
|
|
ommlds/cli/main.py
CHANGED
|
@@ -19,7 +19,11 @@ from .inject import bind_main
|
|
|
19
19
|
from .secrets import install_secrets
|
|
20
20
|
from .sessions.base import Session
|
|
21
21
|
from .sessions.chat.configs import ChatConfig
|
|
22
|
+
from .sessions.chat.interfaces.bare.configs import BareInterfaceConfig
|
|
23
|
+
from .sessions.chat.interfaces.configs import InterfaceConfig
|
|
24
|
+
from .sessions.chat.interfaces.textual.configs import TextualInterfaceConfig
|
|
22
25
|
from .sessions.completion.configs import CompletionConfig
|
|
26
|
+
from .sessions.configs import SessionConfig
|
|
23
27
|
from .sessions.embedding.configs import EmbeddingConfig
|
|
24
28
|
|
|
25
29
|
|
|
@@ -45,7 +49,7 @@ def _process_main_extra_args(args: ap.Namespace) -> None:
|
|
|
45
49
|
|
|
46
50
|
class Profile(lang.Abstract):
|
|
47
51
|
@abc.abstractmethod
|
|
48
|
-
def
|
|
52
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
49
53
|
raise NotImplementedError
|
|
50
54
|
|
|
51
55
|
|
|
@@ -94,12 +98,13 @@ class ChatProfile(Profile):
|
|
|
94
98
|
raise NotImplementedError
|
|
95
99
|
|
|
96
100
|
if self._args.textual:
|
|
101
|
+
check.isinstance(cfg.interface, BareInterfaceConfig)
|
|
97
102
|
cfg = dc.replace(
|
|
98
103
|
cfg,
|
|
99
|
-
interface=
|
|
100
|
-
cfg.interface,
|
|
101
|
-
|
|
102
|
-
),
|
|
104
|
+
interface=TextualInterfaceConfig(**{
|
|
105
|
+
f.name: getattr(cfg.interface, f.name)
|
|
106
|
+
for f in dc.fields(InterfaceConfig)
|
|
107
|
+
}),
|
|
103
108
|
)
|
|
104
109
|
|
|
105
110
|
else:
|
|
@@ -113,7 +118,7 @@ class ChatProfile(Profile):
|
|
|
113
118
|
),
|
|
114
119
|
),
|
|
115
120
|
interface=dc.replace(
|
|
116
|
-
cfg.interface,
|
|
121
|
+
check.isinstance(cfg.interface, BareInterfaceConfig),
|
|
117
122
|
interactive=self._args.interactive,
|
|
118
123
|
),
|
|
119
124
|
)
|
|
@@ -273,7 +278,7 @@ class ChatProfile(Profile):
|
|
|
273
278
|
|
|
274
279
|
#
|
|
275
280
|
|
|
276
|
-
|
|
281
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
277
282
|
parser = ap.ArgumentParser()
|
|
278
283
|
|
|
279
284
|
for grp_name, grp_args in [
|
|
@@ -300,17 +305,14 @@ class ChatProfile(Profile):
|
|
|
300
305
|
cfg = self.configure_tools(cfg)
|
|
301
306
|
cfg = self.configure_code(cfg)
|
|
302
307
|
|
|
303
|
-
|
|
304
|
-
session_cfg=cfg,
|
|
305
|
-
)) as injector:
|
|
306
|
-
await injector[Session].run()
|
|
308
|
+
return cfg
|
|
307
309
|
|
|
308
310
|
|
|
309
311
|
##
|
|
310
312
|
|
|
311
313
|
|
|
312
314
|
class CompletionProfile(Profile):
|
|
313
|
-
|
|
315
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
314
316
|
parser = ap.ArgumentParser()
|
|
315
317
|
parser.add_argument('prompt', nargs='*')
|
|
316
318
|
parser.add_argument('-b', '--backend', default='openai')
|
|
@@ -319,21 +321,18 @@ class CompletionProfile(Profile):
|
|
|
319
321
|
content = ' '.join(args.prompt)
|
|
320
322
|
|
|
321
323
|
cfg = CompletionConfig(
|
|
322
|
-
check.non_empty_str(content),
|
|
324
|
+
content=check.non_empty_str(content),
|
|
323
325
|
backend=args.backend,
|
|
324
326
|
)
|
|
325
327
|
|
|
326
|
-
|
|
327
|
-
session_cfg=cfg,
|
|
328
|
-
)) as injector:
|
|
329
|
-
await injector[Session].run()
|
|
328
|
+
return cfg
|
|
330
329
|
|
|
331
330
|
|
|
332
331
|
##
|
|
333
332
|
|
|
334
333
|
|
|
335
334
|
class EmbedProfile(Profile):
|
|
336
|
-
|
|
335
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
337
336
|
parser = ap.ArgumentParser()
|
|
338
337
|
parser.add_argument('prompt', nargs='*')
|
|
339
338
|
parser.add_argument('-b', '--backend', default='openai')
|
|
@@ -342,14 +341,11 @@ class EmbedProfile(Profile):
|
|
|
342
341
|
content = ' '.join(args.prompt)
|
|
343
342
|
|
|
344
343
|
cfg = EmbeddingConfig(
|
|
345
|
-
check.non_empty_str(content),
|
|
344
|
+
content=check.non_empty_str(content),
|
|
346
345
|
backend=args.backend,
|
|
347
346
|
)
|
|
348
347
|
|
|
349
|
-
|
|
350
|
-
session_cfg=cfg,
|
|
351
|
-
)) as injector:
|
|
352
|
-
await injector[Session].run()
|
|
348
|
+
return cfg
|
|
353
349
|
|
|
354
350
|
|
|
355
351
|
##
|
|
@@ -365,6 +361,16 @@ PROFILE_TYPES: ta.Mapping[str, type[Profile]] = {
|
|
|
365
361
|
##
|
|
366
362
|
|
|
367
363
|
|
|
364
|
+
async def _run_session_cfg(session_cfg: SessionConfig) -> None:
|
|
365
|
+
async with inj.create_async_managed_injector(bind_main(
|
|
366
|
+
session_cfg=session_cfg,
|
|
367
|
+
)) as injector:
|
|
368
|
+
await (await injector[Session]).run()
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
##
|
|
372
|
+
|
|
373
|
+
|
|
368
374
|
MAIN_PROFILE_ARGS: ta.Sequence[ap.Arg] = [
|
|
369
375
|
ap.arg('-p', '--profile', default='chat'),
|
|
370
376
|
ap.arg('args', nargs=ap.REMAINDER),
|
|
@@ -385,7 +391,10 @@ async def _a_main(argv: ta.Any = None) -> None:
|
|
|
385
391
|
|
|
386
392
|
profile_cls = PROFILE_TYPES[args.profile]
|
|
387
393
|
profile = profile_cls()
|
|
388
|
-
|
|
394
|
+
|
|
395
|
+
session_cfg = profile.configure([*unk_args, *args.args])
|
|
396
|
+
|
|
397
|
+
await _run_session_cfg(session_cfg)
|
|
389
398
|
|
|
390
399
|
|
|
391
400
|
def _main(args: ta.Any = None) -> None:
|
ommlds/cli/sessions/base.py
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
|
-
from omlish import dataclasses as dc
|
|
5
4
|
from omlish import lang
|
|
6
|
-
from omlish.configs import all as cfgs
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
SessionConfigT = ta.TypeVar('SessionConfigT', bound='Session.Config')
|
|
10
5
|
|
|
11
6
|
|
|
12
7
|
##
|
|
13
8
|
|
|
14
9
|
|
|
15
|
-
class Session(
|
|
16
|
-
@dc.dataclass(frozen=True)
|
|
17
|
-
class Config(cfgs.Configurable.Config):
|
|
18
|
-
pass
|
|
19
|
-
|
|
10
|
+
class Session(lang.Abstract):
|
|
20
11
|
@abc.abstractmethod
|
|
21
12
|
def run(self) -> ta.Awaitable[None]:
|
|
22
13
|
raise NotImplementedError
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
from omlish import dataclasses as dc
|
|
2
2
|
|
|
3
3
|
from ...rendering.configs import RenderingConfig
|
|
4
|
+
from ..configs import SessionConfig
|
|
4
5
|
from .drivers.configs import DriverConfig
|
|
6
|
+
from .facades.configs import FacadeConfig
|
|
7
|
+
from .interfaces.bare.configs import BareInterfaceConfig
|
|
5
8
|
from .interfaces.configs import InterfaceConfig
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
##
|
|
9
12
|
|
|
10
13
|
|
|
11
|
-
DEFAULT_BACKEND = 'openai'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
|
|
17
14
|
@dc.dataclass(frozen=True, kw_only=True)
|
|
18
|
-
class ChatConfig:
|
|
15
|
+
class ChatConfig(SessionConfig):
|
|
19
16
|
driver: DriverConfig = DriverConfig()
|
|
20
|
-
|
|
17
|
+
facade: FacadeConfig = FacadeConfig()
|
|
18
|
+
interface: InterfaceConfig = BareInterfaceConfig()
|
|
21
19
|
rendering: RenderingConfig = RenderingConfig()
|
|
@@ -3,6 +3,8 @@ TODO:
|
|
|
3
3
|
- lifecycles
|
|
4
4
|
- StreamService
|
|
5
5
|
"""
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
6
8
|
from omlish import lang
|
|
7
9
|
|
|
8
10
|
from ..... import minichain as mc
|
|
@@ -17,7 +19,7 @@ from .state.types import ChatStateManager
|
|
|
17
19
|
##
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
class ChatDriverGetter(lang.Func0['ChatDriver']):
|
|
22
|
+
class ChatDriverGetter(lang.Func0[ta.Awaitable['ChatDriver']]):
|
|
21
23
|
pass
|
|
22
24
|
|
|
23
25
|
|
|
@@ -27,7 +27,7 @@ with lang.auto_proxy_import(globals()):
|
|
|
27
27
|
##
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def bind_driver(cfg: DriverConfig) -> inj.Elements:
|
|
30
|
+
def bind_driver(cfg: DriverConfig = DriverConfig()) -> inj.Elements:
|
|
31
31
|
els: list[inj.Elemental] = []
|
|
32
32
|
|
|
33
33
|
#
|
|
@@ -52,8 +52,7 @@ def bind_driver(cfg: DriverConfig) -> inj.Elements:
|
|
|
52
52
|
|
|
53
53
|
els.extend([
|
|
54
54
|
inj.bind(_driver.ChatDriver, singleton=True),
|
|
55
|
-
|
|
56
|
-
inj.bind_late(_driver.ChatDriver, _driver.ChatDriverGetter),
|
|
55
|
+
inj.bind_async_late(_driver.ChatDriver, _driver.ChatDriverGetter),
|
|
57
56
|
])
|
|
58
57
|
|
|
59
58
|
#
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .storage import StateStorage
|
|
2
|
+
from .types import ChatId
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_LAST_CHAT_ID_STATE_KEY: str = 'last_chat_id'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LastChatIdManager:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
*,
|
|
15
|
+
state_storage: StateStorage,
|
|
16
|
+
) -> None:
|
|
17
|
+
super().__init__()
|
|
18
|
+
|
|
19
|
+
self._state_storage = state_storage
|
|
20
|
+
|
|
21
|
+
async def get_last_chat_id(self) -> ChatId | None:
|
|
22
|
+
return await self._state_storage.load_state(_LAST_CHAT_ID_STATE_KEY, ChatId)
|
|
23
|
+
|
|
24
|
+
async def set_last_chat_id(self, chat_id: ChatId | None) -> None:
|
|
25
|
+
await self._state_storage.save_state(_LAST_CHAT_ID_STATE_KEY, chat_id, ChatId)
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- given chat-id + ephemeral = continue in mem
|
|
4
|
+
- fork chats
|
|
5
|
+
"""
|
|
6
|
+
import uuid
|
|
7
|
+
|
|
1
8
|
from omlish import inject as inj
|
|
2
9
|
from omlish import lang
|
|
3
10
|
|
|
@@ -8,6 +15,7 @@ from .configs import StateConfig
|
|
|
8
15
|
|
|
9
16
|
|
|
10
17
|
with lang.auto_proxy_import(globals()):
|
|
18
|
+
from . import ids as _ids
|
|
11
19
|
from . import inmemory as _inmemory
|
|
12
20
|
from . import storage as _storage
|
|
13
21
|
from . import types as _types
|
|
@@ -16,21 +24,61 @@ with lang.auto_proxy_import(globals()):
|
|
|
16
24
|
##
|
|
17
25
|
|
|
18
26
|
|
|
27
|
+
def _new_chat_id() -> '_types.ChatId':
|
|
28
|
+
return _types.ChatId(uuid.uuid4())
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def _get_last_or_new_chat_id(lcim: '_ids.LastChatIdManager') -> '_types.ChatId':
|
|
32
|
+
return lcid if (lcid := await lcim.get_last_chat_id()) is not None else _new_chat_id()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
19
38
|
def bind_state(cfg: StateConfig = StateConfig()) -> inj.Elements:
|
|
20
39
|
els: list[inj.Elemental] = []
|
|
21
40
|
|
|
22
41
|
if cfg.state in ('continue', 'new'):
|
|
23
|
-
els.append(inj.bind(_types.ChatStateManager, to_ctor=_storage.StateStorageChatStateManager, singleton=True))
|
|
24
|
-
|
|
25
42
|
if cfg.state == 'new':
|
|
26
|
-
els.append(
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
els.append(inj.bind(_types.ChatId(uuid.UUID(cfg.chat_id)) if cfg.chat_id is not None else _new_chat_id()))
|
|
44
|
+
|
|
45
|
+
elif cfg.chat_id is not None:
|
|
46
|
+
els.append(inj.bind(_types.ChatId(uuid.UUID(cfg.chat_id))))
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
els.append(inj.bind(_types.ChatId, to_async_fn=_get_last_or_new_chat_id, singleton=True))
|
|
50
|
+
|
|
51
|
+
els.extend([
|
|
52
|
+
inj.bind(_storage.build_chat_storage_key),
|
|
53
|
+
|
|
54
|
+
inj.bind(_types.ChatStateManager, to_ctor=_storage.StateStorageChatStateManager, singleton=True),
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
|
|
59
|
+
els.extend([
|
|
60
|
+
inj.bind(_ids.LastChatIdManager, singleton=True),
|
|
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)),
|
|
64
|
+
lcim=_ids.LastChatIdManager,
|
|
65
|
+
cid=_types.ChatId,
|
|
66
|
+
)),
|
|
67
|
+
])
|
|
29
68
|
|
|
30
69
|
elif cfg.state == 'ephemeral':
|
|
31
|
-
|
|
70
|
+
if cfg.chat_id is not None:
|
|
71
|
+
raise ValueError('chat-id is not allowed for ephemeral state')
|
|
72
|
+
|
|
73
|
+
els.extend([
|
|
74
|
+
inj.bind(_new_chat_id()),
|
|
75
|
+
|
|
76
|
+
inj.bind(_types.ChatStateManager, to_ctor=_inmemory.InMemoryChatStateManager, singleton=True),
|
|
77
|
+
])
|
|
32
78
|
|
|
33
79
|
else:
|
|
34
80
|
raise TypeError(cfg.state)
|
|
35
81
|
|
|
82
|
+
#
|
|
83
|
+
|
|
36
84
|
return inj.as_elements(*els)
|
|
@@ -20,10 +20,6 @@ class InMemoryChatStateManager(ChatStateManager):
|
|
|
20
20
|
async def get_state(self) -> ChatState:
|
|
21
21
|
return self._state
|
|
22
22
|
|
|
23
|
-
async def clear_state(self) -> ChatState:
|
|
24
|
-
self._state = ChatState()
|
|
25
|
-
return self._state
|
|
26
|
-
|
|
27
23
|
async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
|
|
28
24
|
self._state = dc.replace(
|
|
29
25
|
self._state,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from omlish import check
|
|
2
2
|
from omlish import dataclasses as dc
|
|
3
3
|
from omlish import lang
|
|
4
|
+
from omlish import typedvalues as tv
|
|
4
5
|
|
|
5
6
|
from ...... import minichain as mc
|
|
6
7
|
from .....state.storage import StateStorage
|
|
8
|
+
from .types import ChatId
|
|
7
9
|
from .types import ChatState
|
|
8
10
|
from .types import ChatStateManager
|
|
9
11
|
|
|
@@ -11,35 +13,40 @@ from .types import ChatStateManager
|
|
|
11
13
|
##
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
class ChatStateStorageKey(tv.UniqueScalarTypedValue[str]):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_chat_storage_key(chat_id: ChatId) -> ChatStateStorageKey:
|
|
21
|
+
return ChatStateStorageKey(f'chat:{chat_id.v}')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
14
27
|
class StateStorageChatStateManager(ChatStateManager):
|
|
15
28
|
def __init__(
|
|
16
29
|
self,
|
|
17
30
|
*,
|
|
18
31
|
storage: StateStorage,
|
|
19
|
-
key:
|
|
32
|
+
key: ChatStateStorageKey,
|
|
20
33
|
) -> None:
|
|
21
34
|
super().__init__()
|
|
22
35
|
|
|
23
36
|
self._storage = storage
|
|
24
|
-
self._key = check.
|
|
37
|
+
self._key = check.isinstance(key, ChatStateStorageKey)
|
|
25
38
|
|
|
26
39
|
self._state: ChatState | None = None
|
|
27
40
|
|
|
28
41
|
async def get_state(self) -> ChatState:
|
|
29
42
|
if self._state is not None:
|
|
30
43
|
return self._state
|
|
31
|
-
state: ChatState | None = await self._storage.load_state(self._key, ChatState)
|
|
44
|
+
state: ChatState | None = await self._storage.load_state(self._key.v, ChatState)
|
|
32
45
|
if state is None:
|
|
33
46
|
state = ChatState()
|
|
34
47
|
self._state = state
|
|
35
48
|
return state
|
|
36
49
|
|
|
37
|
-
async def clear_state(self) -> ChatState:
|
|
38
|
-
state = ChatState()
|
|
39
|
-
await self._storage.save_state(self._key, state, ChatState)
|
|
40
|
-
self._state = state
|
|
41
|
-
return state
|
|
42
|
-
|
|
43
50
|
async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
|
|
44
51
|
state = await self.get_state()
|
|
45
52
|
state = dc.replace(
|
|
@@ -47,6 +54,6 @@ class StateStorageChatStateManager(ChatStateManager):
|
|
|
47
54
|
chat=[*state.chat, *chat_additions],
|
|
48
55
|
updated_at=lang.utcnow(),
|
|
49
56
|
)
|
|
50
|
-
await self._storage.save_state(self._key, state, ChatState)
|
|
57
|
+
await self._storage.save_state(self._key.v, state, ChatState)
|
|
51
58
|
self._state = state
|
|
52
59
|
return state
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import datetime
|
|
3
3
|
import typing as ta
|
|
4
|
+
import uuid
|
|
4
5
|
|
|
5
6
|
from omlish import dataclasses as dc
|
|
6
7
|
from omlish import lang
|
|
8
|
+
from omlish import typedvalues as tv
|
|
7
9
|
|
|
8
10
|
from ...... import minichain as mc
|
|
9
11
|
|
|
@@ -11,6 +13,13 @@ from ...... import minichain as mc
|
|
|
11
13
|
##
|
|
12
14
|
|
|
13
15
|
|
|
16
|
+
class ChatId(tv.UniqueScalarTypedValue[uuid.UUID]):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
@dc.dataclass(frozen=True)
|
|
15
24
|
class ChatState:
|
|
16
25
|
name: str | None = None
|
|
@@ -29,10 +38,6 @@ class ChatStateManager(lang.Abstract):
|
|
|
29
38
|
def get_state(self) -> ta.Awaitable[ChatState]:
|
|
30
39
|
raise NotImplementedError
|
|
31
40
|
|
|
32
|
-
@abc.abstractmethod
|
|
33
|
-
def clear_state(self) -> ta.Awaitable[ChatState]:
|
|
34
|
-
raise NotImplementedError
|
|
35
|
-
|
|
36
41
|
@abc.abstractmethod
|
|
37
42
|
def extend_chat(self, chat_additions: 'mc.Chat') -> ta.Awaitable[ChatState]:
|
|
38
43
|
raise NotImplementedError
|
|
@@ -29,11 +29,11 @@ def bind_user(cfg: UserConfig = UserConfig()) -> inj.Elements:
|
|
|
29
29
|
)))
|
|
30
30
|
|
|
31
31
|
if cfg.initial_user_content is not None:
|
|
32
|
-
async def add_initial_user_content(
|
|
33
|
-
await
|
|
32
|
+
async def add_initial_user_content(cdg: '_driver.ChatDriverGetter') -> None:
|
|
33
|
+
await (await cdg()).send_user_messages([mc.UserMessage(cfg.initial_user_content)])
|
|
34
34
|
|
|
35
35
|
els.append(phase_callbacks().bind_item(to_fn=inj.KwargsTarget.of(
|
|
36
|
-
lambda cdg: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_user_content(cdg
|
|
36
|
+
lambda cdg: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_user_content(cdg)),
|
|
37
37
|
cdg=_driver.ChatDriverGetter,
|
|
38
38
|
)))
|
|
39
39
|
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ..... import minichain as mc
|
|
2
|
+
from ..drivers.driver import ChatDriver
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ChatFacade:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
*,
|
|
12
|
+
driver: ChatDriver,
|
|
13
|
+
) -> None:
|
|
14
|
+
super().__init__()
|
|
15
|
+
|
|
16
|
+
self._driver = driver
|
|
17
|
+
|
|
18
|
+
async def handle_user_input(self, text: str) -> None:
|
|
19
|
+
await self._driver.send_user_messages([mc.UserMessage(text)])
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from omlish import inject as inj
|
|
2
|
+
from omlish import lang
|
|
3
|
+
|
|
4
|
+
from .configs import FacadeConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
from . import facade as _facade
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def bind_facade(cfg: FacadeConfig = FacadeConfig()) -> inj.Elements:
|
|
15
|
+
els: list[inj.Elemental] = []
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
els.append(inj.bind(_facade.ChatFacade, singleton=True))
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
|
|
23
|
+
return inj.as_elements(*els)
|
|
@@ -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
|
|
|
@@ -10,13 +9,14 @@ with lang.auto_proxy_import(globals()):
|
|
|
10
9
|
from ...rendering import inject as _rendering
|
|
11
10
|
from . import session as _session
|
|
12
11
|
from .drivers import inject as _drivers
|
|
12
|
+
from .facades import inject as _facades
|
|
13
13
|
from .interfaces import inject as _interfaces
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
##
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def bind_chat(cfg: ChatConfig) -> inj.Elements:
|
|
19
|
+
def bind_chat(cfg: ChatConfig = ChatConfig()) -> inj.Elements:
|
|
20
20
|
els: list[inj.Elemental] = []
|
|
21
21
|
|
|
22
22
|
#
|
|
@@ -24,6 +24,8 @@ def bind_chat(cfg: ChatConfig) -> inj.Elements:
|
|
|
24
24
|
els.extend([
|
|
25
25
|
_drivers.bind_driver(cfg.driver),
|
|
26
26
|
|
|
27
|
+
_facades.bind_facade(cfg.facade),
|
|
28
|
+
|
|
27
29
|
_interfaces.bind_interface(cfg.interface),
|
|
28
30
|
|
|
29
31
|
_rendering.bind_rendering(cfg.rendering),
|
|
@@ -32,7 +34,7 @@ def bind_chat(cfg: ChatConfig) -> inj.Elements:
|
|
|
32
34
|
#
|
|
33
35
|
|
|
34
36
|
els.extend([
|
|
35
|
-
inj.bind(
|
|
37
|
+
# inj.bind(cfg), # NOTE: *not* done - the code is properly structured around not needing it.
|
|
36
38
|
inj.bind(Session, to_ctor=_session.ChatSession, singleton=True),
|
|
37
39
|
])
|
|
38
40
|
|
|
@@ -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'
|