ommlds 0.0.0.dev464__py3-none-any.whl → 0.0.0.dev465__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.

@@ -18,7 +18,7 @@
18
18
  "module": ".minichain.backends.impls.anthropic.chat",
19
19
  "attr": null,
20
20
  "file": "ommlds/minichain/backends/impls/anthropic/chat.py",
21
- "line": 39,
21
+ "line": 38,
22
22
  "value": {
23
23
  "!.minichain.registries.manifests.RegistryManifest": {
24
24
  "module": "ommlds.minichain.backends.impls.anthropic.chat",
@@ -63,7 +63,7 @@
63
63
  "module": ".minichain.backends.impls.anthropic.stream",
64
64
  "attr": null,
65
65
  "file": "ommlds/minichain/backends/impls/anthropic/stream.py",
66
- "line": 33,
66
+ "line": 35,
67
67
  "value": {
68
68
  "!.minichain.registries.manifests.RegistryManifest": {
69
69
  "module": "ommlds.minichain.backends.impls.anthropic.stream",
ommlds/cli/inject.py CHANGED
@@ -1,28 +1,13 @@
1
- import os.path
1
+ import typing as ta
2
2
 
3
- from omdev.home.paths import get_home_paths
4
3
  from omlish import inject as inj
5
4
  from omlish import lang
6
5
 
7
6
 
8
7
  with lang.auto_proxy_import(globals()):
9
- from . import state
10
- from .backends import inject as backends_inj
11
- from .sessions import base as sessions_base
12
- from .sessions import inject as sessions_inj
13
-
14
-
15
- ##
16
-
17
-
18
- def _provide_state_storage() -> 'state.StateStorage':
19
- state_dir = os.path.join(get_home_paths().state_dir, 'minichain', 'cli')
20
- if not os.path.exists(state_dir):
21
- os.makedirs(state_dir, exist_ok=True)
22
- os.chmod(state_dir, 0o770) # noqa
23
-
24
- state_file = os.path.join(state_dir, 'state.json')
25
- return state.JsonFileStateStorage(state_file)
8
+ from .backends import inject as _backends
9
+ from .sessions import inject as _sessions
10
+ from .state import inject as _state
26
11
 
27
12
 
28
13
  ##
@@ -30,20 +15,22 @@ def _provide_state_storage() -> 'state.StateStorage':
30
15
 
31
16
  def bind_main(
32
17
  *,
33
- session_cfg: 'sessions_base.Session.Config',
18
+ session_cfg: ta.Any,
34
19
  enable_backend_strings: bool = False,
35
20
  ) -> inj.Elements:
36
- els: list[inj.Elemental] = [
37
- backends_inj.bind_backends(
21
+ els: list[inj.Elemental] = []
22
+
23
+ #
24
+
25
+ els.extend([
26
+ _backends.bind_backends(
38
27
  enable_backend_strings=enable_backend_strings,
39
28
  ),
40
29
 
41
- sessions_inj.bind_sessions(session_cfg),
42
- ]
43
-
44
- #
30
+ _sessions.bind_sessions(session_cfg),
45
31
 
46
- els.append(inj.bind(_provide_state_storage, singleton=True))
32
+ _state.bind_state(),
33
+ ])
47
34
 
48
35
  #
49
36
 
ommlds/cli/main.py CHANGED
@@ -24,9 +24,9 @@ from omlish.subprocesses.sync import subprocesses
24
24
  from .. import minichain as mc
25
25
  from .inject import bind_main
26
26
  from .sessions.base import Session
27
- from .sessions.chat.session import ChatSession
28
- from .sessions.completion.completion import CompletionSession
29
- from .sessions.embedding.embedding import EmbeddingSession
27
+ from .sessions.chat.configs import ChatConfig
28
+ from .sessions.completion.configs import CompletionConfig
29
+ from .sessions.embedding.configs import EmbeddingConfig
30
30
 
31
31
 
32
32
  if ta.TYPE_CHECKING:
@@ -125,30 +125,33 @@ async def _a_main(args: ta.Any = None) -> None:
125
125
 
126
126
  #
127
127
 
128
- session_cfg: Session.Config
128
+ session_cfg: ta.Any
129
129
 
130
130
  if args.embed:
131
- session_cfg = EmbeddingSession.Config(
131
+ session_cfg = EmbeddingConfig(
132
132
  check.not_none(content), # noqa
133
133
  backend=args.backend,
134
134
  )
135
135
 
136
136
  elif args.completion:
137
- session_cfg = CompletionSession.Config(
137
+ session_cfg = CompletionConfig(
138
138
  check.not_none(content), # noqa
139
139
  backend=args.backend,
140
140
  )
141
141
 
142
142
  else:
143
- from ..minichain.lib.code.prompts import CODE_AGENT_SYSTEM_PROMPT
143
+ system_content: mc.Content | None = None
144
+ if (args.new or args.ephemeral) and args.code:
145
+ from ..minichain.lib.code.prompts import CODE_AGENT_SYSTEM_PROMPT
146
+ system_content = CODE_AGENT_SYSTEM_PROMPT
144
147
 
145
- session_cfg = ChatSession.Config(
148
+ session_cfg = ChatConfig(
146
149
  backend=args.backend,
147
150
  model_name=args.model_name,
148
151
  state='ephemeral' if args.ephemeral else 'new' if args.new else 'continue',
149
- initial_system_content=CODE_AGENT_SYSTEM_PROMPT if args.new and args.code else None,
152
+ initial_system_content=system_content,
150
153
  initial_user_content=content, # noqa
151
- interactive=args.interactive,
154
+ interactive=bool(args.interactive),
152
155
  markdown=bool(args.markdown),
153
156
  stream=bool(args.stream),
154
157
  enable_tools=(
@@ -171,7 +174,7 @@ async def _a_main(args: ta.Any = None) -> None:
171
174
 
172
175
  with inj.create_managed_injector(bind_main(
173
176
  session_cfg=session_cfg,
174
- enable_backend_strings=isinstance(session_cfg, ChatSession.Config),
177
+ enable_backend_strings=isinstance(session_cfg, ChatConfig),
175
178
  )) as injector:
176
179
  await injector[Session].run()
177
180
 
@@ -36,34 +36,31 @@ def bind_ai(
36
36
 
37
37
  #
38
38
 
39
+ ai_stack = inj.wrapper_binder_helper(_types.AiChatGenerator)
40
+
39
41
  if stream:
40
- ai_stack = inj.wrapper_binder_helper(_types.StreamAiChatGenerator)
42
+ stream_ai_stack = inj.wrapper_binder_helper(_types.StreamAiChatGenerator)
41
43
 
42
- els.append(ai_stack.push_bind(to_ctor=_services.ChatChoicesStreamServiceStreamAiChatGenerator, singleton=True))
44
+ els.append(stream_ai_stack.push_bind(to_ctor=_services.ChatChoicesStreamServiceStreamAiChatGenerator, singleton=True)) # noqa
43
45
 
44
46
  if not silent:
45
- els.append(ai_stack.push_bind(to_ctor=_rendering.RenderingStreamAiChatGenerator, singleton=True))
46
-
47
- if enable_tools:
48
- raise NotImplementedError
47
+ els.append(stream_ai_stack.push_bind(to_ctor=_rendering.RenderingStreamAiChatGenerator, singleton=True))
49
48
 
50
49
  els.extend([
51
- inj.bind(_types.StreamAiChatGenerator, to_key=ai_stack.top),
52
- inj.bind(_types.AiChatGenerator, to_key=_types.StreamAiChatGenerator),
50
+ inj.bind(_types.StreamAiChatGenerator, to_key=stream_ai_stack.top),
51
+ ai_stack.push_bind(to_key=_types.StreamAiChatGenerator),
53
52
  ])
54
53
 
55
54
  else:
56
- ai_stack = inj.wrapper_binder_helper(_types.AiChatGenerator)
57
-
58
55
  els.append(ai_stack.push_bind(to_ctor=_services.ChatChoicesServiceAiChatGenerator, singleton=True))
59
56
 
60
57
  if not silent:
61
58
  els.append(ai_stack.push_bind(to_ctor=_rendering.RenderingAiChatGenerator, singleton=True))
62
59
 
63
- if enable_tools:
64
- els.append(ai_stack.push_bind(to_ctor=_tools.ToolExecutingAiChatGenerator, singleton=True))
60
+ if enable_tools:
61
+ els.append(ai_stack.push_bind(to_ctor=_tools.ToolExecutingAiChatGenerator, singleton=True))
65
62
 
66
- els.append(inj.bind(_types.AiChatGenerator, to_key=ai_stack.top))
63
+ els.append(inj.bind(_types.AiChatGenerator, to_key=ai_stack.top))
67
64
 
68
65
  #
69
66
 
@@ -4,7 +4,7 @@ from omlish import check
4
4
  from omlish import lang
5
5
 
6
6
  from ...... import minichain as mc
7
- from .....state import StateStorage
7
+ from .....state.storage import StateStorage
8
8
  from .types import ChatState
9
9
  from .types import ChatStateManager
10
10
 
@@ -1,12 +1,15 @@
1
+ from omlish import dataclasses as dc
1
2
  from omlish import inject as inj
2
3
  from omlish import lang
3
4
 
5
+ from ..base import Session
4
6
  from .configs import DEFAULT_CHAT_MODEL_BACKEND
5
7
  from .configs import ChatConfig
6
8
 
7
9
 
8
10
  with lang.auto_proxy_import(globals()):
9
11
  from . import driver as _driver
12
+ from . import session as _session
10
13
  from .backends import inject as _backends
11
14
  from .chat.ai import inject as _chat_ai
12
15
  from .chat.state import inject as _chat_state
@@ -69,4 +72,11 @@ def bind_chat(cfg: ChatConfig) -> inj.Elements:
69
72
 
70
73
  #
71
74
 
75
+ els.extend([
76
+ inj.bind(_session.ChatSession.Config(**dc.asdict(cfg))),
77
+ inj.bind(Session, to_ctor=_session.ChatSession, singleton=True),
78
+ ])
79
+
80
+ #
81
+
72
82
  return inj.as_elements(*els)
@@ -0,0 +1,21 @@
1
+ import dataclasses as dc
2
+
3
+ from .... import minichain as mc
4
+
5
+
6
+ ##
7
+
8
+
9
+ DEFAULT_COMPLETION_MODEL_BACKEND = 'openai'
10
+
11
+
12
+ ##
13
+
14
+
15
+ @dc.dataclass(frozen=True)
16
+ class CompletionConfig:
17
+ content: 'mc.Content'
18
+
19
+ _: dc.KW_ONLY
20
+
21
+ backend: str | None = None
@@ -0,0 +1,28 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import inject as inj
3
+ from omlish import lang
4
+
5
+ from ..base import Session
6
+ from .configs import CompletionConfig
7
+
8
+
9
+ with lang.auto_proxy_import(globals()):
10
+ from . import session as _session
11
+
12
+
13
+ ##
14
+
15
+
16
+ def bind_completion(cfg: CompletionConfig) -> inj.Elements:
17
+ els: list[inj.Elemental] = []
18
+
19
+ #
20
+
21
+ els.extend([
22
+ inj.bind(_session.CompletionSession.Config(**dc.asdict(cfg))),
23
+ inj.bind(Session, to_ctor=_session.CompletionSession, singleton=True),
24
+ ])
25
+
26
+ #
27
+
28
+ return inj.as_elements(*els)
@@ -5,22 +5,17 @@ from omlish import lang
5
5
 
6
6
  from .... import minichain as mc
7
7
  from ..base import Session
8
+ from .configs import DEFAULT_COMPLETION_MODEL_BACKEND
9
+ from .configs import CompletionConfig
8
10
 
9
11
 
10
12
  ##
11
13
 
12
14
 
13
- DEFAULT_COMPLETION_MODEL_BACKEND = 'openai'
14
-
15
-
16
15
  class CompletionSession(Session['CompletionSession.Config']):
17
16
  @dc.dataclass(frozen=True)
18
- class Config(Session.Config):
19
- content: mc.Content
20
-
21
- _: dc.KW_ONLY
22
-
23
- backend: str | None = None
17
+ class Config(Session.Config, CompletionConfig):
18
+ pass
24
19
 
25
20
  def __init__(
26
21
  self,
@@ -0,0 +1,21 @@
1
+ import dataclasses as dc
2
+
3
+ from .... import minichain as mc
4
+
5
+
6
+ ##
7
+
8
+
9
+ DEFAULT_EMBEDDING_MODEL_BACKEND = 'openai'
10
+
11
+
12
+ ##
13
+
14
+
15
+ @dc.dataclass(frozen=True)
16
+ class EmbeddingConfig:
17
+ content: 'mc.Content'
18
+
19
+ _: dc.KW_ONLY
20
+
21
+ backend: str | None = None
@@ -0,0 +1,28 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import inject as inj
3
+ from omlish import lang
4
+
5
+ from ..base import Session
6
+ from .configs import EmbeddingConfig
7
+
8
+
9
+ with lang.auto_proxy_import(globals()):
10
+ from . import session as _session
11
+
12
+
13
+ ##
14
+
15
+
16
+ def bind_embedding(cfg: EmbeddingConfig) -> inj.Elements:
17
+ els: list[inj.Elemental] = []
18
+
19
+ #
20
+
21
+ els.extend([
22
+ inj.bind(_session.EmbeddingSession.Config(**dc.asdict(cfg))),
23
+ inj.bind(Session, to_ctor=_session.EmbeddingSession, singleton=True),
24
+ ])
25
+
26
+ #
27
+
28
+ return inj.as_elements(*els)
@@ -5,22 +5,17 @@ from omlish.formats import json
5
5
 
6
6
  from .... import minichain as mc
7
7
  from ..base import Session
8
+ from .configs import DEFAULT_EMBEDDING_MODEL_BACKEND
9
+ from .configs import EmbeddingConfig
8
10
 
9
11
 
10
12
  ##
11
13
 
12
14
 
13
- DEFAULT_EMBEDDING_MODEL_BACKEND = 'openai'
14
-
15
-
16
15
  class EmbeddingSession(Session['EmbeddingSession.Config']):
17
16
  @dc.dataclass(frozen=True)
18
- class Config(Session.Config):
19
- content: mc.Content
20
-
21
- _: dc.KW_ONLY
22
-
23
- backend: str | None = None
17
+ class Config(Session.Config, EmbeddingConfig):
18
+ pass
24
19
 
25
20
  def __init__(
26
21
  self,
@@ -1,21 +1,38 @@
1
+ import typing as ta
2
+
1
3
  from omlish import inject as inj
4
+ from omlish import lang
5
+
2
6
 
3
- from .base import Session
4
- from .chat.session import ChatSession
7
+ with lang.auto_proxy_import(globals()):
8
+ from .chat import configs as _chat_cfgs
9
+ from .chat import inject as _chat_inj
10
+ from .completion import configs as _completion_cfgs
11
+ from .completion import inject as _completion_inj
12
+ from .embedding import configs as _embedding_cfgs
13
+ from .embedding import inject as _embedding_inj
5
14
 
6
15
 
7
16
  ##
8
17
 
9
18
 
10
- def bind_sessions(session_cfg: Session.Config) -> inj.Elements:
11
- els: list[inj.Elemental] = [
12
- inj.bind(session_cfg),
13
- inj.bind(session_cfg.configurable_cls, singleton=True),
14
- inj.bind(Session, to_key=session_cfg.configurable_cls),
15
- ]
19
+ def bind_sessions(cfg: ta.Any) -> inj.Elements:
20
+ els: list[inj.Elemental] = []
21
+
22
+ #
23
+
24
+ if isinstance(cfg, _chat_cfgs.ChatConfig):
25
+ els.append(_chat_inj.bind_chat(cfg))
26
+
27
+ elif isinstance(cfg, _completion_cfgs.CompletionConfig):
28
+ els.append(_completion_inj.bind_completion(cfg))
29
+
30
+ elif isinstance(cfg, _embedding_cfgs.EmbeddingConfig):
31
+ els.append(_embedding_inj.bind_embedding(cfg))
32
+
33
+ else:
34
+ raise TypeError(cfg)
16
35
 
17
- if isinstance(session_cfg, ChatSession.Config):
18
- from .chat.inject import bind_chat
19
- els.append(bind_chat(session_cfg)) # noqa
36
+ #
20
37
 
21
38
  return inj.as_elements(*els)
File without changes
@@ -0,0 +1,28 @@
1
+ import os.path
2
+
3
+ from omdev.home.paths import get_home_paths
4
+ from omlish import inject as inj
5
+ from omlish import lang
6
+
7
+
8
+ with lang.auto_proxy_import(globals()):
9
+ from . import storage as _storage
10
+
11
+
12
+ ##
13
+
14
+
15
+ def _provide_state_storage() -> '_storage.StateStorage':
16
+ state_dir = os.path.join(get_home_paths().state_dir, 'minichain', 'cli')
17
+ if not os.path.exists(state_dir):
18
+ os.makedirs(state_dir, exist_ok=True)
19
+ os.chmod(state_dir, 0o770) # noqa
20
+
21
+ state_file = os.path.join(state_dir, 'state.json')
22
+ return _storage.JsonFileStateStorage(state_file)
23
+
24
+
25
+ def bind_state() -> inj.Elements:
26
+ return inj.as_elements(
27
+ inj.bind(_provide_state_storage, singleton=True),
28
+ )
@@ -22,15 +22,14 @@ from ....chat.messages import AnyAiMessage
22
22
  from ....chat.messages import Message
23
23
  from ....chat.messages import SystemMessage
24
24
  from ....chat.messages import ToolUseMessage
25
- from ....chat.messages import ToolUseResultMessage
26
25
  from ....chat.messages import UserMessage
27
26
  from ....chat.tools.types import Tool
28
- from ....content.prepare import prepare_content_str
29
27
  from ....models.configs import ModelName
30
28
  from ....standard import ApiKey
31
- from ....tools.jsonschema import build_tool_spec_params_json_schema
32
29
  from ....tools.types import ToolUse
33
30
  from .names import MODEL_NAMES
31
+ from .protocol import build_protocol_chat_messages
32
+ from .protocol import build_protocol_tool
34
33
 
35
34
 
36
35
  ##
@@ -44,13 +43,6 @@ from .names import MODEL_NAMES
44
43
  class AnthropicChatChoicesService:
45
44
  DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(MODEL_NAMES.default))
46
45
 
47
- ROLES_MAP: ta.ClassVar[ta.Mapping[type[Message], str]] = {
48
- SystemMessage: 'system',
49
- UserMessage: 'user',
50
- AiMessage: 'assistant',
51
- ToolUseMessage: 'assistant',
52
- }
53
-
54
46
  def __init__(
55
47
  self,
56
48
  *configs: ApiKey | ModelName,
@@ -78,62 +70,13 @@ class AnthropicChatChoicesService:
78
70
  *,
79
71
  max_tokens: int = 4096, # FIXME: ChatOption
80
72
  ) -> ChatChoicesResponse:
81
- messages: list[pt.Message] = []
82
- system: list[pt.Content] | None = None
83
- for i, m in enumerate(request.v):
84
- if isinstance(m, SystemMessage):
85
- if i != 0 or system is not None:
86
- raise Exception('Only supports one system message and must be first')
87
- system = [pt.Text(check.not_none(self._get_msg_content(m)))]
88
-
89
- elif isinstance(m, ToolUseResultMessage):
90
- messages.append(pt.Message(
91
- role='user',
92
- content=[pt.ToolResult(
93
- tool_use_id=check.not_none(m.tur.id),
94
- content=json.dumps_compact(msh.marshal(m.tur.c)) if not isinstance(m.tur.c, str) else m.tur.c,
95
- )],
96
- ))
97
-
98
- elif isinstance(m, AiMessage):
99
- # messages.append(pt.Message(
100
- # role=self.ROLES_MAP[type(m)], # noqa
101
- # content=[pt.Text(check.isinstance(self._get_msg_content(m), str))],
102
- # ))
103
- messages.append(pt.Message(
104
- role='assistant',
105
- content=[
106
- *([pt.Text(check.isinstance(m.c, str))] if m.c is not None else []),
107
- ],
108
- ))
109
-
110
- elif isinstance(m, ToolUseMessage):
111
- messages.append(pt.Message(
112
- role='assistant',
113
- content=[
114
- pt.ToolUse(
115
- id=check.not_none(m.tu.id),
116
- name=check.not_none(m.tu.name),
117
- input=m.tu.args,
118
- ),
119
- ],
120
- ))
121
-
122
- else:
123
- messages.append(pt.Message(
124
- role=self.ROLES_MAP[type(m)], # type: ignore[arg-type]
125
- content=[pt.Text(check.isinstance(self._get_msg_content(m), str))],
126
- ))
73
+ messages, system = build_protocol_chat_messages(request.v)
127
74
 
128
75
  tools: list[pt.ToolSpec] = []
129
76
  with tv.TypedValues(*request.options).consume() as oc:
130
77
  t: Tool
131
78
  for t in oc.pop(Tool, []):
132
- tools.append(pt.ToolSpec(
133
- name=check.not_none(t.spec.name),
134
- description=prepare_content_str(t.spec.desc),
135
- input_schema=build_tool_spec_params_json_schema(t.spec),
136
- ))
79
+ tools.append(build_protocol_tool(t))
137
80
 
138
81
  a_req = pt.MessagesRequest(
139
82
  model=MODEL_NAMES.resolve(self._model_name.v),
@@ -0,0 +1,109 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import marshal as msh
5
+ from omlish.formats import json
6
+
7
+ from .....backends.anthropic.protocol import types as pt
8
+ from ....chat.messages import AiMessage
9
+ from ....chat.messages import Message
10
+ from ....chat.messages import SystemMessage
11
+ from ....chat.messages import ToolUseMessage
12
+ from ....chat.messages import ToolUseResultMessage
13
+ from ....chat.messages import UserMessage
14
+ from ....chat.tools.types import Tool
15
+ from ....content.prepare import prepare_content_str
16
+ from ....tools.jsonschema import build_tool_spec_params_json_schema
17
+
18
+
19
+ ##
20
+
21
+
22
+ def get_message_content(m: Message) -> str | None:
23
+ if isinstance(m, AiMessage):
24
+ return check.isinstance(m.c, str)
25
+
26
+ elif isinstance(m, (UserMessage, SystemMessage)):
27
+ return check.isinstance(m.c, str)
28
+
29
+ else:
30
+ raise TypeError(m)
31
+
32
+
33
+ #
34
+
35
+
36
+ class BuiltChatMessages(ta.NamedTuple):
37
+ messages: list[pt.Message]
38
+ system: list[pt.Content] | None
39
+
40
+
41
+ ROLES_MAP: ta.Mapping[type[Message], str] = {
42
+ SystemMessage: 'system',
43
+ UserMessage: 'user',
44
+ AiMessage: 'assistant',
45
+ ToolUseMessage: 'assistant',
46
+ }
47
+
48
+
49
+ def build_protocol_chat_messages(msgs: ta.Iterable[Message]) -> BuiltChatMessages:
50
+ messages: list[pt.Message] = []
51
+ system: list[pt.Content] | None = None
52
+
53
+ for i, m in enumerate(msgs):
54
+ if isinstance(m, SystemMessage):
55
+ if i or system is not None:
56
+ raise Exception('Only supports one system message and must be first')
57
+ system = [pt.Text(check.not_none(get_message_content(m)))]
58
+
59
+ elif isinstance(m, ToolUseResultMessage):
60
+ messages.append(pt.Message(
61
+ role='user',
62
+ content=[pt.ToolResult(
63
+ tool_use_id=check.not_none(m.tur.id),
64
+ content=json.dumps_compact(msh.marshal(m.tur.c)) if not isinstance(m.tur.c, str) else m.tur.c,
65
+ )],
66
+ ))
67
+
68
+ elif isinstance(m, AiMessage):
69
+ # messages.append(pt.Message(
70
+ # role=ROLES_MAP[type(m)], # noqa
71
+ # content=[pt.Text(check.isinstance(get_message_content(m), str))],
72
+ # ))
73
+ messages.append(pt.Message(
74
+ role='assistant',
75
+ content=[
76
+ *([pt.Text(check.isinstance(m.c, str))] if m.c is not None else []),
77
+ ],
78
+ ))
79
+
80
+ elif isinstance(m, ToolUseMessage):
81
+ messages.append(pt.Message(
82
+ role='assistant',
83
+ content=[
84
+ pt.ToolUse(
85
+ id=check.not_none(m.tu.id),
86
+ name=check.not_none(m.tu.name),
87
+ input=m.tu.args,
88
+ ),
89
+ ],
90
+ ))
91
+
92
+ else:
93
+ messages.append(pt.Message(
94
+ role=ROLES_MAP[type(m)], # type: ignore[arg-type]
95
+ content=[pt.Text(check.isinstance(get_message_content(m), str))],
96
+ ))
97
+
98
+ return BuiltChatMessages(messages, system)
99
+
100
+
101
+ ##
102
+
103
+
104
+ def build_protocol_tool(t: Tool) -> pt.ToolSpec:
105
+ return pt.ToolSpec(
106
+ name=check.not_none(t.spec.name),
107
+ description=prepare_content_str(t.spec.desc),
108
+ input_schema=build_tool_spec_params_json_schema(t.spec),
109
+ )
@@ -1,7 +1,6 @@
1
1
  import typing as ta
2
2
 
3
3
  from omlish import check
4
- from omlish import lang
5
4
  from omlish import marshal as msh
6
5
  from omlish import typedvalues as tv
7
6
  from omlish.formats import json
@@ -9,15 +8,16 @@ from omlish.http import all as http
9
8
  from omlish.http import sse
10
9
  from omlish.io.buffers import DelimitingBuffer
11
10
 
11
+ from .....backends.anthropic.protocol import types as pt
12
12
  from .....backends.anthropic.protocol.sse.events import AnthropicSseDecoderEvents
13
13
  from ....chat.choices.services import ChatChoicesOutputs
14
- from ....chat.messages import SystemMessage
15
14
  from ....chat.stream.services import ChatChoicesStreamRequest
16
15
  from ....chat.stream.services import ChatChoicesStreamResponse
17
16
  from ....chat.stream.services import static_check_is_chat_choices_stream_service
18
17
  from ....chat.stream.types import AiChoiceDeltas
19
18
  from ....chat.stream.types import AiChoicesDeltas
20
19
  from ....chat.stream.types import ContentAiChoiceDelta
20
+ from ....chat.tools.types import Tool
21
21
  from ....configs import Config
22
22
  from ....resources import UseResources
23
23
  from ....standard import ApiKey
@@ -25,6 +25,8 @@ from ....stream.services import StreamResponseSink
25
25
  from ....stream.services import new_stream_response
26
26
  from .chat import AnthropicChatChoicesService
27
27
  from .names import MODEL_NAMES
28
+ from .protocol import build_protocol_chat_messages
29
+ from .protocol import build_protocol_tool
28
30
 
29
31
 
30
32
  ##
@@ -51,27 +53,25 @@ class AnthropicChatChoicesStreamService:
51
53
  *,
52
54
  max_tokens: int = 4096, # FIXME: ChatOption
53
55
  ) -> ChatChoicesStreamResponse:
54
- messages = []
55
- system: str | None = None
56
- for i, m in enumerate(request.v):
57
- if isinstance(m, SystemMessage):
58
- if i != 0 or system is not None:
59
- raise Exception('Only supports one system message and must be first')
60
- system = AnthropicChatChoicesService._get_msg_content(m) # noqa
61
- else:
62
- messages.append(dict(
63
- role=AnthropicChatChoicesService.ROLES_MAP[type(m)], # noqa
64
- content=check.isinstance(AnthropicChatChoicesService._get_msg_content(m), str), # noqa
65
- ))
66
-
67
- raw_request = dict(
56
+ messages, system = build_protocol_chat_messages(request.v)
57
+
58
+ tools: list[pt.ToolSpec] = []
59
+ with tv.TypedValues(*request.options).consume() as oc:
60
+ t: Tool
61
+ for t in oc.pop(Tool, []):
62
+ tools.append(build_protocol_tool(t))
63
+
64
+ a_req = pt.MessagesRequest(
68
65
  model=MODEL_NAMES.resolve(self._model_name.v),
69
- **lang.opt_kw(system=system),
66
+ system=system,
70
67
  messages=messages,
68
+ tools=tools or None,
71
69
  max_tokens=max_tokens,
72
70
  stream=True,
73
71
  )
74
72
 
73
+ raw_request = msh.marshal(a_req)
74
+
75
75
  http_request = http.HttpRequest(
76
76
  'https://api.anthropic.com/v1/messages',
77
77
  headers={
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev464
3
+ Version: 0.0.0.dev465
4
4
  Summary: ommlds
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omdev==0.0.0.dev464
18
- Requires-Dist: omlish==0.0.0.dev464
17
+ Requires-Dist: omdev==0.0.0.dev465
18
+ Requires-Dist: omlish==0.0.0.dev465
19
19
  Provides-Extra: all
20
20
  Requires-Dist: llama-cpp-python~=0.3; extra == "all"
21
21
  Requires-Dist: mlx~=0.29; extra == "all"
@@ -1,4 +1,4 @@
1
- ommlds/.omlish-manifests.json,sha256=5W7s8h2xvV_Y2YA1m4hYz0949Yb5XoDNTFAQCrvrDlQ,17930
1
+ ommlds/.omlish-manifests.json,sha256=5HdEXx21B_B9DSFeEoQ1teSsanB_NOU5F2o5yjrAtKI,17930
2
2
  ommlds/__about__.py,sha256=uAJgr2I_m_oZPlV5P8XLFeYpBlEM-DdzeyF6O5OK_qs,1759
3
3
  ommlds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ommlds/huggingface.py,sha256=JfEyfKOxU3-SY_ojtXBJFNeD-NIuKjvMe3GL3e93wNA,1175
@@ -77,19 +77,18 @@ ommlds/backends/torch/devices.py,sha256=KWkeyArPdUwVqckQTJPkN-4GQdv39cpOgCMv_Xfk
77
77
  ommlds/backends/torch/purge.py,sha256=sp6XUxNLoVCepxIPKw3tevHn-cQqgorILvIQzixauiI,1834
78
78
  ommlds/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  ommlds/cli/__main__.py,sha256=1ffCb0fcUOJMzxROJmJRXQ8PSOVYv7KrcuBtT95cf0c,140
80
- ommlds/cli/inject.py,sha256=QEuipqBPsET3rA0aO2c8hKCU77kgWwMfWktW8cGDkyw,1160
81
- ommlds/cli/main.py,sha256=dCSmhu0Xk6thP6pu_nmo1kp-WiR4aC1RBOfG0Y8BKjI,5534
82
- ommlds/cli/state.py,sha256=tRPmgCANRrw7A5Qr700OaH58F6S96O37I8Ivrbo7_gI,3001
80
+ ommlds/cli/inject.py,sha256=WhTDabJz9b1NRRHVH-UyVN5nj6UncvIeTvgkGrcE9vc,666
81
+ ommlds/cli/main.py,sha256=rOsdlBHHiF5ZjMBXSNojXFyiWin7pN6RJMZUwGEyXVs,5608
83
82
  ommlds/cli/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
83
  ommlds/cli/backends/inject.py,sha256=OVstNsoeVnprM9PBL_zP0N46KkoDg3_Wz90BWcQ7km4,1734
85
84
  ommlds/cli/backends/standard.py,sha256=HnammWyAXJHeqXJrAMBdarcT4Nyt2CxudZdD2fW_Y9M,631
86
85
  ommlds/cli/sessions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
86
  ommlds/cli/sessions/base.py,sha256=oTqsqZ9jhBWFblANpVWLLIzmRfP8HO9QYtPnZ-GZxS0,452
88
- ommlds/cli/sessions/inject.py,sha256=oDiEuBkchaBKHVTJcd11v4P_pSurhrvwhbaAVCcUhOM,548
87
+ ommlds/cli/sessions/inject.py,sha256=9SrtsozIhqok3jZtepKTJwpOxHkU7FrqKw6pc78mEO4,926
89
88
  ommlds/cli/sessions/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
89
  ommlds/cli/sessions/chat/configs.py,sha256=7NtpOaSyjABKLuJ1jAbp1lCT1tPjvPVJ_vakbzvuk8g,692
91
90
  ommlds/cli/sessions/chat/driver.py,sha256=ddnCYTKqWiPxV8U4UbFwb7E3yi81ItjZ9j3AJd3a3Mk,1395
92
- ommlds/cli/sessions/chat/inject.py,sha256=tlZ2kmS6PxLbGzgKhbtUyzUw2uyeZ9TpWt0QFdgmAOM,1656
91
+ ommlds/cli/sessions/chat/inject.py,sha256=7Yg6wUs2Oej4UjNZCAWCJCEsDJZWvT4G8XvkvVUMC7U,1928
93
92
  ommlds/cli/sessions/chat/session.py,sha256=eqwelLE74JFC-fBpk_hdwMD2nP4pLv3ZPwUn99200B8,521
94
93
  ommlds/cli/sessions/chat/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
94
  ommlds/cli/sessions/chat/backends/catalog.py,sha256=GroogxBQf_zlrhEEwTSq18v13HWsuXOP276VcuphPic,1756
@@ -98,7 +97,7 @@ ommlds/cli/sessions/chat/backends/injection.py,sha256=GCn5OvNIEowgB70kQVuU84z3i8
98
97
  ommlds/cli/sessions/chat/backends/types.py,sha256=5eImYHXLKqbC5MDrN443eMGamP9snCmV1n7LtAsqgPk,696
99
98
  ommlds/cli/sessions/chat/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
99
  ommlds/cli/sessions/chat/chat/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
- ommlds/cli/sessions/chat/chat/ai/inject.py,sha256=Uq1zDpFj3cZqWns2gfMu1Oun4ssbfzVB1JfpUdwh8wc,2572
100
+ ommlds/cli/sessions/chat/chat/ai/inject.py,sha256=FCq-E8auVJ-gnK18NoKiINGBHgDllroZRG7z5ZY63L8,2514
102
101
  ommlds/cli/sessions/chat/chat/ai/injection.py,sha256=2O_ELMusxQsYaB2oqvzjYpZQzjshocJup7Z5unzoUIY,404
103
102
  ommlds/cli/sessions/chat/chat/ai/rendering.py,sha256=cicA6NwkM9UJb1dgsakhkhIMWGzBsArPFvYoINBmuec,2285
104
103
  ommlds/cli/sessions/chat/chat/ai/services.py,sha256=RNO3emXEFPr0M04wThRQo332x0n5R3n8aQFctcePCHQ,2629
@@ -107,7 +106,7 @@ ommlds/cli/sessions/chat/chat/ai/types.py,sha256=QH6Cn6DrdxBi0Tnura7Fq-WtGUm0Fds
107
106
  ommlds/cli/sessions/chat/chat/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
107
  ommlds/cli/sessions/chat/chat/state/inject.py,sha256=usHhiE1fKtttJvxgVN-LuwIsfyau_0n17-99bDuPNnw,1131
109
108
  ommlds/cli/sessions/chat/chat/state/inmemory.py,sha256=2lSCWnNEH_vj9RJUVzM8huAHAXBJ6Go02og2nzew1hk,861
110
- ommlds/cli/sessions/chat/chat/state/storage.py,sha256=IabpjwrD89kX7s_hcKjpumu0He0jMmK-uOig1RmAxIA,1428
109
+ ommlds/cli/sessions/chat/chat/state/storage.py,sha256=Q7OOjZKjvkRbiEWIVFKG66xxrxHqFFzx0IcL5cNJrG4,1436
111
110
  ommlds/cli/sessions/chat/chat/state/types.py,sha256=iMovPXnRvZJ8ieM5gPnTBi1X7j-9GUtziiPBZJJf034,794
112
111
  ommlds/cli/sessions/chat/chat/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
112
  ommlds/cli/sessions/chat/chat/user/inject.py,sha256=x0DV0kMxEiHVTjQwsQlW4wKHfACu0Nlnhbsc4Uum_wI,2164
@@ -135,9 +134,16 @@ ommlds/cli/sessions/chat/tools/injection.py,sha256=xsVbJAKsDIP-2LbzDXZ3LBJgeY0FJ
135
134
  ommlds/cli/sessions/chat/tools/rendering.py,sha256=a9gIhxuGF9tfXGlkGXpdweSz4dacIsxoqJNKpRSvUN4,1402
136
135
  ommlds/cli/sessions/chat/tools/weather.py,sha256=vZXOHdRGGlkDEXasW5foc58MyEMOc79bhIfIgrumRlo,302
137
136
  ommlds/cli/sessions/completion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
138
- ommlds/cli/sessions/completion/completion.py,sha256=2ZrzmHBCF3mG13ABcoiHva6OUzPpdF7qzByteaLNsxk,1077
137
+ ommlds/cli/sessions/completion/configs.py,sha256=KeOmvNrq6KkI96FaRRPnpWNx3yDlD-FgaiOwPEL9HPU,245
138
+ ommlds/cli/sessions/completion/inject.py,sha256=qpyp2Ro0sPOIOssk-7eXOseKG1Fu_vf1zvyLjZi5qIE,564
139
+ ommlds/cli/sessions/completion/session.py,sha256=YLq9beHxfgCAu8t9Xj54qQxXj8ebwqrcN8JsJ3_2KOw,1067
139
140
  ommlds/cli/sessions/embedding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
- ommlds/cli/sessions/embedding/embedding.py,sha256=9U5Maj-XI5nsrk7m-O3P2rZggey0z2p7onZn2QfQe38,1051
141
+ ommlds/cli/sessions/embedding/configs.py,sha256=WwWwJ7GpUYUEOPcYr2EhNLV8m1cK6llp5N_bMnFX_cw,243
142
+ ommlds/cli/sessions/embedding/inject.py,sha256=XYjqDT5GajZ0LNks36aZRNNKhH1m-xfAL1bbMxfISYQ,559
143
+ ommlds/cli/sessions/embedding/session.py,sha256=SccMICjbS1BL7wMkDzd17d2TWRjbzAmMwFn_CjC2-a8,1039
144
+ ommlds/cli/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
145
+ ommlds/cli/state/inject.py,sha256=-wqovpgxLYBpJkkCzscn2o6F6AD-agF8PoNjVac6FuQ,702
146
+ ommlds/cli/state/storage.py,sha256=tRPmgCANRrw7A5Qr700OaH58F6S96O37I8Ivrbo7_gI,3001
141
147
  ommlds/datasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
148
  ommlds/datasets/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
143
149
  ommlds/datasets/lib/movies.py,sha256=LmdfoXsZU9XMM_r-sxCLv_s06BFzwWO4xUj6sc9XVcI,1961
@@ -163,9 +169,10 @@ ommlds/minichain/backends/impls/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
163
169
  ommlds/minichain/backends/impls/mistral.py,sha256=I_HTwXqAoQi2xyw_nLTeUamtOZLLl-oQZ234AVJZZLA,2649
164
170
  ommlds/minichain/backends/impls/sqlite.py,sha256=NOFm_fgr-OZ8mo7etj0zwvxsDnidRwKzhdDom58e6ks,2157
165
171
  ommlds/minichain/backends/impls/anthropic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
- ommlds/minichain/backends/impls/anthropic/chat.py,sha256=S1Z1v4Wl7YEcdDj-XmCLaZ4d7tn3k10f59O-m34IPOg,6554
172
+ ommlds/minichain/backends/impls/anthropic/chat.py,sha256=LVaRP_Pbz4UghSHD8GZ22hMWE4Rsd_6bSysgl2BR_AM,4166
167
173
  ommlds/minichain/backends/impls/anthropic/names.py,sha256=GPPeYt0CcDcDCR8I6BMd7bMjC_Zk_bjnLLpF9ClwXcg,1099
168
- ommlds/minichain/backends/impls/anthropic/stream.py,sha256=znEBkTn3Yh8KjV3xVDIaUcbs-_PTAeabJSKBSnzsSYQ,7825
174
+ ommlds/minichain/backends/impls/anthropic/protocol.py,sha256=whPVYuKShKiMCzasHl77sCIiymhzXj8mFZXEyhZvld8,3292
175
+ ommlds/minichain/backends/impls/anthropic/stream.py,sha256=agET8PPbKg-7gYL3FGyU45J-QqV65VkEo15ipScEzUA,7667
169
176
  ommlds/minichain/backends/impls/duckduckgo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
177
  ommlds/minichain/backends/impls/duckduckgo/search.py,sha256=igzeU9P9b1MMiu4KAJVS9H6KLIoPm68wXi4Kx3_DHyQ,940
171
178
  ommlds/minichain/backends/impls/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -360,9 +367,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
360
367
  ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
361
368
  ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
362
369
  ommlds/wiki/utils/xml.py,sha256=vVV8Ctn13aaRM9eYfs9Wd6rHn5WOCEUzQ44fIhOvJdg,3754
363
- ommlds-0.0.0.dev464.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
364
- ommlds-0.0.0.dev464.dist-info/METADATA,sha256=tgOdigb0BtGCCBTpaDgfW0Rog8sK9jpRUYW1jsIxMxo,3224
365
- ommlds-0.0.0.dev464.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
366
- ommlds-0.0.0.dev464.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
367
- ommlds-0.0.0.dev464.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
368
- ommlds-0.0.0.dev464.dist-info/RECORD,,
370
+ ommlds-0.0.0.dev465.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
371
+ ommlds-0.0.0.dev465.dist-info/METADATA,sha256=JAD5TLj7Vqeese-787MMUhAcQFykg29h_upNHQ5XUFA,3224
372
+ ommlds-0.0.0.dev465.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
373
+ ommlds-0.0.0.dev465.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
374
+ ommlds-0.0.0.dev465.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
375
+ ommlds-0.0.0.dev465.dist-info/RECORD,,
File without changes