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.
- ommlds/backends/mlx/loading.py +58 -1
- ommlds/cli/inject.py +0 -5
- ommlds/cli/main.py +26 -38
- ommlds/cli/sessions/chat/backends/catalog.py +56 -0
- ommlds/cli/sessions/chat/backends/inject.py +37 -0
- ommlds/cli/sessions/chat/backends/injection.py +16 -0
- ommlds/cli/sessions/chat/backends/types.py +36 -0
- ommlds/cli/sessions/chat/chat/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/ai/inject.py +81 -0
- ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
- ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
- ommlds/cli/sessions/chat/chat/ai/services.py +81 -0
- ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
- ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
- ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/state/inject.py +40 -0
- ommlds/cli/sessions/chat/chat/state/inmemory.py +34 -0
- ommlds/cli/sessions/chat/chat/state/storage.py +53 -0
- ommlds/cli/sessions/chat/chat/state/types.py +38 -0
- ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/user/inject.py +61 -0
- ommlds/cli/sessions/chat/chat/user/interactive.py +29 -0
- ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
- ommlds/cli/sessions/chat/chat/user/types.py +15 -0
- ommlds/cli/sessions/chat/configs.py +36 -0
- ommlds/cli/sessions/chat/content/__init__.py +0 -0
- ommlds/cli/sessions/chat/content/messages.py +34 -0
- ommlds/cli/sessions/chat/content/strings.py +42 -0
- ommlds/cli/sessions/chat/driver.py +43 -0
- ommlds/cli/sessions/chat/inject.py +40 -66
- ommlds/cli/sessions/chat/phases/__init__.py +0 -0
- ommlds/cli/sessions/chat/phases/inject.py +27 -0
- ommlds/cli/sessions/chat/phases/injection.py +14 -0
- ommlds/cli/sessions/chat/phases/manager.py +29 -0
- ommlds/cli/sessions/chat/phases/types.py +29 -0
- ommlds/cli/sessions/chat/rendering/__init__.py +0 -0
- ommlds/cli/sessions/chat/rendering/inject.py +32 -0
- ommlds/cli/sessions/chat/rendering/markdown.py +52 -0
- ommlds/cli/sessions/chat/rendering/raw.py +73 -0
- ommlds/cli/sessions/chat/rendering/types.py +21 -0
- ommlds/cli/sessions/chat/session.py +27 -0
- ommlds/cli/sessions/chat/tools/__init__.py +0 -0
- ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
- ommlds/cli/sessions/chat/tools/execution.py +66 -0
- ommlds/cli/sessions/chat/tools/inject.py +145 -0
- ommlds/cli/sessions/chat/tools/injection.py +29 -0
- ommlds/cli/sessions/chat/tools/rendering.py +58 -0
- ommlds/cli/{tools → sessions/chat/tools}/weather.py +1 -1
- ommlds/cli/sessions/inject.py +3 -3
- ommlds/cli/state.py +40 -23
- {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/RECORD +58 -23
- ommlds/cli/sessions/chat/base.py +0 -42
- ommlds/cli/sessions/chat/code.py +0 -125
- ommlds/cli/sessions/chat/interactive.py +0 -70
- ommlds/cli/sessions/chat/printing.py +0 -126
- ommlds/cli/sessions/chat/prompt.py +0 -161
- ommlds/cli/sessions/chat/state.py +0 -110
- ommlds/cli/sessions/chat/tools.py +0 -97
- ommlds/cli/tools/config.py +0 -14
- ommlds/cli/tools/inject.py +0 -75
- /ommlds/cli/{tools → sessions/chat/backends}/__init__.py +0 -0
- {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev462.dist-info → ommlds-0.0.0.dev464.dist-info}/licenses/LICENSE +0 -0
- {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
|
ommlds/cli/sessions/inject.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from omlish import inject as inj
|
|
2
2
|
|
|
3
3
|
from .base import Session
|
|
4
|
-
from .chat.
|
|
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
|
-
|
|
18
|
+
from .chat.inject import bind_chat
|
|
19
|
+
els.append(bind_chat(session_cfg)) # noqa
|
|
20
20
|
|
|
21
21
|
return inj.as_elements(*els)
|