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
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
|
|
|
@@ -36,7 +40,8 @@ def _process_main_extra_args(args: ap.Namespace) -> None:
|
|
|
36
40
|
logs.configure_standard_logging('DEBUG')
|
|
37
41
|
else:
|
|
38
42
|
logs.configure_standard_logging('INFO')
|
|
39
|
-
|
|
43
|
+
|
|
44
|
+
logs.silence_noisy_loggers()
|
|
40
45
|
|
|
41
46
|
|
|
42
47
|
##
|
|
@@ -44,7 +49,7 @@ def _process_main_extra_args(args: ap.Namespace) -> None:
|
|
|
44
49
|
|
|
45
50
|
class Profile(lang.Abstract):
|
|
46
51
|
@abc.abstractmethod
|
|
47
|
-
def
|
|
52
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
48
53
|
raise NotImplementedError
|
|
49
54
|
|
|
50
55
|
|
|
@@ -93,12 +98,13 @@ class ChatProfile(Profile):
|
|
|
93
98
|
raise NotImplementedError
|
|
94
99
|
|
|
95
100
|
if self._args.textual:
|
|
101
|
+
check.isinstance(cfg.interface, BareInterfaceConfig)
|
|
96
102
|
cfg = dc.replace(
|
|
97
103
|
cfg,
|
|
98
|
-
interface=
|
|
99
|
-
cfg.interface,
|
|
100
|
-
|
|
101
|
-
),
|
|
104
|
+
interface=TextualInterfaceConfig(**{
|
|
105
|
+
f.name: getattr(cfg.interface, f.name)
|
|
106
|
+
for f in dc.fields(InterfaceConfig)
|
|
107
|
+
}),
|
|
102
108
|
)
|
|
103
109
|
|
|
104
110
|
else:
|
|
@@ -112,7 +118,7 @@ class ChatProfile(Profile):
|
|
|
112
118
|
),
|
|
113
119
|
),
|
|
114
120
|
interface=dc.replace(
|
|
115
|
-
cfg.interface,
|
|
121
|
+
check.isinstance(cfg.interface, BareInterfaceConfig),
|
|
116
122
|
interactive=self._args.interactive,
|
|
117
123
|
),
|
|
118
124
|
)
|
|
@@ -272,7 +278,7 @@ class ChatProfile(Profile):
|
|
|
272
278
|
|
|
273
279
|
#
|
|
274
280
|
|
|
275
|
-
|
|
281
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
276
282
|
parser = ap.ArgumentParser()
|
|
277
283
|
|
|
278
284
|
for grp_name, grp_args in [
|
|
@@ -299,17 +305,14 @@ class ChatProfile(Profile):
|
|
|
299
305
|
cfg = self.configure_tools(cfg)
|
|
300
306
|
cfg = self.configure_code(cfg)
|
|
301
307
|
|
|
302
|
-
|
|
303
|
-
session_cfg=cfg,
|
|
304
|
-
)) as injector:
|
|
305
|
-
await injector[Session].run()
|
|
308
|
+
return cfg
|
|
306
309
|
|
|
307
310
|
|
|
308
311
|
##
|
|
309
312
|
|
|
310
313
|
|
|
311
314
|
class CompletionProfile(Profile):
|
|
312
|
-
|
|
315
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
313
316
|
parser = ap.ArgumentParser()
|
|
314
317
|
parser.add_argument('prompt', nargs='*')
|
|
315
318
|
parser.add_argument('-b', '--backend', default='openai')
|
|
@@ -318,21 +321,18 @@ class CompletionProfile(Profile):
|
|
|
318
321
|
content = ' '.join(args.prompt)
|
|
319
322
|
|
|
320
323
|
cfg = CompletionConfig(
|
|
321
|
-
check.non_empty_str(content),
|
|
324
|
+
content=check.non_empty_str(content),
|
|
322
325
|
backend=args.backend,
|
|
323
326
|
)
|
|
324
327
|
|
|
325
|
-
|
|
326
|
-
session_cfg=cfg,
|
|
327
|
-
)) as injector:
|
|
328
|
-
await injector[Session].run()
|
|
328
|
+
return cfg
|
|
329
329
|
|
|
330
330
|
|
|
331
331
|
##
|
|
332
332
|
|
|
333
333
|
|
|
334
334
|
class EmbedProfile(Profile):
|
|
335
|
-
|
|
335
|
+
def configure(self, argv: ta.Sequence[str]) -> SessionConfig:
|
|
336
336
|
parser = ap.ArgumentParser()
|
|
337
337
|
parser.add_argument('prompt', nargs='*')
|
|
338
338
|
parser.add_argument('-b', '--backend', default='openai')
|
|
@@ -341,14 +341,11 @@ class EmbedProfile(Profile):
|
|
|
341
341
|
content = ' '.join(args.prompt)
|
|
342
342
|
|
|
343
343
|
cfg = EmbeddingConfig(
|
|
344
|
-
check.non_empty_str(content),
|
|
344
|
+
content=check.non_empty_str(content),
|
|
345
345
|
backend=args.backend,
|
|
346
346
|
)
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
session_cfg=cfg,
|
|
350
|
-
)) as injector:
|
|
351
|
-
await injector[Session].run()
|
|
348
|
+
return cfg
|
|
352
349
|
|
|
353
350
|
|
|
354
351
|
##
|
|
@@ -364,6 +361,16 @@ PROFILE_TYPES: ta.Mapping[str, type[Profile]] = {
|
|
|
364
361
|
##
|
|
365
362
|
|
|
366
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
|
+
|
|
367
374
|
MAIN_PROFILE_ARGS: ta.Sequence[ap.Arg] = [
|
|
368
375
|
ap.arg('-p', '--profile', default='chat'),
|
|
369
376
|
ap.arg('args', nargs=ap.REMAINDER),
|
|
@@ -384,7 +391,10 @@ async def _a_main(argv: ta.Any = None) -> None:
|
|
|
384
391
|
|
|
385
392
|
profile_cls = PROFILE_TYPES[args.profile]
|
|
386
393
|
profile = profile_cls()
|
|
387
|
-
|
|
394
|
+
|
|
395
|
+
session_cfg = profile.configure([*unk_args, *args.args])
|
|
396
|
+
|
|
397
|
+
await _run_session_cfg(session_cfg)
|
|
388
398
|
|
|
389
399
|
|
|
390
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,10 @@ TODO:
|
|
|
3
3
|
- lifecycles
|
|
4
4
|
- StreamService
|
|
5
5
|
"""
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
from omlish import lang
|
|
9
|
+
|
|
6
10
|
from ..... import minichain as mc
|
|
7
11
|
from .ai.types import AiChatGenerator
|
|
8
12
|
from .events.manager import ChatEventsManager
|
|
@@ -15,6 +19,10 @@ from .state.types import ChatStateManager
|
|
|
15
19
|
##
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
class ChatDriverGetter(lang.Func0[ta.Awaitable['ChatDriver']]):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
18
26
|
class ChatDriver:
|
|
19
27
|
def __init__(
|
|
20
28
|
self,
|
|
@@ -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),
|
|
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,12 +29,12 @@ 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
|
|
37
|
-
|
|
36
|
+
lambda cdg: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_user_content(cdg)),
|
|
37
|
+
cdg=_driver.ChatDriverGetter,
|
|
38
38
|
)))
|
|
39
39
|
|
|
40
40
|
return inj.as_elements(*els)
|
|
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
|
|