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.
Files changed (52) hide show
  1. ommlds/__about__.py +1 -1
  2. ommlds/cli/_dataclasses.py +617 -724
  3. ommlds/cli/backends/catalog.py +0 -5
  4. ommlds/cli/backends/inject.py +2 -0
  5. ommlds/cli/inject.py +11 -3
  6. ommlds/cli/main.py +33 -24
  7. ommlds/cli/sessions/base.py +1 -10
  8. ommlds/cli/sessions/chat/configs.py +6 -8
  9. ommlds/cli/sessions/chat/drivers/driver.py +3 -1
  10. ommlds/cli/sessions/chat/drivers/inject.py +2 -3
  11. ommlds/cli/sessions/chat/drivers/state/configs.py +2 -0
  12. ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
  13. ommlds/cli/sessions/chat/drivers/state/inject.py +54 -6
  14. ommlds/cli/sessions/chat/drivers/state/inmemory.py +0 -4
  15. ommlds/cli/sessions/chat/drivers/state/storage.py +17 -10
  16. ommlds/cli/sessions/chat/drivers/state/types.py +9 -4
  17. ommlds/cli/sessions/chat/drivers/user/inject.py +3 -3
  18. ommlds/cli/sessions/chat/facades/__init__.py +0 -0
  19. ommlds/cli/sessions/chat/facades/configs.py +9 -0
  20. ommlds/cli/sessions/chat/facades/facade.py +19 -0
  21. ommlds/cli/sessions/chat/facades/inject.py +23 -0
  22. ommlds/cli/sessions/chat/inject.py +5 -3
  23. ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
  24. ommlds/cli/sessions/chat/interfaces/bare/inject.py +2 -2
  25. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +4 -2
  26. ommlds/cli/sessions/chat/interfaces/configs.py +2 -14
  27. ommlds/cli/sessions/chat/interfaces/inject.py +8 -3
  28. ommlds/cli/sessions/chat/interfaces/textual/app.py +53 -30
  29. ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
  30. ommlds/cli/sessions/chat/interfaces/textual/inject.py +29 -6
  31. ommlds/cli/sessions/chat/interfaces/textual/interface.py +2 -82
  32. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +8 -5
  33. ommlds/cli/sessions/chat/interfaces/textual/tools.py +4 -3
  34. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +11 -3
  35. ommlds/cli/sessions/chat/session.py +2 -15
  36. ommlds/cli/sessions/completion/configs.py +3 -4
  37. ommlds/cli/sessions/completion/inject.py +1 -2
  38. ommlds/cli/sessions/completion/session.py +4 -8
  39. ommlds/cli/sessions/configs.py +10 -0
  40. ommlds/cli/sessions/embedding/configs.py +3 -4
  41. ommlds/cli/sessions/embedding/inject.py +1 -2
  42. ommlds/cli/sessions/embedding/session.py +4 -8
  43. ommlds/cli/sessions/inject.py +15 -15
  44. ommlds/cli/state/storage.py +7 -1
  45. ommlds/minichain/backends/impls/cerebras/protocol.py +4 -4
  46. ommlds/minichain/backends/impls/ollama/chat.py +2 -2
  47. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/METADATA +6 -6
  48. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/RECORD +52 -44
  49. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/WHEEL +0 -0
  50. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
  51. {ommlds-0.0.0.dev492.dist-info → ommlds-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
  52. {ommlds-0.0.0.dev492.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
 
@@ -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 run(self, argv: ta.Sequence[str]) -> ta.Awaitable[None]:
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=dc.replace(
100
- cfg.interface,
101
- use_textual=True,
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- async def run(self, argv: ta.Sequence[str]) -> None:
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
- with inj.create_managed_injector(bind_main(
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
- 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)
389
398
 
390
399
 
391
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,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
  #
@@ -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,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(cd: '_driver.ChatDriver') -> None:
33
- await cd.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 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,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
 
@@ -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'