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.
Files changed (55) hide show
  1. ommlds/.omlish-manifests.json +9 -7
  2. ommlds/__about__.py +1 -1
  3. ommlds/cli/_dataclasses.py +617 -724
  4. ommlds/cli/backends/catalog.py +0 -5
  5. ommlds/cli/backends/inject.py +2 -0
  6. ommlds/cli/inject.py +11 -3
  7. ommlds/cli/main.py +35 -25
  8. ommlds/cli/sessions/base.py +1 -10
  9. ommlds/cli/sessions/chat/configs.py +6 -8
  10. ommlds/cli/sessions/chat/drivers/driver.py +8 -0
  11. ommlds/cli/sessions/chat/drivers/inject.py +2 -3
  12. ommlds/cli/sessions/chat/drivers/state/configs.py +2 -0
  13. ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
  14. ommlds/cli/sessions/chat/drivers/state/inject.py +54 -6
  15. ommlds/cli/sessions/chat/drivers/state/inmemory.py +0 -4
  16. ommlds/cli/sessions/chat/drivers/state/storage.py +17 -10
  17. ommlds/cli/sessions/chat/drivers/state/types.py +9 -4
  18. ommlds/cli/sessions/chat/drivers/user/inject.py +4 -4
  19. ommlds/cli/sessions/chat/facades/__init__.py +0 -0
  20. ommlds/cli/sessions/chat/facades/configs.py +9 -0
  21. ommlds/cli/sessions/chat/facades/facade.py +19 -0
  22. ommlds/cli/sessions/chat/facades/inject.py +23 -0
  23. ommlds/cli/sessions/chat/inject.py +5 -3
  24. ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
  25. ommlds/cli/sessions/chat/interfaces/bare/inject.py +2 -2
  26. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +10 -2
  27. ommlds/cli/sessions/chat/interfaces/configs.py +2 -14
  28. ommlds/cli/sessions/chat/interfaces/inject.py +9 -4
  29. ommlds/cli/sessions/chat/interfaces/textual/app.py +113 -45
  30. ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
  31. ommlds/cli/sessions/chat/interfaces/textual/inject.py +39 -15
  32. ommlds/cli/sessions/chat/interfaces/textual/interface.py +5 -0
  33. ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +3 -1
  34. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +76 -22
  35. ommlds/cli/sessions/chat/interfaces/textual/tools.py +38 -0
  36. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +60 -2
  37. ommlds/cli/sessions/chat/session.py +2 -15
  38. ommlds/cli/sessions/completion/configs.py +3 -4
  39. ommlds/cli/sessions/completion/inject.py +1 -2
  40. ommlds/cli/sessions/completion/session.py +4 -8
  41. ommlds/cli/sessions/configs.py +10 -0
  42. ommlds/cli/sessions/embedding/configs.py +3 -4
  43. ommlds/cli/sessions/embedding/inject.py +1 -2
  44. ommlds/cli/sessions/embedding/session.py +4 -8
  45. ommlds/cli/sessions/inject.py +15 -15
  46. ommlds/cli/state/storage.py +7 -1
  47. ommlds/minichain/backends/impls/cerebras/protocol.py +4 -4
  48. ommlds/minichain/backends/impls/ollama/chat.py +26 -0
  49. ommlds/minichain/backends/impls/openai/names.py +3 -1
  50. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/METADATA +6 -6
  51. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/RECORD +55 -46
  52. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/WHEEL +0 -0
  53. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  54. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  55. {ommlds-0.0.0.dev491.dist-info → ommlds-0.0.0.dev493.dist-info}/top_level.txt +0 -0
@@ -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
 
@@ -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: ta.Any,
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
- logs.silence_noisy_loggers()
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 run(self, argv: ta.Sequence[str]) -> ta.Awaitable[None]:
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=dc.replace(
99
- cfg.interface,
100
- use_textual=True,
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- await profile.run([*unk_args, *args.args])
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:
@@ -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(cfgs.Configurable[SessionConfigT], lang.Abstract):
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
- interface: InterfaceConfig = InterfaceConfig()
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
  #
@@ -9,3 +9,5 @@ from omlish import dataclasses as dc
9
9
  @dc.dataclass(frozen=True, kw_only=True)
10
10
  class StateConfig:
11
11
  state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue'
12
+
13
+ chat_id: str | None = None
@@ -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(phase_callbacks().bind_item(to_fn=lang.typed_lambda(cm=_types.ChatStateManager)(
27
- lambda cm: ChatPhaseCallback(ChatPhase.STARTING, cm.clear_state),
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
- els.append(inj.bind(_types.ChatStateManager, to_ctor=_inmemory.InMemoryChatStateManager, singleton=True))
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: str = 'chat',
32
+ key: ChatStateStorageKey,
20
33
  ) -> None:
21
34
  super().__init__()
22
35
 
23
36
  self._storage = storage
24
- self._key = check.non_empty_str(key)
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(ca: '_driver.ChatDriver') -> None:
33
- await ca.send_user_messages([mc.UserMessage(cfg.initial_user_content)])
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 ca: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_user_content(ca())),
37
- ca=inj.Late[_driver.ChatDriver],
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,9 @@
1
+ from omlish import dataclasses as dc
2
+
3
+
4
+ ##
5
+
6
+
7
+ @dc.dataclass(frozen=True, kw_only=True)
8
+ class FacadeConfig:
9
+ pass
@@ -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(_session.ChatSession.Config(**dc.asdict(cfg))),
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