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,14 +23,21 @@ def _tv_field_coercer(
23
23
  ) -> ta.Callable[[ta.Sequence], tv.TypedValues]:
24
24
  if isinstance(tvc, tuple):
25
25
  check.arg(all(issubclass(e, tv.TypedValue) for e in tvc))
26
+
26
27
  else:
27
28
  check.issubclass(tvc, tv.TypedValue)
28
29
 
29
30
  def inner(seq):
30
- return tv.TypedValues(*[
31
- check.isinstance(e, tvc)
32
- for e in check.isinstance(seq, ta.Sequence)
33
- ])
31
+ if isinstance(seq, tv.TypedValues):
32
+ for e in seq:
33
+ check.isinstance(e, tvc)
34
+ return seq
35
+
36
+ else:
37
+ return tv.TypedValues(*[
38
+ check.isinstance(e, tvc)
39
+ for e in check.isinstance(seq, ta.Sequence)
40
+ ])
34
41
 
35
42
  return inner
36
43
 
@@ -14,9 +14,9 @@ from ...strings.manifests import BackendStringsManifest
14
14
  MODEL_NAMES = ModelNameCollection(
15
15
  default='claude',
16
16
  aliases={
17
- 'claude-opus-4-1-20250805': None,
18
- 'claude-opus-4-1': 'claude-opus-4-1-20250805',
19
- 'claude-opus': 'claude-opus-4-1',
17
+ 'claude-opus-4-5-20251101': None,
18
+ 'claude-opus-4-5': 'claude-opus-4-5-20251101',
19
+ 'claude-opus': 'claude-opus-4-5',
20
20
 
21
21
  'claude-sonnet-4-5-20250929': None,
22
22
  'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929',
@@ -12,7 +12,7 @@ from ....chat.messages import ToolUseMessage
12
12
  from ....chat.messages import ToolUseResultMessage
13
13
  from ....chat.messages import UserMessage
14
14
  from ....chat.tools.types import Tool
15
- from ....content.prepare import prepare_content_str
15
+ from ....content.transform.prepare import prepare_content_str
16
16
  from ....tools.jsonschema import build_tool_spec_params_json_schema
17
17
 
18
18
 
@@ -104,6 +104,6 @@ def build_protocol_chat_messages(msgs: ta.Iterable[Message]) -> BuiltChatMessage
104
104
  def build_protocol_tool(t: Tool) -> pt.ToolSpec:
105
105
  return pt.ToolSpec(
106
106
  name=check.not_none(t.spec.name),
107
- description=prepare_content_str(t.spec.desc),
107
+ description=prepare_content_str(t.spec.desc) if t.spec.desc is not None else '',
108
108
  input_schema=build_tool_spec_params_json_schema(t.spec),
109
109
  )
@@ -105,7 +105,7 @@ class AnthropicChatChoicesStreamService:
105
105
  for l in db.feed(b):
106
106
  if isinstance(l, DelimitingBuffer.Incomplete):
107
107
  # FIXME: handle
108
- return []
108
+ raise TypeError(l)
109
109
 
110
110
  # FIXME: https://docs.anthropic.com/en/docs/build-with-claude/streaming
111
111
  for so in sd.process_line(l):
File without changes
@@ -0,0 +1,80 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import marshal as msh
5
+ from omlish import typedvalues as tv
6
+ from omlish.formats import json
7
+ from omlish.http import all as http
8
+
9
+ from .....backends.cerebras import protocol as pt
10
+ from ....chat.choices.services import ChatChoicesRequest
11
+ from ....chat.choices.services import ChatChoicesResponse
12
+ from ....chat.choices.services import static_check_is_chat_choices_service
13
+ from ....chat.tools.types import Tool
14
+ from ....models.configs import ModelName
15
+ from ....standard import ApiKey
16
+ from ....standard import DefaultOptions
17
+ from .names import MODEL_NAMES
18
+ from .protocol import build_cer_request_messages
19
+ from .protocol import build_cer_request_tool
20
+ from .protocol import build_mc_choices_response
21
+
22
+
23
+ ##
24
+
25
+
26
+ # @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
27
+ # name='cerebras',
28
+ # type='ChatChoicesService',
29
+ # )
30
+ @static_check_is_chat_choices_service
31
+ class CerebrasChatChoicesService:
32
+ DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(MODEL_NAMES.default))
33
+
34
+ def __init__(
35
+ self,
36
+ *configs: ApiKey | ModelName | DefaultOptions,
37
+ http_client: http.AsyncHttpClient | None = None,
38
+ ) -> None:
39
+ super().__init__()
40
+
41
+ self._http_client = http_client
42
+
43
+ with tv.consume(*configs) as cc:
44
+ self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
45
+ self._api_key = ApiKey.pop_secret(cc, env='CEREBRAS_API_KEY')
46
+ self._default_options: tv.TypedValues = DefaultOptions.pop(cc)
47
+
48
+ async def invoke(self, request: ChatChoicesRequest) -> ChatChoicesResponse:
49
+ tools: list[pt.ChatCompletionRequest.Tool] = []
50
+ with tv.TypedValues(*request.options).consume() as oc:
51
+ t: Tool
52
+ for t in oc.pop(Tool, []):
53
+ tools.append(build_cer_request_tool(t))
54
+
55
+ cer_request = pt.ChatCompletionRequest(
56
+ messages=build_cer_request_messages(request.v),
57
+ model=MODEL_NAMES.resolve(self._model_name.v),
58
+ tools=tools or None,
59
+ )
60
+
61
+ raw_request = msh.marshal(cer_request)
62
+
63
+ # TODO: headers:
64
+ # - CF-RAY
65
+ # - X-Amz-Cf-Id
66
+ # - X-delay-time
67
+
68
+ http_response = await http.async_request(
69
+ 'https://api.cerebras.ai/v1/chat/completions',
70
+ headers={
71
+ http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
72
+ http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
73
+ },
74
+ data=json.dumps(raw_request).encode('utf-8'),
75
+ client=self._http_client,
76
+ )
77
+
78
+ raw_response = json.loads(check.not_none(http_response.data).decode('utf-8'))
79
+
80
+ return build_mc_choices_response(msh.unmarshal(raw_response, pt.ChatCompletionResponse))
@@ -0,0 +1,45 @@
1
+ """
2
+ https://inference-docs.cerebras.ai/models/overview
3
+ """
4
+ from ....models.names import ModelNameCollection
5
+ from ...strings.manifests import BackendStringsManifest
6
+
7
+
8
+ ##
9
+
10
+
11
+ MODEL_NAMES = ModelNameCollection(
12
+ default='gpt-oss-120b',
13
+ aliases={
14
+ 'llama3.1-8b': None,
15
+
16
+ 'llama-3.3-70b': None,
17
+ 'llama3': 'llama-3.3-70b',
18
+
19
+ 'gpt-oss-120b': None,
20
+ 'gpt-oss': 'gpt-oss-120b',
21
+
22
+ 'qwen-3-32b': None,
23
+ 'qwen3': 'qwen-3-32b',
24
+
25
+ ##
26
+ # preview
27
+
28
+ 'qwen-3-235b-a22b-instruct-2507': None,
29
+ 'qwen-3-235b': 'qwen-3-235b-a22b-instruct-2507',
30
+
31
+ 'zai-glm-4.7': None,
32
+ 'glm': 'zai-glm-4.7',
33
+ },
34
+ )
35
+
36
+
37
+ # @omlish-manifest
38
+ _BACKEND_STRINGS_MANIFEST = BackendStringsManifest(
39
+ [
40
+ 'ChatChoicesService',
41
+ 'ChatChoicesStreamService',
42
+ ],
43
+ 'cerebras',
44
+ model_names=MODEL_NAMES,
45
+ )
@@ -0,0 +1,143 @@
1
+ import itertools
2
+
3
+ from omlish import check
4
+ from omlish.formats import json
5
+
6
+ from .....backends.cerebras import protocol as pt
7
+ from ....chat.choices.services import ChatChoicesResponse
8
+ from ....chat.choices.stream.types import AiChoiceDeltas
9
+ from ....chat.choices.types import AiChoice
10
+ from ....chat.messages import AiMessage
11
+ from ....chat.messages import AnyAiMessage
12
+ from ....chat.messages import Chat
13
+ from ....chat.messages import SystemMessage
14
+ from ....chat.messages import ToolUseMessage
15
+ from ....chat.messages import ToolUseResultMessage
16
+ from ....chat.messages import UserMessage
17
+ from ....chat.stream.types import AiDelta
18
+ from ....chat.stream.types import ContentAiDelta
19
+ from ....chat.stream.types import PartialToolUseAiDelta
20
+ from ....chat.tools.types import Tool
21
+ from ....content.transform.prepare import prepare_content_str
22
+ from ....tools.jsonschema import build_tool_spec_params_json_schema
23
+ from ....tools.types import ToolUse
24
+
25
+
26
+ ##
27
+
28
+
29
+ def build_cer_request_messages(chat: Chat) -> list[pt.ChatCompletionRequest.Message]:
30
+ cer_msgs: list[pt.ChatCompletionRequest.Message] = []
31
+
32
+ for _, g in itertools.groupby(chat, lambda mc_m: isinstance(mc_m, AnyAiMessage)):
33
+ mc_msgs = list(g)
34
+
35
+ if isinstance(mc_msgs[0], AnyAiMessage):
36
+ tups: list[tuple[AiMessage | None, list[ToolUseMessage]]] = []
37
+ for mc_msg in mc_msgs:
38
+ if isinstance(mc_msg, AiMessage):
39
+ tups.append((mc_msg, []))
40
+
41
+ elif isinstance(mc_msg, ToolUseMessage):
42
+ if not tups:
43
+ tups.append((None, []))
44
+ tups[-1][1].append(mc_msg)
45
+
46
+ else:
47
+ raise TypeError(mc_msg)
48
+
49
+ for mc_ai_msg, mc_tu_msgs in tups:
50
+ cer_msgs.append(pt.ChatCompletionRequest.AssistantMessage(
51
+ content=check.isinstance(mc_ai_msg.c, str) if mc_ai_msg is not None else None,
52
+ tool_calls=[
53
+ pt.ChatCompletionRequest.AssistantMessage.ToolCall(
54
+ function=pt.ChatCompletionRequest.AssistantMessage.ToolCall.Function(
55
+ name=mc_tu_msg.tu.name,
56
+ arguments=check.not_none(mc_tu_msg.tu.raw_args),
57
+ ),
58
+ id=check.not_none(mc_tu_msg.tu.id),
59
+ )
60
+ for mc_tu_msg in mc_tu_msgs
61
+ ] if mc_tu_msgs else None,
62
+ ))
63
+
64
+ else:
65
+ for mc_msg in mc_msgs:
66
+ if isinstance(mc_msg, SystemMessage):
67
+ cer_msgs.append(pt.ChatCompletionRequest.SystemMessage(
68
+ content=check.isinstance(mc_msg.c, str),
69
+ ))
70
+
71
+ elif isinstance(mc_msg, UserMessage):
72
+ cer_msgs.append(pt.ChatCompletionRequest.UserMessage(
73
+ content=check.isinstance(mc_msg.c, str),
74
+ ))
75
+
76
+ elif isinstance(mc_msg, ToolUseResultMessage):
77
+ cer_msgs.append(pt.ChatCompletionRequest.ToolMessage(
78
+ tool_call_id=check.not_none(mc_msg.tur.id),
79
+ content=check.isinstance(mc_msg.tur.c, str),
80
+ ))
81
+
82
+ else:
83
+ raise TypeError(mc_msg)
84
+
85
+ return cer_msgs
86
+
87
+
88
+ def build_cer_request_tool(t: Tool) -> pt.ChatCompletionRequest.Tool:
89
+ return pt.ChatCompletionRequest.Tool(
90
+ function=pt.ChatCompletionRequest.Tool.Function(
91
+ name=check.not_none(t.spec.name),
92
+ description=prepare_content_str(t.spec.desc) if t.spec.desc is not None else None,
93
+ parameters=build_tool_spec_params_json_schema(t.spec),
94
+ ),
95
+ )
96
+
97
+
98
+ def build_mc_choices_response(cer_resp: pt.ChatCompletionResponse) -> ChatChoicesResponse:
99
+ def build_choice(cer_choice: pt.ChatCompletionResponse.Choice) -> AiChoice:
100
+ cer_msg = cer_choice.message
101
+
102
+ lst: list[AnyAiMessage] = []
103
+
104
+ if cer_msg.content is not None:
105
+ lst.append(AiMessage(
106
+ check.isinstance(cer_msg.content, str),
107
+ ))
108
+
109
+ for cer_tc in cer_msg.tool_calls or []:
110
+ lst.append(ToolUseMessage(ToolUse(
111
+ id=cer_tc.id,
112
+ name=cer_tc.function.name,
113
+ args=json.loads(cer_tc.function.arguments or '{}'),
114
+ raw_args=cer_tc.function.arguments,
115
+ )))
116
+
117
+ return AiChoice(lst)
118
+
119
+ return ChatChoicesResponse(list(map(build_choice, cer_resp.choices)))
120
+
121
+
122
+ def build_mc_ai_choice_deltas(delta: pt.ChatCompletionChunk.Choice.Delta) -> AiChoiceDeltas:
123
+ if delta.role in (None, 'assistant'):
124
+ lst: list[AiDelta] = []
125
+
126
+ if delta.content is not None:
127
+ lst.append(ContentAiDelta(delta.content))
128
+
129
+ for tc in delta.tool_calls or []:
130
+ tc_fn = check.not_none(tc.function)
131
+ lst.append(PartialToolUseAiDelta(
132
+ id=tc.id,
133
+ name=tc_fn.name,
134
+ raw_args=tc_fn.arguments,
135
+ ))
136
+
137
+ return AiChoiceDeltas(lst)
138
+
139
+ elif delta.channel in ('analysis', 'commentary'):
140
+ return AiChoiceDeltas([])
141
+
142
+ else:
143
+ raise ValueError(delta)
@@ -0,0 +1,125 @@
1
+ import typing as ta
2
+
3
+ from omlish import check
4
+ from omlish import marshal as msh
5
+ from omlish import typedvalues as tv
6
+ from omlish.formats import json
7
+ from omlish.http import all as http
8
+ from omlish.http import sse
9
+ from omlish.io.buffers import DelimitingBuffer
10
+
11
+ from .....backends.cerebras import protocol as pt
12
+ from ....chat.choices.services import ChatChoicesOutputs
13
+ from ....chat.choices.stream.services import ChatChoicesStreamRequest
14
+ from ....chat.choices.stream.services import ChatChoicesStreamResponse
15
+ from ....chat.choices.stream.services import static_check_is_chat_choices_stream_service
16
+ from ....chat.choices.stream.types import AiChoicesDeltas
17
+ from ....chat.tools.types import Tool
18
+ from ....configs import Config
19
+ from ....resources import UseResources
20
+ from ....standard import ApiKey
21
+ from ....stream.services import StreamResponseSink
22
+ from ....stream.services import new_stream_response
23
+ from .chat import CerebrasChatChoicesService
24
+ from .names import MODEL_NAMES
25
+ from .protocol import build_cer_request_messages
26
+ from .protocol import build_cer_request_tool
27
+ from .protocol import build_mc_ai_choice_deltas
28
+
29
+
30
+ ##
31
+
32
+
33
+ # @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
34
+ # name='cerebras',
35
+ # type='ChatChoicesStreamService',
36
+ # )
37
+ @static_check_is_chat_choices_stream_service
38
+ class CerebrasChatChoicesStreamService:
39
+ def __init__(
40
+ self,
41
+ *configs: Config,
42
+ http_client: http.AsyncHttpClient | None = None,
43
+ ) -> None:
44
+ super().__init__()
45
+
46
+ self._http_client = http_client
47
+
48
+ with tv.consume(*configs) as cc:
49
+ self._model_name = cc.pop(CerebrasChatChoicesService.DEFAULT_MODEL_NAME)
50
+ self._api_key = ApiKey.pop_secret(cc, env='CEREBRAS_API_KEY')
51
+
52
+ READ_CHUNK_SIZE: ta.ClassVar[int] = -1
53
+
54
+ async def invoke(self, request: ChatChoicesStreamRequest) -> ChatChoicesStreamResponse:
55
+ tools: list[pt.ChatCompletionRequest.Tool] = []
56
+ with tv.TypedValues(*request.options).consume() as oc:
57
+ t: Tool
58
+ for t in oc.pop(Tool, []):
59
+ tools.append(build_cer_request_tool(t))
60
+
61
+ cer_request = pt.ChatCompletionRequest(
62
+ messages=build_cer_request_messages(request.v),
63
+ model=MODEL_NAMES.resolve(self._model_name.v),
64
+ tools=tools or None,
65
+ stream=True,
66
+ )
67
+
68
+ raw_request = msh.marshal(cer_request)
69
+
70
+ http_request = http.HttpRequest(
71
+ 'https://api.cerebras.ai/v1/chat/completions',
72
+ headers={
73
+ http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
74
+ http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
75
+ },
76
+ data=json.dumps(raw_request).encode('utf-8'),
77
+ )
78
+
79
+ async with UseResources.or_new(request.options) as rs:
80
+ http_client = await rs.enter_async_context(http.manage_async_client(self._http_client))
81
+ http_response = await rs.enter_async_context(await http_client.stream_request(http_request))
82
+
83
+ async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
84
+ db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
85
+ sd = sse.SseDecoder()
86
+ while True:
87
+ b = await http_response.stream.read1(self.READ_CHUNK_SIZE)
88
+ for l in db.feed(b):
89
+ if isinstance(l, DelimitingBuffer.Incomplete):
90
+ # FIXME: handle
91
+ raise TypeError(l)
92
+
93
+ # FIXME: https://platform.openai.com/docs/guides/function-calling?api-mode=responses#streaming
94
+ for so in sd.process_line(l):
95
+ if isinstance(so, sse.SseEvent) and so.type == b'message':
96
+ ss = so.data.decode('utf-8')
97
+ if ss == '[DONE]':
98
+ return []
99
+
100
+ sj = json.loads(ss) # ChatCompletionChunk
101
+
102
+ check.state(sj['object'] == 'chat.completion.chunk')
103
+
104
+ ccc = msh.unmarshal(sj, pt.ChatCompletionChunk)
105
+
106
+ # FIXME: stop reason
107
+ if not ccc.choices:
108
+ continue
109
+
110
+ if any(choice.finish_reason for choice in ccc.choices):
111
+ check.state(all(choice.finish_reason for choice in ccc.choices))
112
+ break
113
+
114
+ await sink.emit(AiChoicesDeltas([
115
+ build_mc_ai_choice_deltas(choice.delta)
116
+ for choice in ccc.choices
117
+ ]))
118
+
119
+ if not b:
120
+ return []
121
+
122
+ # raw_response = json.loads(check.not_none(http_response.data).decode('utf-8'))
123
+ # return rh.build_response(raw_response)
124
+
125
+ return await new_stream_response(rs, inner)
@@ -1,4 +1,4 @@
1
- import ddgs
1
+ from omlish import lang
2
2
 
3
3
  from ....search import SearchHit
4
4
  from ....search import SearchHits
@@ -7,6 +7,10 @@ from ....search import SearchResponse
7
7
  from ....search import static_check_is_search_service
8
8
 
9
9
 
10
+ with lang.auto_proxy_import(globals()):
11
+ import ddgs
12
+
13
+
10
14
  ##
11
15
 
12
16
 
@@ -16,6 +16,12 @@ from ...strings.manifests import BackendStringsManifest
16
16
  MODEL_NAMES = ModelNameCollection(
17
17
  default='gemini',
18
18
  aliases={
19
+ 'gemini-3-pro-preview': None,
20
+ 'gemini-3-pro': 'gemini-3-pro-preview',
21
+
22
+ 'gemini-3-flash-preview': None,
23
+ 'gemini-3-flash': 'gemini-3-flash-preview',
24
+
19
25
  'gemini-2.5-pro': None,
20
26
  'gemini-2.5-flash': None,
21
27
  'gemini-2.5-flash-lite': None,
@@ -179,7 +179,7 @@ class GoogleChatChoicesStreamService:
179
179
  for bl in db.feed(b):
180
180
  if isinstance(bl, DelimitingBuffer.Incomplete):
181
181
  # FIXME: handle
182
- return []
182
+ raise TypeError(bl)
183
183
 
184
184
  l = bl.decode('utf-8')
185
185
  if not l:
@@ -7,8 +7,8 @@ from omlish import check
7
7
  from omlish import dataclasses as dc
8
8
 
9
9
  from .....backends.google.protocol import types as pt
10
- from ....content.prepare import ContentStrPreparer
11
- from ....content.prepare import default_content_str_preparer
10
+ from ....content.transform.prepare import ContentStrPreparer
11
+ from ....content.transform.prepare import default_content_str_preparer
12
12
  from ....tools.types import EnumToolDtype
13
13
  from ....tools.types import MappingToolDtype
14
14
  from ....tools.types import NullableToolDtype
@@ -7,6 +7,7 @@ from omlish.formats import json
7
7
  from omlish.http import all as http
8
8
 
9
9
  from .....backends.groq import protocol as pt
10
+ from .....backends.groq.clients import REQUIRED_HTTP_HEADERS
10
11
  from ....chat.choices.services import ChatChoicesRequest
11
12
  from ....chat.choices.services import ChatChoicesResponse
12
13
  from ....chat.choices.services import static_check_is_chat_choices_service
@@ -65,6 +66,7 @@ class GroqChatChoicesService:
65
66
  headers={
66
67
  http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
67
68
  http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
69
+ **REQUIRED_HTTP_HEADERS,
68
70
  },
69
71
  data=json.dumps(raw_request).encode('utf-8'),
70
72
  client=self._http_client,
@@ -18,7 +18,7 @@ from ....chat.stream.types import AiDelta
18
18
  from ....chat.stream.types import ContentAiDelta
19
19
  from ....chat.stream.types import ToolUseAiDelta
20
20
  from ....chat.tools.types import Tool
21
- from ....content.prepare import prepare_content_str
21
+ from ....content.transform.prepare import prepare_content_str
22
22
  from ....tools.jsonschema import build_tool_spec_params_json_schema
23
23
  from ....tools.types import ToolUse
24
24
 
@@ -89,7 +89,7 @@ def build_gq_request_tool(t: Tool) -> pt.ChatCompletionRequest.Tool:
89
89
  return pt.ChatCompletionRequest.Tool(
90
90
  function=pt.ChatCompletionRequest.Tool.Function(
91
91
  name=check.not_none(t.spec.name),
92
- description=prepare_content_str(t.spec.desc),
92
+ description=prepare_content_str(t.spec.desc) if t.spec.desc is not None else None,
93
93
  parameters=build_tool_spec_params_json_schema(t.spec),
94
94
  ),
95
95
  )
@@ -9,6 +9,7 @@ from omlish.http import sse
9
9
  from omlish.io.buffers import DelimitingBuffer
10
10
 
11
11
  from .....backends.groq import protocol as pt
12
+ from .....backends.groq.clients import REQUIRED_HTTP_HEADERS
12
13
  from ....chat.choices.services import ChatChoicesOutputs
13
14
  from ....chat.choices.stream.services import ChatChoicesStreamRequest
14
15
  from ....chat.choices.stream.services import ChatChoicesStreamResponse
@@ -72,6 +73,7 @@ class GroqChatChoicesStreamService:
72
73
  headers={
73
74
  http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
74
75
  http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
76
+ **REQUIRED_HTTP_HEADERS,
75
77
  },
76
78
  data=json.dumps(raw_request).encode('utf-8'),
77
79
  )
@@ -88,7 +90,7 @@ class GroqChatChoicesStreamService:
88
90
  for l in db.feed(b):
89
91
  if isinstance(l, DelimitingBuffer.Incomplete):
90
92
  # FIXME: handle
91
- return []
93
+ raise TypeError(l)
92
94
 
93
95
  # FIXME: https://platform.openai.com/docs/guides/function-calling?api-mode=responses#streaming
94
96
  for so in sd.process_line(l):
@@ -3,8 +3,6 @@ TODO:
3
3
  - local-only check first
4
4
  - cat ~/.cache/.../models/.../refs/main -> c5bfd839cd4cda0e5a39a97e00218d9c56e468af
5
5
  """
6
- import typing as ta
7
-
8
6
  from omlish import lang
9
7
 
10
8
  from ....models.configs import ModelRepo
@@ -12,10 +10,8 @@ from ....models.repos.resolving import ModelRepoResolver
12
10
  from ....models.repos.resolving import ResolvedModelRepo
13
11
 
14
12
 
15
- if ta.TYPE_CHECKING:
13
+ with lang.auto_proxy_import(globals()):
16
14
  import huggingface_hub as hf
17
- else:
18
- hf = lang.proxy_import('huggingface_hub')
19
15
 
20
16
 
21
17
  ##
@@ -2,13 +2,10 @@ import contextlib
2
2
  import os.path
3
3
  import typing as ta
4
4
 
5
- import llama_cpp as lcc
6
-
7
5
  from omlish import check
8
6
  from omlish import lang
9
7
  from omlish import typedvalues as tv
10
8
 
11
- from .....backends import llamacpp as lcu
12
9
  from ....chat.choices.services import ChatChoicesRequest
13
10
  from ....chat.choices.services import ChatChoicesResponse
14
11
  from ....chat.choices.services import static_check_is_chat_choices_service
@@ -27,6 +24,12 @@ from .format import ROLES_MAP
27
24
  from .format import get_msg_content
28
25
 
29
26
 
27
+ with lang.auto_proxy_import(globals()):
28
+ import llama_cpp as lcc
29
+
30
+ from .....backends import llamacpp as lcu
31
+
32
+
30
33
  ##
31
34
 
32
35
 
@@ -2,11 +2,9 @@ import contextlib
2
2
  import os.path
3
3
  import typing as ta
4
4
 
5
- import llama_cpp as lcc
6
-
5
+ from omlish import lang
7
6
  from omlish import typedvalues as tv
8
7
 
9
- from .....backends import llamacpp as lcu
10
8
  from ....completion import CompletionOption
11
9
  from ....completion import CompletionRequest
12
10
  from ....completion import CompletionResponse
@@ -18,6 +16,12 @@ from ....llms.types import Temperature
18
16
  from ....models.configs import ModelPath
19
17
 
20
18
 
19
+ with lang.auto_proxy_import(globals()):
20
+ import llama_cpp as lcc
21
+
22
+ from .....backends import llamacpp as lcu
23
+
24
+
21
25
  ##
22
26
 
23
27