ommlds 0.0.0.dev463__py3-none-any.whl → 0.0.0.dev464__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.

Potentially problematic release.


This version of ommlds might be problematic. Click here for more details.

Files changed (69) hide show
  1. ommlds/cli/inject.py +0 -5
  2. ommlds/cli/main.py +26 -54
  3. ommlds/cli/sessions/{chat2 → chat}/backends/catalog.py +5 -5
  4. ommlds/cli/sessions/chat/backends/inject.py +37 -0
  5. ommlds/cli/sessions/chat/backends/injection.py +16 -0
  6. ommlds/cli/sessions/{chat2 → chat}/backends/types.py +3 -3
  7. ommlds/cli/sessions/chat/chat/ai/inject.py +81 -0
  8. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  9. ommlds/cli/sessions/{chat2 → chat}/chat/ai/rendering.py +13 -10
  10. ommlds/cli/sessions/chat/chat/ai/services.py +81 -0
  11. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  12. ommlds/cli/sessions/{chat2 → chat}/chat/ai/types.py +5 -5
  13. ommlds/cli/sessions/chat/chat/state/inject.py +40 -0
  14. ommlds/cli/sessions/{chat2 → chat}/chat/state/inmemory.py +1 -1
  15. ommlds/cli/sessions/{chat2 → chat}/chat/state/storage.py +1 -1
  16. ommlds/cli/sessions/{chat2 → chat}/chat/state/types.py +1 -1
  17. ommlds/cli/sessions/chat/chat/user/inject.py +61 -0
  18. ommlds/cli/sessions/{chat2 → chat}/chat/user/interactive.py +1 -1
  19. ommlds/cli/sessions/{chat2 → chat}/chat/user/oneshot.py +1 -1
  20. ommlds/cli/sessions/{chat2 → chat}/chat/user/types.py +1 -1
  21. ommlds/cli/sessions/{chat2 → chat}/configs.py +4 -1
  22. ommlds/cli/sessions/{chat2 → chat}/content/messages.py +6 -2
  23. ommlds/cli/sessions/{chat2 → chat}/content/strings.py +2 -2
  24. ommlds/cli/sessions/{chat2 → chat}/driver.py +2 -2
  25. ommlds/cli/sessions/chat/inject.py +40 -66
  26. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  27. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  28. ommlds/cli/sessions/{chat2/phases.py → chat/phases/manager.py} +2 -28
  29. ommlds/cli/sessions/chat/phases/types.py +29 -0
  30. ommlds/cli/sessions/chat/rendering/inject.py +32 -0
  31. ommlds/cli/sessions/{chat2 → chat}/rendering/markdown.py +2 -2
  32. ommlds/cli/sessions/{chat2 → chat}/rendering/raw.py +6 -6
  33. ommlds/cli/sessions/{chat2 → chat}/rendering/types.py +1 -1
  34. ommlds/cli/sessions/{chat2 → chat}/session.py +1 -1
  35. ommlds/cli/sessions/{chat2 → chat}/tools/confirmation.py +4 -4
  36. ommlds/cli/sessions/{chat2 → chat}/tools/execution.py +18 -5
  37. ommlds/cli/sessions/chat/tools/inject.py +145 -0
  38. ommlds/cli/sessions/chat/tools/injection.py +29 -0
  39. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  40. ommlds/cli/{tools → sessions/chat/tools}/weather.py +1 -1
  41. ommlds/cli/sessions/inject.py +2 -7
  42. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/METADATA +3 -3
  43. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/RECORD +56 -54
  44. ommlds/cli/sessions/chat/base.py +0 -42
  45. ommlds/cli/sessions/chat/code.py +0 -125
  46. ommlds/cli/sessions/chat/interactive.py +0 -70
  47. ommlds/cli/sessions/chat/printing.py +0 -126
  48. ommlds/cli/sessions/chat/prompt.py +0 -161
  49. ommlds/cli/sessions/chat/state.py +0 -110
  50. ommlds/cli/sessions/chat/tools.py +0 -97
  51. ommlds/cli/sessions/chat2/_inject.py +0 -105
  52. ommlds/cli/sessions/chat2/chat/ai/services.py +0 -70
  53. ommlds/cli/sessions/chat2/inject.py +0 -143
  54. ommlds/cli/tools/__init__.py +0 -0
  55. ommlds/cli/tools/config.py +0 -14
  56. ommlds/cli/tools/inject.py +0 -75
  57. /ommlds/cli/sessions/{chat2 → chat/backends}/__init__.py +0 -0
  58. /ommlds/cli/sessions/{chat2/backends → chat/chat}/__init__.py +0 -0
  59. /ommlds/cli/sessions/{chat2/chat → chat/chat/ai}/__init__.py +0 -0
  60. /ommlds/cli/sessions/{chat2/chat/ai → chat/chat/state}/__init__.py +0 -0
  61. /ommlds/cli/sessions/{chat2/chat/state → chat/chat/user}/__init__.py +0 -0
  62. /ommlds/cli/sessions/{chat2/chat/user → chat/content}/__init__.py +0 -0
  63. /ommlds/cli/sessions/{chat2/content → chat/phases}/__init__.py +0 -0
  64. /ommlds/cli/sessions/{chat2 → chat}/rendering/__init__.py +0 -0
  65. /ommlds/cli/sessions/{chat2 → chat}/tools/__init__.py +0 -0
  66. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/WHEEL +0 -0
  67. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/entry_points.txt +0 -0
  68. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/licenses/LICENSE +0 -0
  69. {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
1
+ import typing as ta
2
+
3
+ from omlish import inject as inj
4
+ from omlish import lang
5
+
6
+ from ...... import minichain as mc
7
+ from ...phases.injection import phase_callbacks
8
+ from ...phases.types import ChatPhase
9
+ from ...phases.types import ChatPhaseCallback
10
+
11
+
12
+ with lang.auto_proxy_import(globals()):
13
+ from ..state import types as _state
14
+ from . import interactive as _interactive
15
+ from . import oneshot as _oneshot
16
+ from . import types as _types
17
+
18
+
19
+ ##
20
+
21
+
22
+ def bind_user(
23
+ *,
24
+ initial_system_content: ta.Optional['mc.Content'] = None,
25
+ initial_user_content: ta.Optional['mc.Content'] = None,
26
+ interactive: bool = False,
27
+ ) -> inj.Elements:
28
+ els: list[inj.Elemental] = []
29
+
30
+ # FIXME: barf
31
+ if initial_system_content is not None:
32
+ async def add_initial_system_content(cm: '_state.ChatStateManager') -> None:
33
+ await cm.extend_chat([mc.SystemMessage(initial_system_content)])
34
+
35
+ els.append(phase_callbacks().bind_item(to_fn=lang.typed_lambda(cm=_state.ChatStateManager)(
36
+ lambda cm: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_system_content(cm)),
37
+ )))
38
+
39
+ if interactive:
40
+ if initial_user_content is not None:
41
+ async def add_initial_user_content(cm: '_state.ChatStateManager') -> None:
42
+ await cm.extend_chat([mc.UserMessage(initial_user_content)])
43
+
44
+ els.append(phase_callbacks().bind_item(to_fn=lang.typed_lambda(cm=_state.ChatStateManager)(
45
+ lambda cm: ChatPhaseCallback(ChatPhase.STARTED, lambda: add_initial_user_content(cm)),
46
+ )))
47
+
48
+ raise NotImplementedError
49
+
50
+ els.append(inj.bind(_types.UserChatInput, to_ctor=_interactive.InteractiveUserChatInput, singleton=True))
51
+
52
+ else:
53
+ if initial_user_content is None:
54
+ raise ValueError('Initial user content is required for non-interactive chat')
55
+
56
+ els.extend([
57
+ inj.bind(_oneshot.OneshotUserChatInputInitialChat, to_const=[mc.UserMessage(initial_user_content)]),
58
+ inj.bind(_types.UserChatInput, to_ctor=_oneshot.OneshotUserChatInput, singleton=True),
59
+ ])
60
+
61
+ return inj.as_elements(*els)
@@ -21,7 +21,7 @@ class InteractiveUserChatInput(UserChatInput):
21
21
  string_input = lang.as_async(functools.partial(input, '> '))
22
22
  self._string_input = string_input
23
23
 
24
- async def get_next_user_messages(self) -> mc.UserChat:
24
+ async def get_next_user_messages(self) -> 'mc.UserChat':
25
25
  try:
26
26
  s = await self._string_input()
27
27
  except EOFError:
@@ -19,7 +19,7 @@ class OneshotUserChatInput(UserChatInput):
19
19
 
20
20
  self._pending_chat: mc.UserChat | None = initial_chat
21
21
 
22
- async def get_next_user_messages(self) -> mc.UserChat:
22
+ async def get_next_user_messages(self) -> 'mc.UserChat':
23
23
  ret = self._pending_chat
24
24
  self._pending_chat = None
25
25
  return ret or []
@@ -11,5 +11,5 @@ from ...... import minichain as mc
11
11
 
12
12
  class UserChatInput(lang.Abstract):
13
13
  @abc.abstractmethod
14
- def get_next_user_messages(self) -> ta.Awaitable[mc.UserChat]:
14
+ def get_next_user_messages(self) -> ta.Awaitable['mc.UserChat']:
15
15
  raise NotImplementedError
@@ -22,7 +22,8 @@ class ChatConfig:
22
22
 
23
23
  state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue'
24
24
 
25
- initial_content: mc.Content | None = None
25
+ initial_system_content: ta.Optional['mc.Content'] = None
26
+ initial_user_content: ta.Optional['mc.Content'] = None
26
27
  interactive: bool = False
27
28
 
28
29
  silent: bool = False
@@ -30,4 +31,6 @@ class ChatConfig:
30
31
 
31
32
  stream: bool = False
32
33
 
34
+ enable_tools: bool = False
35
+ enabled_tools: ta.AbstractSet[str] | None = None
33
36
  dangerous_no_tool_confirmation: bool = False
@@ -1,4 +1,5 @@
1
1
  import abc
2
+ import typing as ta
2
3
 
3
4
  from omlish import check
4
5
  from omlish import lang
@@ -11,18 +12,21 @@ from ..... import minichain as mc
11
12
 
12
13
  class MessageContentExtractor(lang.Abstract):
13
14
  @abc.abstractmethod
14
- def extract_message_content(self, message: mc.Message) -> mc.Content | None:
15
+ def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
15
16
  raise NotImplementedError
16
17
 
17
18
 
18
19
  class MessageContentExtractorImpl(MessageContentExtractor):
19
- def extract_message_content(self, message: mc.Message) -> mc.Content | None:
20
+ def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
20
21
  if isinstance(message, (mc.SystemMessage, mc.UserMessage, mc.AiMessage)):
21
22
  if message.c is not None:
22
23
  return check.isinstance(message.c, str)
23
24
  else:
24
25
  return None
25
26
 
27
+ elif isinstance(message, mc.ToolUseMessage):
28
+ return None
29
+
26
30
  elif isinstance(message, mc.ToolUseResultMessage):
27
31
  return check.isinstance(message.tur.c, str)
28
32
 
@@ -12,12 +12,12 @@ from ..... import minichain as mc
12
12
 
13
13
  class ContentStringifier(lang.Abstract):
14
14
  @abc.abstractmethod
15
- def stringify_content(self, content: mc.Content) -> str | None:
15
+ def stringify_content(self, content: 'mc.Content') -> str | None:
16
16
  raise NotImplementedError
17
17
 
18
18
 
19
19
  class ContentStringifierImpl(ContentStringifier):
20
- def stringify_content(self, content: mc.Content) -> str | None:
20
+ def stringify_content(self, content: 'mc.Content') -> str | None:
21
21
  if isinstance(content, str):
22
22
  return content
23
23
 
@@ -1,8 +1,8 @@
1
1
  from .chat.ai.types import AiChatGenerator
2
2
  from .chat.state.types import ChatStateManager
3
3
  from .chat.user.types import UserChatInput
4
- from .phases import ChatPhase
5
- from .phases import ChatPhaseManager
4
+ from .phases.manager import ChatPhaseManager
5
+ from .phases.types import ChatPhase
6
6
 
7
7
 
8
8
  ##
@@ -1,96 +1,70 @@
1
- import typing as ta
2
-
3
- from omlish import dataclasses as dc
4
1
  from omlish import inject as inj
5
2
  from omlish import lang
6
3
 
7
- from .base import ChatOption
8
- from .base import ChatOptions
9
- from .base import ChatSession
10
- from .printing import ChatSessionPrinter
11
- from .printing import MarkdownStringChatSessionPrinter
12
- from .printing import SimpleStringChatSessionPrinter
13
- from .state import ChatStateManager
14
- from .state import StateStorageChatStateManager
15
- from .tools import AskingToolExecutionConfirmation
16
- from .tools import NopToolExecutionConfirmation
17
- from .tools import ToolExecutionConfirmation
18
- from .tools import ToolUseExecutor
19
- from .tools import ToolUseExecutorImpl
20
-
21
-
22
- ##
23
-
4
+ from .configs import DEFAULT_CHAT_MODEL_BACKEND
5
+ from .configs import ChatConfig
24
6
 
25
- @dc.dataclass(frozen=True, eq=False)
26
- class _InjectedChatOptions:
27
- v: ChatOptions
28
7
 
29
-
30
- def bind_chat_options(*cos: ChatOption) -> inj.Elements:
31
- return inj.bind_set_entry_const(ta.AbstractSet[_InjectedChatOptions], _InjectedChatOptions(ChatOptions(cos)))
8
+ with lang.auto_proxy_import(globals()):
9
+ from . import driver as _driver
10
+ from .backends import inject as _backends
11
+ from .chat.ai import inject as _chat_ai
12
+ from .chat.state import inject as _chat_state
13
+ from .chat.user import inject as _chat_user
14
+ from .phases import inject as _phases
15
+ from .rendering import inject as _rendering
16
+ from .tools import inject as _tools
32
17
 
33
18
 
34
19
  ##
35
20
 
36
21
 
37
- def bind_chat_session(cfg: ChatSession.Config) -> inj.Elements:
22
+ def bind_chat(cfg: ChatConfig) -> inj.Elements:
38
23
  els: list[inj.Elemental] = []
39
24
 
40
25
  #
41
26
 
42
27
  els.extend([
43
- inj.set_binder[_InjectedChatOptions](),
44
- inj.bind(
45
- lang.typed_lambda(ChatOptions, s=ta.AbstractSet[_InjectedChatOptions])(
46
- lambda s: ChatOptions([
47
- co
48
- for ico in s
49
- for co in ico.v
50
- ]),
51
- ),
52
- singleton=True,
28
+ _backends.bind_backends(
29
+ backend=cfg.backend or DEFAULT_CHAT_MODEL_BACKEND,
53
30
  ),
54
- ])
55
31
 
56
- #
32
+ _chat_ai.bind_ai(
33
+ stream=cfg.stream,
34
+ silent=cfg.silent,
35
+ enable_tools=cfg.enable_tools,
36
+ ),
57
37
 
58
- els.extend([
59
- inj.bind(StateStorageChatStateManager, singleton=True),
60
- inj.bind(ChatStateManager, to_key=StateStorageChatStateManager),
61
- ])
38
+ _chat_user.bind_user(
39
+ initial_system_content=cfg.initial_system_content,
40
+ initial_user_content=cfg.initial_user_content,
41
+ interactive=cfg.interactive,
42
+ ),
62
43
 
63
- #
44
+ _chat_state.bind_state(
45
+ state=cfg.state,
46
+ ),
47
+
48
+ _phases.bind_phases(),
64
49
 
65
- if cfg.markdown:
66
- els.extend([
67
- inj.bind(MarkdownStringChatSessionPrinter, singleton=True),
68
- inj.bind(ChatSessionPrinter, to_key=MarkdownStringChatSessionPrinter),
69
- ])
70
- else:
71
- els.extend([
72
- inj.bind(SimpleStringChatSessionPrinter, singleton=True),
73
- inj.bind(ChatSessionPrinter, to_key=SimpleStringChatSessionPrinter),
74
- ])
50
+ _rendering.bind_rendering(
51
+ markdown=cfg.markdown,
52
+ ),
53
+ ])
75
54
 
76
55
  #
77
56
 
78
- if cfg.dangerous_no_tool_confirmation:
79
- els.extend([
80
- inj.bind(NopToolExecutionConfirmation, singleton=True),
81
- inj.bind(ToolExecutionConfirmation, to_key=NopToolExecutionConfirmation),
82
- ])
83
- else:
84
- els.extend([
85
- inj.bind(AskingToolExecutionConfirmation, singleton=True),
86
- inj.bind(ToolExecutionConfirmation, to_key=AskingToolExecutionConfirmation),
87
- ])
57
+ if cfg.enable_tools:
58
+ els.append(_tools.bind_tools(
59
+ silent=cfg.silent,
60
+ dangerous_no_confirmation=cfg.dangerous_no_tool_confirmation,
61
+ enabled_tools=cfg.enabled_tools,
62
+ ))
88
63
 
89
64
  #
90
65
 
91
66
  els.extend([
92
- inj.bind(ToolUseExecutorImpl, singleton=True),
93
- inj.bind(ToolUseExecutor, to_key=ToolUseExecutorImpl),
67
+ inj.bind(_driver.ChatDriver, singleton=True),
94
68
  ])
95
69
 
96
70
  #
@@ -0,0 +1,27 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+ from .injection import phase_callbacks
5
+
6
+
7
+ with lang.auto_proxy_import(globals()):
8
+ from . import manager as _manager
9
+
10
+
11
+ ##
12
+
13
+
14
+ def bind_phases() -> inj.Elements:
15
+ els: list[inj.Elemental] = []
16
+
17
+ #
18
+
19
+ els.append(phase_callbacks().bind_items_provider(singleton=True))
20
+
21
+ #
22
+
23
+ els.append(inj.bind(_manager.ChatPhaseManager, singleton=True))
24
+
25
+ #
26
+
27
+ return inj.as_elements(*els)
@@ -0,0 +1,14 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+
5
+ with lang.auto_proxy_import(globals()):
6
+ from . import types as _types
7
+
8
+
9
+ ##
10
+
11
+
12
+ @lang.cached_function
13
+ def phase_callbacks() -> 'inj.ItemsBinderHelper[_types.ChatPhaseCallback]':
14
+ return inj.items_binder_helper[_types.ChatPhaseCallback](_types.ChatPhaseCallbacks)
@@ -1,34 +1,8 @@
1
- import enum
2
- import typing as ta
3
-
4
1
  from omlish import check
5
2
  from omlish import collections as col
6
- from omlish import dataclasses as dc
7
-
8
-
9
- ##
10
-
11
-
12
- class ChatPhase(enum.Enum):
13
- NEW = enum.auto()
14
-
15
- STARTING = enum.auto()
16
- STARTED = enum.auto()
17
-
18
- STOPPING = enum.auto()
19
- STOPPED = enum.auto()
20
-
21
-
22
- ##
23
-
24
-
25
- @dc.dataclass(frozen=True)
26
- class ChatPhaseCallback:
27
- phase: ChatPhase = dc.xfield(validate=lambda v: v != ChatPhase.NEW)
28
- fn: ta.Callable[[], ta.Awaitable[None]] = dc.xfield()
29
-
30
3
 
31
- ChatPhaseCallbacks = ta.NewType('ChatPhaseCallbacks', ta.Sequence[ChatPhaseCallback])
4
+ from .types import ChatPhase
5
+ from .types import ChatPhaseCallbacks
32
6
 
33
7
 
34
8
  ##
@@ -0,0 +1,29 @@
1
+ import enum
2
+ import typing as ta
3
+
4
+ from omlish import dataclasses as dc
5
+
6
+
7
+ ##
8
+
9
+
10
+ class ChatPhase(enum.Enum):
11
+ NEW = enum.auto()
12
+
13
+ STARTING = enum.auto()
14
+ STARTED = enum.auto()
15
+
16
+ STOPPING = enum.auto()
17
+ STOPPED = enum.auto()
18
+
19
+
20
+ ##
21
+
22
+
23
+ @dc.dataclass(frozen=True)
24
+ class ChatPhaseCallback:
25
+ phase: ChatPhase = dc.xfield(validate=lambda v: v != ChatPhase.NEW)
26
+ fn: ta.Callable[[], ta.Awaitable[None]] = dc.xfield()
27
+
28
+
29
+ ChatPhaseCallbacks = ta.NewType('ChatPhaseCallbacks', ta.Sequence[ChatPhaseCallback])
@@ -0,0 +1,32 @@
1
+ from omlish import inject as inj
2
+ from omlish import lang
3
+
4
+
5
+ with lang.auto_proxy_import(globals()):
6
+ from . import markdown as _markdown
7
+ from . import raw as _raw
8
+ from . import types as _types
9
+
10
+
11
+ ##
12
+
13
+
14
+ def bind_rendering(
15
+ *,
16
+ markdown: bool = False,
17
+ ) -> inj.Elements:
18
+ els: list[inj.Elemental] = []
19
+
20
+ if markdown:
21
+ els.extend([
22
+ inj.bind(_types.ContentRendering, to_ctor=_markdown.MarkdownContentRendering, singleton=True),
23
+ inj.bind(_types.StreamContentRendering, to_ctor=_markdown.MarkdownStreamContentRendering, singleton=True),
24
+ ])
25
+
26
+ else:
27
+ els.extend([
28
+ inj.bind(_types.ContentRendering, to_ctor=_raw.RawContentRendering, singleton=True),
29
+ inj.bind(_types.StreamContentRendering, to_ctor=_raw.RawStreamContentRendering, singleton=True),
30
+ ])
31
+
32
+ return inj.as_elements(*els)
@@ -21,7 +21,7 @@ class MarkdownContentRendering(ContentRendering, HasContentStringifier):
21
21
  ) -> None:
22
22
  super().__init__(content_stringifier=content_stringifier)
23
23
 
24
- async def render_content(self, content: mc.Content) -> None:
24
+ async def render_content(self, content: 'mc.Content') -> None:
25
25
  if (s := self._content_stringifier.stringify_content(content)) is not None and (s := s.strip()):
26
26
  rich.Console().print(rich.Markdown(s))
27
27
 
@@ -44,7 +44,7 @@ class MarkdownStreamContentRendering(StreamContentRendering, HasContentStringifi
44
44
  async def _async_enter_contexts(self) -> None:
45
45
  self._ir = self._enter_context(rich.IncrementalMarkdownLiveStream())
46
46
 
47
- async def render_content(self, content: mc.Content) -> None:
47
+ async def render_content(self, content: 'mc.Content') -> None:
48
48
  if (s := self._owner._content_stringifier.stringify_content(content)) is not None: # noqa: SLF001
49
49
  self._ir.feed(s)
50
50
 
@@ -25,12 +25,12 @@ class RawContentRendering(ContentRendering, HasContentStringifier):
25
25
  printer = lang.as_async(print)
26
26
  self._printer = printer
27
27
 
28
- async def render_content(self, content: mc.Content) -> None:
28
+ async def render_content(self, content: 'mc.Content') -> None:
29
29
  if (s := self._content_stringifier.stringify_content(content)) is not None:
30
30
  await self._printer(s)
31
31
 
32
32
 
33
- class RawContentStreamRendering(StreamContentRendering, HasContentStringifier):
33
+ class RawStreamContentRendering(StreamContentRendering, HasContentStringifier):
34
34
  class Output(ta.Protocol):
35
35
  def write(self, s: str) -> ta.Awaitable[None]: ...
36
36
  def flush(self) -> ta.Awaitable[None]: ...
@@ -51,12 +51,12 @@ class RawContentStreamRendering(StreamContentRendering, HasContentStringifier):
51
51
  super().__init__(content_stringifier=content_stringifier)
52
52
 
53
53
  if output is None:
54
- output = RawContentStreamRendering.PrintOutput()
54
+ output = RawStreamContentRendering.PrintOutput()
55
55
  self._output = output
56
56
 
57
57
  @ta.final
58
58
  class _ContextInstance(ContentRendering, ta.AsyncContextManager):
59
- def __init__(self, owner: 'RawContentStreamRendering') -> None:
59
+ def __init__(self, owner: 'RawStreamContentRendering') -> None:
60
60
  self._owner = owner
61
61
 
62
62
  async def __aenter__(self) -> ta.Self:
@@ -65,9 +65,9 @@ class RawContentStreamRendering(StreamContentRendering, HasContentStringifier):
65
65
  async def __aexit__(self, *exc_info) -> None:
66
66
  await self._owner._output.flush() # noqa: SLF001
67
67
 
68
- async def render_content(self, content: mc.Content) -> None:
68
+ async def render_content(self, content: 'mc.Content') -> None:
69
69
  if (s := self._owner._content_stringifier.stringify_content(content)) is not None: # noqa: SLF001
70
70
  await self._owner._output.write(s) # noqa: SLF001
71
71
 
72
72
  def create_context(self) -> ta.AsyncContextManager[ContentRendering]:
73
- return RawContentStreamRendering._ContextInstance(self)
73
+ return RawStreamContentRendering._ContextInstance(self)
@@ -11,7 +11,7 @@ from ..... import minichain as mc
11
11
 
12
12
  class ContentRendering(lang.Abstract):
13
13
  @abc.abstractmethod
14
- def render_content(self, content: mc.Content) -> ta.Awaitable[None]:
14
+ def render_content(self, content: 'mc.Content') -> ta.Awaitable[None]:
15
15
  raise NotImplementedError
16
16
 
17
17
 
@@ -8,7 +8,7 @@ from .driver import ChatDriver
8
8
  ##
9
9
 
10
10
 
11
- class Chat2Session(Session['Chat2Session.Config']):
11
+ class ChatSession(Session['ChatSession.Config']):
12
12
  @dc.dataclass(frozen=True)
13
13
  class Config(Session.Config, ChatConfig):
14
14
  pass
@@ -19,8 +19,8 @@ class ToolExecutionConfirmation(lang.Abstract):
19
19
  @abc.abstractmethod
20
20
  def confirm_tool_execution_or_raise(
21
21
  self,
22
- use: mc.ToolUse,
23
- entry: mc.ToolCatalogEntry,
22
+ use: 'mc.ToolUse',
23
+ entry: 'mc.ToolCatalogEntry',
24
24
  ) -> ta.Awaitable[None]:
25
25
  raise NotImplementedError
26
26
 
@@ -31,8 +31,8 @@ class ToolExecutionConfirmation(lang.Abstract):
31
31
  class InteractiveToolExecutionConfirmation(ToolExecutionConfirmation):
32
32
  async def confirm_tool_execution_or_raise(
33
33
  self,
34
- use: mc.ToolUse,
35
- entry: mc.ToolCatalogEntry,
34
+ use: 'mc.ToolUse',
35
+ entry: 'mc.ToolCatalogEntry',
36
36
  ) -> None:
37
37
  tr_dct = dict(
38
38
  id=use.id,
@@ -11,13 +11,23 @@ from .confirmation import ToolExecutionConfirmation
11
11
  ##
12
12
 
13
13
 
14
+ class ToolContextProvider(lang.Func0[ta.Sequence[ta.Any]]):
15
+ pass
16
+
17
+
18
+ ToolContextProviders = ta.NewType('ToolContextProviders', ta.Sequence[ToolContextProvider])
19
+
20
+
21
+ ##
22
+
23
+
14
24
  class ToolUseExecutor(lang.Abstract):
15
25
  @abc.abstractmethod
16
26
  def execute_tool_use(
17
27
  self,
18
- use: mc.ToolUse,
28
+ use: 'mc.ToolUse',
19
29
  *ctx_items: ta.Any,
20
- ) -> ta.Awaitable[mc.ToolUseResultMessage]:
30
+ ) -> ta.Awaitable['mc.ToolUseResultMessage']:
21
31
  raise NotImplementedError
22
32
 
23
33
 
@@ -25,19 +35,21 @@ class ToolUseExecutorImpl(ToolUseExecutor):
25
35
  def __init__(
26
36
  self,
27
37
  *,
28
- catalog: mc.ToolCatalog,
38
+ catalog: 'mc.ToolCatalog',
39
+ ctx_provider: ToolContextProvider,
29
40
  confirmation: ToolExecutionConfirmation | None = None,
30
41
  ) -> None:
31
42
  super().__init__()
32
43
 
33
44
  self._catalog = catalog
45
+ self._ctx_provider = ctx_provider
34
46
  self._confirmation = confirmation
35
47
 
36
48
  async def execute_tool_use(
37
49
  self,
38
- use: mc.ToolUse,
50
+ use: 'mc.ToolUse',
39
51
  *ctx_items: ta.Any,
40
- ) -> mc.ToolUseResultMessage:
52
+ ) -> 'mc.ToolUseResultMessage':
41
53
  tce = self._catalog.by_name[check.non_empty_str(use.name)]
42
54
 
43
55
  if self._confirmation is not None:
@@ -46,6 +58,7 @@ class ToolUseExecutorImpl(ToolUseExecutor):
46
58
  return await mc.execute_tool_use(
47
59
  mc.ToolContext(
48
60
  use,
61
+ *self._ctx_provider(),
49
62
  *ctx_items,
50
63
  ),
51
64
  tce.executor(),