ommlds 0.0.0.dev480__py3-none-any.whl → 0.0.0.dev503__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 (277) hide show
  1. ommlds/.omlish-manifests.json +100 -33
  2. ommlds/README.md +11 -0
  3. ommlds/__about__.py +9 -6
  4. ommlds/backends/anthropic/protocol/__init__.py +13 -1
  5. ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
  6. ommlds/backends/anthropic/protocol/sse/events.py +2 -0
  7. ommlds/backends/cerebras/__init__.py +7 -0
  8. ommlds/backends/cerebras/_dataclasses.py +4254 -0
  9. ommlds/backends/cerebras/_marshal.py +24 -0
  10. ommlds/backends/cerebras/protocol.py +312 -0
  11. ommlds/backends/google/protocol/__init__.py +13 -0
  12. ommlds/backends/google/protocol/_dataclasses.py +5997 -0
  13. ommlds/backends/groq/__init__.py +7 -0
  14. ommlds/backends/groq/_dataclasses.py +3901 -0
  15. ommlds/backends/groq/clients.py +9 -0
  16. ommlds/backends/llamacpp/logging.py +4 -1
  17. ommlds/backends/mlx/caching.py +7 -3
  18. ommlds/backends/mlx/cli.py +10 -7
  19. ommlds/backends/mlx/generation.py +18 -16
  20. ommlds/backends/mlx/limits.py +10 -6
  21. ommlds/backends/mlx/loading.py +7 -4
  22. ommlds/backends/ollama/__init__.py +7 -0
  23. ommlds/backends/ollama/_dataclasses.py +3488 -0
  24. ommlds/backends/ollama/protocol.py +3 -0
  25. ommlds/backends/openai/protocol/__init__.py +15 -1
  26. ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
  27. ommlds/backends/tavily/__init__.py +7 -0
  28. ommlds/backends/tavily/_dataclasses.py +1734 -0
  29. ommlds/backends/transformers/__init__.py +14 -0
  30. ommlds/cli/__init__.py +7 -0
  31. ommlds/cli/_dataclasses.py +3515 -0
  32. ommlds/cli/backends/catalog.py +0 -5
  33. ommlds/cli/backends/inject.py +70 -7
  34. ommlds/cli/backends/meta.py +82 -0
  35. ommlds/cli/content/messages.py +1 -1
  36. ommlds/cli/inject.py +11 -3
  37. ommlds/cli/main.py +137 -68
  38. ommlds/cli/rendering/types.py +6 -0
  39. ommlds/cli/secrets.py +2 -1
  40. ommlds/cli/sessions/base.py +1 -10
  41. ommlds/cli/sessions/chat/configs.py +9 -17
  42. ommlds/cli/sessions/chat/{chat → drivers}/ai/configs.py +3 -1
  43. ommlds/cli/sessions/chat/drivers/ai/events.py +57 -0
  44. ommlds/cli/sessions/chat/{chat → drivers}/ai/inject.py +10 -3
  45. ommlds/cli/sessions/chat/{chat → drivers}/ai/rendering.py +1 -1
  46. ommlds/cli/sessions/chat/{chat → drivers}/ai/services.py +1 -1
  47. ommlds/cli/sessions/chat/{chat → drivers}/ai/tools.py +4 -8
  48. ommlds/cli/sessions/chat/{chat → drivers}/ai/types.py +9 -0
  49. ommlds/cli/sessions/chat/drivers/configs.py +25 -0
  50. ommlds/cli/sessions/chat/drivers/events/inject.py +27 -0
  51. ommlds/cli/sessions/chat/drivers/events/injection.py +14 -0
  52. ommlds/cli/sessions/chat/drivers/events/manager.py +16 -0
  53. ommlds/cli/sessions/chat/drivers/events/types.py +38 -0
  54. ommlds/cli/sessions/chat/drivers/impl.py +50 -0
  55. ommlds/cli/sessions/chat/drivers/inject.py +70 -0
  56. ommlds/cli/sessions/chat/{chat → drivers}/state/configs.py +2 -0
  57. ommlds/cli/sessions/chat/drivers/state/ids.py +25 -0
  58. ommlds/cli/sessions/chat/drivers/state/inject.py +83 -0
  59. ommlds/cli/sessions/chat/{chat → drivers}/state/inmemory.py +0 -4
  60. ommlds/cli/sessions/chat/{chat → drivers}/state/storage.py +17 -10
  61. ommlds/cli/sessions/chat/{chat → drivers}/state/types.py +10 -5
  62. ommlds/cli/sessions/chat/{tools → drivers/tools}/configs.py +2 -2
  63. ommlds/cli/sessions/chat/drivers/tools/confirmation.py +44 -0
  64. ommlds/cli/sessions/chat/drivers/tools/errorhandling.py +39 -0
  65. ommlds/cli/sessions/chat/{tools → drivers/tools}/execution.py +3 -4
  66. ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/inject.py +3 -3
  67. ommlds/cli/sessions/chat/{tools → drivers/tools}/inject.py +7 -12
  68. ommlds/cli/sessions/chat/{tools → drivers/tools}/injection.py +5 -5
  69. ommlds/cli/sessions/chat/{tools → drivers/tools}/rendering.py +3 -3
  70. ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/inject.py +3 -3
  71. ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/tools.py +1 -1
  72. ommlds/cli/sessions/chat/drivers/types.py +31 -0
  73. ommlds/cli/sessions/chat/{chat → drivers}/user/configs.py +0 -3
  74. ommlds/cli/sessions/chat/drivers/user/inject.py +41 -0
  75. ommlds/cli/sessions/chat/facades/__init__.py +0 -0
  76. ommlds/cli/sessions/chat/facades/commands/__init__.py +0 -0
  77. ommlds/cli/sessions/chat/facades/commands/base.py +83 -0
  78. ommlds/cli/sessions/chat/facades/commands/configs.py +9 -0
  79. ommlds/cli/sessions/chat/facades/commands/inject.py +41 -0
  80. ommlds/cli/sessions/chat/facades/commands/injection.py +15 -0
  81. ommlds/cli/sessions/chat/facades/commands/manager.py +59 -0
  82. ommlds/cli/sessions/chat/facades/commands/simple.py +34 -0
  83. ommlds/cli/sessions/chat/facades/commands/types.py +13 -0
  84. ommlds/cli/sessions/chat/facades/configs.py +11 -0
  85. ommlds/cli/sessions/chat/facades/facade.py +26 -0
  86. ommlds/cli/sessions/chat/facades/inject.py +35 -0
  87. ommlds/cli/sessions/chat/facades/ui.py +34 -0
  88. ommlds/cli/sessions/chat/inject.py +8 -31
  89. ommlds/cli/sessions/chat/interfaces/__init__.py +0 -0
  90. ommlds/cli/sessions/chat/interfaces/bare/__init__.py +0 -0
  91. ommlds/cli/sessions/chat/interfaces/bare/configs.py +15 -0
  92. ommlds/cli/sessions/chat/interfaces/bare/inject.py +69 -0
  93. ommlds/cli/sessions/chat/interfaces/bare/interactive.py +49 -0
  94. ommlds/cli/sessions/chat/interfaces/bare/oneshot.py +21 -0
  95. ommlds/cli/sessions/chat/{tools/confirmation.py → interfaces/bare/tools.py} +3 -22
  96. ommlds/cli/sessions/chat/interfaces/base.py +13 -0
  97. ommlds/cli/sessions/chat/interfaces/configs.py +11 -0
  98. ommlds/cli/sessions/chat/interfaces/inject.py +29 -0
  99. ommlds/cli/sessions/chat/interfaces/textual/__init__.py +0 -0
  100. ommlds/cli/sessions/chat/interfaces/textual/app.py +310 -0
  101. ommlds/cli/sessions/chat/interfaces/textual/configs.py +11 -0
  102. ommlds/cli/sessions/chat/interfaces/textual/facades.py +19 -0
  103. ommlds/cli/sessions/chat/interfaces/textual/inject.py +97 -0
  104. ommlds/cli/sessions/chat/interfaces/textual/interface.py +24 -0
  105. ommlds/cli/sessions/chat/interfaces/textual/styles/__init__.py +29 -0
  106. ommlds/cli/sessions/chat/interfaces/textual/styles/input.tcss +53 -0
  107. ommlds/cli/sessions/chat/interfaces/textual/styles/markdown.tcss +7 -0
  108. ommlds/cli/sessions/chat/interfaces/textual/styles/messages.tcss +157 -0
  109. ommlds/cli/sessions/chat/interfaces/textual/tools.py +38 -0
  110. ommlds/cli/sessions/chat/interfaces/textual/widgets/__init__.py +0 -0
  111. ommlds/cli/sessions/chat/interfaces/textual/widgets/input.py +36 -0
  112. ommlds/cli/sessions/chat/interfaces/textual/widgets/messages.py +197 -0
  113. ommlds/cli/sessions/chat/session.py +8 -13
  114. ommlds/cli/sessions/completion/configs.py +3 -4
  115. ommlds/cli/sessions/completion/inject.py +1 -2
  116. ommlds/cli/sessions/completion/session.py +4 -8
  117. ommlds/cli/sessions/configs.py +10 -0
  118. ommlds/cli/sessions/embedding/configs.py +3 -4
  119. ommlds/cli/sessions/embedding/inject.py +1 -2
  120. ommlds/cli/sessions/embedding/session.py +4 -8
  121. ommlds/cli/sessions/inject.py +15 -15
  122. ommlds/cli/state/storage.py +7 -1
  123. ommlds/minichain/__init__.py +161 -38
  124. ommlds/minichain/_dataclasses.py +20452 -0
  125. ommlds/minichain/_typedvalues.py +11 -4
  126. ommlds/minichain/backends/impls/anthropic/names.py +3 -3
  127. ommlds/minichain/backends/impls/anthropic/protocol.py +2 -2
  128. ommlds/minichain/backends/impls/anthropic/stream.py +1 -1
  129. ommlds/minichain/backends/impls/cerebras/__init__.py +0 -0
  130. ommlds/minichain/backends/impls/cerebras/chat.py +80 -0
  131. ommlds/minichain/backends/impls/cerebras/names.py +45 -0
  132. ommlds/minichain/backends/impls/cerebras/protocol.py +143 -0
  133. ommlds/minichain/backends/impls/cerebras/stream.py +125 -0
  134. ommlds/minichain/backends/impls/duckduckgo/search.py +5 -1
  135. ommlds/minichain/backends/impls/google/names.py +6 -0
  136. ommlds/minichain/backends/impls/google/stream.py +1 -1
  137. ommlds/minichain/backends/impls/google/tools.py +2 -2
  138. ommlds/minichain/backends/impls/groq/chat.py +2 -0
  139. ommlds/minichain/backends/impls/groq/protocol.py +2 -2
  140. ommlds/minichain/backends/impls/groq/stream.py +3 -1
  141. ommlds/minichain/backends/impls/huggingface/repos.py +1 -5
  142. ommlds/minichain/backends/impls/llamacpp/chat.py +6 -3
  143. ommlds/minichain/backends/impls/llamacpp/completion.py +7 -3
  144. ommlds/minichain/backends/impls/llamacpp/stream.py +6 -3
  145. ommlds/minichain/backends/impls/mlx/chat.py +6 -3
  146. ommlds/minichain/backends/impls/ollama/chat.py +51 -57
  147. ommlds/minichain/backends/impls/ollama/protocol.py +144 -0
  148. ommlds/minichain/backends/impls/openai/format.py +4 -3
  149. ommlds/minichain/backends/impls/openai/names.py +3 -1
  150. ommlds/minichain/backends/impls/openai/stream.py +33 -1
  151. ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
  152. ommlds/minichain/backends/impls/tinygrad/chat.py +7 -4
  153. ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
  154. ommlds/minichain/backends/impls/transformers/sentence.py +5 -2
  155. ommlds/minichain/backends/impls/transformers/tokens.py +9 -6
  156. ommlds/minichain/backends/impls/transformers/transformers.py +10 -8
  157. ommlds/minichain/backends/strings/resolving.py +1 -1
  158. ommlds/minichain/chat/content.py +42 -0
  159. ommlds/minichain/chat/messages.py +43 -39
  160. ommlds/minichain/chat/stream/joining.py +36 -12
  161. ommlds/minichain/chat/stream/types.py +1 -1
  162. ommlds/minichain/chat/templating.py +3 -3
  163. ommlds/minichain/content/__init__.py +19 -3
  164. ommlds/minichain/content/_marshal.py +181 -55
  165. ommlds/minichain/content/code.py +26 -0
  166. ommlds/minichain/content/composite.py +28 -0
  167. ommlds/minichain/content/content.py +27 -0
  168. ommlds/minichain/content/dynamic.py +12 -0
  169. ommlds/minichain/content/emphasis.py +27 -0
  170. ommlds/minichain/content/images.py +2 -2
  171. ommlds/minichain/content/json.py +2 -2
  172. ommlds/minichain/content/link.py +13 -0
  173. ommlds/minichain/content/markdown.py +12 -0
  174. ommlds/minichain/content/metadata.py +10 -0
  175. ommlds/minichain/content/namespaces.py +8 -0
  176. ommlds/minichain/content/placeholders.py +10 -9
  177. ommlds/minichain/content/quote.py +26 -0
  178. ommlds/minichain/content/raw.py +49 -0
  179. ommlds/minichain/content/recursive.py +12 -0
  180. ommlds/minichain/content/section.py +26 -0
  181. ommlds/minichain/content/sequence.py +17 -3
  182. ommlds/minichain/content/standard.py +32 -0
  183. ommlds/minichain/content/tag.py +28 -0
  184. ommlds/minichain/content/templates.py +13 -0
  185. ommlds/minichain/content/text.py +2 -2
  186. ommlds/minichain/content/transform/__init__.py +0 -0
  187. ommlds/minichain/content/transform/json.py +55 -0
  188. ommlds/minichain/content/transform/markdown.py +8 -0
  189. ommlds/minichain/content/transform/materialize.py +51 -0
  190. ommlds/minichain/content/transform/metadata.py +16 -0
  191. ommlds/minichain/content/{prepare.py → transform/prepare.py} +10 -15
  192. ommlds/minichain/content/transform/recursive.py +97 -0
  193. ommlds/minichain/content/transform/standard.py +43 -0
  194. ommlds/minichain/content/{transforms → transform}/stringify.py +1 -7
  195. ommlds/minichain/content/transform/strings.py +33 -0
  196. ommlds/minichain/content/transform/templates.py +25 -0
  197. ommlds/minichain/content/visitors.py +231 -0
  198. ommlds/minichain/lib/fs/tools/read.py +1 -1
  199. ommlds/minichain/lib/fs/tools/recursivels/rendering.py +1 -1
  200. ommlds/minichain/lib/fs/tools/recursivels/running.py +1 -1
  201. ommlds/minichain/lib/todo/tools/write.py +2 -1
  202. ommlds/minichain/lib/todo/types.py +1 -1
  203. ommlds/minichain/metadata.py +56 -2
  204. ommlds/minichain/resources.py +22 -1
  205. ommlds/minichain/services/README.md +154 -0
  206. ommlds/minichain/services/__init__.py +6 -2
  207. ommlds/minichain/services/_marshal.py +46 -10
  208. ommlds/minichain/services/_origclasses.py +11 -0
  209. ommlds/minichain/services/_typedvalues.py +8 -3
  210. ommlds/minichain/services/requests.py +73 -3
  211. ommlds/minichain/services/responses.py +73 -3
  212. ommlds/minichain/services/services.py +9 -0
  213. ommlds/minichain/stream/services.py +24 -1
  214. ommlds/minichain/text/applypatch.py +2 -1
  215. ommlds/minichain/text/toolparsing/llamacpp/types.py +1 -1
  216. ommlds/minichain/tokens/specials.py +1 -1
  217. ommlds/minichain/tools/execution/catalog.py +1 -1
  218. ommlds/minichain/tools/execution/errorhandling.py +36 -0
  219. ommlds/minichain/tools/execution/errors.py +2 -2
  220. ommlds/minichain/tools/execution/executors.py +1 -1
  221. ommlds/minichain/tools/fns.py +1 -1
  222. ommlds/minichain/tools/jsonschema.py +2 -2
  223. ommlds/minichain/tools/reflect.py +6 -6
  224. ommlds/minichain/tools/types.py +12 -15
  225. ommlds/minichain/vectors/_marshal.py +1 -1
  226. ommlds/minichain/vectors/embeddings.py +1 -1
  227. ommlds/minichain/wrappers/__init__.py +7 -0
  228. ommlds/minichain/wrappers/firstinwins.py +144 -0
  229. ommlds/minichain/wrappers/instrument.py +146 -0
  230. ommlds/minichain/wrappers/retry.py +168 -0
  231. ommlds/minichain/wrappers/services.py +98 -0
  232. ommlds/minichain/wrappers/stream.py +57 -0
  233. ommlds/nanochat/rustbpe/README.md +9 -0
  234. ommlds/nanochat/tokenizers.py +40 -6
  235. ommlds/specs/mcp/clients.py +146 -0
  236. ommlds/specs/mcp/protocol.py +123 -18
  237. ommlds/tools/git.py +82 -65
  238. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/METADATA +13 -11
  239. ommlds-0.0.0.dev503.dist-info/RECORD +520 -0
  240. ommlds/cli/sessions/chat/chat/state/inject.py +0 -36
  241. ommlds/cli/sessions/chat/chat/user/inject.py +0 -62
  242. ommlds/cli/sessions/chat/chat/user/interactive.py +0 -31
  243. ommlds/cli/sessions/chat/chat/user/oneshot.py +0 -25
  244. ommlds/cli/sessions/chat/chat/user/types.py +0 -15
  245. ommlds/cli/sessions/chat/driver.py +0 -43
  246. ommlds/minichain/content/materialize.py +0 -196
  247. ommlds/minichain/content/simple.py +0 -47
  248. ommlds/minichain/content/transforms/base.py +0 -46
  249. ommlds/minichain/content/transforms/interleave.py +0 -70
  250. ommlds/minichain/content/transforms/squeeze.py +0 -72
  251. ommlds/minichain/content/transforms/strings.py +0 -24
  252. ommlds/minichain/content/types.py +0 -43
  253. ommlds/minichain/stream/wrap.py +0 -62
  254. ommlds-0.0.0.dev480.dist-info/RECORD +0 -427
  255. /ommlds/cli/sessions/chat/{chat → drivers}/__init__.py +0 -0
  256. /ommlds/cli/sessions/chat/{chat → drivers}/ai/__init__.py +0 -0
  257. /ommlds/cli/sessions/chat/{chat → drivers}/ai/injection.py +0 -0
  258. /ommlds/cli/sessions/chat/{chat/state → drivers/events}/__init__.py +0 -0
  259. /ommlds/cli/sessions/chat/{chat/user → drivers/phases}/__init__.py +0 -0
  260. /ommlds/cli/sessions/chat/{phases → drivers/phases}/inject.py +0 -0
  261. /ommlds/cli/sessions/chat/{phases → drivers/phases}/injection.py +0 -0
  262. /ommlds/cli/sessions/chat/{phases → drivers/phases}/manager.py +0 -0
  263. /ommlds/cli/sessions/chat/{phases → drivers/phases}/types.py +0 -0
  264. /ommlds/cli/sessions/chat/{phases → drivers/state}/__init__.py +0 -0
  265. /ommlds/cli/sessions/chat/{tools → drivers/tools}/__init__.py +0 -0
  266. /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/__init__.py +0 -0
  267. /ommlds/cli/sessions/chat/{tools → drivers/tools}/fs/configs.py +0 -0
  268. /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/__init__.py +0 -0
  269. /ommlds/cli/sessions/chat/{tools → drivers/tools}/todo/configs.py +0 -0
  270. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/__init__.py +0 -0
  271. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/configs.py +0 -0
  272. /ommlds/cli/sessions/chat/{tools → drivers/tools}/weather/inject.py +0 -0
  273. /ommlds/{minichain/content/transforms → cli/sessions/chat/drivers/user}/__init__.py +0 -0
  274. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/WHEEL +0 -0
  275. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/entry_points.txt +0 -0
  276. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/licenses/LICENSE +0 -0
  277. {ommlds-0.0.0.dev480.dist-info → ommlds-0.0.0.dev503.dist-info}/top_level.txt +0 -0
@@ -23,7 +23,7 @@ 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
+ from ..content.content import Content
27
27
  from .types import EnumToolDtype
28
28
  from .types import MappingToolDtype
29
29
  from .types import NullableToolDtype
@@ -159,7 +159,7 @@ class ToolReflector:
159
159
 
160
160
  #
161
161
 
162
- def _prepare_desc(self, s: CanContent) -> CanContent:
162
+ def _prepare_desc(self, s: Content | None) -> Content | None:
163
163
  if s is None:
164
164
  return None
165
165
  if not self._raw_descs and isinstance(s, str):
@@ -178,14 +178,14 @@ class ToolReflector:
178
178
  for o in md.get_object_metadata(fn, type=_ToolSpecOverride):
179
179
  ts_ovr.update({
180
180
  k: v
181
- for k, v in dc.asdict(o).items()
181
+ for k, v in dc.shallow_asdict(o).items()
182
182
  if k != 'params'
183
183
  and v is not None
184
184
  })
185
185
  for op in (o.params or []):
186
186
  p_ovr_dct.setdefault(check.non_empty_str(op.name), {}).update({
187
187
  k: v
188
- for k, v in dc.asdict(op).items()
188
+ for k, v in dc.shallow_asdict(op).items()
189
189
  if v is not None
190
190
  })
191
191
 
@@ -240,7 +240,7 @@ class ToolReflector:
240
240
  ds_p = ds_p_dct.get(pn)
241
241
  sig_p = sig_p_dct.get(pn)
242
242
 
243
- p_desc: CanContent
243
+ p_desc: Content | None
244
244
  if (p_desc := ovr_p.get('desc')) is None:
245
245
  if ds_p is not None:
246
246
  p_desc = ds_p.description
@@ -286,7 +286,7 @@ class ToolReflector:
286
286
  if md_tp is not None:
287
287
  tp_kw.update({
288
288
  k: v
289
- for k, v in dc.asdict(md_tp).items()
289
+ for k, v in dc.shallow_asdict(md_tp).items()
290
290
  if v is not None
291
291
  })
292
292
 
@@ -6,14 +6,11 @@ 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
10
9
  from omlish import lang
11
10
  from omlish import marshal as msh
12
11
  from omlish import reflect as rfl
13
12
 
14
- from ..content.materialize import CanContent
15
- from ..content.transforms.base import ContentTransform
16
- from ..content.types import Content
13
+ from ..content.content import Content
17
14
 
18
15
 
19
16
  msh.register_global_module_import('._marshal', __package__)
@@ -150,7 +147,7 @@ class ToolParam:
150
147
 
151
148
  _: dc.KW_ONLY
152
149
 
153
- desc: CanContent = None
150
+ desc: Content | None = None
154
151
 
155
152
  type: ToolDtype | None = None
156
153
 
@@ -168,12 +165,12 @@ class ToolSpec:
168
165
 
169
166
  _: dc.KW_ONLY
170
167
 
171
- desc: CanContent = None
168
+ desc: Content | None = None
172
169
 
173
170
  params: ta.Sequence[ToolParam] | None = None
174
171
  allow_additional_params: bool | None = None
175
172
 
176
- returns_desc: CanContent = None
173
+ returns_desc: Content | None = None
177
174
  returns_type: ToolDtype | None = None
178
175
 
179
176
  @cached.property
@@ -207,11 +204,11 @@ class ToolUseResult(lang.Final):
207
204
  ##
208
205
 
209
206
 
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
207
+ # class _ToolUseContentTransform(ContentTransform, lang.Final, lang.NotInstantiable):
208
+ # @dispatch.install_method(ContentTransform.apply)
209
+ # def apply_tool_use(self, tu: ToolUse) -> ToolUse:
210
+ # return tu # TODO: args are Content
211
+ #
212
+ # @dispatch.install_method(ContentTransform.apply)
213
+ # def apply_tool_use_result(self, tur: ToolUseResult) -> ToolUseResult:
214
+ # return dc.replace(tur, c=self.apply(tur.c)) # noqa
@@ -2,10 +2,10 @@
2
2
  TODO:
3
3
  - serialize as base64 bytes? at least support deserializing as it
4
4
  """
5
- import dataclasses as dc
6
5
  import typing as ta
7
6
 
8
7
  from omlish import check
8
+ from omlish import dataclasses as dc
9
9
  from omlish import lang
10
10
  from omlish import marshal as msh
11
11
  from omlish import reflect as rfl
@@ -2,7 +2,7 @@ import typing as ta
2
2
 
3
3
  from omlish import lang
4
4
 
5
- from ..content.types import Content
5
+ from ..content.content import Content
6
6
  from ..registries.globals import register_type
7
7
  from ..services import Request
8
8
  from ..services import Response
@@ -0,0 +1,7 @@
1
+ """
2
+ TODO:
3
+ - ratelimit
4
+ - option - output injection / removal
5
+ - 'switch'
6
+ - ** reflection passthrough **
7
+ """
@@ -0,0 +1,144 @@
1
+ """
2
+ TODO:
3
+ - stream lol
4
+ - take first open vs take first chunk
5
+ """
6
+ import typing as ta
7
+
8
+ from omlish import dataclasses as dc
9
+ from omlish import lang
10
+
11
+ from ..services.services import Service
12
+ from ..types import Output
13
+ from .services import MultiWrapperService
14
+ from .services import WrappedOptionT
15
+ from .services import WrappedOutputT
16
+ from .services import WrappedRequest
17
+ from .services import WrappedRequestV
18
+ from .services import WrappedResponse
19
+ from .services import WrappedResponseV
20
+ from .stream import WrappedStreamOutputT
21
+ from .stream import WrapperStreamService
22
+
23
+
24
+ with lang.auto_proxy_import(globals()):
25
+ import asyncio
26
+
27
+
28
+ AnyFirstInWinsService: ta.TypeAlias = ta.Union[
29
+ 'FirstInWinsService',
30
+ 'FirstInWinsStreamService',
31
+ ]
32
+
33
+
34
+ ##
35
+
36
+
37
+ @dc.dataclass()
38
+ class FirstInWinsServiceCancelledError(Exception):
39
+ e: BaseException
40
+
41
+
42
+ class FirstInWinsServiceExceptionGroup(ExceptionGroup):
43
+ pass
44
+
45
+
46
+ @dc.dataclass(frozen=True)
47
+ class FirstInWinsServiceOutput(Output):
48
+ first_in_wins_service: AnyFirstInWinsService
49
+ response_service: Service
50
+ service_exceptions: ta.Mapping[Service, Exception] | None = None
51
+
52
+
53
+ ##
54
+
55
+
56
+ class FirstInWinsService(
57
+ MultiWrapperService[
58
+ WrappedRequestV,
59
+ WrappedOptionT,
60
+ WrappedResponseV,
61
+ WrappedOutputT,
62
+ ],
63
+ lang.Abstract,
64
+ ):
65
+ pass
66
+
67
+
68
+ ##
69
+
70
+
71
+ class FirstInWinsStreamService(
72
+ WrapperStreamService[
73
+ WrappedRequestV,
74
+ WrappedOptionT,
75
+ WrappedResponseV,
76
+ WrappedOutputT,
77
+ WrappedStreamOutputT,
78
+ ],
79
+ lang.Abstract,
80
+ ):
81
+ pass
82
+
83
+
84
+ ##
85
+
86
+
87
+ class AsyncioFirstInWinsService(
88
+ FirstInWinsService[
89
+ WrappedRequestV,
90
+ WrappedOptionT,
91
+ WrappedResponseV,
92
+ WrappedOutputT,
93
+ ],
94
+ ):
95
+ async def invoke(self, request: WrappedRequest) -> WrappedResponse:
96
+ tasks: list = []
97
+ services_by_task: dict = {}
98
+ for svc in self._services:
99
+ task: asyncio.Task = asyncio.create_task(svc.invoke(request)) # type: ignore[arg-type]
100
+ tasks.append(task)
101
+ services_by_task[task] = svc
102
+
103
+ failures_by_service: dict[Service, Exception] = {}
104
+
105
+ try:
106
+ pending = set(tasks)
107
+
108
+ while pending:
109
+ done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
110
+
111
+ for t in done:
112
+ svc = services_by_task[t]
113
+
114
+ try:
115
+ response = t.result()
116
+ except asyncio.CancelledError as exc:
117
+ failures_by_service[svc] = FirstInWinsServiceCancelledError(exc)
118
+ continue
119
+ except Exception as exc: # noqa
120
+ failures_by_service[svc] = exc
121
+ continue
122
+
123
+ for p in pending:
124
+ p.cancel()
125
+
126
+ await asyncio.gather(*pending, return_exceptions=True)
127
+
128
+ return response.with_outputs(FirstInWinsServiceOutput(
129
+ self,
130
+ svc,
131
+ failures_by_service,
132
+ ))
133
+
134
+ raise FirstInWinsServiceExceptionGroup(
135
+ 'All service calls failed',
136
+ list(failures_by_service.values()),
137
+ )
138
+
139
+ finally:
140
+ for t in tasks:
141
+ if not t.done():
142
+ t.cancel()
143
+
144
+ await asyncio.gather(*tasks, return_exceptions=True)
@@ -0,0 +1,146 @@
1
+ """
2
+ TODO:
3
+ - final stream outputs
4
+ """
5
+ import datetime
6
+ import typing as ta
7
+
8
+ from omlish import dataclasses as dc
9
+ from omlish import lang
10
+
11
+ from ..services.requests import Request
12
+ from ..services.responses import Response
13
+ from .services import WrappedOptionT
14
+ from .services import WrappedOutputT
15
+ from .services import WrappedRequest
16
+ from .services import WrappedRequestV
17
+ from .services import WrappedResponse
18
+ from .services import WrappedResponseV
19
+ from .services import WrappedService
20
+ from .services import WrapperService
21
+ from .stream import WrappedStreamOutputT
22
+ from .stream import WrappedStreamResponse
23
+ from .stream import WrappedStreamService
24
+ from .stream import WrapperStreamService
25
+
26
+
27
+ ##
28
+
29
+
30
+ @dc.dataclass(frozen=True, kw_only=True)
31
+ @dc.extra_class_params(default_repr_fn=lang.opt_or_just_repr)
32
+ class InstrumentedServiceEvent:
33
+ dt: datetime.datetime = dc.field(default_factory=lang.utcnow)
34
+
35
+ req: Request | None = None
36
+
37
+ resp: Response | None = None
38
+ exc: BaseException | None = None
39
+
40
+ stream_v: lang.Maybe[ta.Any] = lang.empty()
41
+
42
+
43
+ class InstrumentedServiceEventSink(ta.Protocol):
44
+ def __call__(self, ev: InstrumentedServiceEvent) -> ta.Awaitable[None]: ...
45
+
46
+
47
+ class ListInstrumentedServiceEventSink:
48
+ def __init__(self, lst: list[InstrumentedServiceEvent] | None = None) -> None:
49
+ super().__init__()
50
+
51
+ if lst is None:
52
+ lst = []
53
+ self._lst = lst
54
+
55
+ @property
56
+ def events(self) -> ta.Sequence[InstrumentedServiceEvent]:
57
+ return self._lst
58
+
59
+ async def __call__(self, ev: InstrumentedServiceEvent) -> None:
60
+ self._lst.append(ev)
61
+
62
+
63
+ ##
64
+
65
+
66
+ class InstrumentedService(
67
+ WrapperService[
68
+ WrappedRequestV,
69
+ WrappedOptionT,
70
+ WrappedResponseV,
71
+ WrappedOutputT,
72
+ ],
73
+ ):
74
+ def __init__(
75
+ self,
76
+ service: WrappedService,
77
+ sink: InstrumentedServiceEventSink | None = None,
78
+ ) -> None:
79
+ super().__init__(service)
80
+
81
+ if sink is None:
82
+ sink = ListInstrumentedServiceEventSink()
83
+ self._sink = sink
84
+
85
+ async def invoke(self, request: WrappedRequest) -> WrappedResponse:
86
+ await self._sink(InstrumentedServiceEvent(req=request))
87
+
88
+ try:
89
+ resp = await self._service.invoke(request)
90
+
91
+ except Exception as e: # noqa
92
+ await self._sink(InstrumentedServiceEvent(req=request, exc=e))
93
+
94
+ raise
95
+
96
+ await self._sink(InstrumentedServiceEvent(req=request, resp=resp))
97
+
98
+ return resp
99
+
100
+
101
+ ##
102
+
103
+
104
+ class InstrumentedStreamService(
105
+ WrapperStreamService[
106
+ WrappedRequestV,
107
+ WrappedOptionT,
108
+ WrappedResponseV,
109
+ WrappedOutputT,
110
+ WrappedStreamOutputT,
111
+ ],
112
+ ):
113
+ def __init__(
114
+ self,
115
+ service: WrappedStreamService,
116
+ sink: InstrumentedServiceEventSink | None = None,
117
+ ) -> None:
118
+ super().__init__(service)
119
+
120
+ if sink is None:
121
+ sink = ListInstrumentedServiceEventSink()
122
+ self._sink = sink
123
+
124
+ async def invoke(self, request: WrappedRequest) -> WrappedStreamResponse:
125
+ await self._sink(InstrumentedServiceEvent(req=request))
126
+
127
+ try:
128
+ resp = await self._service.invoke(request)
129
+
130
+ except Exception as e: # noqa
131
+ await self._sink(InstrumentedServiceEvent(req=request, exc=e))
132
+
133
+ raise
134
+
135
+ await self._sink(InstrumentedServiceEvent(req=request, resp=resp))
136
+
137
+ async def inner(sink): # noqa
138
+ async with resp.v as st:
139
+ async for v in st:
140
+ await self._sink(InstrumentedServiceEvent(req=request, resp=resp, stream_v=v))
141
+
142
+ await sink(v)
143
+
144
+ # return resp.with_v(inner())
145
+
146
+ raise NotImplementedError
@@ -0,0 +1,168 @@
1
+ """
2
+ TODO:
3
+ - tenacity shit
4
+ - exception filter - retryable vs not
5
+ - explicit RetryableError wrapper?
6
+ - sleep
7
+ - jitter
8
+ - log, on retry / on except callbacks, blah blah
9
+ - stream retry:
10
+ - failed to open
11
+ - failed during stream
12
+ - buffer and replay??
13
+ - accept death mid-stream?
14
+ - ** probably **: cannot sanely impose any nontrivial stream retry strat at this layer -
15
+ """
16
+ import typing as ta
17
+
18
+ from omlish import dataclasses as dc
19
+
20
+ from ..resources import Resources
21
+ from ..stream.services import StreamResponseSink
22
+ from ..stream.services import new_stream_response
23
+ from ..types import Output
24
+ from .services import WrappedOptionT
25
+ from .services import WrappedOutputT
26
+ from .services import WrappedRequest
27
+ from .services import WrappedRequestV
28
+ from .services import WrappedResponse
29
+ from .services import WrappedResponseV
30
+ from .services import WrappedService
31
+ from .services import WrapperService
32
+ from .stream import WrappedStreamOutputT
33
+ from .stream import WrappedStreamResponse
34
+ from .stream import WrappedStreamService
35
+ from .stream import WrapperStreamService
36
+
37
+
38
+ AnyRetryService: ta.TypeAlias = ta.Union[
39
+ 'RetryService',
40
+ 'RetryStreamService',
41
+ ]
42
+
43
+
44
+ ##
45
+
46
+
47
+ @dc.dataclass()
48
+ class RetryServiceMaxRetriesExceededError(Exception):
49
+ pass
50
+
51
+
52
+ @dc.dataclass(frozen=True)
53
+ class RetryServiceOutput(Output):
54
+ retry_service: AnyRetryService
55
+ num_retries: int
56
+
57
+
58
+ ##
59
+
60
+
61
+ class RetryService(
62
+ WrapperService[
63
+ WrappedRequestV,
64
+ WrappedOptionT,
65
+ WrappedResponseV,
66
+ WrappedOutputT,
67
+ ],
68
+ ):
69
+ DEFAULT_MAX_RETRIES: ta.ClassVar[int] = 3
70
+
71
+ def __init__(
72
+ self,
73
+ service: WrappedService,
74
+ *,
75
+ max_retries: int | None = None,
76
+ ) -> None:
77
+ super().__init__(service)
78
+
79
+ if max_retries is None:
80
+ max_retries = self.DEFAULT_MAX_RETRIES
81
+ self._max_retries = max_retries
82
+
83
+ async def invoke(self, request: WrappedRequest) -> WrappedResponse:
84
+ n = 0
85
+
86
+ while True:
87
+ try:
88
+ resp = await self._service.invoke(request)
89
+
90
+ except Exception as e: # noqa
91
+ if n < self._max_retries:
92
+ n += 1
93
+ continue
94
+
95
+ raise RetryServiceMaxRetriesExceededError from e
96
+
97
+ return resp.with_outputs(RetryServiceOutput(
98
+ retry_service=self,
99
+ num_retries=n,
100
+ ))
101
+
102
+ raise RuntimeError # unreachable
103
+
104
+
105
+ ##
106
+
107
+
108
+ class RetryStreamService(
109
+ WrapperStreamService[
110
+ WrappedRequestV,
111
+ WrappedOptionT,
112
+ WrappedResponseV,
113
+ WrappedOutputT,
114
+ WrappedStreamOutputT,
115
+ ],
116
+ ):
117
+ DEFAULT_MAX_RETRIES: ta.ClassVar[int] = 3
118
+
119
+ def __init__(
120
+ self,
121
+ service: WrappedStreamService,
122
+ *,
123
+ max_retries: int | None = None,
124
+ ) -> None:
125
+ super().__init__(service)
126
+
127
+ if max_retries is None:
128
+ max_retries = self.DEFAULT_MAX_RETRIES
129
+ self._max_retries = max_retries
130
+
131
+ async def invoke(self, request: WrappedRequest) -> WrappedStreamResponse:
132
+ n = 0
133
+
134
+ while True:
135
+ try:
136
+ async with Resources.new() as rs:
137
+ in_resp = await self._service.invoke(request)
138
+ in_vs = await rs.enter_async_context(in_resp.v)
139
+
140
+ async def inner(sink: StreamResponseSink[WrappedResponseV]) -> ta.Sequence[WrappedOutputT] | None:
141
+ async for v in in_vs:
142
+ await sink.emit(v)
143
+
144
+ return in_vs.outputs
145
+
146
+ outs = [
147
+ *in_resp.outputs,
148
+ RetryServiceOutput(
149
+ retry_service=self,
150
+ num_retries=n,
151
+ ),
152
+ ]
153
+
154
+ # FIXME: ??
155
+ # if (ur := tv.as_collection(request.options).get(UseResources)) is not None:
156
+
157
+ return await new_stream_response(
158
+ rs,
159
+ inner,
160
+ outs,
161
+ )
162
+
163
+ except Exception as e: # noqa
164
+ if n < self._max_retries:
165
+ n += 1
166
+ continue
167
+
168
+ raise RetryServiceMaxRetriesExceededError from e
@@ -0,0 +1,98 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import lang
5
+
6
+ from ..services.requests import Request
7
+ from ..services.responses import Response
8
+ from ..services.services import Service
9
+ from ..types import Option
10
+ from ..types import Output
11
+
12
+
13
+ P = ta.ParamSpec('P')
14
+
15
+
16
+ WrappedRequestV = ta.TypeVar('WrappedRequestV')
17
+ WrappedOptionT = ta.TypeVar('WrappedOptionT', bound=Option)
18
+
19
+ WrappedResponseV = ta.TypeVar('WrappedResponseV')
20
+ WrappedOutputT = ta.TypeVar('WrappedOutputT', bound=Output)
21
+
22
+
23
+ WrappedRequest: ta.TypeAlias = Request[
24
+ WrappedRequestV,
25
+ WrappedOptionT,
26
+ ]
27
+
28
+ WrappedResponse: ta.TypeAlias = Response[
29
+ WrappedResponseV,
30
+ WrappedOutputT,
31
+ ]
32
+
33
+ WrappedService: ta.TypeAlias = Service[
34
+ WrappedRequest,
35
+ WrappedResponse,
36
+ ]
37
+
38
+
39
+ ##
40
+
41
+
42
+ class WrapperService(
43
+ lang.Abstract,
44
+ ta.Generic[
45
+ WrappedRequestV,
46
+ WrappedOptionT,
47
+ WrappedResponseV,
48
+ WrappedOutputT,
49
+ ],
50
+ ):
51
+ def __init__(
52
+ self,
53
+ service: WrappedService,
54
+ ) -> None:
55
+ super().__init__()
56
+
57
+ self._service = service
58
+
59
+
60
+ class MultiWrapperService(
61
+ lang.Abstract,
62
+ ta.Generic[
63
+ WrappedRequestV,
64
+ WrappedOptionT,
65
+ WrappedResponseV,
66
+ WrappedOutputT,
67
+ ],
68
+ ):
69
+ def __init__(
70
+ self,
71
+ *services: WrappedService,
72
+ ) -> None:
73
+ super().__init__()
74
+
75
+ self._services = check.not_empty(services)
76
+
77
+
78
+ ##
79
+
80
+
81
+ def wrap_service(
82
+ wrapped: WrappedService,
83
+ wrapper: ta.Callable[
84
+ ta.Concatenate[
85
+ WrappedService,
86
+ P,
87
+ ],
88
+ WrapperService[
89
+ WrappedRequestV,
90
+ WrappedOptionT,
91
+ WrappedResponseV,
92
+ WrappedOutputT,
93
+ ],
94
+ ],
95
+ *args: P.args,
96
+ **kwargs: P.kwargs,
97
+ ) -> WrappedService:
98
+ return ta.cast(ta.Any, wrapper(wrapped, *args, **kwargs))