ommlds 0.0.0.dev476__py3-none-any.whl → 0.0.0.dev477__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.
Files changed (27) hide show
  1. ommlds/.omlish-manifests.json +3 -3
  2. ommlds/backends/groq/protocol.py +69 -4
  3. ommlds/cli/sessions/chat/tools/configs.py +9 -0
  4. ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
  5. ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
  6. ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
  7. ommlds/cli/sessions/chat/tools/inject.py +11 -64
  8. ommlds/cli/sessions/chat/tools/injection.py +14 -0
  9. ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
  10. ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
  11. ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
  12. ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
  13. ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
  14. ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
  15. ommlds/cli/sessions/chat/tools/{weather.py → weather/tools.py} +1 -1
  16. ommlds/minichain/backends/impls/groq/chat.py +11 -5
  17. ommlds/minichain/backends/impls/groq/names.py +13 -0
  18. ommlds/minichain/backends/impls/groq/protocol.py +118 -21
  19. ommlds/minichain/backends/impls/groq/stream.py +10 -6
  20. ommlds/minichain/chat/stream/joining.py +1 -0
  21. ommlds/minichain/tools/reflect.py +5 -1
  22. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/METADATA +3 -3
  23. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/RECORD +27 -18
  24. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/WHEEL +0 -0
  25. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/entry_points.txt +0 -0
  26. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/licenses/LICENSE +0 -0
  27. {ommlds-0.0.0.dev476.dist-info → ommlds-0.0.0.dev477.dist-info}/top_level.txt +0 -0
@@ -198,7 +198,7 @@
198
198
  "module": ".minichain.backends.impls.groq.chat",
199
199
  "attr": null,
200
200
  "file": "ommlds/minichain/backends/impls/groq/chat.py",
201
- "line": 24,
201
+ "line": 26,
202
202
  "value": {
203
203
  "!.minichain.registries.manifests.RegistryManifest": {
204
204
  "module": "ommlds.minichain.backends.impls.groq.chat",
@@ -213,7 +213,7 @@
213
213
  "module": ".minichain.backends.impls.groq.names",
214
214
  "attr": "_BACKEND_STRINGS_MANIFEST",
215
215
  "file": "ommlds/minichain/backends/impls/groq/names.py",
216
- "line": 27,
216
+ "line": 40,
217
217
  "value": {
218
218
  "!.minichain.backends.strings.manifests.BackendStringsManifest": {
219
219
  "service_cls_names": [
@@ -237,7 +237,7 @@
237
237
  "module": ".minichain.backends.impls.groq.stream",
238
238
  "attr": null,
239
239
  "file": "ommlds/minichain/backends/impls/groq/stream.py",
240
- "line": 31,
240
+ "line": 33,
241
241
  "value": {
242
242
  "!.minichain.registries.manifests.RegistryManifest": {
243
243
  "module": "ommlds.minichain.backends.impls.groq.stream",
@@ -103,13 +103,41 @@ class ChatCompletionRequest(lang.Final):
103
103
  stream: bool | None = None
104
104
  stream_options: ta.Mapping[str, ta.Any] | None = None
105
105
  temperature: float | None = None
106
- tool_choice: str | None = None
107
- tools: ta.Sequence[ta.Mapping[str, ta.Any]] | None = None
106
+ ool_choice: str | None = None
107
+
108
+ @dc.dataclass(frozen=True, kw_only=True)
109
+ @_set_class_marshal_options
110
+ class Tool(lang.Final):
111
+ @dc.dataclass(frozen=True, kw_only=True)
112
+ @_set_class_marshal_options
113
+ class Function(lang.Final):
114
+ description: str | None = None
115
+ name: str
116
+ parameters: ta.Mapping[str, ta.Any] | None = None # json schema
117
+ strict: bool | None = None
118
+
119
+ function: Function
120
+ type: ta.Literal['function', 'browser_search', 'code_interpreter'] = 'function'
121
+
122
+ tools: ta.Sequence[Tool] | None = None
123
+
108
124
  top_logprobs: int | None = None
109
125
  top_p: float | None = None
110
126
  user: str | None = None
111
127
 
112
128
 
129
+ @dc.dataclass(frozen=True, kw_only=True)
130
+ @_set_class_marshal_options
131
+ class ExecutedTool(lang.Final):
132
+ arguments: str
133
+ index: int
134
+ type: str
135
+ browser_results: ta.Sequence[ta.Any] | None = None
136
+ code_results: ta.Sequence[ta.Any] | None = None
137
+ output: str | None = None
138
+ search_results: ta.Any | None = None
139
+
140
+
113
141
  @dc.dataclass(frozen=True, kw_only=True)
114
142
  @_set_class_marshal_options
115
143
  class ChatCompletionResponse(lang.Final):
@@ -125,10 +153,27 @@ class ChatCompletionResponse(lang.Final):
125
153
  class Message(lang.Final):
126
154
  annotations: ta.Sequence[ta.Mapping[str, ta.Any]] | None = None
127
155
  content: str | None = None
128
- executed_tools: ta.Sequence[ta.Mapping[str, ta.Any]] | None = None
156
+
157
+ executed_tools: ta.Sequence[ExecutedTool] | None = None
158
+
129
159
  reasoning: str | None = None
130
160
  role: ta.Literal['assistant'] = 'assistant'
131
- tool_calls: ta.Sequence[ta.Mapping[str, ta.Any]] | None = None
161
+
162
+ @dc.dataclass(frozen=True, kw_only=True)
163
+ @_set_class_marshal_options
164
+ class ToolCall(lang.Final):
165
+ id: str
166
+
167
+ @dc.dataclass(frozen=True, kw_only=True)
168
+ @_set_class_marshal_options
169
+ class Function(lang.Final):
170
+ arguments: str
171
+ name: str
172
+
173
+ function: Function
174
+ type: ta.Literal['function'] = 'function'
175
+
176
+ tool_calls: ta.Sequence[ToolCall] | None = None
132
177
 
133
178
  message: Message
134
179
 
@@ -167,6 +212,26 @@ class ChatCompletionChunk(lang.Final):
167
212
  channel: str | None = None
168
213
  reasoning: str | None = None
169
214
 
215
+ @dc.dataclass(frozen=True, kw_only=True)
216
+ @_set_class_marshal_options
217
+ class ToolCall(lang.Final):
218
+ index: int
219
+ id: str | None = None
220
+
221
+ @dc.dataclass(frozen=True, kw_only=True)
222
+ @_set_class_marshal_options
223
+ class Function(lang.Final):
224
+ arguments: str | None = None
225
+ name: str | None = None
226
+
227
+ function: Function | None = None
228
+
229
+ type: ta.Literal['function'] = 'function'
230
+
231
+ tool_calls: ta.Sequence[ToolCall] | None = None
232
+
233
+ executed_tools: ta.Sequence[ExecutedTool] | None = None
234
+
170
235
  delta: Delta
171
236
  logprobs: ta.Mapping[str, ta.Any] | None = None
172
237
  finish_reason: ta.Literal['stop', 'length', 'tool_calls', 'function_call'] | None = None
@@ -1,6 +1,7 @@
1
1
  import typing as ta
2
2
 
3
3
  from omlish import dataclasses as dc
4
+ from omlish import lang
4
5
 
5
6
 
6
7
  ##
@@ -11,3 +12,11 @@ class ToolsConfig:
11
12
  silent: bool = False
12
13
  dangerous_no_confirmation: bool = False
13
14
  enabled_tools: ta.Iterable[str] | None = None
15
+
16
+
17
+ ##
18
+
19
+
20
+ @dc.dataclass(frozen=True, kw_only=True)
21
+ class ToolSetConfig(lang.Abstract):
22
+ pass
File without changes
@@ -0,0 +1,12 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from ..configs import ToolSetConfig
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True, kw_only=True)
11
+ class FsToolSetConfig(ToolSetConfig, lang.Final):
12
+ pass
@@ -0,0 +1,35 @@
1
+ import os
2
+
3
+ from omlish import inject as inj
4
+
5
+ from ..injection import ToolSetBinder
6
+ from ..injection import bind_tool_context_provider_to_key
7
+ from ..injection import tool_catalog_entries
8
+ from .configs import FsToolSetConfig
9
+
10
+
11
+ ##
12
+
13
+
14
+ def bind_fs_tools(cfg: FsToolSetConfig) -> inj.Elements:
15
+ from ......minichain.lib.fs.context import FsContext
16
+ from ......minichain.lib.fs.tools.ls import ls_tool
17
+ from ......minichain.lib.fs.tools.read import read_tool
18
+
19
+ return inj.as_elements(
20
+ tool_catalog_entries().bind_item_consts(
21
+ ls_tool(),
22
+ read_tool(),
23
+ ),
24
+
25
+ inj.bind(FsContext(
26
+ root_dir=os.getcwd(),
27
+ )),
28
+ bind_tool_context_provider_to_key(FsContext),
29
+ )
30
+
31
+
32
+ ##
33
+
34
+
35
+ FS_TOOL_SET_BINDER = ToolSetBinder(FsToolSetConfig, bind_fs_tools)
@@ -1,13 +1,10 @@
1
- import os
2
- import typing as ta
3
-
4
1
  from omlish import check
5
2
  from omlish import inject as inj
6
3
  from omlish import lang
7
4
 
8
5
  from ..... import minichain as mc
9
6
  from .configs import ToolsConfig
10
- from .injection import bind_tool_context_provider_to_key
7
+ from .injection import ToolSetBinder
11
8
  from .injection import tool_catalog_entries
12
9
  from .injection import tool_context_providers
13
10
 
@@ -21,65 +18,6 @@ with lang.auto_proxy_import(globals()):
21
18
  ##
22
19
 
23
20
 
24
- _TOOL_BINDERS: dict[str, ta.Callable[[], inj.Elements]] = {}
25
-
26
-
27
- def _tool_binder(name: str) -> ta.Callable[[ta.Callable[[], inj.Elements]], ta.Callable[[], inj.Elements]]:
28
- def inner(fn):
29
- check.not_in(name, _TOOL_BINDERS)
30
- _TOOL_BINDERS[name] = fn
31
- return fn
32
- return inner
33
-
34
-
35
- #
36
-
37
-
38
- @_tool_binder('weather')
39
- def _bind_weather_tool() -> inj.Elements:
40
- from .weather import WEATHER_TOOL
41
-
42
- return inj.as_elements(
43
- tool_catalog_entries().bind_item_consts(WEATHER_TOOL),
44
- )
45
-
46
-
47
- @_tool_binder('todo')
48
- def _bind_todo_tools() -> inj.Elements:
49
- from .....minichain.lib.todo.context import TodoContext
50
- from .....minichain.lib.todo.tools.read import todo_read_tool
51
- from .....minichain.lib.todo.tools.write import todo_write_tool
52
-
53
- return inj.as_elements(
54
- tool_catalog_entries().bind_item_consts(
55
- todo_read_tool(),
56
- todo_write_tool(),
57
- ),
58
-
59
- inj.bind(TodoContext()),
60
- bind_tool_context_provider_to_key(TodoContext),
61
- )
62
-
63
-
64
- @_tool_binder('fs')
65
- def _bind_fs_tools() -> inj.Elements:
66
- from .....minichain.lib.fs.context import FsContext
67
- from .....minichain.lib.fs.tools.ls import ls_tool
68
- from .....minichain.lib.fs.tools.read import read_tool
69
-
70
- return inj.as_elements(
71
- tool_catalog_entries().bind_item_consts(
72
- ls_tool(),
73
- read_tool(),
74
- ),
75
-
76
- inj.bind(FsContext(
77
- root_dir=os.getcwd(),
78
- )),
79
- bind_tool_context_provider_to_key(FsContext),
80
- )
81
-
82
-
83
21
  # if tools_config.enable_unsafe_tools_do_not_use_lol:
84
22
  # from ...minichain.lib.bash import bash_tool
85
23
  # els.append(bind_tool(bash_tool()))
@@ -103,7 +41,16 @@ def bind_tools(cfg: ToolsConfig = ToolsConfig()) -> inj.Elements:
103
41
  els.append(tool_catalog_entries().bind_items_provider(singleton=True))
104
42
 
105
43
  for etn in check.not_isinstance(cfg.enabled_tools or [], str):
106
- els.append(_TOOL_BINDERS[etn]())
44
+ from .fs.inject import FS_TOOL_SET_BINDER
45
+ from .todo.inject import TODO_TOOL_SET_BINDER
46
+ from .weather.inject import WEATHER_TOOL_SET_BINDER
47
+ ts_binder: ToolSetBinder = { # type: ignore[assignment] # FIXME: placeholder obviously lol
48
+ 'fs': FS_TOOL_SET_BINDER,
49
+ 'todo': TODO_TOOL_SET_BINDER,
50
+ 'weather': WEATHER_TOOL_SET_BINDER,
51
+ }[etn]
52
+
53
+ els.append(ts_binder.fn(ts_binder.cfg_cls()))
107
54
 
108
55
  #
109
56
 
@@ -1,15 +1,20 @@
1
1
  import typing as ta
2
2
 
3
+ from omlish import dataclasses as dc
3
4
  from omlish import inject as inj
4
5
  from omlish import lang
5
6
 
6
7
  from ..... import minichain as mc
8
+ from .configs import ToolSetConfig
7
9
 
8
10
 
9
11
  with lang.auto_proxy_import(globals()):
10
12
  from . import execution as _execution
11
13
 
12
14
 
15
+ ToolSetConfigT = ta.TypeVar('ToolSetConfigT', bound='ToolSetConfig')
16
+
17
+
13
18
  ##
14
19
 
15
20
 
@@ -28,3 +33,12 @@ def bind_tool_context_provider_to_key(key: ta.Any) -> inj.Elements:
28
33
  lambda v: _execution.ToolContextProvider(lambda: [v]),
29
34
  v=key,
30
35
  ), singleton=True)
36
+
37
+
38
+ ##
39
+
40
+
41
+ @dc.dataclass(frozen=True)
42
+ class ToolSetBinder(lang.Final, ta.Generic[ToolSetConfigT]):
43
+ cfg_cls: type[ToolSetConfig]
44
+ fn: ta.Callable[[ToolSetConfigT], inj.Elements]
File without changes
@@ -0,0 +1,12 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from ..configs import ToolSetConfig
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True, kw_only=True)
11
+ class TodoToolSetConfig(ToolSetConfig, lang.Final):
12
+ pass
@@ -0,0 +1,31 @@
1
+ from omlish import inject as inj
2
+
3
+ from ..injection import ToolSetBinder
4
+ from ..injection import bind_tool_context_provider_to_key
5
+ from ..injection import tool_catalog_entries
6
+ from .configs import TodoToolSetConfig
7
+
8
+
9
+ ##
10
+
11
+
12
+ def bind_todo_tools(cfg: TodoToolSetConfig) -> inj.Elements:
13
+ from ......minichain.lib.todo.context import TodoContext
14
+ from ......minichain.lib.todo.tools.read import todo_read_tool
15
+ from ......minichain.lib.todo.tools.write import todo_write_tool
16
+
17
+ return inj.as_elements(
18
+ tool_catalog_entries().bind_item_consts(
19
+ todo_read_tool(),
20
+ todo_write_tool(),
21
+ ),
22
+
23
+ inj.bind(TodoContext()),
24
+ bind_tool_context_provider_to_key(TodoContext),
25
+ )
26
+
27
+
28
+ ##
29
+
30
+
31
+ TODO_TOOL_SET_BINDER = ToolSetBinder(TodoToolSetConfig, bind_todo_tools)
File without changes
@@ -0,0 +1,12 @@
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
3
+
4
+ from ..configs import ToolSetConfig
5
+
6
+
7
+ ##
8
+
9
+
10
+ @dc.dataclass(frozen=True, kw_only=True)
11
+ class WeatherToolSetConfig(ToolSetConfig, lang.Final):
12
+ pass
@@ -0,0 +1,22 @@
1
+ from omlish import inject as inj
2
+
3
+ from ..injection import ToolSetBinder
4
+ from ..injection import tool_catalog_entries
5
+ from .configs import WeatherToolSetConfig
6
+
7
+
8
+ ##
9
+
10
+
11
+ def bind_weather_tools(cfg: WeatherToolSetConfig) -> inj.Elements:
12
+ from .tools import WEATHER_TOOL
13
+
14
+ return inj.as_elements(
15
+ tool_catalog_entries().bind_item_consts(WEATHER_TOOL),
16
+ )
17
+
18
+
19
+ ##
20
+
21
+
22
+ WEATHER_TOOL_SET_BINDER = ToolSetBinder(WeatherToolSetConfig, bind_weather_tools)
@@ -1,4 +1,4 @@
1
- from ..... import minichain as mc
1
+ from ...... import minichain as mc
2
2
 
3
3
 
4
4
  ##
@@ -10,11 +10,13 @@ from .....backends.groq import protocol as pt
10
10
  from ....chat.choices.services import ChatChoicesRequest
11
11
  from ....chat.choices.services import ChatChoicesResponse
12
12
  from ....chat.choices.services import static_check_is_chat_choices_service
13
+ from ....chat.tools.types import Tool
13
14
  from ....models.configs import ModelName
14
15
  from ....standard import ApiKey
15
16
  from ....standard import DefaultOptions
16
17
  from .names import MODEL_NAMES
17
- from .protocol import build_gq_request_message
18
+ from .protocol import build_gq_request_messages
19
+ from .protocol import build_gq_request_tool
18
20
  from .protocol import build_mc_choices_response
19
21
 
20
22
 
@@ -44,12 +46,16 @@ class GroqChatChoicesService:
44
46
  self._default_options: tv.TypedValues = DefaultOptions.pop(cc)
45
47
 
46
48
  async def invoke(self, request: ChatChoicesRequest) -> ChatChoicesResponse:
49
+ tools: list[pt.ChatCompletionRequest.Tool] = []
50
+ with tv.TypedValues(*request.options).consume() as oc:
51
+ t: Tool
52
+ for t in oc.pop(Tool, []):
53
+ tools.append(build_gq_request_tool(t))
54
+
47
55
  gq_request = pt.ChatCompletionRequest(
48
- messages=[
49
- build_gq_request_message(m)
50
- for m in request.v
51
- ],
56
+ messages=build_gq_request_messages(request.v),
52
57
  model=MODEL_NAMES.resolve(self._model_name.v),
58
+ tools=tools or None,
53
59
  )
54
60
 
55
61
  raw_request = msh.marshal(gq_request)
@@ -4,6 +4,19 @@ https://console.groq.com/docs/models
4
4
  curl -X GET "https://api.groq.com/openai/v1/models" \
5
5
  -H "Authorization: Bearer $GROQ_API_KEY" \
6
6
  -H "Content-Type: application/json"
7
+
8
+ "compound-beta",
9
+ "compound-beta-mini",
10
+ "gemma2-9b-it",
11
+ "llama-3.1-8b-instant",
12
+ "llama-3.3-70b-versatile",
13
+ "meta-llama/llama-4-maverick-17b-128e-instruct",
14
+ "meta-llama/llama-4-scout-17b-16e-instruct",
15
+ "meta-llama/llama-guard-4-12b",
16
+ "moonshotai/kimi-k2-instruct",
17
+ "openai/gpt-oss-120b",
18
+ "openai/gpt-oss-20b",
19
+ "qwen/qwen3-32b",
7
20
  """
8
21
  from ....models.names import ModelNameCollection
9
22
  from ...strings.manifests import BackendStringsManifest
@@ -1,46 +1,143 @@
1
+ import itertools
2
+
1
3
  from omlish import check
4
+ from omlish.formats import json
2
5
 
3
6
  from .....backends.groq import protocol as pt
4
7
  from ....chat.choices.services import ChatChoicesResponse
5
8
  from ....chat.choices.types import AiChoice
6
9
  from ....chat.messages import AiMessage
7
- from ....chat.messages import Message
10
+ from ....chat.messages import AnyAiMessage
11
+ from ....chat.messages import Chat
8
12
  from ....chat.messages import SystemMessage
13
+ from ....chat.messages import ToolUseMessage
14
+ from ....chat.messages import ToolUseResultMessage
9
15
  from ....chat.messages import UserMessage
16
+ from ....chat.stream.types import AiChoiceDelta
10
17
  from ....chat.stream.types import AiChoiceDeltas
11
18
  from ....chat.stream.types import ContentAiChoiceDelta
19
+ from ....chat.stream.types import ToolUseAiChoiceDelta
20
+ from ....chat.tools.types import Tool
21
+ from ....content.prepare import prepare_content_str
22
+ from ....tools.jsonschema import build_tool_spec_params_json_schema
23
+ from ....tools.types import ToolUse
12
24
 
13
25
 
14
26
  ##
15
27
 
16
28
 
17
- def build_gq_request_message(mc_msg: Message) -> pt.ChatCompletionRequest.Message:
18
- if isinstance(mc_msg, SystemMessage):
19
- return pt.ChatCompletionRequest.SystemMessage(
20
- content=check.isinstance(mc_msg.c, str),
21
- )
29
+ def build_gq_request_messages(chat: Chat) -> list[pt.ChatCompletionRequest.Message]:
30
+ gq_msgs: list[pt.ChatCompletionRequest.Message] = []
22
31
 
23
- elif isinstance(mc_msg, UserMessage):
24
- return pt.ChatCompletionRequest.UserMessage(
25
- content=check.isinstance(mc_msg.c, str),
26
- )
32
+ for _, g in itertools.groupby(chat, lambda mc_m: isinstance(mc_m, AnyAiMessage)):
33
+ mc_msgs = list(g)
27
34
 
28
- else:
29
- raise TypeError(mc_msg)
35
+ if isinstance(mc_msgs[0], AnyAiMessage):
36
+ tups: list[tuple[AiMessage | None, list[ToolUseMessage]]] = []
37
+ for mc_msg in mc_msgs:
38
+ if isinstance(mc_msg, AiMessage):
39
+ tups.append((mc_msg, []))
40
+
41
+ elif isinstance(mc_msg, ToolUseMessage):
42
+ if not tups:
43
+ tups.append((None, []))
44
+ tups[-1][1].append(mc_msg)
45
+
46
+ else:
47
+ raise TypeError(mc_msg)
48
+
49
+ for mc_ai_msg, mc_tu_msgs in tups:
50
+ gq_msgs.append(pt.ChatCompletionRequest.AssistantMessage(
51
+ content=check.isinstance(mc_ai_msg.c, str) if mc_ai_msg is not None else None,
52
+ tool_calls=[
53
+ pt.ChatCompletionRequest.AssistantMessage.ToolCall(
54
+ function=pt.ChatCompletionRequest.AssistantMessage.ToolCall.Function(
55
+ name=mc_tu_msg.tu.name,
56
+ arguments=check.not_none(mc_tu_msg.tu.raw_args),
57
+ ),
58
+ id=check.not_none(mc_tu_msg.tu.id),
59
+ )
60
+ for mc_tu_msg in mc_tu_msgs
61
+ ] if mc_tu_msgs else None,
62
+ ))
63
+
64
+ else:
65
+ for mc_msg in mc_msgs:
66
+ if isinstance(mc_msg, SystemMessage):
67
+ gq_msgs.append(pt.ChatCompletionRequest.SystemMessage(
68
+ content=check.isinstance(mc_msg.c, str),
69
+ ))
70
+
71
+ elif isinstance(mc_msg, UserMessage):
72
+ gq_msgs.append(pt.ChatCompletionRequest.UserMessage(
73
+ content=check.isinstance(mc_msg.c, str),
74
+ ))
75
+
76
+ elif isinstance(mc_msg, ToolUseResultMessage):
77
+ gq_msgs.append(pt.ChatCompletionRequest.ToolMessage(
78
+ tool_call_id=check.not_none(mc_msg.tur.id),
79
+ content=check.isinstance(mc_msg.tur.c, str),
80
+ ))
81
+
82
+ else:
83
+ raise TypeError(mc_msg)
84
+
85
+ return gq_msgs
86
+
87
+
88
+ def build_gq_request_tool(t: Tool) -> pt.ChatCompletionRequest.Tool:
89
+ return pt.ChatCompletionRequest.Tool(
90
+ function=pt.ChatCompletionRequest.Tool.Function(
91
+ name=check.not_none(t.spec.name),
92
+ description=prepare_content_str(t.spec.desc),
93
+ parameters=build_tool_spec_params_json_schema(t.spec),
94
+ ),
95
+ )
30
96
 
31
97
 
32
98
  def build_mc_choices_response(gq_resp: pt.ChatCompletionResponse) -> ChatChoicesResponse:
33
- return ChatChoicesResponse([
34
- AiChoice([AiMessage(
35
- check.isinstance(gq_choice.message.content, str),
36
- )])
37
- for gq_choice in gq_resp.choices
38
- ])
99
+ def build_choice(gq_choice: pt.ChatCompletionResponse.Choice) -> AiChoice:
100
+ gq_msg = gq_choice.message
101
+
102
+ lst: list[AnyAiMessage] = []
103
+
104
+ if gq_msg.content is not None:
105
+ lst.append(AiMessage(
106
+ check.isinstance(gq_msg.content, str),
107
+ ))
108
+
109
+ for gq_tc in gq_msg.tool_calls or []:
110
+ lst.append(ToolUseMessage(ToolUse(
111
+ id=gq_tc.id,
112
+ name=gq_tc.function.name,
113
+ args=json.loads(gq_tc.function.arguments or '{}'),
114
+ raw_args=gq_tc.function.arguments,
115
+ )))
116
+
117
+ return AiChoice(lst)
118
+
119
+ return ChatChoicesResponse(list(map(build_choice, gq_resp.choices)))
39
120
 
40
121
 
41
122
  def build_mc_ai_choice_deltas(delta: pt.ChatCompletionChunk.Choice.Delta) -> AiChoiceDeltas:
42
- if delta.role in (None, 'assistant') and delta.content is not None:
43
- return AiChoiceDeltas([ContentAiChoiceDelta(delta.content)])
123
+ if delta.role in (None, 'assistant'):
124
+ lst: list[AiChoiceDelta] = []
44
125
 
45
- else:
126
+ if delta.content is not None:
127
+ lst.append(ContentAiChoiceDelta(delta.content))
128
+
129
+ for tc in delta.tool_calls or []:
130
+ tc_fn = check.not_none(tc.function)
131
+ lst.append(ToolUseAiChoiceDelta(
132
+ id=tc.id,
133
+ name=check.not_none(tc_fn.name),
134
+ args=json.loads(tc_fn.arguments or '{}'),
135
+ ))
136
+
137
+ return AiChoiceDeltas(lst)
138
+
139
+ elif delta.channel in ('analysis', 'commentary'):
46
140
  return AiChoiceDeltas([])
141
+
142
+ else:
143
+ raise ValueError(delta)
@@ -14,6 +14,7 @@ from ....chat.stream.services import ChatChoicesStreamRequest
14
14
  from ....chat.stream.services import ChatChoicesStreamResponse
15
15
  from ....chat.stream.services import static_check_is_chat_choices_stream_service
16
16
  from ....chat.stream.types import AiChoicesDeltas
17
+ from ....chat.tools.types import Tool
17
18
  from ....configs import Config
18
19
  from ....resources import UseResources
19
20
  from ....standard import ApiKey
@@ -21,7 +22,8 @@ from ....stream.services import StreamResponseSink
21
22
  from ....stream.services import new_stream_response
22
23
  from .chat import GroqChatChoicesService
23
24
  from .names import MODEL_NAMES
24
- from .protocol import build_gq_request_message
25
+ from .protocol import build_gq_request_messages
26
+ from .protocol import build_gq_request_tool
25
27
  from .protocol import build_mc_ai_choice_deltas
26
28
 
27
29
 
@@ -50,14 +52,16 @@ class GroqChatChoicesStreamService:
50
52
  READ_CHUNK_SIZE: ta.ClassVar[int] = -1
51
53
 
52
54
  async def invoke(self, request: ChatChoicesStreamRequest) -> ChatChoicesStreamResponse:
53
- # check.isinstance(request, ChatRequest)
55
+ tools: list[pt.ChatCompletionRequest.Tool] = []
56
+ with tv.TypedValues(*request.options).consume() as oc:
57
+ t: Tool
58
+ for t in oc.pop(Tool, []):
59
+ tools.append(build_gq_request_tool(t))
54
60
 
55
61
  gq_request = pt.ChatCompletionRequest(
56
- messages=[
57
- build_gq_request_message(m)
58
- for m in request.v
59
- ],
62
+ messages=build_gq_request_messages(request.v),
60
63
  model=MODEL_NAMES.resolve(self._model_name.v),
64
+ tools=tools or None,
61
65
  stream=True,
62
66
  )
63
67
 
@@ -73,6 +73,7 @@ class AiChoiceDeltaJoiner:
73
73
  id=d.id,
74
74
  name=check.not_none(d.name),
75
75
  args=d.args or {},
76
+ raw_args=json.dumps_compact(d.args),
76
77
  )))
77
78
 
78
79
  else:
@@ -15,10 +15,10 @@ import textwrap
15
15
  import types
16
16
  import typing as ta
17
17
 
18
- from omdev.py import docstrings
19
18
  from omlish import check
20
19
  from omlish import collections as col
21
20
  from omlish import dataclasses as dc
21
+ from omlish import lang
22
22
  from omlish import metadata as md
23
23
  from omlish import reflect as rfl
24
24
  from omlish.lite.cached import cached_nullary
@@ -37,6 +37,10 @@ from .types import TupleToolDtype
37
37
  from .types import UnionToolDtype
38
38
 
39
39
 
40
+ with lang.auto_proxy_import(globals()):
41
+ from omdev.py import docstrings
42
+
43
+
40
44
  ##
41
45
 
42
46
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev476
3
+ Version: 0.0.0.dev477
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.dev476
18
- Requires-Dist: omlish==0.0.0.dev476
17
+ Requires-Dist: omdev==0.0.0.dev477
18
+ Requires-Dist: omlish==0.0.0.dev477
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=iFZm89KS4aONhIRo1mbtKiHARMrNZ4SfQLSNgJO_Iek,25355
1
+ ommlds/.omlish-manifests.json,sha256=8dexRefvFsIfCdj0aNLHnjBb_Qn14SRsImSkiO-R-G8,25355
2
2
  ommlds/__about__.py,sha256=5ekfRjb6SiK6o6SAusykyPQ_oxy46_IEga7nD0EL0cI,1839
3
3
  ommlds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ommlds/_hacks/__init__.py,sha256=ajfw7dMKH8UuloeQ5MSxWwgAmdWf2v8gm-K3uLP9wtY,196
@@ -22,7 +22,7 @@ ommlds/backends/google/protocol/_marshal.py,sha256=LWikcdMDrKQ0lMtclNXwK9qamijUB
22
22
  ommlds/backends/google/protocol/types.py,sha256=JDft5-5sPuy8sFWTvid4JuC9a9EnuQizt9dyAV2L56s,17536
23
23
  ommlds/backends/groq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  ommlds/backends/groq/_marshal.py,sha256=3o4CVK9VbP_UnEbXZ4fQJuZK3Sy9xnkXTTempSDSvgE,602
25
- ommlds/backends/groq/protocol.py,sha256=ufZ6UIJnQfei28q9o6GpPiD9AJ_LZa9SSOWP7B79qcg,5871
25
+ ommlds/backends/groq/protocol.py,sha256=ieKrjlxcDE5RqWcugc996O5Ksy4Chse_RdigPQPbP2Y,7823
26
26
  ommlds/backends/llamacpp/__init__.py,sha256=zXFpLXE4a2vEl0jcPDyKlPHHfZ3Z8Dz0twhEIyZ8-vg,59
27
27
  ommlds/backends/llamacpp/buildwheel.py,sha256=q9ghCLVbm8Jm6syrZlBP-x1qNDd0wSl15B2OXBtDBQ8,3813
28
28
  ommlds/backends/llamacpp/logging.py,sha256=nCEC6ASEuTpJqx47DMLhnbr5KelDlbxhM0nKQt4bc3w,773
@@ -145,13 +145,22 @@ ommlds/cli/sessions/chat/phases/injection.py,sha256=ZUFRRLp3HNhKaPBW6aiw5XFEGNkq
145
145
  ommlds/cli/sessions/chat/phases/manager.py,sha256=_wfc0FfL5h_5L-6hnYb3U3kiRmkGDkJu7wGj5Cgz7_Y,727
146
146
  ommlds/cli/sessions/chat/phases/types.py,sha256=w1pAEydJYaHzSzdlqygCwKxvVZnfo_R_xJqq8wBzZsQ,512
147
147
  ommlds/cli/sessions/chat/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
148
- ommlds/cli/sessions/chat/tools/configs.py,sha256=nyOD8J5dWUCaYY-AXEOAqUwZA46wl70c9WD7TZh4Vl8,244
148
+ ommlds/cli/sessions/chat/tools/configs.py,sha256=DuRT3In_VHLur6UpvFI8EY5bOqmdLNMSNTUvbzCLFOo,361
149
149
  ommlds/cli/sessions/chat/tools/confirmation.py,sha256=I_nYBIIoeprCShUaVpN4v01YOAdY6iJEM5-ybc0p8VA,1072
150
150
  ommlds/cli/sessions/chat/tools/execution.py,sha256=4sRuZWZjNBo852hhLZjqTqEZpgu9KPXHEawiICIMUdc,1576
151
- ommlds/cli/sessions/chat/tools/inject.py,sha256=oz1UOx-HM0h6LJ2BXYLDED2C-m7v54RI_2ILk_5lfB4,3780
152
- ommlds/cli/sessions/chat/tools/injection.py,sha256=RDtsOuy4qycaVq_v8J6_u8ZCUycBg0hAN8qr0rojFKw,838
151
+ ommlds/cli/sessions/chat/tools/inject.py,sha256=MzORGqT4sNLu3ApNoExNjD8OLFKFmwpHmN_-qNVBlp8,2634
152
+ ommlds/cli/sessions/chat/tools/injection.py,sha256=BGpkYciYNVlfyi6HcQqgrtLyWbFuIXJx-yr2eC4TmYY,1161
153
153
  ommlds/cli/sessions/chat/tools/rendering.py,sha256=-BO-rY_WycmAf1WC9jhiRqnBJiQOtUA5jaXcOC2K6aw,1404
154
- ommlds/cli/sessions/chat/tools/weather.py,sha256=vZXOHdRGGlkDEXasW5foc58MyEMOc79bhIfIgrumRlo,302
154
+ ommlds/cli/sessions/chat/tools/fs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
+ ommlds/cli/sessions/chat/tools/fs/configs.py,sha256=hwQ3WRP6hYxjIx-m8z_ZZZ-HwiREuIpa2gRKzeqkoHk,205
156
+ ommlds/cli/sessions/chat/tools/fs/inject.py,sha256=hq2PHA9ZgfmghOZ4RlagtfJfJAk0cdEsH90WZuVjlvY,806
157
+ ommlds/cli/sessions/chat/tools/todo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
+ ommlds/cli/sessions/chat/tools/todo/configs.py,sha256=pcdjTKsRB4J_iBET2tcOx-32EHqtecxLg4JtelMyh1o,207
159
+ ommlds/cli/sessions/chat/tools/todo/inject.py,sha256=FU4fCGWJKXviNvSsjoAENM1My1-zUrrbkL2RPelLqik,805
160
+ ommlds/cli/sessions/chat/tools/weather/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
161
+ ommlds/cli/sessions/chat/tools/weather/configs.py,sha256=VzamzIHkTOVVEKeH5pCq3oWZdREpT2O73v0CImCerDU,210
162
+ ommlds/cli/sessions/chat/tools/weather/inject.py,sha256=HSB6gCp23T2HME07FzBiiFpWnloErCIO7yTWcOu5DVk,456
163
+ ommlds/cli/sessions/chat/tools/weather/tools.py,sha256=TeXOmxTtuKgiGNl00t4pcHqhh3HSTeiEZRyZv1rs7Pg,303
155
164
  ommlds/cli/sessions/completion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
165
  ommlds/cli/sessions/completion/configs.py,sha256=ZibOyJxIMbclzKDK_mj8jVzFJxjkyVWmo1Tb9ZnQQ1M,240
157
166
  ommlds/cli/sessions/completion/inject.py,sha256=vk3ewhXWGemKcOllr1iG_nMz5523qEu40MI62Dlx9AQ,933
@@ -203,10 +212,10 @@ ommlds/minichain/backends/impls/google/search.py,sha256=y5_6seSRU8CFnLA_Ja8XEMbI
203
212
  ommlds/minichain/backends/impls/google/stream.py,sha256=ITownhKSOJB4IG23wWZJepUImSM6vJsDMOM9W1STpwU,8013
204
213
  ommlds/minichain/backends/impls/google/tools.py,sha256=Tty0gsyx7-PbeoNqMuql_ewQ6q-ZsDaDdsD5ShinGVY,5089
205
214
  ommlds/minichain/backends/impls/groq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
- ommlds/minichain/backends/impls/groq/chat.py,sha256=jXomRPouYrlFBsFvviSsR4T6l6Cpf8Ut5TiqhjuzkjY,2451
207
- ommlds/minichain/backends/impls/groq/names.py,sha256=M_NRKpcbFgnN0ZAVaQMQFHRZV62lE8hZ2qoDiM_A8gs,748
208
- ommlds/minichain/backends/impls/groq/protocol.py,sha256=kLHcVjwyh5AosKXWengDJZBdHWUwMrmCd_2ZKgor0EA,1451
209
- ommlds/minichain/backends/impls/groq/stream.py,sha256=RDcDfTb2zH1rco0jEzNNqu04MXv7Fbqm5AlTimdQqK0,4850
215
+ ommlds/minichain/backends/impls/groq/chat.py,sha256=XXJznM6h2fBBtrY8MggMUNJf8thyFTM3p1XzFImwkAk,2743
216
+ ommlds/minichain/backends/impls/groq/names.py,sha256=RnvBSytxPF1a9Bj_OPVELuD1nAKJnJrG3ZwJfYo6Szs,1075
217
+ ommlds/minichain/backends/impls/groq/protocol.py,sha256=3sLC8wX0jnUOSMf3WqXH8zKT8RCSFKrbx5VyqaPViUQ,5242
218
+ ommlds/minichain/backends/impls/groq/stream.py,sha256=in62MWfQLfXRVPpoFdJyLNGZ_bRfXymmVTdQpEuAfCU,5092
210
219
  ommlds/minichain/backends/impls/huggingface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
211
220
  ommlds/minichain/backends/impls/huggingface/configs.py,sha256=6jsBtPNXOP57PcpxNTVLGWLc-18Iwn_lDbGouwCJTIQ,258
212
221
  ommlds/minichain/backends/impls/huggingface/repos.py,sha256=8BDxJmra9elSQL2vzp2nr2p4Hpq56A3zTk7hTTnfJU4,861
@@ -256,7 +265,7 @@ ommlds/minichain/chat/choices/types.py,sha256=OPnMUp0KUKGGPJKsQZiXUtd8Z9b_JnhOy7
256
265
  ommlds/minichain/chat/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
266
  ommlds/minichain/chat/stream/_marshal.py,sha256=r6NYBUviV7jIssaFprzv2rVEj8cFEuBlt71BSMZ-448,397
258
267
  ommlds/minichain/chat/stream/adapters.py,sha256=3hKo3-MLtVIB-Nhdlxt17LP9vZESr-2fBZQ3Yr6l_Ps,1077
259
- ommlds/minichain/chat/stream/joining.py,sha256=oPxLT4qEYWCaxclnZvt54ztQP5md4V6u6Uwn4qd2e9M,2936
268
+ ommlds/minichain/chat/stream/joining.py,sha256=N0vAg1UJbwnXl5dRkmb-EP5_MfhRgF0liZ45OEDY0XA,2989
260
269
  ommlds/minichain/chat/stream/services.py,sha256=TxNEOm85QEFYtKb59q_uP6eSNh75v1fF-IpsJjhY4to,1252
261
270
  ommlds/minichain/chat/stream/types.py,sha256=kpHsWLNHk7hmaNPDSCqLH-ECSAiz83lRfr00LhSWb5U,1589
262
271
  ommlds/minichain/chat/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -358,7 +367,7 @@ ommlds/minichain/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
358
367
  ommlds/minichain/tools/_marshal.py,sha256=9np2tuzGfRwL5BMCd6QYChzimtTBUfHxT4IoEA6v3Eg,410
359
368
  ommlds/minichain/tools/fns.py,sha256=UVgj9f0vnjFzP5uP4zpN4slOA5nEOD8Ob1aJwKrSggA,3930
360
369
  ommlds/minichain/tools/jsonschema.py,sha256=20i3Yrw91nzgYRcZfVfrDjtFEpGD1Gc_5iiFhq1KV8w,4233
361
- ommlds/minichain/tools/reflect.py,sha256=V3hjemdLxH-0pI_XwuFPSyQaWlWjvQcGs_Wss5qvQr8,8624
370
+ ommlds/minichain/tools/reflect.py,sha256=fI0b6ZGaJbk-Ll7oXuByX5fVWHh5UaUW37O5WHbZlyA,8694
362
371
  ommlds/minichain/tools/types.py,sha256=z1OP4sHumsSnqxTD3eL8tgTHarDmw7VWUyJu9cW7Yho,4868
363
372
  ommlds/minichain/tools/execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
364
373
  ommlds/minichain/tools/execution/catalog.py,sha256=sBde49FSClTreX8KO5pZ2-EE1NyTZOoagNdowN3n5pE,2002
@@ -399,9 +408,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
399
408
  ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
400
409
  ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
401
410
  ommlds/wiki/utils/xml.py,sha256=sNJNkZ9rT8B-kJMO6bRz8J1USy4fyPx0m2PwTX7vxYY,3846
402
- ommlds-0.0.0.dev476.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
403
- ommlds-0.0.0.dev476.dist-info/METADATA,sha256=Jx-CeBHDUtMiNkl1KouPEGBdWRs7bTxw2f4_tf8cs7w,3344
404
- ommlds-0.0.0.dev476.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
405
- ommlds-0.0.0.dev476.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
406
- ommlds-0.0.0.dev476.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
407
- ommlds-0.0.0.dev476.dist-info/RECORD,,
411
+ ommlds-0.0.0.dev477.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
412
+ ommlds-0.0.0.dev477.dist-info/METADATA,sha256=ncQdlJg91UEeoFaW8OiTM2ipyNxyVbm4oHwycfYcSHM,3344
413
+ ommlds-0.0.0.dev477.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
414
+ ommlds-0.0.0.dev477.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
415
+ ommlds-0.0.0.dev477.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
416
+ ommlds-0.0.0.dev477.dist-info/RECORD,,