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.
- ommlds/cli/inject.py +0 -5
- ommlds/cli/main.py +26 -54
- ommlds/cli/sessions/{chat2 → chat}/backends/catalog.py +5 -5
- ommlds/cli/sessions/chat/backends/inject.py +37 -0
- ommlds/cli/sessions/chat/backends/injection.py +16 -0
- ommlds/cli/sessions/{chat2 → chat}/backends/types.py +3 -3
- ommlds/cli/sessions/chat/chat/ai/inject.py +81 -0
- ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
- ommlds/cli/sessions/{chat2 → chat}/chat/ai/rendering.py +13 -10
- ommlds/cli/sessions/chat/chat/ai/services.py +81 -0
- ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
- ommlds/cli/sessions/{chat2 → chat}/chat/ai/types.py +5 -5
- ommlds/cli/sessions/chat/chat/state/inject.py +40 -0
- ommlds/cli/sessions/{chat2 → chat}/chat/state/inmemory.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/chat/state/storage.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/chat/state/types.py +1 -1
- ommlds/cli/sessions/chat/chat/user/inject.py +61 -0
- ommlds/cli/sessions/{chat2 → chat}/chat/user/interactive.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/chat/user/oneshot.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/chat/user/types.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/configs.py +4 -1
- ommlds/cli/sessions/{chat2 → chat}/content/messages.py +6 -2
- ommlds/cli/sessions/{chat2 → chat}/content/strings.py +2 -2
- ommlds/cli/sessions/{chat2 → chat}/driver.py +2 -2
- ommlds/cli/sessions/chat/inject.py +40 -66
- ommlds/cli/sessions/chat/phases/inject.py +27 -0
- ommlds/cli/sessions/chat/phases/injection.py +14 -0
- ommlds/cli/sessions/{chat2/phases.py → chat/phases/manager.py} +2 -28
- ommlds/cli/sessions/chat/phases/types.py +29 -0
- ommlds/cli/sessions/chat/rendering/inject.py +32 -0
- ommlds/cli/sessions/{chat2 → chat}/rendering/markdown.py +2 -2
- ommlds/cli/sessions/{chat2 → chat}/rendering/raw.py +6 -6
- ommlds/cli/sessions/{chat2 → chat}/rendering/types.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/session.py +1 -1
- ommlds/cli/sessions/{chat2 → chat}/tools/confirmation.py +4 -4
- ommlds/cli/sessions/{chat2 → chat}/tools/execution.py +18 -5
- 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 +2 -7
- {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/RECORD +56 -54
- 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/sessions/chat2/_inject.py +0 -105
- ommlds/cli/sessions/chat2/chat/ai/services.py +0 -70
- ommlds/cli/sessions/chat2/inject.py +0 -143
- ommlds/cli/tools/__init__.py +0 -0
- ommlds/cli/tools/config.py +0 -14
- ommlds/cli/tools/inject.py +0 -75
- /ommlds/cli/sessions/{chat2 → chat/backends}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/backends → chat/chat}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/chat → chat/chat/ai}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/chat/ai → chat/chat/state}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/chat/state → chat/chat/user}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/chat/user → chat/content}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2/content → chat/phases}/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2 → chat}/rendering/__init__.py +0 -0
- /ommlds/cli/sessions/{chat2 → chat}/tools/__init__.py +0 -0
- {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev463.dist-info → ommlds-0.0.0.dev464.dist-info}/licenses/LICENSE +0 -0
- {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 []
|
|
@@ -22,7 +22,8 @@ class ChatConfig:
|
|
|
22
22
|
|
|
23
23
|
state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue'
|
|
24
24
|
|
|
25
|
-
|
|
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
|
|
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
|
|
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
|
|
5
|
-
from .phases import
|
|
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 .
|
|
8
|
-
from .
|
|
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
|
-
|
|
31
|
-
|
|
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
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
79
|
-
els.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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: '
|
|
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
|
|
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
|
|
|
@@ -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(),
|