fleetmesh-node 0.2.0__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 (338) hide show
  1. fleetmesh_node-0.2.0.dist-info/METADATA +57 -0
  2. fleetmesh_node-0.2.0.dist-info/RECORD +338 -0
  3. fleetmesh_node-0.2.0.dist-info/WHEEL +5 -0
  4. fleetmesh_node-0.2.0.dist-info/entry_points.txt +2 -0
  5. fleetmesh_node-0.2.0.dist-info/licenses/LICENSE +121 -0
  6. fleetmesh_node-0.2.0.dist-info/top_level.txt +2 -0
  7. node/RELEASE +1 -0
  8. node/__init__.py +35 -0
  9. node/app/__init__.py +49 -0
  10. node/app/commands.py +265 -0
  11. node/app/config.py +131 -0
  12. node/app/control.py +670 -0
  13. node/app/core.py +867 -0
  14. node/app/plugins.py +76 -0
  15. node/app/router.py +83 -0
  16. node/app/socket.py +431 -0
  17. node/app/systemd.py +36 -0
  18. node/app/views.py +204 -0
  19. node/channels/__init__.py +378 -0
  20. node/channels/bus.py +331 -0
  21. node/channels/journal.py +46 -0
  22. node/commands/__init__.py +140 -0
  23. node/commands/adapter.py +210 -0
  24. node/commands/base.py +278 -0
  25. node/commands/cluster_cmds.py +183 -0
  26. node/commands/compute_cmds.py +401 -0
  27. node/commands/core.py +474 -0
  28. node/commands/gossip_cmds.py +106 -0
  29. node/commands/nsite_cmds.py +141 -0
  30. node/commands/peering.py +402 -0
  31. node/commands/plugin_cmds.py +135 -0
  32. node/commands/sidecar_cmds.py +120 -0
  33. node/commands/sync.py +388 -0
  34. node/commands/transport_cmds.py +451 -0
  35. node/commands/video_cmds.py +99 -0
  36. node/compartment/__init__.py +124 -0
  37. node/compartment/base.py +868 -0
  38. node/compartment/command.py +458 -0
  39. node/compartment/context.py +163 -0
  40. node/compartment/every.py +108 -0
  41. node/compartment/handler.py +131 -0
  42. node/compartment/lifecycle.py +460 -0
  43. node/compartment/lint.py +388 -0
  44. node/compartment/need.py +158 -0
  45. node/compartment/problem.py +521 -0
  46. node/compartment/refusals.py +120 -0
  47. node/compartment/registry.py +790 -0
  48. node/compartment/schema.py +290 -0
  49. node/compartment/sidecar.py +499 -0
  50. node/compartment/stores.py +662 -0
  51. node/compartment/wire.py +225 -0
  52. node/compartment/worker.py +61 -0
  53. node/config/__init__.py +43 -0
  54. node/config/base.py +116 -0
  55. node/config/change.py +441 -0
  56. node/config/changestore.py +201 -0
  57. node/config/compartments.py +1144 -0
  58. node/config/node.py +318 -0
  59. node/config/parameter.py +563 -0
  60. node/config/protocols.py +274 -0
  61. node/config/records.py +171 -0
  62. node/examples/__init__.py +1 -0
  63. node/examples/demo.py +226 -0
  64. node/examples/follower.py +46 -0
  65. node/interfaces.py +235 -0
  66. node/keystore/__init__.py +27 -0
  67. node/keystore/core.py +103 -0
  68. node/keystore/enclave.py +688 -0
  69. node/meshnode.py +111 -0
  70. node/metrics/__init__.py +396 -0
  71. node/metrics/adapters.py +102 -0
  72. node/metrics/face.py +110 -0
  73. node/planes/__init__.py +21 -0
  74. node/planes/identity/__init__.py +20 -0
  75. node/planes/identity/announce/__init__.py +9 -0
  76. node/planes/identity/announce/compartment.py +447 -0
  77. node/planes/identity/announce/types.py +26 -0
  78. node/planes/identity/discovery/__init__.py +9 -0
  79. node/planes/identity/discovery/compartment.py +522 -0
  80. node/planes/identity/discovery/types.py +13 -0
  81. node/planes/identity/linked/__init__.py +1 -0
  82. node/planes/identity/linked/compartment.py +270 -0
  83. node/planes/identity/linked/types.py +25 -0
  84. node/planes/identity/presence/__init__.py +7 -0
  85. node/planes/identity/presence/compartment.py +409 -0
  86. node/planes/identity/presence/types.py +14 -0
  87. node/planes/operations/__init__.py +13 -0
  88. node/planes/operations/scheduler/__init__.py +9 -0
  89. node/planes/operations/scheduler/compartment.py +353 -0
  90. node/planes/operations/scheduler/types.py +21 -0
  91. node/planes/operations/updates/__init__.py +17 -0
  92. node/planes/operations/updates/core.py +310 -0
  93. node/planes/operations/updates/store.py +79 -0
  94. node/planes/operations/updates/types.py +20 -0
  95. node/planes/peering/__init__.py +28 -0
  96. node/planes/peering/budget/__init__.py +43 -0
  97. node/planes/peering/budget/chain.py +43 -0
  98. node/planes/peering/budget/core.py +537 -0
  99. node/planes/peering/budget/evaluation.py +80 -0
  100. node/planes/peering/budget/meter.py +100 -0
  101. node/planes/peering/budget/receipts.py +439 -0
  102. node/planes/peering/budget/shape.py +101 -0
  103. node/planes/peering/budget/tick.py +110 -0
  104. node/planes/peering/budget/types.py +67 -0
  105. node/planes/peering/budget/windows.py +346 -0
  106. node/planes/peering/capacity/__init__.py +7 -0
  107. node/planes/peering/capacity/compartment.py +342 -0
  108. node/planes/peering/capacity/types.py +24 -0
  109. node/planes/peering/peering/__init__.py +25 -0
  110. node/planes/peering/peering/core.py +464 -0
  111. node/planes/peering/peering/envelope.py +142 -0
  112. node/planes/peering/peering/handshake.py +198 -0
  113. node/planes/peering/peering/shape.py +80 -0
  114. node/planes/peering/peering/types.py +39 -0
  115. node/planes/peering/reporter/__init__.py +42 -0
  116. node/planes/peering/reporter/constants.py +29 -0
  117. node/planes/peering/reporter/core.py +299 -0
  118. node/planes/peering/reporter/finding.py +74 -0
  119. node/planes/peering/reporter/findings.py +171 -0
  120. node/planes/peering/reporter/publish.py +285 -0
  121. node/planes/peering/reporter/reading.py +193 -0
  122. node/planes/peering/reporter/shape.py +86 -0
  123. node/planes/peering/reporter/types.py +50 -0
  124. node/planes/peering/strain/__init__.py +9 -0
  125. node/planes/peering/strain/compartment.py +204 -0
  126. node/planes/peering/strain/types.py +21 -0
  127. node/planes/peering/trust/__init__.py +9 -0
  128. node/planes/peering/trust/compartment.py +239 -0
  129. node/planes/peering/trust/types.py +30 -0
  130. node/planes/services/__init__.py +33 -0
  131. node/planes/services/compute/__init__.py +160 -0
  132. node/planes/services/compute/common.py +134 -0
  133. node/planes/services/compute/compartment.py +681 -0
  134. node/planes/services/compute/executors/__init__.py +121 -0
  135. node/planes/services/compute/executors/agent.py +840 -0
  136. node/planes/services/compute/executors/bacalhau.py +605 -0
  137. node/planes/services/compute/executors/base.py +127 -0
  138. node/planes/services/compute/executors/harness.py +439 -0
  139. node/planes/services/compute/executors/mcp.py +454 -0
  140. node/planes/services/compute/executors/media.py +898 -0
  141. node/planes/services/compute/executors/nondet.py +283 -0
  142. node/planes/services/compute/executors/providers.py +499 -0
  143. node/planes/services/compute/executors/registry.py +140 -0
  144. node/planes/services/compute/executors/wasm.py +244 -0
  145. node/planes/services/compute/executors/zk.py +604 -0
  146. node/planes/services/compute/jobs.py +227 -0
  147. node/planes/services/compute/load.py +180 -0
  148. node/planes/services/compute/market.py +433 -0
  149. node/planes/services/compute/sandbox.py +282 -0
  150. node/planes/services/compute/types.py +84 -0
  151. node/planes/services/compute_wire/__init__.py +58 -0
  152. node/planes/services/compute_wire/compartment.py +256 -0
  153. node/planes/services/compute_wire/messages.py +320 -0
  154. node/planes/services/compute_wire/sealing.py +147 -0
  155. node/planes/services/compute_wire/session.py +572 -0
  156. node/planes/services/compute_wire/types.py +49 -0
  157. node/planes/services/compute_wire/verify.py +331 -0
  158. node/planes/services/dvm/__init__.py +24 -0
  159. node/planes/services/dvm/gateway.py +526 -0
  160. node/planes/services/dvm/protocol.py +35 -0
  161. node/planes/services/dvm/translate.py +15 -0
  162. node/planes/services/dvm/types.py +25 -0
  163. node/planes/services/gateway/__init__.py +40 -0
  164. node/planes/services/gateway/settle.py +279 -0
  165. node/planes/services/gateway/translate.py +644 -0
  166. node/planes/services/gossip/__init__.py +22 -0
  167. node/planes/services/gossip/core.py +929 -0
  168. node/planes/services/gossip/envelope.py +154 -0
  169. node/planes/services/gossip/mesh.py +127 -0
  170. node/planes/services/gossip/types.py +50 -0
  171. node/planes/services/nsite/__init__.py +7 -0
  172. node/planes/services/nsite/client.py +62 -0
  173. node/planes/services/nsite/compartment.py +752 -0
  174. node/planes/services/nsite/types.py +44 -0
  175. node/planes/services/paidfetch/__init__.py +7 -0
  176. node/planes/services/paidfetch/compartment.py +862 -0
  177. node/planes/services/paidfetch/types.py +99 -0
  178. node/planes/services/settle/__init__.py +27 -0
  179. node/planes/services/settle/compartment.py +384 -0
  180. node/planes/services/settle/engine.py +456 -0
  181. node/planes/services/settle/gateways.py +541 -0
  182. node/planes/services/settle/payment.py +512 -0
  183. node/planes/services/settle/store.py +74 -0
  184. node/planes/services/settle/types.py +58 -0
  185. node/planes/services/sidecars/__init__.py +8 -0
  186. node/planes/services/sidecars/compartment.py +461 -0
  187. node/planes/services/sidecars/manifest.py +304 -0
  188. node/planes/services/sidecars/manifests/10-grasp.json +37 -0
  189. node/planes/services/sidecars/manifests/20-ncontainer.json +31 -0
  190. node/planes/services/sidecars/manifests/30-magpie.json +28 -0
  191. node/planes/services/sidecars/types.py +43 -0
  192. node/planes/services/video/__init__.py +106 -0
  193. node/planes/services/video/barter.py +407 -0
  194. node/planes/services/video/cli.py +158 -0
  195. node/planes/services/video/compartment.py +932 -0
  196. node/planes/services/video/ingest.py +524 -0
  197. node/planes/services/video/ledger.py +705 -0
  198. node/planes/services/video/packager.py +847 -0
  199. node/planes/services/video/player.py +456 -0
  200. node/planes/services/video/timed_segments.py +124 -0
  201. node/planes/services/video/transit.py +478 -0
  202. node/planes/store/__init__.py +24 -0
  203. node/planes/store/blob_replication/__init__.py +7 -0
  204. node/planes/store/blob_replication/compartment.py +496 -0
  205. node/planes/store/blob_replication/types.py +35 -0
  206. node/planes/store/blob_wire/__init__.py +48 -0
  207. node/planes/store/blob_wire/client.py +57 -0
  208. node/planes/store/blob_wire/core.py +359 -0
  209. node/planes/store/blob_wire/intake.py +74 -0
  210. node/planes/store/blob_wire/messages.py +85 -0
  211. node/planes/store/projected/__init__.py +7 -0
  212. node/planes/store/projected/compartment.py +258 -0
  213. node/planes/store/projected/types.py +29 -0
  214. node/planes/store/retention/__init__.py +7 -0
  215. node/planes/store/retention/compartment.py +214 -0
  216. node/planes/store/retention/types.py +12 -0
  217. node/planes/store/sync/__init__.py +35 -0
  218. node/planes/store/sync/core.py +311 -0
  219. node/planes/store/sync/envelope.py +106 -0
  220. node/planes/store/sync/handover.py +206 -0
  221. node/planes/store/sync/initiator.py +128 -0
  222. node/planes/store/sync/responder.py +114 -0
  223. node/planes/store/sync/rules.py +179 -0
  224. node/planes/store/sync/shape.py +80 -0
  225. node/planes/store/sync/types.py +51 -0
  226. node/planes/transport/__init__.py +23 -0
  227. node/planes/transport/bridge/__init__.py +13 -0
  228. node/planes/transport/bridge/compartment.py +785 -0
  229. node/planes/transport/bridge/ratelimit.py +44 -0
  230. node/planes/transport/bridge/types.py +43 -0
  231. node/planes/transport/lan/__init__.py +9 -0
  232. node/planes/transport/lan/compartment.py +446 -0
  233. node/planes/transport/lan/types.py +25 -0
  234. node/planes/transport/link/__init__.py +7 -0
  235. node/planes/transport/link/compartment.py +257 -0
  236. node/planes/transport/link/types.py +27 -0
  237. node/planes/transport/mediation/__init__.py +45 -0
  238. node/planes/transport/mediation/compartment.py +238 -0
  239. node/planes/transport/mediation/leaf.py +107 -0
  240. node/planes/transport/mediation/messages.py +302 -0
  241. node/planes/transport/mediation/service.py +233 -0
  242. node/planes/transport/mediation/store.py +37 -0
  243. node/planes/transport/mediation/types.py +20 -0
  244. node/planes/transport/p2p/__init__.py +7 -0
  245. node/planes/transport/p2p/compartment.py +349 -0
  246. node/planes/transport/p2p/types.py +22 -0
  247. node/protocols/__init__.py +277 -0
  248. node/protocols/activitypub/__init__.py +56 -0
  249. node/protocols/activitypub/actor.py +230 -0
  250. node/protocols/activitypub/federation.py +490 -0
  251. node/protocols/activitypub/media.py +145 -0
  252. node/protocols/activitypub/models.py +286 -0
  253. node/protocols/activitypub/package.py +271 -0
  254. node/protocols/activitypub/store.py +564 -0
  255. node/protocols/arcade/__init__.py +26 -0
  256. node/protocols/atproto/__init__.py +13 -0
  257. node/protocols/atproto/firehose.py +126 -0
  258. node/protocols/atproto/metering.py +165 -0
  259. node/protocols/atproto/package.py +155 -0
  260. node/protocols/atproto/repo.py +250 -0
  261. node/protocols/atproto/xrpc.py +285 -0
  262. node/protocols/base.py +109 -0
  263. node/protocols/circles/__init__.py +29 -0
  264. node/protocols/dm/__init__.py +50 -0
  265. node/protocols/grasp/__init__.py +22 -0
  266. node/protocols/isotime.py +15 -0
  267. node/protocols/nostr/__init__.py +51 -0
  268. node/protocols/nostr/blobstore.py +383 -0
  269. node/protocols/nostr/blossom.py +627 -0
  270. node/protocols/nostr/client.py +377 -0
  271. node/protocols/nostr/descriptor.py +211 -0
  272. node/protocols/nostr/eventstore.py +304 -0
  273. node/protocols/nostr/package.py +182 -0
  274. node/protocols/nostr/peers.py +109 -0
  275. node/protocols/nostr/relayserver.py +656 -0
  276. node/protocols/p2panda/__init__.py +22 -0
  277. node/protocols/p2panda/identity.py +19 -0
  278. node/protocols/p2panda/package.py +236 -0
  279. node/protocols/p2panda/rung.py +152 -0
  280. node/protocols/p2panda/store.py +273 -0
  281. node/protocols/rhio/__init__.py +33 -0
  282. node/protocols/rhio/package.py +275 -0
  283. node/protocols/rhio/rung.py +136 -0
  284. node/protocols/rhio/store.py +1423 -0
  285. node/protocols/ssb/muxrpc.py +256 -0
  286. node/protocols/ssb/package.py +267 -0
  287. node/protocols/textile/__init__.py +111 -0
  288. node/protocols/textile/transducers.py +259 -0
  289. node/release.py +119 -0
  290. node/storage/__init__.py +88 -0
  291. node/storage/accounts.py +305 -0
  292. node/storage/adapter.py +161 -0
  293. node/storage/base.py +385 -0
  294. node/storage/dialect.py +209 -0
  295. node/storage/grants.py +317 -0
  296. node/storage/locator.py +150 -0
  297. node/storage/postgres.py +294 -0
  298. node/storage/projected.py +953 -0
  299. node/storage/sharding.py +448 -0
  300. node/storage/standing.py +568 -0
  301. node/testnet/__init__.py +9 -0
  302. node/testnet/cluster.py +731 -0
  303. node/testnet/fips_daemon.py +230 -0
  304. node/transport/__init__.py +94 -0
  305. node/transport/const.py +110 -0
  306. node/transport/didcomm_inbound.py +251 -0
  307. node/transport/dnstxt.py +169 -0
  308. node/transport/fips_rung/__init__.py +67 -0
  309. node/transport/fips_rung/core.py +666 -0
  310. node/transport/fips_rung/listener.py +96 -0
  311. node/transport/fips_rung/types.py +12 -0
  312. node/transport/host.py +334 -0
  313. node/transport/iroh_rung/__init__.py +29 -0
  314. node/transport/iroh_rung/core.py +477 -0
  315. node/transport/iroh_rung/quic.py +942 -0
  316. node/transport/iroh_rung/types.py +22 -0
  317. node/transport/ladder.py +145 -0
  318. node/transport/lineproto.py +49 -0
  319. node/transport/mdns.py +303 -0
  320. node/transport/netfetch.py +283 -0
  321. node/transport/relay_rung/__init__.py +23 -0
  322. node/transport/relay_rung/cooldown.py +87 -0
  323. node/transport/relay_rung/core.py +138 -0
  324. node/transport/relay_rung/inbound.py +161 -0
  325. node/transport/relay_rung/ledger.py +104 -0
  326. node/transport/relay_rung/listen.py +121 -0
  327. node/transport/relay_rung/outbound.py +181 -0
  328. node/transport/relay_rung/shape.py +71 -0
  329. node/transport/relay_rung/types.py +58 -0
  330. node/transport/relay_rung/wire.py +63 -0
  331. node/transport/router.py +761 -0
  332. node/transport/rung.py +276 -0
  333. node/transport/ssb_rung.py +166 -0
  334. node/transport/ws.py +502 -0
  335. sidecar/fleetmesh_announce.py +338 -0
  336. sidecar/fleetmesh_p2p.py +373 -0
  337. sidecar/fleetmesh_rhio.py +352 -0
  338. sidecar/fleetmesh_ssb.py +603 -0
@@ -0,0 +1,283 @@
1
+ """Non-deterministic executors: LLM inference, Bacalhau jobs and agent runs.
2
+
3
+ These call out over HTTP (or run in mock mode) and are settled on evidence
4
+ rather than reproduction; see docs/non-deterministic-compute.md.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import json
9
+ import time
10
+ from typing import Any, Mapping, Optional
11
+
12
+ from fleetmesh_core.crypto import crypto
13
+ from node.transport import netfetch
14
+ from ..common import ComputeError, LLM_MAX_RESPONSE_BYTES
15
+ from ..sandbox import ComputeLimits
16
+ from .base import ComputeExecutionResult, ComputeExecutor, ComputeQuote
17
+ from .providers import (
18
+ PROVIDER_PRESETS,
19
+ LinkedProviderConfig,
20
+ OperatorProviderRouter,
21
+ ProviderPreset,
22
+ )
23
+
24
+ class LlmExecutor(ComputeExecutor):
25
+ """Non-deterministic AI/LLM inference driver with grammar-guided decoding.
26
+
27
+ Supports OpenAI-compatible and Anthropic HTTP endpoints (OpenRouter, Claude,
28
+ OpenAI, Gemini, Copilot, Routstr, PayPerQuery, vLLM, Ollama, llama-server)
29
+ with CFG grammar constraints and fallback offline simulation.
30
+ """
31
+
32
+ engine_id: str = 'llm'
33
+ wasi_profile_digest: str = ''
34
+
35
+ def __init__(
36
+ self,
37
+ endpoint: str = 'http://127.0.0.1:8000/v1',
38
+ default_model: str = 'meta-llama/Llama-3.3-70B-Instruct',
39
+ timeout_seconds: float = 30.0,
40
+ mock_mode: bool = False,
41
+ provider_router: Optional[OperatorProviderRouter] = None,
42
+ api_key: str = '',
43
+ cashu_token: str = '',
44
+ provider_id: str = 'vllm',
45
+ rate_msat_per_token: int = 12,
46
+ ) -> None:
47
+ self.endpoint = endpoint.rstrip('/')
48
+ self.default_model = default_model
49
+ self.timeout_seconds = timeout_seconds
50
+ self.mock_mode = mock_mode
51
+ self.api_key = api_key
52
+ self.cashu_token = cashu_token
53
+ self.provider_id = provider_id
54
+ self.rate_msat_per_token = int(rate_msat_per_token)
55
+ self.provider_router: OperatorProviderRouter = provider_router or OperatorProviderRouter()
56
+
57
+ def _resolve_provider(self, job_payload: Optional[Mapping[str, Any]] = None) -> LinkedProviderConfig:
58
+ if job_payload is not None:
59
+ resolved = self.provider_router.resolve_for_job(job_payload)
60
+ if resolved is not None:
61
+ return resolved
62
+ else:
63
+ resolved = self.provider_router.get_provider()
64
+ if resolved is not None:
65
+ return resolved
66
+ return LinkedProviderConfig(
67
+ provider_id=self.provider_id,
68
+ label=self.provider_id.upper(),
69
+ endpoint=self.endpoint,
70
+ default_model=self.default_model,
71
+ models=(self.default_model,),
72
+ api_key=self.api_key,
73
+ cashu_token=self.cashu_token,
74
+ auth_scheme='bearer' if self.api_key else ('cashu' if self.cashu_token else 'none'),
75
+ wire_format='anthropic_messages' if self.provider_id in ('claude', 'anthropic') else 'openai',
76
+ rate_msat_per_token=self.rate_msat_per_token,
77
+ enabled=True,
78
+ )
79
+
80
+ def is_live_runnable(self) -> bool:
81
+ """Check whether LLM inference can execute on this node."""
82
+ if self.mock_mode:
83
+ return True
84
+ if self.provider_router.runnable_providers():
85
+ return True
86
+ return bool(self.endpoint and self.endpoint.startswith(('http://', 'https://')))
87
+
88
+ def quote(
89
+ self,
90
+ module_spec: Mapping[str, Any],
91
+ input_spec: Mapping[str, Any],
92
+ limits: ComputeLimits,
93
+ ) -> ComputeQuote:
94
+ merged_spec = dict(module_spec)
95
+ merged_spec.update(input_spec)
96
+ provider = self._resolve_provider(merged_spec)
97
+ rate = provider.rate_msat_per_token if provider else self.rate_msat_per_token
98
+ max_tokens = int(input_spec.get('max_tokens', limits.max_tokens or 2048))
99
+ prompt_est = len(str(input_spec.get('prompt', ''))) // 4
100
+ fuel_est = (prompt_est + max_tokens) * rate
101
+ return ComputeQuote(
102
+ fuel_estimate=min(fuel_est, limits.max_fuel),
103
+ rate_unit='msat_per_token',
104
+ rate_amount=rate,
105
+ engine_id=self.engine_id,
106
+ )
107
+
108
+ def execute(
109
+ self,
110
+ job_payload: Mapping[str, Any],
111
+ fuel_cap: int,
112
+ ) -> ComputeExecutionResult:
113
+ provider = self._resolve_provider(job_payload)
114
+ model = job_payload.get('model') or provider.default_model or self.default_model
115
+ prompt = job_payload.get('prompt')
116
+ messages = job_payload.get('messages')
117
+ sampling = job_payload.get('sampling') or {}
118
+ grammar = job_payload.get('grammar') or {}
119
+ stream = bool(job_payload.get('stream', False))
120
+ # The backend endpoint is operator configuration only. An earlier
121
+ # version let ``job_payload['endpoint']`` override it, which handed a
122
+ # requester a POST-anywhere primitive against the node's own network
123
+ # (the default endpoint is loopback, so allow_private must be on).
124
+ verif_mode = self.verification_mode(job_payload)
125
+
126
+ if not messages and prompt is not None:
127
+ messages = [{'role': 'user', 'content': str(prompt)}]
128
+ elif not messages:
129
+ messages = [{'role': 'user', 'content': ''}]
130
+
131
+ max_tokens = int(sampling.get('max_tokens', 512))
132
+ temperature = float(sampling.get('temperature', 0.7))
133
+ top_p = float(sampling.get('top_p', 1.0))
134
+ seed = sampling.get('seed')
135
+
136
+ url, req_body = provider.format_request(
137
+ messages=messages,
138
+ model=model,
139
+ max_tokens=max_tokens,
140
+ temperature=temperature,
141
+ top_p=top_p,
142
+ seed=int(seed) if seed is not None else None,
143
+ grammar=grammar,
144
+ stream=stream,
145
+ )
146
+
147
+ raw_output_text: str = ''
148
+ prompt_tokens = max(1, sum(len(str(m.get('content', ''))) // 4 for m in messages))
149
+ completion_tokens = 0
150
+ gpu_seconds = 0.05
151
+
152
+ if self.mock_mode:
153
+ # Deterministic stand-in for tests and demos. It is only reachable
154
+ # when the operator constructed the executor with mock_mode=True;
155
+ # a live node never falls back to it.
156
+ if '_mock_completion' in job_payload and job_payload['_mock_completion'] is not None:
157
+ raw_output_text = str(job_payload['_mock_completion'])
158
+ elif grammar and (grammar.get('type') == 'json_schema' or 'json_schema' in grammar or grammar.get('response_format') == 'json_object'):
159
+ raw_output_text = json.dumps({
160
+ 'status': 'completed',
161
+ 'model': model,
162
+ 'verified': True,
163
+ 'result': 'synthesized-conforming-payload',
164
+ }, sort_keys=True)
165
+ else:
166
+ user_msg = messages[-1].get('content', '')
167
+ raw_output_text = f"Inference completion for [{model}]: {user_msg[:64]}"
168
+ completion_tokens = max(1, len(raw_output_text) // 4)
169
+ gpu_seconds = 0.025
170
+ else:
171
+ # A backend failure is a failed job, full stop.
172
+ data_bytes = json.dumps(req_body).encode('utf-8')
173
+ headers = provider.build_headers(
174
+ per_query_cashu_token=job_payload.get('_operator_cashu_token'),
175
+ )
176
+ start_t = time.time()
177
+ try:
178
+ body, _ = netfetch.fetch(
179
+ url,
180
+ max_bytes=LLM_MAX_RESPONSE_BYTES,
181
+ timeout=self.timeout_seconds,
182
+ method='POST',
183
+ data=data_bytes,
184
+ headers=headers,
185
+ # The endpoint is operator-configured (loopback vLLM/Ollama or
186
+ # operator-linked cloud/eCash provider), never requester-chosen.
187
+ allow_private=True,
188
+ )
189
+ resp_data = json.loads(body.decode('utf-8'))
190
+ except netfetch.FetchError as e:
191
+ raise ComputeError(f"LLM backend request failed: {e}")
192
+ except (ValueError, UnicodeDecodeError) as e:
193
+ raise ComputeError(f"LLM backend returned malformed JSON: {e}")
194
+ if not isinstance(resp_data, dict):
195
+ raise ComputeError("LLM backend returned a non-object response")
196
+ try:
197
+ raw_output_text, prompt_tokens, completion_tokens = provider.parse_response(
198
+ resp_data,
199
+ default_prompt_tokens=prompt_tokens,
200
+ )
201
+ except ValueError as e:
202
+ raise ComputeError(str(e))
203
+ gpu_seconds = round(time.time() - start_t, 3)
204
+
205
+ total_tokens = prompt_tokens + completion_tokens
206
+ rate_msat = provider.rate_msat_per_token or 12
207
+ fuel_used = total_tokens * rate_msat
208
+ if fuel_used > fuel_cap:
209
+ raise ComputeError(f"LLM generation exceeded fuel cap ({fuel_used} > {fuel_cap})")
210
+
211
+ on_token = job_payload.get('_on_token')
212
+ if callable(on_token) and raw_output_text:
213
+ # Emit token chunks to the streaming callback
214
+ chunk_size = 18
215
+ for idx in range(0, len(raw_output_text), chunk_size):
216
+ try:
217
+ on_token(raw_output_text[idx:idx + chunk_size])
218
+ except Exception:
219
+ break
220
+
221
+ raw_bytes = raw_output_text.encode('utf-8')
222
+ result_hash = crypto.sha256(raw_bytes)
223
+ counters = {
224
+ 'prompt_tokens': prompt_tokens,
225
+ 'completion_tokens': completion_tokens,
226
+ 'total_tokens': total_tokens,
227
+ 'gpu_seconds': gpu_seconds,
228
+ 'rate_msat_per_token': rate_msat,
229
+ }
230
+
231
+ return ComputeExecutionResult(
232
+ result_hash=result_hash,
233
+ raw_output=raw_bytes,
234
+ fuel_used=fuel_used,
235
+ engine_id=self.engine_id,
236
+ wasi_profile_digest=f"llm:{model}",
237
+ counters=counters,
238
+ verification_mode=verif_mode,
239
+ )
240
+
241
+
242
+ from .bacalhau import BacalhauExecutor
243
+
244
+
245
+ from .agent import (
246
+ AgentExecutor,
247
+ AgentExecutionContext,
248
+ AgentToolProtocol,
249
+ AgentToolResult,
250
+ AgentDecision,
251
+ AgentBackendProtocol,
252
+ CalculatorTool,
253
+ Sha256Tool,
254
+ RegexSearchTool,
255
+ FetchBlobTool,
256
+ SubAskTool,
257
+ FinishTool,
258
+ ScriptedAgentBackend,
259
+ LlmAgentBackend,
260
+ )
261
+
262
+ __all__ = [
263
+ 'LlmExecutor',
264
+ 'BacalhauExecutor',
265
+ 'AgentExecutor',
266
+ 'AgentExecutionContext',
267
+ 'AgentToolProtocol',
268
+ 'AgentToolResult',
269
+ 'AgentDecision',
270
+ 'AgentBackendProtocol',
271
+ 'CalculatorTool',
272
+ 'Sha256Tool',
273
+ 'RegexSearchTool',
274
+ 'FetchBlobTool',
275
+ 'SubAskTool',
276
+ 'FinishTool',
277
+ 'ScriptedAgentBackend',
278
+ 'LlmAgentBackend',
279
+ 'PROVIDER_PRESETS',
280
+ 'ProviderPreset',
281
+ 'LinkedProviderConfig',
282
+ 'OperatorProviderRouter',
283
+ ]