ommlds 0.0.0.dev436__py3-none-any.whl → 0.0.0.dev480__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 (271) hide show
  1. ommlds/.omlish-manifests.json +332 -35
  2. ommlds/__about__.py +15 -9
  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/_marshal.py +2 -2
  9. ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
  10. ommlds/backends/anthropic/protocol/sse/assemble.py +23 -7
  11. ommlds/backends/anthropic/protocol/sse/events.py +13 -0
  12. ommlds/backends/anthropic/protocol/types.py +30 -9
  13. ommlds/backends/google/protocol/__init__.py +3 -0
  14. ommlds/backends/google/protocol/_marshal.py +16 -0
  15. ommlds/backends/google/protocol/types.py +626 -0
  16. ommlds/backends/groq/_marshal.py +23 -0
  17. ommlds/backends/groq/protocol.py +249 -0
  18. ommlds/backends/mlx/generation.py +1 -1
  19. ommlds/backends/mlx/loading.py +58 -1
  20. ommlds/backends/ollama/__init__.py +0 -0
  21. ommlds/backends/ollama/protocol.py +170 -0
  22. ommlds/backends/openai/protocol/__init__.py +9 -28
  23. ommlds/backends/openai/protocol/_common.py +18 -0
  24. ommlds/backends/openai/protocol/_marshal.py +27 -0
  25. ommlds/backends/openai/protocol/chatcompletion/chunk.py +58 -31
  26. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +49 -44
  27. ommlds/backends/openai/protocol/chatcompletion/message.py +55 -43
  28. ommlds/backends/openai/protocol/chatcompletion/request.py +114 -66
  29. ommlds/backends/openai/protocol/chatcompletion/response.py +71 -45
  30. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +27 -20
  31. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +16 -7
  32. ommlds/backends/openai/protocol/completionusage.py +24 -15
  33. ommlds/backends/tavily/__init__.py +0 -0
  34. ommlds/backends/tavily/protocol.py +301 -0
  35. ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
  36. ommlds/backends/transformers/__init__.py +0 -0
  37. ommlds/backends/transformers/filecache.py +109 -0
  38. ommlds/backends/transformers/streamers.py +73 -0
  39. ommlds/cli/asyncs.py +30 -0
  40. ommlds/cli/backends/catalog.py +93 -0
  41. ommlds/cli/backends/configs.py +9 -0
  42. ommlds/cli/backends/inject.py +31 -36
  43. ommlds/cli/backends/injection.py +16 -0
  44. ommlds/cli/backends/types.py +46 -0
  45. ommlds/cli/content/__init__.py +0 -0
  46. ommlds/cli/content/messages.py +34 -0
  47. ommlds/cli/content/strings.py +42 -0
  48. ommlds/cli/inject.py +15 -32
  49. ommlds/cli/inputs/__init__.py +0 -0
  50. ommlds/cli/inputs/asyncs.py +32 -0
  51. ommlds/cli/inputs/sync.py +75 -0
  52. ommlds/cli/main.py +270 -110
  53. ommlds/cli/rendering/__init__.py +0 -0
  54. ommlds/cli/rendering/configs.py +9 -0
  55. ommlds/cli/rendering/inject.py +31 -0
  56. ommlds/cli/rendering/markdown.py +52 -0
  57. ommlds/cli/rendering/raw.py +73 -0
  58. ommlds/cli/rendering/types.py +21 -0
  59. ommlds/cli/secrets.py +21 -0
  60. ommlds/cli/sessions/base.py +1 -1
  61. ommlds/cli/sessions/chat/chat/__init__.py +0 -0
  62. ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
  63. ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
  64. ommlds/cli/sessions/chat/chat/ai/inject.py +74 -0
  65. ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
  66. ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
  67. ommlds/cli/sessions/chat/chat/ai/services.py +79 -0
  68. ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
  69. ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
  70. ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
  71. ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
  72. ommlds/cli/sessions/chat/chat/state/inject.py +36 -0
  73. ommlds/cli/sessions/chat/chat/state/inmemory.py +33 -0
  74. ommlds/cli/sessions/chat/chat/state/storage.py +52 -0
  75. ommlds/cli/sessions/chat/chat/state/types.py +38 -0
  76. ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
  77. ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
  78. ommlds/cli/sessions/chat/chat/user/inject.py +62 -0
  79. ommlds/cli/sessions/chat/chat/user/interactive.py +31 -0
  80. ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
  81. ommlds/cli/sessions/chat/chat/user/types.py +15 -0
  82. ommlds/cli/sessions/chat/configs.py +27 -0
  83. ommlds/cli/sessions/chat/driver.py +43 -0
  84. ommlds/cli/sessions/chat/inject.py +33 -65
  85. ommlds/cli/sessions/chat/phases/__init__.py +0 -0
  86. ommlds/cli/sessions/chat/phases/inject.py +27 -0
  87. ommlds/cli/sessions/chat/phases/injection.py +14 -0
  88. ommlds/cli/sessions/chat/phases/manager.py +29 -0
  89. ommlds/cli/sessions/chat/phases/types.py +29 -0
  90. ommlds/cli/sessions/chat/session.py +27 -0
  91. ommlds/cli/sessions/chat/tools/__init__.py +0 -0
  92. ommlds/cli/sessions/chat/tools/configs.py +22 -0
  93. ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
  94. ommlds/cli/sessions/chat/tools/execution.py +66 -0
  95. ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
  96. ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
  97. ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
  98. ommlds/cli/sessions/chat/tools/inject.py +88 -0
  99. ommlds/cli/sessions/chat/tools/injection.py +44 -0
  100. ommlds/cli/sessions/chat/tools/rendering.py +58 -0
  101. ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
  102. ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
  103. ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
  104. ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
  105. ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
  106. ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
  107. ommlds/cli/{tools/weather.py → sessions/chat/tools/weather/tools.py} +1 -1
  108. ommlds/cli/sessions/completion/configs.py +21 -0
  109. ommlds/cli/sessions/completion/inject.py +42 -0
  110. ommlds/cli/sessions/completion/session.py +35 -0
  111. ommlds/cli/sessions/embedding/configs.py +21 -0
  112. ommlds/cli/sessions/embedding/inject.py +42 -0
  113. ommlds/cli/sessions/embedding/session.py +33 -0
  114. ommlds/cli/sessions/inject.py +28 -11
  115. ommlds/cli/state/__init__.py +0 -0
  116. ommlds/cli/state/inject.py +28 -0
  117. ommlds/cli/{state.py → state/storage.py} +41 -24
  118. ommlds/minichain/__init__.py +84 -24
  119. ommlds/minichain/_marshal.py +49 -9
  120. ommlds/minichain/_typedvalues.py +2 -4
  121. ommlds/minichain/backends/catalogs/base.py +20 -1
  122. ommlds/minichain/backends/catalogs/simple.py +2 -2
  123. ommlds/minichain/backends/catalogs/strings.py +10 -8
  124. ommlds/minichain/backends/impls/anthropic/chat.py +65 -27
  125. ommlds/minichain/backends/impls/anthropic/names.py +10 -8
  126. ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
  127. ommlds/minichain/backends/impls/anthropic/stream.py +111 -43
  128. ommlds/minichain/backends/impls/duckduckgo/search.py +1 -1
  129. ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
  130. ommlds/minichain/backends/impls/dummy/chat.py +69 -0
  131. ommlds/minichain/backends/impls/google/chat.py +114 -22
  132. ommlds/minichain/backends/impls/google/search.py +7 -2
  133. ommlds/minichain/backends/impls/google/stream.py +219 -0
  134. ommlds/minichain/backends/impls/google/tools.py +149 -0
  135. ommlds/minichain/backends/impls/groq/__init__.py +0 -0
  136. ommlds/minichain/backends/impls/groq/chat.py +75 -0
  137. ommlds/minichain/backends/impls/groq/names.py +48 -0
  138. ommlds/minichain/backends/impls/groq/protocol.py +143 -0
  139. ommlds/minichain/backends/impls/groq/stream.py +125 -0
  140. ommlds/minichain/backends/impls/llamacpp/chat.py +33 -18
  141. ommlds/minichain/backends/impls/llamacpp/completion.py +1 -1
  142. ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
  143. ommlds/minichain/backends/impls/llamacpp/stream.py +37 -20
  144. ommlds/minichain/backends/impls/mistral.py +20 -5
  145. ommlds/minichain/backends/impls/mlx/chat.py +96 -22
  146. ommlds/minichain/backends/impls/ollama/__init__.py +0 -0
  147. ommlds/minichain/backends/impls/ollama/chat.py +199 -0
  148. ommlds/minichain/backends/impls/openai/chat.py +18 -8
  149. ommlds/minichain/backends/impls/openai/completion.py +10 -3
  150. ommlds/minichain/backends/impls/openai/embedding.py +10 -3
  151. ommlds/minichain/backends/impls/openai/format.py +131 -106
  152. ommlds/minichain/backends/impls/openai/names.py +31 -5
  153. ommlds/minichain/backends/impls/openai/stream.py +43 -25
  154. ommlds/minichain/backends/impls/tavily.py +66 -0
  155. ommlds/minichain/backends/impls/tinygrad/chat.py +23 -16
  156. ommlds/minichain/backends/impls/transformers/sentence.py +1 -1
  157. ommlds/minichain/backends/impls/transformers/tokens.py +1 -1
  158. ommlds/minichain/backends/impls/transformers/transformers.py +155 -34
  159. ommlds/minichain/backends/strings/parsing.py +1 -1
  160. ommlds/minichain/backends/strings/resolving.py +4 -1
  161. ommlds/minichain/chat/_marshal.py +16 -9
  162. ommlds/minichain/chat/choices/adapters.py +4 -4
  163. ommlds/minichain/chat/choices/services.py +1 -1
  164. ommlds/minichain/chat/choices/stream/__init__.py +0 -0
  165. ommlds/minichain/chat/choices/stream/adapters.py +35 -0
  166. ommlds/minichain/chat/choices/stream/joining.py +31 -0
  167. ommlds/minichain/chat/choices/stream/services.py +45 -0
  168. ommlds/minichain/chat/choices/stream/types.py +43 -0
  169. ommlds/minichain/chat/choices/types.py +2 -2
  170. ommlds/minichain/chat/history.py +3 -3
  171. ommlds/minichain/chat/messages.py +55 -19
  172. ommlds/minichain/chat/services.py +3 -3
  173. ommlds/minichain/chat/stream/_marshal.py +16 -0
  174. ommlds/minichain/chat/stream/joining.py +85 -0
  175. ommlds/minichain/chat/stream/services.py +15 -21
  176. ommlds/minichain/chat/stream/types.py +32 -19
  177. ommlds/minichain/chat/tools/execution.py +8 -7
  178. ommlds/minichain/chat/tools/ids.py +9 -15
  179. ommlds/minichain/chat/tools/parsing.py +17 -26
  180. ommlds/minichain/chat/transforms/base.py +29 -38
  181. ommlds/minichain/chat/transforms/metadata.py +30 -4
  182. ommlds/minichain/chat/transforms/services.py +9 -11
  183. ommlds/minichain/content/_marshal.py +44 -20
  184. ommlds/minichain/content/json.py +13 -0
  185. ommlds/minichain/content/materialize.py +14 -21
  186. ommlds/minichain/content/prepare.py +4 -0
  187. ommlds/minichain/content/transforms/interleave.py +1 -1
  188. ommlds/minichain/content/transforms/squeeze.py +1 -1
  189. ommlds/minichain/content/transforms/stringify.py +1 -1
  190. ommlds/minichain/json.py +20 -0
  191. ommlds/minichain/lib/code/__init__.py +0 -0
  192. ommlds/minichain/lib/code/prompts.py +6 -0
  193. ommlds/minichain/lib/fs/binfiles.py +108 -0
  194. ommlds/minichain/lib/fs/context.py +126 -0
  195. ommlds/minichain/lib/fs/errors.py +101 -0
  196. ommlds/minichain/lib/fs/suggestions.py +36 -0
  197. ommlds/minichain/lib/fs/tools/__init__.py +0 -0
  198. ommlds/minichain/lib/fs/tools/edit.py +104 -0
  199. ommlds/minichain/lib/fs/tools/ls.py +38 -0
  200. ommlds/minichain/lib/fs/tools/read.py +115 -0
  201. ommlds/minichain/lib/fs/tools/recursivels/__init__.py +0 -0
  202. ommlds/minichain/lib/fs/tools/recursivels/execution.py +40 -0
  203. ommlds/minichain/lib/todo/__init__.py +0 -0
  204. ommlds/minichain/lib/todo/context.py +54 -0
  205. ommlds/minichain/lib/todo/tools/__init__.py +0 -0
  206. ommlds/minichain/lib/todo/tools/read.py +44 -0
  207. ommlds/minichain/lib/todo/tools/write.py +335 -0
  208. ommlds/minichain/lib/todo/types.py +60 -0
  209. ommlds/minichain/llms/_marshal.py +25 -17
  210. ommlds/minichain/llms/types.py +4 -0
  211. ommlds/minichain/registries/globals.py +18 -4
  212. ommlds/minichain/resources.py +66 -43
  213. ommlds/minichain/search.py +1 -1
  214. ommlds/minichain/services/_marshal.py +46 -39
  215. ommlds/minichain/services/facades.py +3 -3
  216. ommlds/minichain/services/services.py +1 -1
  217. ommlds/minichain/standard.py +8 -0
  218. ommlds/minichain/stream/services.py +152 -38
  219. ommlds/minichain/stream/wrap.py +22 -24
  220. ommlds/minichain/tools/_marshal.py +1 -1
  221. ommlds/minichain/tools/execution/catalog.py +2 -1
  222. ommlds/minichain/tools/execution/context.py +34 -14
  223. ommlds/minichain/tools/execution/errors.py +15 -0
  224. ommlds/minichain/tools/execution/executors.py +8 -3
  225. ommlds/minichain/tools/execution/reflect.py +40 -5
  226. ommlds/minichain/tools/fns.py +46 -9
  227. ommlds/minichain/tools/jsonschema.py +14 -5
  228. ommlds/minichain/tools/reflect.py +54 -18
  229. ommlds/minichain/tools/types.py +33 -1
  230. ommlds/minichain/utils.py +27 -0
  231. ommlds/minichain/vectors/_marshal.py +11 -10
  232. ommlds/nanochat/LICENSE +21 -0
  233. ommlds/nanochat/__init__.py +0 -0
  234. ommlds/nanochat/rustbpe/LICENSE +21 -0
  235. ommlds/nanochat/tokenizers.py +406 -0
  236. ommlds/server/server.py +3 -3
  237. ommlds/specs/__init__.py +0 -0
  238. ommlds/specs/mcp/__init__.py +0 -0
  239. ommlds/specs/mcp/_marshal.py +23 -0
  240. ommlds/specs/mcp/protocol.py +266 -0
  241. ommlds/tools/git.py +27 -10
  242. ommlds/tools/ocr.py +8 -9
  243. ommlds/wiki/analyze.py +2 -2
  244. ommlds/wiki/text/mfh.py +1 -5
  245. ommlds/wiki/text/wtp.py +1 -3
  246. ommlds/wiki/utils/xml.py +5 -5
  247. {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/METADATA +24 -21
  248. ommlds-0.0.0.dev480.dist-info/RECORD +427 -0
  249. ommlds/cli/backends/standard.py +0 -20
  250. ommlds/cli/sessions/chat/base.py +0 -42
  251. ommlds/cli/sessions/chat/interactive.py +0 -73
  252. ommlds/cli/sessions/chat/printing.py +0 -96
  253. ommlds/cli/sessions/chat/prompt.py +0 -143
  254. ommlds/cli/sessions/chat/state.py +0 -109
  255. ommlds/cli/sessions/chat/tools.py +0 -91
  256. ommlds/cli/sessions/completion/completion.py +0 -44
  257. ommlds/cli/sessions/embedding/embedding.py +0 -42
  258. ommlds/cli/tools/config.py +0 -13
  259. ommlds/cli/tools/inject.py +0 -64
  260. ommlds/minichain/chat/stream/adapters.py +0 -69
  261. ommlds/minichain/lib/fs/ls/execution.py +0 -32
  262. ommlds-0.0.0.dev436.dist-info/RECORD +0 -303
  263. /ommlds/{cli/tools → backends/google}/__init__.py +0 -0
  264. /ommlds/{minichain/lib/fs/ls → backends/groq}/__init__.py +0 -0
  265. /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
  266. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/rendering.py +0 -0
  267. /ommlds/minichain/lib/fs/{ls → tools/recursivels}/running.py +0 -0
  268. {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/WHEEL +0 -0
  269. {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/entry_points.txt +0 -0
  270. {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/licenses/LICENSE +0 -0
  271. {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/top_level.txt +0 -0
@@ -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)
@@ -6,11 +6,14 @@ from omlish import cached
6
6
  from omlish import check
7
7
  from omlish import collections as col
8
8
  from omlish import dataclasses as dc
9
+ from omlish import dispatch
9
10
  from omlish import lang
10
11
  from omlish import marshal as msh
11
12
  from omlish import reflect as rfl
12
13
 
13
14
  from ..content.materialize import CanContent
15
+ from ..content.transforms.base import ContentTransform
16
+ from ..content.types import Content
14
17
 
15
18
 
16
19
  msh.register_global_module_import('._marshal', __package__)
@@ -127,6 +130,15 @@ class EnumToolDtype(ToolDtype):
127
130
  values: ta.Sequence[ta.Any]
128
131
 
129
132
 
133
+ #
134
+
135
+
136
+ @dc.dataclass(frozen=True)
137
+ @dc.extra_class_params(terse_repr=True)
138
+ class ObjectToolDtype(ToolDtype):
139
+ fields: ta.Mapping[str, ToolDtype]
140
+
141
+
130
142
  ##
131
143
 
132
144
 
@@ -177,9 +189,29 @@ class ToolSpec:
177
189
 
178
190
 
179
191
  @dc.dataclass(frozen=True, kw_only=True)
180
- class ToolExecRequest(lang.Final):
192
+ class ToolUse(lang.Final):
181
193
  id: str | None = None
182
194
  name: str
183
195
  args: ta.Mapping[str, ta.Any]
184
196
 
185
197
  raw_args: str | None = None
198
+
199
+
200
+ @dc.dataclass(frozen=True, kw_only=True)
201
+ class ToolUseResult(lang.Final):
202
+ id: str | None = None
203
+ name: str
204
+ c: Content
205
+
206
+
207
+ ##
208
+
209
+
210
+ class _ToolUseContentTransform(ContentTransform, lang.Final, lang.NotInstantiable):
211
+ @dispatch.install_method(ContentTransform.apply)
212
+ def apply_tool_use(self, tu: ToolUse) -> ToolUse:
213
+ return tu # TODO: args are Content
214
+
215
+ @dispatch.install_method(ContentTransform.apply)
216
+ def apply_tool_use_result(self, tur: ToolUseResult) -> ToolUseResult:
217
+ return dc.replace(tur, c=self.apply(tur.c)) # noqa
@@ -0,0 +1,27 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import reflect as rfl
5
+
6
+
7
+ ##
8
+
9
+
10
+ def join_human_readable_str_list(
11
+ strs: ta.Iterable[str],
12
+ *,
13
+ sep: str = ', ',
14
+ disj: str = 'or ',
15
+ ) -> str:
16
+ seq = list(strs)
17
+ return sep.join(
18
+ (disj if i == len(seq) - 1 else '') + s
19
+ for i, s in enumerate(seq)
20
+ )
21
+
22
+
23
+ ##
24
+
25
+
26
+ def str_literal_values(lit: ta.Any) -> ta.Sequence[str]:
27
+ return tuple(check.isinstance(rfl.type_(lit), rfl.Literal).args)
@@ -9,7 +9,6 @@ from omlish import check
9
9
  from omlish import lang
10
10
  from omlish import marshal as msh
11
11
  from omlish import reflect as rfl
12
- from omlish.funcs import match as mfs
13
12
 
14
13
  from .types import Vector
15
14
 
@@ -25,10 +24,11 @@ class _VectorMarshaler(msh.Marshaler):
25
24
  return self.et.marshal(ctx, list(map(float, check.isinstance(o, Vector))))
26
25
 
27
26
 
28
- class _VectorMarshalerFactory(msh.MarshalerFactoryMatchClass):
29
- @mfs.simple(lambda _, ctx, rty: rty is Vector)
30
- def _build(self, ctx: msh.MarshalContext, rty: rfl.Type) -> msh.Marshaler:
31
- return _VectorMarshaler(ctx.make(ta.Sequence[float]))
27
+ class _VectorMarshalerFactory(msh.MarshalerFactory):
28
+ def make_marshaler(self, ctx: msh.MarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Marshaler] | None:
29
+ if rty is not Vector:
30
+ return None
31
+ return lambda: _VectorMarshaler(ctx.make_marshaler(ta.Sequence[float]))
32
32
 
33
33
 
34
34
  @dc.dataclass(frozen=True)
@@ -39,17 +39,18 @@ class _VectorUnmarshaler(msh.Unmarshaler):
39
39
  return Vector(self.et.unmarshal(ctx, v))
40
40
 
41
41
 
42
- class _VectorUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
43
- @mfs.simple(lambda _, ctx, rty: rty is Vector)
44
- def _build(self, ctx: msh.UnmarshalContext, rty: rfl.Type) -> msh.Unmarshaler:
45
- return _VectorUnmarshaler(ctx.make(ta.Sequence[float]))
42
+ class _VectorUnmarshalerFactory(msh.UnmarshalerFactory):
43
+ def make_unmarshaler(self, ctx: msh.UnmarshalFactoryContext, rty: rfl.Type) -> ta.Callable[[], msh.Unmarshaler] | None: # noqa
44
+ if rty is not Vector:
45
+ return None
46
+ return lambda: _VectorUnmarshaler(ctx.make_unmarshaler(ta.Sequence[float]))
46
47
 
47
48
 
48
49
  ##
49
50
 
50
51
 
51
52
  @lang.static_init
52
- def _install_standard_marshalling() -> None:
53
+ def _install_standard_marshaling() -> None:
53
54
  msh.install_standard_factories(
54
55
  _VectorMarshalerFactory(),
55
56
  _VectorUnmarshalerFactory(),
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Andrej Karpathy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
File without changes