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,40 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import inject as inj
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ...phases.injection import phase_callbacks
|
|
7
|
+
from ...phases.types import ChatPhase
|
|
8
|
+
from ...phases.types import ChatPhaseCallback
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
with lang.auto_proxy_import(globals()):
|
|
12
|
+
from . import inmemory as _inmemory
|
|
13
|
+
from . import storage as _storage
|
|
14
|
+
from . import types as _types
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def bind_state(
|
|
21
|
+
*,
|
|
22
|
+
state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue',
|
|
23
|
+
) -> inj.Elements:
|
|
24
|
+
els: list[inj.Elemental] = []
|
|
25
|
+
|
|
26
|
+
if state in ('continue', 'new'):
|
|
27
|
+
els.append(inj.bind(_types.ChatStateManager, to_ctor=_storage.StateStorageChatStateManager, singleton=True))
|
|
28
|
+
|
|
29
|
+
if state == 'new':
|
|
30
|
+
els.append(phase_callbacks().bind_item(to_fn=lang.typed_lambda(cm=_types.ChatStateManager)(
|
|
31
|
+
lambda cm: ChatPhaseCallback(ChatPhase.STARTING, cm.clear_state),
|
|
32
|
+
)))
|
|
33
|
+
|
|
34
|
+
elif state == 'ephemeral':
|
|
35
|
+
els.append(inj.bind(_types.ChatStateManager, to_ctor=_inmemory.InMemoryChatStateManager, singleton=True))
|
|
36
|
+
|
|
37
|
+
else:
|
|
38
|
+
raise TypeError(state)
|
|
39
|
+
|
|
40
|
+
return inj.as_elements(*els)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
|
|
3
|
+
from omlish import lang
|
|
4
|
+
|
|
5
|
+
from ...... import minichain as mc
|
|
6
|
+
from .types import ChatState
|
|
7
|
+
from .types import ChatStateManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InMemoryChatStateManager(ChatStateManager):
|
|
14
|
+
def __init__(self, initial_state: ChatState | None = None) -> None:
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
if initial_state is None:
|
|
18
|
+
initial_state = ChatState()
|
|
19
|
+
self._state = initial_state
|
|
20
|
+
|
|
21
|
+
async def get_state(self) -> ChatState:
|
|
22
|
+
return self._state
|
|
23
|
+
|
|
24
|
+
async def clear_state(self) -> ChatState:
|
|
25
|
+
self._state = ChatState()
|
|
26
|
+
return self._state
|
|
27
|
+
|
|
28
|
+
async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
|
|
29
|
+
self._state = dc.replace(
|
|
30
|
+
self._state,
|
|
31
|
+
chat=[*self._state.chat, *chat_additions],
|
|
32
|
+
updated_at=lang.utcnow(),
|
|
33
|
+
)
|
|
34
|
+
return self._state
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ...... import minichain as mc
|
|
7
|
+
from .....state import StateStorage
|
|
8
|
+
from .types import ChatState
|
|
9
|
+
from .types import ChatStateManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StateStorageChatStateManager(ChatStateManager):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
storage: StateStorage,
|
|
20
|
+
key: str = 'chat',
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__()
|
|
23
|
+
|
|
24
|
+
self._storage = storage
|
|
25
|
+
self._key = check.non_empty_str(key)
|
|
26
|
+
|
|
27
|
+
self._state: ChatState | None = None
|
|
28
|
+
|
|
29
|
+
async def get_state(self) -> ChatState:
|
|
30
|
+
if self._state is not None:
|
|
31
|
+
return self._state
|
|
32
|
+
state: ChatState | None = await self._storage.load_state(self._key, ChatState)
|
|
33
|
+
if state is None:
|
|
34
|
+
state = ChatState()
|
|
35
|
+
self._state = state
|
|
36
|
+
return state
|
|
37
|
+
|
|
38
|
+
async def clear_state(self) -> ChatState:
|
|
39
|
+
state = ChatState()
|
|
40
|
+
await self._storage.save_state(self._key, state, ChatState)
|
|
41
|
+
self._state = state
|
|
42
|
+
return state
|
|
43
|
+
|
|
44
|
+
async def extend_chat(self, chat_additions: 'mc.Chat') -> ChatState:
|
|
45
|
+
state = await self.get_state()
|
|
46
|
+
state = dc.replace(
|
|
47
|
+
state,
|
|
48
|
+
chat=[*state.chat, *chat_additions],
|
|
49
|
+
updated_at=lang.utcnow(),
|
|
50
|
+
)
|
|
51
|
+
await self._storage.save_state(self._key, state, ChatState)
|
|
52
|
+
self._state = state
|
|
53
|
+
return state
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import dataclasses as dc
|
|
3
|
+
import datetime
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
from omlish import lang
|
|
7
|
+
|
|
8
|
+
from ...... import minichain as mc
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dc.dataclass(frozen=True)
|
|
15
|
+
class ChatState:
|
|
16
|
+
name: str | None = None
|
|
17
|
+
|
|
18
|
+
created_at: datetime.datetime = dc.field(default_factory=lang.utcnow)
|
|
19
|
+
updated_at: datetime.datetime = dc.field(default_factory=lang.utcnow)
|
|
20
|
+
|
|
21
|
+
chat: mc.Chat = ()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ChatStateManager(lang.Abstract):
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def get_state(self) -> ta.Awaitable[ChatState]:
|
|
30
|
+
raise NotImplementedError
|
|
31
|
+
|
|
32
|
+
@abc.abstractmethod
|
|
33
|
+
def clear_state(self) -> ta.Awaitable[ChatState]:
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
@abc.abstractmethod
|
|
37
|
+
def extend_chat(self, chat_additions: 'mc.Chat') -> ta.Awaitable[ChatState]:
|
|
38
|
+
raise NotImplementedError
|
|
File without changes
|
|
@@ -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)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ...... import minichain as mc
|
|
7
|
+
from .types import UserChatInput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InteractiveUserChatInput(UserChatInput):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
string_input: ta.Callable[[], ta.Awaitable[str]] | None = None,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
20
|
+
if string_input is None:
|
|
21
|
+
string_input = lang.as_async(functools.partial(input, '> '))
|
|
22
|
+
self._string_input = string_input
|
|
23
|
+
|
|
24
|
+
async def get_next_user_messages(self) -> 'mc.UserChat':
|
|
25
|
+
try:
|
|
26
|
+
s = await self._string_input()
|
|
27
|
+
except EOFError:
|
|
28
|
+
return []
|
|
29
|
+
return [mc.UserMessage(s)]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from ...... import minichain as mc
|
|
4
|
+
from .types import UserChatInput
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
OneshotUserChatInputInitialChat = ta.NewType('OneshotUserChatInputInitialChat', mc.UserChat)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OneshotUserChatInput(UserChatInput):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
initial_chat: OneshotUserChatInputInitialChat,
|
|
17
|
+
) -> None:
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
20
|
+
self._pending_chat: mc.UserChat | None = initial_chat
|
|
21
|
+
|
|
22
|
+
async def get_next_user_messages(self) -> 'mc.UserChat':
|
|
23
|
+
ret = self._pending_chat
|
|
24
|
+
self._pending_chat = None
|
|
25
|
+
return ret or []
|
|
@@ -0,0 +1,15 @@
|
|
|
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 UserChatInput(lang.Abstract):
|
|
13
|
+
@abc.abstractmethod
|
|
14
|
+
def get_next_user_messages(self) -> ta.Awaitable['mc.UserChat']:
|
|
15
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from .... import minichain as mc
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
DEFAULT_CHAT_MODEL_BACKEND = 'openai'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
##
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dc.dataclass(frozen=True)
|
|
17
|
+
class ChatConfig:
|
|
18
|
+
_: dc.KW_ONLY
|
|
19
|
+
|
|
20
|
+
backend: str | None = None
|
|
21
|
+
model_name: str | None = None
|
|
22
|
+
|
|
23
|
+
state: ta.Literal['new', 'continue', 'ephemeral'] = 'continue'
|
|
24
|
+
|
|
25
|
+
initial_system_content: ta.Optional['mc.Content'] = None
|
|
26
|
+
initial_user_content: ta.Optional['mc.Content'] = None
|
|
27
|
+
interactive: bool = False
|
|
28
|
+
|
|
29
|
+
silent: bool = False
|
|
30
|
+
markdown: bool = False
|
|
31
|
+
|
|
32
|
+
stream: bool = False
|
|
33
|
+
|
|
34
|
+
enable_tools: bool = False
|
|
35
|
+
enabled_tools: ta.AbstractSet[str] | None = None
|
|
36
|
+
dangerous_no_tool_confirmation: bool = False
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MessageContentExtractor(lang.Abstract):
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MessageContentExtractorImpl(MessageContentExtractor):
|
|
20
|
+
def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
|
|
21
|
+
if isinstance(message, (mc.SystemMessage, mc.UserMessage, mc.AiMessage)):
|
|
22
|
+
if message.c is not None:
|
|
23
|
+
return check.isinstance(message.c, str)
|
|
24
|
+
else:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
elif isinstance(message, mc.ToolUseMessage):
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
elif isinstance(message, mc.ToolUseResultMessage):
|
|
31
|
+
return check.isinstance(message.tur.c, str)
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
raise TypeError(message)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
from omlish.formats import json
|
|
6
|
+
|
|
7
|
+
from ..... import minichain as mc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContentStringifier(lang.Abstract):
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def stringify_content(self, content: 'mc.Content') -> str | None:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ContentStringifierImpl(ContentStringifier):
|
|
20
|
+
def stringify_content(self, content: 'mc.Content') -> str | None:
|
|
21
|
+
if isinstance(content, str):
|
|
22
|
+
return content
|
|
23
|
+
|
|
24
|
+
elif isinstance(content, mc.JsonContent):
|
|
25
|
+
return json.dumps_pretty(content.v)
|
|
26
|
+
|
|
27
|
+
else:
|
|
28
|
+
raise TypeError(content)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HasContentStringifier(lang.Abstract):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
*args: ta.Any,
|
|
35
|
+
content_stringifier: ContentStringifier | None = None,
|
|
36
|
+
**kwargs: ta.Any,
|
|
37
|
+
) -> None:
|
|
38
|
+
super().__init__(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
if content_stringifier is None:
|
|
41
|
+
content_stringifier = ContentStringifierImpl()
|
|
42
|
+
self._content_stringifier = content_stringifier
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .chat.ai.types import AiChatGenerator
|
|
2
|
+
from .chat.state.types import ChatStateManager
|
|
3
|
+
from .chat.user.types import UserChatInput
|
|
4
|
+
from .phases.manager import ChatPhaseManager
|
|
5
|
+
from .phases.types import ChatPhase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChatDriver:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
*,
|
|
15
|
+
phases: ChatPhaseManager,
|
|
16
|
+
ai_chat_generator: AiChatGenerator,
|
|
17
|
+
user_chat_input: UserChatInput,
|
|
18
|
+
chat_state_manager: ChatStateManager,
|
|
19
|
+
):
|
|
20
|
+
super().__init__()
|
|
21
|
+
|
|
22
|
+
self._phases = phases
|
|
23
|
+
self._ai_chat_generator = ai_chat_generator
|
|
24
|
+
self._user_chat_input = user_chat_input
|
|
25
|
+
self._chat_state_manager = chat_state_manager
|
|
26
|
+
|
|
27
|
+
async def run(self) -> None:
|
|
28
|
+
await self._phases.set_phase(ChatPhase.STARTING)
|
|
29
|
+
await self._phases.set_phase(ChatPhase.STARTED)
|
|
30
|
+
|
|
31
|
+
while True:
|
|
32
|
+
next_user_chat = await self._user_chat_input.get_next_user_messages()
|
|
33
|
+
if not next_user_chat:
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
prev_user_chat = (await self._chat_state_manager.get_state()).chat
|
|
37
|
+
|
|
38
|
+
next_ai_chat = await self._ai_chat_generator.get_next_ai_messages([*prev_user_chat, *next_user_chat])
|
|
39
|
+
|
|
40
|
+
await self._chat_state_manager.extend_chat([*next_user_chat, *next_ai_chat])
|
|
41
|
+
|
|
42
|
+
await self._phases.set_phase(ChatPhase.STOPPING)
|
|
43
|
+
await self._phases.set_phase(ChatPhase.STOPPED)
|
|
@@ -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
|
#
|
|
File without changes
|
|
@@ -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)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from omlish import check
|
|
2
|
+
from omlish import collections as col
|
|
3
|
+
|
|
4
|
+
from .types import ChatPhase
|
|
5
|
+
from .types import ChatPhaseCallbacks
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChatPhaseManager:
|
|
12
|
+
def __init__(self, callbacks: ChatPhaseCallbacks) -> None:
|
|
13
|
+
super().__init__()
|
|
14
|
+
|
|
15
|
+
self._callbacks = callbacks
|
|
16
|
+
self._callbacks_by_phase = col.multi_map_by(lambda cb: cb.phase, callbacks)
|
|
17
|
+
|
|
18
|
+
check.state(not self._callbacks_by_phase.get(ChatPhase.NEW))
|
|
19
|
+
|
|
20
|
+
self._phase = ChatPhase.NEW
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def phase(self) -> ChatPhase:
|
|
24
|
+
return self._phase
|
|
25
|
+
|
|
26
|
+
async def set_phase(self, phase: ChatPhase) -> None:
|
|
27
|
+
self._phase = phase
|
|
28
|
+
for cb in self._callbacks_by_phase.get(phase, ()):
|
|
29
|
+
await cb.fn()
|