ommlds 0.0.0.dev426__py3-none-any.whl → 0.0.0.dev485__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 (295) hide show
  1. ommlds/.omlish-manifests.json +336 -39
  2. ommlds/__about__.py +16 -10
  3. ommlds/_hacks/__init__.py +4 -0
  4. ommlds/_hacks/funcs.py +110 -0
  5. ommlds/_hacks/names.py +158 -0
  6. ommlds/_hacks/params.py +73 -0
  7. ommlds/_hacks/patches.py +0 -3
  8. ommlds/backends/anthropic/protocol/__init__.py +13 -1
  9. ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
  10. ommlds/backends/anthropic/protocol/_marshal.py +2 -2
  11. ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
  12. ommlds/backends/anthropic/protocol/sse/assemble.py +23 -7
  13. ommlds/backends/anthropic/protocol/sse/events.py +13 -0
  14. ommlds/backends/anthropic/protocol/types.py +40 -8
  15. ommlds/backends/google/protocol/__init__.py +16 -0
  16. ommlds/backends/google/protocol/_dataclasses.py +5997 -0
  17. ommlds/backends/google/protocol/_marshal.py +16 -0
  18. ommlds/backends/google/protocol/types.py +626 -0
  19. ommlds/backends/groq/__init__.py +7 -0
  20. ommlds/backends/groq/_dataclasses.py +3901 -0
  21. ommlds/backends/groq/_marshal.py +23 -0
  22. ommlds/backends/groq/protocol.py +249 -0
  23. ommlds/backends/llamacpp/logging.py +4 -1
  24. ommlds/backends/mlx/caching.py +7 -3
  25. ommlds/backends/mlx/cli.py +10 -7
  26. ommlds/backends/mlx/generation.py +19 -17
  27. ommlds/backends/mlx/limits.py +10 -6
  28. ommlds/backends/mlx/loading.py +65 -5
  29. ommlds/backends/ollama/__init__.py +7 -0
  30. ommlds/backends/ollama/_dataclasses.py +3458 -0
  31. ommlds/backends/ollama/protocol.py +170 -0
  32. ommlds/backends/openai/protocol/__init__.py +24 -29
  33. ommlds/backends/openai/protocol/_common.py +18 -0
  34. ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
  35. ommlds/backends/openai/protocol/_marshal.py +27 -0
  36. ommlds/backends/openai/protocol/chatcompletion/chunk.py +58 -31
  37. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +49 -44
  38. ommlds/backends/openai/protocol/chatcompletion/message.py +55 -43
  39. ommlds/backends/openai/protocol/chatcompletion/request.py +114 -66
  40. ommlds/backends/openai/protocol/chatcompletion/response.py +71 -45
  41. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +27 -20
  42. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +16 -7
  43. ommlds/backends/openai/protocol/completionusage.py +24 -15
  44. ommlds/backends/tavily/__init__.py +7 -0
  45. ommlds/backends/tavily/_dataclasses.py +1734 -0
  46. ommlds/backends/tavily/protocol.py +301 -0
  47. ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
  48. ommlds/backends/transformers/__init__.py +14 -0
  49. ommlds/backends/transformers/filecache.py +109 -0
  50. ommlds/backends/transformers/streamers.py +73 -0
  51. ommlds/cli/__init__.py +7 -0
  52. ommlds/cli/_dataclasses.py +2562 -0
  53. ommlds/cli/asyncs.py +30 -0
  54. ommlds/cli/backends/catalog.py +93 -0
  55. ommlds/cli/backends/configs.py +9 -0
  56. ommlds/cli/backends/inject.py +31 -36
  57. ommlds/cli/backends/injection.py +16 -0
  58. ommlds/cli/backends/types.py +46 -0
  59. ommlds/cli/content/messages.py +34 -0
  60. ommlds/cli/content/strings.py +42 -0
  61. ommlds/cli/inject.py +17 -32
  62. ommlds/cli/inputs/__init__.py +0 -0
  63. ommlds/cli/inputs/asyncs.py +32 -0
  64. ommlds/cli/inputs/sync.py +75 -0
  65. ommlds/cli/main.py +270 -110
  66. ommlds/cli/rendering/__init__.py +0 -0
  67. ommlds/cli/rendering/configs.py +9 -0
  68. ommlds/cli/rendering/inject.py +31 -0
  69. ommlds/cli/rendering/markdown.py +52 -0
  70. ommlds/cli/rendering/raw.py +73 -0
  71. ommlds/cli/rendering/types.py +21 -0
  72. ommlds/cli/secrets.py +21 -0
  73. ommlds/cli/sessions/base.py +1 -1
  74. ommlds/cli/sessions/chat/chat/__init__.py +0 -0
  75. ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
  76. ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
  77. ommlds/cli/sessions/chat/chat/ai/inject.py +74 -0
  78. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  79. ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
  80. ommlds/cli/sessions/chat/chat/ai/services.py +79 -0
  81. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  82. ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
  83. ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
  84. ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
  85. ommlds/cli/sessions/chat/chat/state/inject.py +36 -0
  86. ommlds/cli/sessions/chat/chat/state/inmemory.py +33 -0
  87. ommlds/cli/sessions/chat/chat/state/storage.py +52 -0
  88. ommlds/cli/sessions/chat/chat/state/types.py +38 -0
  89. ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
  90. ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
  91. ommlds/cli/sessions/chat/chat/user/inject.py +62 -0
  92. ommlds/cli/sessions/chat/chat/user/interactive.py +31 -0
  93. ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
  94. ommlds/cli/sessions/chat/chat/user/types.py +15 -0
  95. ommlds/cli/sessions/chat/configs.py +27 -0
  96. ommlds/cli/sessions/chat/driver.py +43 -0
  97. ommlds/cli/sessions/chat/inject.py +33 -65
  98. ommlds/cli/sessions/chat/phases/__init__.py +0 -0
  99. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  100. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  101. ommlds/cli/sessions/chat/phases/manager.py +29 -0
  102. ommlds/cli/sessions/chat/phases/types.py +29 -0
  103. ommlds/cli/sessions/chat/session.py +27 -0
  104. ommlds/cli/sessions/chat/tools/__init__.py +0 -0
  105. ommlds/cli/sessions/chat/tools/configs.py +22 -0
  106. ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
  107. ommlds/cli/sessions/chat/tools/execution.py +66 -0
  108. ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
  109. ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
  110. ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
  111. ommlds/cli/sessions/chat/tools/inject.py +88 -0
  112. ommlds/cli/sessions/chat/tools/injection.py +44 -0
  113. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  114. ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
  115. ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
  116. ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
  117. ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
  118. ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
  119. ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
  120. ommlds/cli/{tools/weather.py → sessions/chat/tools/weather/tools.py} +1 -1
  121. ommlds/cli/sessions/completion/configs.py +21 -0
  122. ommlds/cli/sessions/completion/inject.py +42 -0
  123. ommlds/cli/sessions/completion/session.py +35 -0
  124. ommlds/cli/sessions/embedding/configs.py +21 -0
  125. ommlds/cli/sessions/embedding/inject.py +42 -0
  126. ommlds/cli/sessions/embedding/session.py +33 -0
  127. ommlds/cli/sessions/inject.py +28 -11
  128. ommlds/cli/state/__init__.py +0 -0
  129. ommlds/cli/state/inject.py +28 -0
  130. ommlds/cli/{state.py → state/storage.py} +41 -24
  131. ommlds/minichain/__init__.py +84 -24
  132. ommlds/minichain/_dataclasses.py +15401 -0
  133. ommlds/minichain/_marshal.py +49 -9
  134. ommlds/minichain/_typedvalues.py +2 -4
  135. ommlds/minichain/backends/catalogs/base.py +20 -1
  136. ommlds/minichain/backends/catalogs/simple.py +2 -2
  137. ommlds/minichain/backends/catalogs/strings.py +10 -8
  138. ommlds/minichain/backends/impls/anthropic/chat.py +65 -27
  139. ommlds/minichain/backends/impls/anthropic/names.py +10 -8
  140. ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
  141. ommlds/minichain/backends/impls/anthropic/stream.py +111 -43
  142. ommlds/minichain/backends/impls/duckduckgo/search.py +6 -2
  143. ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
  144. ommlds/minichain/backends/impls/dummy/chat.py +69 -0
  145. ommlds/minichain/backends/impls/google/chat.py +114 -22
  146. ommlds/minichain/backends/impls/google/search.py +7 -2
  147. ommlds/minichain/backends/impls/google/stream.py +219 -0
  148. ommlds/minichain/backends/impls/google/tools.py +149 -0
  149. ommlds/minichain/backends/impls/groq/__init__.py +0 -0
  150. ommlds/minichain/backends/impls/groq/chat.py +75 -0
  151. ommlds/minichain/backends/impls/groq/names.py +48 -0
  152. ommlds/minichain/backends/impls/groq/protocol.py +143 -0
  153. ommlds/minichain/backends/impls/groq/stream.py +125 -0
  154. ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
  155. ommlds/minichain/backends/impls/llamacpp/chat.py +40 -22
  156. ommlds/minichain/backends/impls/llamacpp/completion.py +9 -5
  157. ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
  158. ommlds/minichain/backends/impls/llamacpp/stream.py +43 -23
  159. ommlds/minichain/backends/impls/mistral.py +20 -5
  160. ommlds/minichain/backends/impls/mlx/chat.py +101 -24
  161. ommlds/minichain/backends/impls/ollama/__init__.py +0 -0
  162. ommlds/minichain/backends/impls/ollama/chat.py +199 -0
  163. ommlds/minichain/backends/impls/openai/chat.py +18 -8
  164. ommlds/minichain/backends/impls/openai/completion.py +10 -3
  165. ommlds/minichain/backends/impls/openai/embedding.py +10 -3
  166. ommlds/minichain/backends/impls/openai/format.py +131 -106
  167. ommlds/minichain/backends/impls/openai/names.py +31 -5
  168. ommlds/minichain/backends/impls/openai/stream.py +43 -25
  169. ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
  170. ommlds/minichain/backends/impls/tavily.py +66 -0
  171. ommlds/minichain/backends/impls/tinygrad/chat.py +30 -20
  172. ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
  173. ommlds/minichain/backends/impls/transformers/sentence.py +6 -3
  174. ommlds/minichain/backends/impls/transformers/tokens.py +10 -7
  175. ommlds/minichain/backends/impls/transformers/transformers.py +160 -37
  176. ommlds/minichain/backends/strings/parsing.py +1 -1
  177. ommlds/minichain/backends/strings/resolving.py +4 -1
  178. ommlds/minichain/chat/_marshal.py +16 -9
  179. ommlds/minichain/chat/choices/adapters.py +4 -4
  180. ommlds/minichain/chat/choices/services.py +1 -1
  181. ommlds/minichain/chat/choices/stream/__init__.py +0 -0
  182. ommlds/minichain/chat/choices/stream/adapters.py +35 -0
  183. ommlds/minichain/chat/choices/stream/joining.py +31 -0
  184. ommlds/minichain/chat/choices/stream/services.py +45 -0
  185. ommlds/minichain/chat/choices/stream/types.py +43 -0
  186. ommlds/minichain/chat/choices/types.py +2 -2
  187. ommlds/minichain/chat/history.py +3 -3
  188. ommlds/minichain/chat/messages.py +55 -19
  189. ommlds/minichain/chat/services.py +3 -3
  190. ommlds/minichain/chat/stream/_marshal.py +16 -0
  191. ommlds/minichain/chat/stream/joining.py +85 -0
  192. ommlds/minichain/chat/stream/services.py +15 -21
  193. ommlds/minichain/chat/stream/types.py +32 -19
  194. ommlds/minichain/chat/tools/execution.py +8 -7
  195. ommlds/minichain/chat/tools/ids.py +9 -15
  196. ommlds/minichain/chat/tools/parsing.py +17 -26
  197. ommlds/minichain/chat/transforms/base.py +29 -38
  198. ommlds/minichain/chat/transforms/metadata.py +30 -4
  199. ommlds/minichain/chat/transforms/services.py +9 -11
  200. ommlds/minichain/content/_marshal.py +44 -20
  201. ommlds/minichain/content/json.py +13 -0
  202. ommlds/minichain/content/materialize.py +14 -21
  203. ommlds/minichain/content/prepare.py +4 -0
  204. ommlds/minichain/content/transforms/interleave.py +1 -1
  205. ommlds/minichain/content/transforms/squeeze.py +1 -1
  206. ommlds/minichain/content/transforms/stringify.py +1 -1
  207. ommlds/minichain/json.py +20 -0
  208. ommlds/minichain/lib/code/__init__.py +0 -0
  209. ommlds/minichain/lib/code/prompts.py +6 -0
  210. ommlds/minichain/lib/fs/binfiles.py +108 -0
  211. ommlds/minichain/lib/fs/context.py +126 -0
  212. ommlds/minichain/lib/fs/errors.py +101 -0
  213. ommlds/minichain/lib/fs/suggestions.py +36 -0
  214. ommlds/minichain/lib/fs/tools/__init__.py +0 -0
  215. ommlds/minichain/lib/fs/tools/edit.py +104 -0
  216. ommlds/minichain/lib/fs/tools/ls.py +38 -0
  217. ommlds/minichain/lib/fs/tools/read.py +115 -0
  218. ommlds/minichain/lib/fs/tools/recursivels/__init__.py +0 -0
  219. ommlds/minichain/lib/fs/tools/recursivels/execution.py +40 -0
  220. ommlds/minichain/lib/todo/__init__.py +0 -0
  221. ommlds/minichain/lib/todo/context.py +54 -0
  222. ommlds/minichain/lib/todo/tools/__init__.py +0 -0
  223. ommlds/minichain/lib/todo/tools/read.py +44 -0
  224. ommlds/minichain/lib/todo/tools/write.py +335 -0
  225. ommlds/minichain/lib/todo/types.py +60 -0
  226. ommlds/minichain/llms/_marshal.py +25 -17
  227. ommlds/minichain/llms/types.py +4 -0
  228. ommlds/minichain/registries/globals.py +18 -4
  229. ommlds/minichain/resources.py +68 -45
  230. ommlds/minichain/search.py +1 -1
  231. ommlds/minichain/services/_marshal.py +46 -39
  232. ommlds/minichain/services/facades.py +3 -3
  233. ommlds/minichain/services/services.py +1 -1
  234. ommlds/minichain/standard.py +8 -0
  235. ommlds/minichain/stream/services.py +152 -38
  236. ommlds/minichain/stream/wrap.py +22 -24
  237. ommlds/minichain/text/toolparsing/llamacpp/hermes2.py +3 -2
  238. ommlds/minichain/text/toolparsing/llamacpp/llama31.py +3 -2
  239. ommlds/minichain/text/toolparsing/llamacpp/utils.py +3 -2
  240. ommlds/minichain/tools/_marshal.py +1 -1
  241. ommlds/minichain/tools/execution/catalog.py +2 -1
  242. ommlds/minichain/tools/execution/context.py +34 -14
  243. ommlds/minichain/tools/execution/errors.py +15 -0
  244. ommlds/minichain/tools/execution/executors.py +8 -3
  245. ommlds/minichain/tools/execution/reflect.py +40 -5
  246. ommlds/minichain/tools/fns.py +46 -9
  247. ommlds/minichain/tools/jsonschema.py +14 -5
  248. ommlds/minichain/tools/reflect.py +54 -18
  249. ommlds/minichain/tools/types.py +33 -1
  250. ommlds/minichain/utils.py +27 -0
  251. ommlds/minichain/vectors/_marshal.py +11 -10
  252. ommlds/minichain/vectors/types.py +1 -1
  253. ommlds/nanochat/LICENSE +21 -0
  254. ommlds/nanochat/__init__.py +0 -0
  255. ommlds/nanochat/rustbpe/LICENSE +21 -0
  256. ommlds/nanochat/tokenizers.py +406 -0
  257. ommlds/server/cli.py +1 -2
  258. ommlds/server/server.py +5 -5
  259. ommlds/server/service.py +1 -1
  260. ommlds/specs/__init__.py +0 -0
  261. ommlds/specs/mcp/__init__.py +0 -0
  262. ommlds/specs/mcp/_marshal.py +23 -0
  263. ommlds/specs/mcp/clients.py +146 -0
  264. ommlds/specs/mcp/protocol.py +371 -0
  265. ommlds/tools/git.py +35 -12
  266. ommlds/tools/ocr.py +8 -9
  267. ommlds/wiki/analyze.py +6 -7
  268. ommlds/wiki/text/mfh.py +1 -5
  269. ommlds/wiki/text/wtp.py +1 -3
  270. ommlds/wiki/utils/xml.py +5 -5
  271. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/METADATA +24 -21
  272. ommlds-0.0.0.dev485.dist-info/RECORD +436 -0
  273. ommlds/cli/backends/standard.py +0 -20
  274. ommlds/cli/sessions/chat/base.py +0 -42
  275. ommlds/cli/sessions/chat/interactive.py +0 -73
  276. ommlds/cli/sessions/chat/printing.py +0 -96
  277. ommlds/cli/sessions/chat/prompt.py +0 -143
  278. ommlds/cli/sessions/chat/state.py +0 -109
  279. ommlds/cli/sessions/chat/tools.py +0 -91
  280. ommlds/cli/sessions/completion/completion.py +0 -44
  281. ommlds/cli/sessions/embedding/embedding.py +0 -42
  282. ommlds/cli/tools/config.py +0 -13
  283. ommlds/cli/tools/inject.py +0 -64
  284. ommlds/minichain/chat/stream/adapters.py +0 -69
  285. ommlds/minichain/lib/fs/ls/execution.py +0 -32
  286. ommlds-0.0.0.dev426.dist-info/RECORD +0 -303
  287. /ommlds/{cli/tools → backends/google}/__init__.py +0 -0
  288. /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
  289. /ommlds/{minichain/lib/fs/ls → cli/content}/__init__.py +0 -0
  290. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/rendering.py +0 -0
  291. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/running.py +0 -0
  292. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/WHEEL +0 -0
  293. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/entry_points.txt +0 -0
  294. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/licenses/LICENSE +0 -0
  295. {ommlds-0.0.0.dev426.dist-info → ommlds-0.0.0.dev485.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  # ruff: noqa: UP028
2
2
  import typing as ta
3
3
 
4
- from ..resources import Resources
4
+ from ..resources import UseResources
5
5
  from ..services import Request
6
6
  from ..services import Service
7
7
  from ..types import Output
8
- from .services import ResponseGenerator
9
8
  from .services import StreamResponse
9
+ from .services import StreamResponseSink
10
10
  from .services import new_stream_response
11
11
 
12
12
 
@@ -29,36 +29,34 @@ class WrappedStreamService(ta.Generic[StreamRequestT, V, OutputT, StreamOutputT]
29
29
 
30
30
  #
31
31
 
32
- def _process_request(self, request: StreamRequestT) -> StreamRequestT:
32
+ async def _process_request(self, request: StreamRequestT) -> StreamRequestT:
33
33
  return request
34
34
 
35
- def _process_stream_outputs(self, outputs: ta.Sequence[StreamOutputT]) -> ta.Sequence[StreamOutputT]:
35
+ async def _process_stream_outputs(self, outputs: ta.Sequence[StreamOutputT]) -> ta.Sequence[StreamOutputT]:
36
36
  return outputs
37
37
 
38
- def _process_vs(self, vs: ta.Iterator[V]) -> ta.Iterator[V]:
39
- return vs
38
+ async def _process_value(self, v: V) -> ta.Iterable[V]:
39
+ return [v]
40
40
 
41
- def _process_outputs(self, outputs: ta.Sequence[OutputT]) -> ta.Sequence[OutputT]:
41
+ async def _process_outputs(self, outputs: ta.Sequence[OutputT]) -> ta.Sequence[OutputT]:
42
42
  return outputs
43
43
 
44
44
  #
45
45
 
46
- def invoke(self, request: StreamRequestT) -> StreamResponse[V, OutputT, StreamOutputT]:
47
- with Resources.new() as rs:
48
- in_response = self._inner.invoke(self._process_request(request))
49
- in_vs: ResponseGenerator[V, OutputT] = rs.enter_context(in_response.v)
50
- out_vs = self._process_vs(in_vs)
51
-
52
- def yield_vs() -> ta.Generator[V, None, ta.Sequence[OutputT] | None]:
53
- while True:
54
- try:
55
- out_v = next(out_vs)
56
- except StopIteration as se:
57
- return self._process_outputs(se.value)
58
- yield out_v
59
-
60
- return new_stream_response(
46
+ async def invoke(self, request: StreamRequestT) -> StreamResponse[V, OutputT, StreamOutputT]:
47
+ async with UseResources.or_new(request.options) as rs: # noqa
48
+ in_resp = await self._inner.invoke(await self._process_request(request))
49
+ in_vs = await rs.enter_async_context(in_resp.v)
50
+
51
+ async def inner(sink: StreamResponseSink[V]) -> ta.Sequence[OutputT] | None:
52
+ async for in_v in in_vs:
53
+ for out_v in (await self._process_value(in_v)):
54
+ await sink.emit(out_v)
55
+
56
+ return await self._process_outputs(in_vs.outputs)
57
+
58
+ return await new_stream_response(
61
59
  rs,
62
- yield_vs(),
63
- self._process_stream_outputs(in_response.outputs),
60
+ inner,
61
+ await self._process_stream_outputs(in_resp.outputs),
64
62
  )
@@ -1,14 +1,15 @@
1
1
  # @omlish-llm-author gemini-2.5-pro
2
- import logging
3
2
  import re
4
3
 
4
+ from omlish.logs import all as logs
5
+
5
6
  from .types import ChatMsg
6
7
  from .utils import handle_think_tag_prelude
7
8
  from .utils import parse_json_from_string
8
9
  from .utils import process_tool_call
9
10
 
10
11
 
11
- log = logging.getLogger(__name__)
12
+ log = logs.get_module_logger(globals())
12
13
 
13
14
 
14
15
  ##
@@ -1,14 +1,15 @@
1
1
  # @omlish-llm-author gemini-2.5-pro
2
2
  import json
3
- import logging
4
3
  import re
5
4
 
5
+ from omlish.logs import all as logs
6
+
6
7
  from .types import ChatMsg
7
8
  from .types import ChatToolCall
8
9
  from .utils import parse_json_tool_calls
9
10
 
10
11
 
11
- log = logging.getLogger(__name__)
12
+ log = logs.get_module_logger(globals())
12
13
 
13
14
 
14
15
  ##
@@ -1,14 +1,15 @@
1
1
  # @omlish-llm-author gemini-2.5-pro
2
2
  import json
3
- import logging
4
3
  import re
5
4
  import typing as ta
6
5
 
6
+ from omlish.logs import all as logs
7
+
7
8
  from .types import ChatMsg
8
9
  from .types import ChatToolCall
9
10
 
10
11
 
11
- log = logging.getLogger(__name__)
12
+ log = logs.get_module_logger(globals())
12
13
 
13
14
 
14
15
  ##
@@ -8,7 +8,7 @@ from .types import ToolDtype
8
8
 
9
9
 
10
10
  @lang.static_init
11
- def _install_standard_marshalling() -> None:
11
+ def _install_standard_marshaling() -> None:
12
12
  tool_dtype_poly = msh.polymorphism_from_subclasses(ToolDtype, naming=msh.Naming.SNAKE)
13
13
  msh.install_standard_factories(
14
14
  msh.PolymorphismMarshalerFactory(tool_dtype_poly),
@@ -4,6 +4,7 @@ from omlish import check
4
4
  from omlish import dataclasses as dc
5
5
  from omlish import lang
6
6
 
7
+ from ...content.types import Content
7
8
  from ..fns import ToolFn
8
9
  from ..types import ToolSpec
9
10
  from .context import ToolContext
@@ -68,7 +69,7 @@ class ToolCatalog(ToolExecutor):
68
69
  ctx: ToolContext,
69
70
  name: str,
70
71
  args: ta.Mapping[str, ta.Any],
71
- ) -> str:
72
+ ) -> Content:
72
73
  e = self._by_name[name]
73
74
 
74
75
  return await e.executor().execute_tool(
@@ -2,8 +2,8 @@ import contextlib
2
2
  import contextvars
3
3
  import typing as ta
4
4
 
5
+ from omlish import check
5
6
  from omlish import collections as col
6
- from omlish import dataclasses as dc
7
7
  from omlish import lang
8
8
 
9
9
 
@@ -13,30 +13,34 @@ T = ta.TypeVar('T')
13
13
  ##
14
14
 
15
15
 
16
- @dc.dataclass(frozen=True)
17
16
  class ToolContext(lang.Final):
18
- dct: col.TypeMap = col.TypeMap()
17
+ def __init__(self, *items: ta.Any) -> None:
18
+ super().__init__()
19
19
 
20
- @classmethod
21
- def new(cls, *objs: ta.Any) -> 'ToolContext':
22
- return cls(col.TypeMap(objs))
20
+ self._dct: col.TypeMap = col.TypeMap(items)
21
+ if ToolContext in self._dct:
22
+ raise KeyError(ToolContext)
23
23
 
24
- #
24
+ def __repr__(self) -> str:
25
+ return f'{self.__class__.__name__}<{", ".join(ic.__name__ for ic in self._dct)}>'
25
26
 
26
27
  def __len__(self) -> int:
27
- return len(self.dct)
28
+ return len(self._dct)
28
29
 
29
30
  def __iter__(self) -> ta.Iterator[ta.Any]:
30
- return iter(self.dct)
31
+ return iter(self._dct)
32
+
33
+ def __contains__(self, ty: type[T]) -> bool:
34
+ return ty in self._dct
31
35
 
32
36
  def get(self, ty: type[T]) -> T | None:
33
- return self.dct.get(ty)
37
+ return self._dct.get(ty)
34
38
 
35
39
  def __getitem__(self, cls: type[T]) -> T:
36
- return self.dct[cls]
40
+ return self._dct[cls]
37
41
 
38
42
  def get_any(self, cls: type | tuple[type, ...]) -> ta.Sequence[T]:
39
- return self.dct.get_any(cls)
43
+ return self._dct.get_any(cls)
40
44
 
41
45
 
42
46
  ##
@@ -45,8 +49,24 @@ class ToolContext(lang.Final):
45
49
  _TOOL_CONTEXT: contextvars.ContextVar[ToolContext] = contextvars.ContextVar(f'{__name__}._TOOL_CONTEXT')
46
50
 
47
51
 
48
- @contextlib.contextmanager
49
- def bind_tool_context(ctx: ToolContext) -> ta.Generator[ToolContext]:
52
+ @ta.overload
53
+ def bind_tool_context(ctx: ToolContext) -> ta.ContextManager[ToolContext]:
54
+ ...
55
+
56
+
57
+ @ta.overload
58
+ def bind_tool_context(*items: ta.Any) -> ta.ContextManager[ToolContext]:
59
+ ...
60
+
61
+
62
+ @contextlib.contextmanager # type: ignore[misc]
63
+ def bind_tool_context(*args):
64
+ if args and isinstance(args[0], ToolContext):
65
+ check.arg(len(args) == 1)
66
+ ctx = args[0]
67
+ else:
68
+ ctx = ToolContext(*args)
69
+
50
70
  try:
51
71
  cur = _TOOL_CONTEXT.get()
52
72
  except LookupError:
@@ -0,0 +1,15 @@
1
+ import abc
2
+
3
+ from omlish import lang
4
+
5
+ from ...content.materialize import CanContent
6
+
7
+
8
+ ##
9
+
10
+
11
+ class ToolExecutionError(Exception, lang.Abstract):
12
+ @property
13
+ @abc.abstractmethod
14
+ def content(self) -> CanContent:
15
+ raise NotImplementedError
@@ -1,9 +1,14 @@
1
+ """
2
+ TODO:
3
+ - args is Mapping[str, Content] too
4
+ """
1
5
  import abc
2
6
  import typing as ta
3
7
 
4
8
  from omlish import dataclasses as dc
5
9
  from omlish import lang
6
10
 
11
+ from ...content.types import Content
7
12
  from ..fns import ToolFn
8
13
  from ..fns import execute_tool_fn
9
14
  from .context import ToolContext
@@ -20,7 +25,7 @@ class ToolExecutor(lang.Abstract):
20
25
  ctx: ToolContext,
21
26
  name: str,
22
27
  args: ta.Mapping[str, ta.Any],
23
- ) -> ta.Awaitable[str]:
28
+ ) -> ta.Awaitable[Content]:
24
29
  raise NotImplementedError
25
30
 
26
31
 
@@ -36,7 +41,7 @@ class ToolFnToolExecutor(ToolExecutor):
36
41
  ctx: ToolContext,
37
42
  name: str,
38
43
  args: ta.Mapping[str, ta.Any],
39
- ) -> str:
44
+ ) -> Content:
40
45
  with bind_tool_context(ctx):
41
46
  return await execute_tool_fn(
42
47
  self.tool_fn,
@@ -56,5 +61,5 @@ class NameSwitchedToolExecutor(ToolExecutor):
56
61
  ctx: ToolContext,
57
62
  name: str,
58
63
  args: ta.Mapping[str, ta.Any],
59
- ) -> str:
64
+ ) -> Content:
60
65
  return await self.tool_executors_by_name[name].execute_tool(ctx, name, args)
@@ -1,10 +1,13 @@
1
1
  """
2
2
  TODO:
3
+ - ta.type_hints
3
4
  """
4
5
  import inspect
5
6
  import typing as ta
6
7
 
7
8
  from omlish import lang
9
+ from omlish import marshal as msh
10
+ from omlish import reflect as rfl
8
11
 
9
12
  from ..fns import ToolFn
10
13
  from ..reflect import reflect_tool_spec
@@ -14,7 +17,13 @@ from .catalog import ToolCatalogEntry
14
17
  ##
15
18
 
16
19
 
17
- def reflect_tool_catalog_entry(fn: ta.Callable) -> ToolCatalogEntry:
20
+ def reflect_tool_catalog_entry(
21
+ fn: ta.Callable,
22
+ *,
23
+ marshal_input: bool = False,
24
+ marshal_output: bool = False,
25
+ no_marshal_check: bool = False,
26
+ ) -> ToolCatalogEntry:
18
27
  impl: ToolFn.Impl
19
28
  if lang.is_maysync(fn):
20
29
  impl = ToolFn.MaysyncImpl(fn)
@@ -23,15 +32,41 @@ def reflect_tool_catalog_entry(fn: ta.Callable) -> ToolCatalogEntry:
23
32
  else:
24
33
  impl = ToolFn.FnImpl(s=fn, a=lang.as_async(fn, wrap=True))
25
34
 
35
+ #
36
+
37
+ tf_input: ToolFn.Input
26
38
  sig = inspect.signature(fn)
27
- if sig.return_annotation is not str:
28
- raise NotImplementedError(fn)
39
+ if marshal_input:
40
+ in_rtys: dict[str, rfl.Type] = {}
41
+ for p in sig.parameters.values():
42
+ p_rty = rfl.type_(p.annotation)
43
+ if not no_marshal_check:
44
+ msh.global_marshaling().new_unmarshal_factory_context().make_unmarshaler(p_rty)
45
+ in_rtys[p.name] = p_rty
46
+ tf_input = ToolFn.MarshalInput(in_rtys)
47
+ else:
48
+ tf_input = ToolFn.RawKwargsInput()
49
+
50
+ #
51
+
52
+ tf_output: ToolFn.Output
53
+ if marshal_output:
54
+ out_rty = rfl.type_(sig.return_annotation)
55
+ if not no_marshal_check:
56
+ msh.global_marshaling().new_marshal_factory_context().make_marshaler(out_rty)
57
+ tf_output = ToolFn.MarshalOutput(out_rty)
58
+ else:
59
+ if sig.return_annotation is not str:
60
+ raise NotImplementedError(fn)
61
+ tf_output = ToolFn.RawStringOutput()
62
+
63
+ #
29
64
 
30
65
  return ToolCatalogEntry(
31
66
  reflect_tool_spec(fn),
32
67
  ToolFn(
33
68
  impl,
34
- ToolFn.KwargsInput(),
35
- ToolFn.RawStringOutput(),
69
+ tf_input,
70
+ tf_output,
36
71
  ),
37
72
  )
@@ -7,6 +7,11 @@ import typing as ta
7
7
  from omlish import check
8
8
  from omlish import dataclasses as dc
9
9
  from omlish import lang
10
+ from omlish import marshal as msh
11
+ from omlish import reflect as rfl
12
+
13
+ from ..content.json import JsonContent
14
+ from ..content.types import Content
10
15
 
11
16
 
12
17
  D = ta.TypeVar('D')
@@ -51,7 +56,11 @@ class ToolFn(lang.Final):
51
56
  check.arg(dc.is_dataclass(self.cls))
52
57
 
53
58
  @dc.dataclass(frozen=True)
54
- class KwargsInput(Input, lang.Final):
59
+ class MarshalInput(Input, lang.Final):
60
+ rtys: ta.Mapping[str, rfl.Type]
61
+
62
+ @dc.dataclass(frozen=True)
63
+ class RawKwargsInput(Input, lang.Final):
55
64
  pass
56
65
 
57
66
  input: Input
@@ -70,6 +79,10 @@ class ToolFn(lang.Final):
70
79
  check.isinstance(self.cls, type)
71
80
  check.arg(dc.is_dataclass(self.cls))
72
81
 
82
+ @dc.dataclass(frozen=True)
83
+ class MarshalOutput(Output, lang.Final):
84
+ rty: rfl.Type
85
+
73
86
  @dc.dataclass(frozen=True)
74
87
  class RawStringOutput(Output, lang.Final):
75
88
  pass
@@ -100,7 +113,7 @@ async def execute_tool_fn(
100
113
  args: ta.Mapping[str, ta.Any],
101
114
  *,
102
115
  forbid_sync_as_async: bool = False,
103
- ) -> str:
116
+ ) -> Content:
104
117
  m_fn: ta.Callable[..., ta.Awaitable[ta.Any]]
105
118
  if isinstance(tfn.impl, ToolFn.FnImpl):
106
119
  s_fn = tfn.impl.s
@@ -115,20 +128,44 @@ async def execute_tool_fn(
115
128
  else:
116
129
  raise TypeError(tfn.impl)
117
130
 
118
- out: ta.Any
131
+ #
132
+
133
+ fn_kw: ta.Mapping[str, ta.Any]
119
134
  if isinstance(tfn.input, ToolFn.DataclassInput):
120
135
  raise NotImplementedError
121
- elif isinstance(tfn.input, ToolFn.KwargsInput):
122
- out = await m_fn(**args)
136
+
137
+ elif isinstance(tfn.input, ToolFn.MarshalInput):
138
+ fn_kw_dct: dict[str, ta.Any] = {}
139
+ for k, v in args.items():
140
+ fn_kw_dct[k] = msh.unmarshal(v, tfn.input.rtys[k])
141
+ fn_kw = fn_kw_dct
142
+
143
+ elif isinstance(tfn.input, ToolFn.RawKwargsInput):
144
+ fn_kw = args
145
+
123
146
  else:
124
- raise NotImplementedError
147
+ raise TypeError(tfn.input)
125
148
 
126
- ret: str
149
+ #
150
+
151
+ fn_out = await m_fn(**fn_kw)
152
+
153
+ #
154
+
155
+ ret: Content
127
156
  if isinstance(tfn.output, ToolFn.DataclassOutput):
128
157
  raise NotImplementedError
158
+
159
+ elif isinstance(tfn.output, ToolFn.MarshalOutput):
160
+ out_v = msh.marshal(fn_out, tfn.output.rty)
161
+ ret = JsonContent(out_v)
162
+
129
163
  elif isinstance(tfn.output, ToolFn.RawStringOutput):
130
- ret = check.isinstance(out, str)
164
+ ret = check.isinstance(fn_out, str)
165
+
131
166
  else:
132
- raise NotImplementedError
167
+ raise TypeError(tfn.output)
168
+
169
+ #
133
170
 
134
171
  return ret
@@ -9,6 +9,7 @@ from ..content.prepare import default_content_str_preparer
9
9
  from .types import EnumToolDtype
10
10
  from .types import MappingToolDtype
11
11
  from .types import NullableToolDtype
12
+ from .types import ObjectToolDtype
12
13
  from .types import PrimitiveToolDtype
13
14
  from .types import SequenceToolDtype
14
15
  from .types import ToolDtype
@@ -72,15 +73,23 @@ class ToolJsonschemaRenderer:
72
73
  'enum': list(t.values),
73
74
  }
74
75
 
76
+ if isinstance(t, ObjectToolDtype):
77
+ return {
78
+ 'type': 'object',
79
+ 'properties': {
80
+ k: self.render_type(v)
81
+ for k, v in t.fields.items()
82
+ },
83
+ }
84
+
75
85
  raise TypeError(t)
76
86
 
77
87
  def render_tool_params(self, ts: ToolSpec) -> dict:
78
- pr_dct: dict[str, dict] | None = None
88
+ pr_dct: dict[str, dict] = {}
79
89
  req_lst: list[str] | None = None
80
- if ts.params is not None:
81
- pr_dct = {}
90
+ if ts.params:
82
91
  req_lst = []
83
- for p in ts.params or []:
92
+ for p in ts.params:
84
93
  pr_dct[check.non_empty_str(p.name)] = {
85
94
  **({'description': self._content_str_preparer.prepare_str(p.desc)} if p.desc is not None else {}),
86
95
  **(self.render_type(p.type) if p.type is not None else {}),
@@ -90,7 +99,7 @@ class ToolJsonschemaRenderer:
90
99
 
91
100
  return {
92
101
  'type': 'object',
93
- **({'properties': pr_dct} if pr_dct is not None else {}),
102
+ 'properties': pr_dct,
94
103
  **({'required': req_lst} if req_lst is not None else {}),
95
104
  # By default any additional properties are allowed.
96
105
  # https://json-schema.org/understanding-json-schema/reference/object#additionalproperties
@@ -15,17 +15,19 @@ 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
25
25
 
26
+ from ..content.materialize import CanContent
26
27
  from .types import EnumToolDtype
27
28
  from .types import MappingToolDtype
28
29
  from .types import NullableToolDtype
30
+ from .types import ObjectToolDtype
29
31
  from .types import PrimitiveToolDtype
30
32
  from .types import SequenceToolDtype
31
33
  from .types import ToolDtype
@@ -35,6 +37,10 @@ from .types import TupleToolDtype
35
37
  from .types import UnionToolDtype
36
38
 
37
39
 
40
+ with lang.auto_proxy_import(globals()):
41
+ from omdev.py import docstrings
42
+
43
+
38
44
  ##
39
45
 
40
46
 
@@ -108,6 +114,12 @@ class ToolReflector:
108
114
  ])
109
115
 
110
116
  def reflect_type(self, rty: rfl.Type) -> ToolDtype:
117
+ if isinstance(rty, type) and dc.is_dataclass(rty):
118
+ return ObjectToolDtype({
119
+ f.name: self.reflect_type(rfl.type_(f.type))
120
+ for f in dc.fields(rty)
121
+ })
122
+
111
123
  if isinstance(rty, (type, rfl.Any)):
112
124
  return PrimitiveToolDtype.of(rty)
113
125
 
@@ -147,10 +159,10 @@ class ToolReflector:
147
159
 
148
160
  #
149
161
 
150
- def _prepare_desc(self, s: str | None) -> str | None:
162
+ def _prepare_desc(self, s: CanContent) -> CanContent:
151
163
  if s is None:
152
164
  return None
153
- if not self._raw_descs:
165
+ if not self._raw_descs and isinstance(s, str):
154
166
  s = s.strip()
155
167
  return s
156
168
 
@@ -160,14 +172,22 @@ class ToolReflector:
160
172
 
161
173
  #
162
174
 
175
+ p_ovr_dct: dict[str, dict[str, ta.Any]] = {}
163
176
  ts_ovr: dict[str, ta.Any] = {}
177
+ o: _ToolSpecOverride
164
178
  for o in md.get_object_metadata(fn, type=_ToolSpecOverride):
165
- # TODO: better params handling / merging
166
179
  ts_ovr.update({
167
180
  k: v
168
181
  for k, v in dc.asdict(o).items()
169
- if v is not None
182
+ if k != 'params'
183
+ and v is not None
170
184
  })
185
+ for op in (o.params or []):
186
+ p_ovr_dct.setdefault(check.non_empty_str(op.name), {}).update({
187
+ k: v
188
+ for k, v in dc.asdict(op).items()
189
+ if v is not None
190
+ })
171
191
 
172
192
  #
173
193
 
@@ -207,23 +227,39 @@ class ToolReflector:
207
227
  if 'params' not in ts_kw:
208
228
  ds_p_dct = {
209
229
  ds_p.arg_name: ds_p
210
- for ds_p in (ds.params if ds is not None else [])
230
+ for ds_p in (ds.params if ds is not None else {})
211
231
  }
212
232
 
213
- params: dict[str, ToolParam] = {}
214
- for sig_p in sig().parameters.values():
215
- check.not_in(sig_p.name, params)
216
-
217
- ds_p = ds_p_dct.get(sig_p.name)
233
+ sig_p_dct = sig().parameters
218
234
 
219
- params[sig_p.name] = ToolParam(
220
- sig_p.name,
235
+ pns: list[str] = list({**p_ovr_dct, **ds_p_dct, **sig_p_dct})
221
236
 
222
- desc=self._prepare_desc(ds_p.description) if ds_p is not None else None,
223
-
224
- type=self.reflect_type(rfl.type_(th()[sig_p.name])) if sig_p.name in th() else None,
225
-
226
- required=sig_p.default is inspect.Parameter.empty,
237
+ params: dict[str, ToolParam] = {}
238
+ for pn in pns:
239
+ ovr_p = p_ovr_dct.get(pn, {})
240
+ ds_p = ds_p_dct.get(pn)
241
+ sig_p = sig_p_dct.get(pn)
242
+
243
+ p_desc: CanContent
244
+ if (p_desc := ovr_p.get('desc')) is None:
245
+ if ds_p is not None:
246
+ p_desc = ds_p.description
247
+
248
+ p_type: ToolDtype | None
249
+ if (p_type := ovr_p.get('type')) is None:
250
+ if sig_p is not None and sig_p.name in th():
251
+ p_type = self.reflect_type(rfl.type_(th()[sig_p.name]))
252
+
253
+ p_required: bool | None
254
+ if (p_required := ovr_p.get('required')) is None:
255
+ if sig_p is not None:
256
+ p_required = sig_p.default is inspect.Parameter.empty
257
+
258
+ params[pn] = ToolParam(
259
+ pn,
260
+ desc=self._prepare_desc(p_desc) if p_desc is not None else None,
261
+ type=p_type,
262
+ required=p_required,
227
263
  )
228
264
 
229
265
  ts_kw.update(params=tuple(params.values()) if params else None)