ommlds 0.0.0.dev462__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 (67) hide show
  1. ommlds/backends/mlx/loading.py +58 -1
  2. ommlds/cli/inject.py +0 -5
  3. ommlds/cli/main.py +26 -38
  4. ommlds/cli/sessions/chat/backends/catalog.py +56 -0
  5. ommlds/cli/sessions/chat/backends/inject.py +37 -0
  6. ommlds/cli/sessions/chat/backends/injection.py +16 -0
  7. ommlds/cli/sessions/chat/backends/types.py +36 -0
  8. ommlds/cli/sessions/chat/chat/__init__.py +0 -0
  9. ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
  10. ommlds/cli/sessions/chat/chat/ai/inject.py +81 -0
  11. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  12. ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
  13. ommlds/cli/sessions/chat/chat/ai/services.py +81 -0
  14. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  15. ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
  16. ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
  17. ommlds/cli/sessions/chat/chat/state/inject.py +40 -0
  18. ommlds/cli/sessions/chat/chat/state/inmemory.py +34 -0
  19. ommlds/cli/sessions/chat/chat/state/storage.py +53 -0
  20. ommlds/cli/sessions/chat/chat/state/types.py +38 -0
  21. ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
  22. ommlds/cli/sessions/chat/chat/user/inject.py +61 -0
  23. ommlds/cli/sessions/chat/chat/user/interactive.py +29 -0
  24. ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
  25. ommlds/cli/sessions/chat/chat/user/types.py +15 -0
  26. ommlds/cli/sessions/chat/configs.py +36 -0
  27. ommlds/cli/sessions/chat/content/__init__.py +0 -0
  28. ommlds/cli/sessions/chat/content/messages.py +34 -0
  29. ommlds/cli/sessions/chat/content/strings.py +42 -0
  30. ommlds/cli/sessions/chat/driver.py +43 -0
  31. ommlds/cli/sessions/chat/inject.py +40 -66
  32. ommlds/cli/sessions/chat/phases/__init__.py +0 -0
  33. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  34. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  35. ommlds/cli/sessions/chat/phases/manager.py +29 -0
  36. ommlds/cli/sessions/chat/phases/types.py +29 -0
  37. ommlds/cli/sessions/chat/rendering/__init__.py +0 -0
  38. ommlds/cli/sessions/chat/rendering/inject.py +32 -0
  39. ommlds/cli/sessions/chat/rendering/markdown.py +52 -0
  40. ommlds/cli/sessions/chat/rendering/raw.py +73 -0
  41. ommlds/cli/sessions/chat/rendering/types.py +21 -0
  42. ommlds/cli/sessions/chat/session.py +27 -0
  43. ommlds/cli/sessions/chat/tools/__init__.py +0 -0
  44. ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
  45. ommlds/cli/sessions/chat/tools/execution.py +66 -0
  46. ommlds/cli/sessions/chat/tools/inject.py +145 -0
  47. ommlds/cli/sessions/chat/tools/injection.py +29 -0
  48. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  49. ommlds/cli/{tools → sessions/chat/tools}/weather.py +1 -1
  50. ommlds/cli/sessions/inject.py +3 -3
  51. ommlds/cli/state.py +40 -23
  52. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/METADATA +3 -3
  53. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/RECORD +58 -23
  54. ommlds/cli/sessions/chat/base.py +0 -42
  55. ommlds/cli/sessions/chat/code.py +0 -125
  56. ommlds/cli/sessions/chat/interactive.py +0 -70
  57. ommlds/cli/sessions/chat/printing.py +0 -126
  58. ommlds/cli/sessions/chat/prompt.py +0 -161
  59. ommlds/cli/sessions/chat/state.py +0 -110
  60. ommlds/cli/sessions/chat/tools.py +0 -97
  61. ommlds/cli/tools/config.py +0 -14
  62. ommlds/cli/tools/inject.py +0 -75
  63. /ommlds/cli/{tools → sessions/chat/backends}/__init__.py +0 -0
  64. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/WHEEL +0 -0
  65. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/entry_points.txt +0 -0
  66. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/licenses/LICENSE +0 -0
  67. {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/top_level.txt +0 -0
@@ -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])
File without changes
@@ -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)
@@ -0,0 +1,52 @@
1
+ import typing as ta
2
+
3
+ from omdev.tui import rich
4
+ from omlish import lang
5
+
6
+ from ..... import minichain as mc
7
+ from ..content.strings import ContentStringifier
8
+ from ..content.strings import HasContentStringifier
9
+ from .types import ContentRendering
10
+ from .types import StreamContentRendering
11
+
12
+
13
+ ##
14
+
15
+
16
+ class MarkdownContentRendering(ContentRendering, HasContentStringifier):
17
+ def __init__(
18
+ self,
19
+ *,
20
+ content_stringifier: ContentStringifier | None = None,
21
+ ) -> None:
22
+ super().__init__(content_stringifier=content_stringifier)
23
+
24
+ async def render_content(self, content: 'mc.Content') -> None:
25
+ if (s := self._content_stringifier.stringify_content(content)) is not None and (s := s.strip()):
26
+ rich.Console().print(rich.Markdown(s))
27
+
28
+
29
+ class MarkdownStreamContentRendering(StreamContentRendering, HasContentStringifier):
30
+ def __init__(
31
+ self,
32
+ *,
33
+ content_stringifier: ContentStringifier | None = None,
34
+ ) -> None:
35
+ super().__init__(content_stringifier=content_stringifier)
36
+
37
+ @ta.final
38
+ class _ContextInstance(ContentRendering, lang.AsyncExitStacked):
39
+ def __init__(self, owner: 'MarkdownStreamContentRendering') -> None:
40
+ self._owner = owner
41
+
42
+ _ir: rich.MarkdownLiveStream
43
+
44
+ async def _async_enter_contexts(self) -> None:
45
+ self._ir = self._enter_context(rich.IncrementalMarkdownLiveStream())
46
+
47
+ async def render_content(self, content: 'mc.Content') -> None:
48
+ if (s := self._owner._content_stringifier.stringify_content(content)) is not None: # noqa: SLF001
49
+ self._ir.feed(s)
50
+
51
+ def create_context(self) -> ta.AsyncContextManager[ContentRendering]:
52
+ return MarkdownStreamContentRendering._ContextInstance(self)
@@ -0,0 +1,73 @@
1
+ import typing as ta
2
+
3
+ from omlish import lang
4
+
5
+ from ..... import minichain as mc
6
+ from ..content.strings import ContentStringifier
7
+ from ..content.strings import HasContentStringifier
8
+ from .types import ContentRendering
9
+ from .types import StreamContentRendering
10
+
11
+
12
+ ##
13
+
14
+
15
+ class RawContentRendering(ContentRendering, HasContentStringifier):
16
+ def __init__(
17
+ self,
18
+ *,
19
+ printer: ta.Callable[[str], ta.Awaitable[None]] | None = None,
20
+ content_stringifier: ContentStringifier | None = None,
21
+ ) -> None:
22
+ super().__init__(content_stringifier=content_stringifier)
23
+
24
+ if printer is None:
25
+ printer = lang.as_async(print)
26
+ self._printer = printer
27
+
28
+ async def render_content(self, content: 'mc.Content') -> None:
29
+ if (s := self._content_stringifier.stringify_content(content)) is not None:
30
+ await self._printer(s)
31
+
32
+
33
+ class RawStreamContentRendering(StreamContentRendering, HasContentStringifier):
34
+ class Output(ta.Protocol):
35
+ def write(self, s: str) -> ta.Awaitable[None]: ...
36
+ def flush(self) -> ta.Awaitable[None]: ...
37
+
38
+ class PrintOutput:
39
+ async def write(self, s: str) -> None:
40
+ print(s, end='', flush=True)
41
+
42
+ async def flush(self) -> None:
43
+ print(flush=True)
44
+
45
+ def __init__(
46
+ self,
47
+ *,
48
+ output: Output | None = None,
49
+ content_stringifier: ContentStringifier | None = None,
50
+ ) -> None:
51
+ super().__init__(content_stringifier=content_stringifier)
52
+
53
+ if output is None:
54
+ output = RawStreamContentRendering.PrintOutput()
55
+ self._output = output
56
+
57
+ @ta.final
58
+ class _ContextInstance(ContentRendering, ta.AsyncContextManager):
59
+ def __init__(self, owner: 'RawStreamContentRendering') -> None:
60
+ self._owner = owner
61
+
62
+ async def __aenter__(self) -> ta.Self:
63
+ return self
64
+
65
+ async def __aexit__(self, *exc_info) -> None:
66
+ await self._owner._output.flush() # noqa: SLF001
67
+
68
+ async def render_content(self, content: 'mc.Content') -> None:
69
+ if (s := self._owner._content_stringifier.stringify_content(content)) is not None: # noqa: SLF001
70
+ await self._owner._output.write(s) # noqa: SLF001
71
+
72
+ def create_context(self) -> ta.AsyncContextManager[ContentRendering]:
73
+ return RawStreamContentRendering._ContextInstance(self)
@@ -0,0 +1,21 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import lang
5
+
6
+ from ..... import minichain as mc
7
+
8
+
9
+ ##
10
+
11
+
12
+ class ContentRendering(lang.Abstract):
13
+ @abc.abstractmethod
14
+ def render_content(self, content: 'mc.Content') -> ta.Awaitable[None]:
15
+ raise NotImplementedError
16
+
17
+
18
+ class StreamContentRendering(lang.Abstract):
19
+ @abc.abstractmethod
20
+ def create_context(self) -> ta.AsyncContextManager[ContentRendering]:
21
+ raise NotImplementedError
@@ -0,0 +1,27 @@
1
+ import dataclasses as dc
2
+
3
+ from ..base import Session
4
+ from .configs import ChatConfig
5
+ from .driver import ChatDriver
6
+
7
+
8
+ ##
9
+
10
+
11
+ class ChatSession(Session['ChatSession.Config']):
12
+ @dc.dataclass(frozen=True)
13
+ class Config(Session.Config, ChatConfig):
14
+ pass
15
+
16
+ def __init__(
17
+ self,
18
+ config: Config,
19
+ *,
20
+ driver: ChatDriver,
21
+ ) -> None:
22
+ super().__init__(config)
23
+
24
+ self._driver = driver
25
+
26
+ async def run(self) -> None:
27
+ await self._driver.run()
File without changes
@@ -0,0 +1,46 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import lang
5
+ from omlish.formats import json
6
+ from omlish.term.confirm import confirm_action
7
+
8
+ from ..... import minichain as mc
9
+
10
+
11
+ ##
12
+
13
+
14
+ class ToolExecutionRequestDeniedError(Exception):
15
+ pass
16
+
17
+
18
+ class ToolExecutionConfirmation(lang.Abstract):
19
+ @abc.abstractmethod
20
+ def confirm_tool_execution_or_raise(
21
+ self,
22
+ use: 'mc.ToolUse',
23
+ entry: 'mc.ToolCatalogEntry',
24
+ ) -> ta.Awaitable[None]:
25
+ raise NotImplementedError
26
+
27
+
28
+ ##
29
+
30
+
31
+ class InteractiveToolExecutionConfirmation(ToolExecutionConfirmation):
32
+ async def confirm_tool_execution_or_raise(
33
+ self,
34
+ use: 'mc.ToolUse',
35
+ entry: 'mc.ToolCatalogEntry',
36
+ ) -> None:
37
+ tr_dct = dict(
38
+ id=use.id,
39
+ name=entry.spec.name,
40
+ args=use.args,
41
+ # spec=msh.marshal(tce.spec),
42
+ )
43
+ cr = confirm_action(f'Execute requested tool?\n\n{json.dumps_pretty(tr_dct)}') # FIXME: async lol
44
+
45
+ if not cr:
46
+ raise ToolExecutionRequestDeniedError
@@ -0,0 +1,66 @@
1
+ import abc
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish import lang
6
+
7
+ from ..... import minichain as mc
8
+ from .confirmation import ToolExecutionConfirmation
9
+
10
+
11
+ ##
12
+
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
+
24
+ class ToolUseExecutor(lang.Abstract):
25
+ @abc.abstractmethod
26
+ def execute_tool_use(
27
+ self,
28
+ use: 'mc.ToolUse',
29
+ *ctx_items: ta.Any,
30
+ ) -> ta.Awaitable['mc.ToolUseResultMessage']:
31
+ raise NotImplementedError
32
+
33
+
34
+ class ToolUseExecutorImpl(ToolUseExecutor):
35
+ def __init__(
36
+ self,
37
+ *,
38
+ catalog: 'mc.ToolCatalog',
39
+ ctx_provider: ToolContextProvider,
40
+ confirmation: ToolExecutionConfirmation | None = None,
41
+ ) -> None:
42
+ super().__init__()
43
+
44
+ self._catalog = catalog
45
+ self._ctx_provider = ctx_provider
46
+ self._confirmation = confirmation
47
+
48
+ async def execute_tool_use(
49
+ self,
50
+ use: 'mc.ToolUse',
51
+ *ctx_items: ta.Any,
52
+ ) -> 'mc.ToolUseResultMessage':
53
+ tce = self._catalog.by_name[check.non_empty_str(use.name)]
54
+
55
+ if self._confirmation is not None:
56
+ await self._confirmation.confirm_tool_execution_or_raise(use, tce)
57
+
58
+ return await mc.execute_tool_use(
59
+ mc.ToolContext(
60
+ use,
61
+ *self._ctx_provider(),
62
+ *ctx_items,
63
+ ),
64
+ tce.executor(),
65
+ use,
66
+ )
@@ -0,0 +1,145 @@
1
+ import os
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish import inject as inj
6
+ from omlish import lang
7
+
8
+ from ..... import minichain as mc
9
+ from .injection import bind_tool_context_provider_to_key
10
+ from .injection import tool_catalog_entries
11
+ from .injection import tool_context_providers
12
+
13
+
14
+ with lang.auto_proxy_import(globals()):
15
+ from . import confirmation as _confirmation
16
+ from . import execution as _execution
17
+ from . import rendering as _rendering
18
+
19
+
20
+ ##
21
+
22
+
23
+ _TOOL_BINDERS: dict[str, ta.Callable[[], inj.Elements]] = {}
24
+
25
+
26
+ def _tool_binder(name: str) -> ta.Callable[[ta.Callable[[], inj.Elements]], ta.Callable[[], inj.Elements]]:
27
+ def inner(fn):
28
+ check.not_in(name, _TOOL_BINDERS)
29
+ _TOOL_BINDERS[name] = fn
30
+ return fn
31
+ return inner
32
+
33
+
34
+ #
35
+
36
+
37
+ @_tool_binder('weather')
38
+ def _bind_weather_tool() -> inj.Elements:
39
+ from .weather import WEATHER_TOOL
40
+
41
+ return inj.as_elements(
42
+ tool_catalog_entries().bind_item_consts(WEATHER_TOOL),
43
+ )
44
+
45
+
46
+ @_tool_binder('todo')
47
+ def _bind_todo_tools() -> inj.Elements:
48
+ from .....minichain.lib.todo.context import TodoContext
49
+ from .....minichain.lib.todo.tools.read import todo_read_tool
50
+ from .....minichain.lib.todo.tools.write import todo_write_tool
51
+
52
+ return inj.as_elements(
53
+ tool_catalog_entries().bind_item_consts(
54
+ todo_read_tool(),
55
+ todo_write_tool(),
56
+ ),
57
+
58
+ inj.bind(TodoContext()),
59
+ bind_tool_context_provider_to_key(TodoContext),
60
+ )
61
+
62
+
63
+ @_tool_binder('fs')
64
+ def _bind_fs_tools() -> inj.Elements:
65
+ from .....minichain.lib.fs.context import FsContext
66
+ from .....minichain.lib.fs.tools.ls import ls_tool
67
+ from .....minichain.lib.fs.tools.read import read_tool
68
+
69
+ return inj.as_elements(
70
+ tool_catalog_entries().bind_item_consts(
71
+ ls_tool(),
72
+ read_tool(),
73
+ ),
74
+
75
+ inj.bind(FsContext(
76
+ root_dir=os.getcwd(),
77
+ )),
78
+ bind_tool_context_provider_to_key(FsContext),
79
+ )
80
+
81
+
82
+ # if tools_config.enable_unsafe_tools_do_not_use_lol:
83
+ # from ...minichain.lib.bash import bash_tool
84
+ # els.append(bind_tool(bash_tool()))
85
+ #
86
+ # from ...minichain.lib.fs.tools.edit import edit_tool
87
+ # els.append(bind_tool(edit_tool()))
88
+
89
+
90
+ ##
91
+
92
+
93
+ def bind_tools(
94
+ *,
95
+ silent: bool = False,
96
+ dangerous_no_confirmation: bool = False,
97
+ enabled_tools: ta.Iterable[str] | None = None,
98
+ ) -> inj.Elements:
99
+ els: list[inj.Elemental] = []
100
+
101
+ #
102
+
103
+ els.append(inj.bind(mc.ToolCatalog, singleton=True))
104
+
105
+ #
106
+
107
+ els.append(tool_catalog_entries().bind_items_provider(singleton=True))
108
+
109
+ for etn in check.not_isinstance(enabled_tools or [], str):
110
+ els.append(_TOOL_BINDERS[etn]())
111
+
112
+ #
113
+
114
+ exec_stack = inj.wrapper_binder_helper(_execution.ToolUseExecutor)
115
+
116
+ els.append(exec_stack.push_bind(to_ctor=_execution.ToolUseExecutorImpl, singleton=True))
117
+
118
+ if not silent:
119
+ els.append(exec_stack.push_bind(to_ctor=_rendering.ResultRenderingToolUseExecutor, singleton=True))
120
+
121
+ if dangerous_no_confirmation:
122
+ els.append(exec_stack.push_bind(to_ctor=_rendering.ArgsRenderingToolUseExecutor, singleton=True))
123
+
124
+ els.extend([
125
+ inj.bind(_execution.ToolUseExecutor, to_key=exec_stack.top),
126
+ ])
127
+
128
+ #
129
+
130
+ if not dangerous_no_confirmation:
131
+ els.append(inj.bind(_confirmation.ToolExecutionConfirmation, to_ctor=_confirmation.InteractiveToolExecutionConfirmation, singleton=True)) # noqa
132
+
133
+ #
134
+
135
+ els.extend([
136
+ tool_context_providers().bind_items_provider(singleton=True),
137
+
138
+ inj.bind(_execution.ToolContextProvider, to_fn=lang.typed_lambda(tcps=_execution.ToolContextProviders)(
139
+ lambda tcps: _execution.ToolContextProvider(lambda: [tc for tcp in tcps for tc in tcp()]),
140
+ ), singleton=True),
141
+ ])
142
+
143
+ #
144
+
145
+ return inj.as_elements(*els)
@@ -0,0 +1,29 @@
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
+
8
+
9
+ with lang.auto_proxy_import(globals()):
10
+ from . import execution as _execution
11
+
12
+ ##
13
+
14
+
15
+ @lang.cached_function
16
+ def tool_catalog_entries() -> 'inj.ItemsBinderHelper[mc.ToolCatalogEntry]':
17
+ return inj.items_binder_helper[mc.ToolCatalogEntry](mc.ToolCatalogEntries)
18
+
19
+
20
+ @lang.cached_function
21
+ def tool_context_providers() -> 'inj.ItemsBinderHelper[_execution.ToolContextProvider]':
22
+ return inj.items_binder_helper[_execution.ToolContextProvider](_execution.ToolContextProviders)
23
+
24
+
25
+ def bind_tool_context_provider_to_key(key: ta.Any) -> inj.Elements:
26
+ return tool_context_providers().bind_item(to_fn=inj.KwargsTarget.of(
27
+ lambda v: _execution.ToolContextProvider(lambda: [v]),
28
+ v=key,
29
+ ), singleton=True)
@@ -0,0 +1,58 @@
1
+ import typing as ta
2
+
3
+ from ..... import minichain as mc
4
+ from ..rendering.types import ContentRendering
5
+ from .execution import ToolUseExecutor
6
+
7
+
8
+ ##
9
+
10
+
11
+ class ArgsRenderingToolUseExecutor(ToolUseExecutor):
12
+ def __init__(
13
+ self,
14
+ *,
15
+ wrapped: ToolUseExecutor,
16
+ renderer: ContentRendering,
17
+ ) -> None:
18
+ super().__init__()
19
+
20
+ self._wrapped = wrapped
21
+ self._renderer = renderer
22
+
23
+ async def execute_tool_use(
24
+ self,
25
+ use: 'mc.ToolUse',
26
+ *ctx_items: ta.Any,
27
+ ) -> 'mc.ToolUseResultMessage':
28
+ await self._renderer.render_content(mc.JsonContent(dict(
29
+ id=use.id,
30
+ name=use.name,
31
+ args=use.args,
32
+ )))
33
+
34
+ return await self._wrapped.execute_tool_use(use, *ctx_items)
35
+
36
+
37
+ class ResultRenderingToolUseExecutor(ToolUseExecutor):
38
+ def __init__(
39
+ self,
40
+ *,
41
+ wrapped: ToolUseExecutor,
42
+ renderer: ContentRendering,
43
+ ) -> None:
44
+ super().__init__()
45
+
46
+ self._wrapped = wrapped
47
+ self._renderer = renderer
48
+
49
+ async def execute_tool_use(
50
+ self,
51
+ use: 'mc.ToolUse',
52
+ *ctx_items: ta.Any,
53
+ ) -> 'mc.ToolUseResultMessage':
54
+ out = await self._wrapped.execute_tool_use(use, *ctx_items)
55
+
56
+ await self._renderer.render_content(out.tur.c)
57
+
58
+ return out
@@ -1,4 +1,4 @@
1
- from ... import minichain as mc
1
+ from ..... import minichain as mc
2
2
 
3
3
 
4
4
  ##
@@ -1,8 +1,7 @@
1
1
  from omlish import inject as inj
2
2
 
3
3
  from .base import Session
4
- from .chat.base import ChatSession
5
- from .chat.inject import bind_chat_session
4
+ from .chat.session import ChatSession
6
5
 
7
6
 
8
7
  ##
@@ -16,6 +15,7 @@ def bind_sessions(session_cfg: Session.Config) -> inj.Elements:
16
15
  ]
17
16
 
18
17
  if isinstance(session_cfg, ChatSession.Config):
19
- els.append(bind_chat_session(session_cfg))
18
+ from .chat.inject import bind_chat
19
+ els.append(bind_chat(session_cfg)) # noqa
20
20
 
21
21
  return inj.as_elements(*els)