agent-hitch 0.2.16 → 0.2.17

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 (361) hide show
  1. package/README.md +7 -28
  2. package/README.zh-CN.md +6 -26
  3. package/dist/scripts/benchmark-dataset-verification.js +118 -0
  4. package/dist/scripts/benchmark-dataset-verification.js.map +1 -0
  5. package/dist/scripts/canary-external-endpoint.js +56 -0
  6. package/dist/scripts/canary-external-endpoint.js.map +1 -0
  7. package/dist/scripts/canary-native-training.js +8 -6
  8. package/dist/scripts/canary-native-training.js.map +1 -1
  9. package/dist/scripts/canary-remote-training.js +8 -6
  10. package/dist/scripts/canary-remote-training.js.map +1 -1
  11. package/dist/scripts/check-architecture.js +1 -1
  12. package/dist/scripts/check-architecture.js.map +1 -1
  13. package/dist/src/adapters/providers/codex.js +5 -2
  14. package/dist/src/adapters/providers/codex.js.map +1 -1
  15. package/dist/src/adapters/providers/model-call.js +1 -2
  16. package/dist/src/adapters/providers/model-call.js.map +1 -1
  17. package/dist/src/adapters/providers/training-tool.js +2 -0
  18. package/dist/src/adapters/providers/training-tool.js.map +1 -1
  19. package/dist/src/backends/harbor/artifact-pins.js +42 -0
  20. package/dist/src/backends/harbor/artifact-pins.js.map +1 -0
  21. package/dist/src/backends/harbor/backend.js +16 -8
  22. package/dist/src/backends/harbor/backend.js.map +1 -1
  23. package/dist/src/backends/harbor/bound-credential-helper.js +24 -0
  24. package/dist/src/backends/harbor/bound-credential-helper.js.map +1 -0
  25. package/dist/src/backends/harbor/credential-snapshot.js +71 -0
  26. package/dist/src/backends/harbor/credential-snapshot.js.map +1 -0
  27. package/dist/src/backends/harbor/environment-config.js +25 -2
  28. package/dist/src/backends/harbor/environment-config.js.map +1 -1
  29. package/dist/src/backends/harbor/environment-files.js +143 -0
  30. package/dist/src/backends/harbor/environment-files.js.map +1 -0
  31. package/dist/src/backends/harbor/evaluation-binding.js +160 -0
  32. package/dist/src/backends/harbor/evaluation-binding.js.map +1 -0
  33. package/dist/src/backends/harbor/host-credential-helper.js +5 -0
  34. package/dist/src/backends/harbor/host-credential-helper.js.map +1 -1
  35. package/dist/src/backends/harbor/index.js +2 -0
  36. package/dist/src/backends/harbor/index.js.map +1 -1
  37. package/dist/src/backends/harbor/model-proxy-config.js +3 -18
  38. package/dist/src/backends/harbor/model-proxy-config.js.map +1 -1
  39. package/dist/src/backends/harbor/portable-regrade.js +15 -3
  40. package/dist/src/backends/harbor/portable-regrade.js.map +1 -1
  41. package/dist/src/backends/harbor/python-runtime.js +71 -0
  42. package/dist/src/backends/harbor/python-runtime.js.map +1 -0
  43. package/dist/src/backends/harbor/tools.js +6 -4
  44. package/dist/src/backends/harbor/tools.js.map +1 -1
  45. package/dist/src/backends/index.js +3 -0
  46. package/dist/src/backends/index.js.map +1 -1
  47. package/dist/src/cli/arguments.js +30 -30
  48. package/dist/src/cli/arguments.js.map +1 -1
  49. package/dist/src/cli/commands/capabilities.js +7 -3
  50. package/dist/src/cli/commands/capabilities.js.map +1 -1
  51. package/dist/src/cli/commands/daemon.js +0 -17
  52. package/dist/src/cli/commands/daemon.js.map +1 -1
  53. package/dist/src/cli/commands/eval-binding.js +32 -0
  54. package/dist/src/cli/commands/eval-binding.js.map +1 -0
  55. package/dist/src/cli/commands/eval.js +21 -15
  56. package/dist/src/cli/commands/eval.js.map +1 -1
  57. package/dist/src/cli/commands/models.js +32 -45
  58. package/dist/src/cli/commands/models.js.map +1 -1
  59. package/dist/src/cli/commands/run.js +0 -35
  60. package/dist/src/cli/commands/run.js.map +1 -1
  61. package/dist/src/cli/main.js +0 -4
  62. package/dist/src/cli/main.js.map +1 -1
  63. package/dist/src/cli/output.js +5 -16
  64. package/dist/src/cli/output.js.map +1 -1
  65. package/dist/src/control-plane/eval-records.js +3 -1
  66. package/dist/src/control-plane/eval-records.js.map +1 -1
  67. package/dist/src/control-plane/eval-recovery.js +4 -1
  68. package/dist/src/control-plane/eval-recovery.js.map +1 -1
  69. package/dist/src/control-plane/eval-scheduler.js +16 -8
  70. package/dist/src/control-plane/eval-scheduler.js.map +1 -1
  71. package/dist/src/control-plane/index.js +0 -2
  72. package/dist/src/control-plane/index.js.map +1 -1
  73. package/dist/src/control-plane/model-capture-planning.js +3 -4
  74. package/dist/src/control-plane/model-capture-planning.js.map +1 -1
  75. package/dist/src/control-plane/remote-model-routes.js +4 -24
  76. package/dist/src/control-plane/remote-model-routes.js.map +1 -1
  77. package/dist/src/control-plane/remote-offer-contract.js +2 -2
  78. package/dist/src/control-plane/remote-offer-contract.js.map +1 -1
  79. package/dist/src/control-plane/remote-result-transport.js +7 -8
  80. package/dist/src/control-plane/remote-result-transport.js.map +1 -1
  81. package/dist/src/control-plane/remote-work-coordinator.js +2 -2
  82. package/dist/src/control-plane/remote-work-coordinator.js.map +1 -1
  83. package/dist/src/control-plane/remote-work-recovery.js +2 -2
  84. package/dist/src/control-plane/remote-work-recovery.js.map +1 -1
  85. package/dist/src/control-plane/remote-worker-protocol.js +1 -1
  86. package/dist/src/control-plane/remote-worker-protocol.js.map +1 -1
  87. package/dist/src/control-plane/remote-worker-runner.js +21 -6
  88. package/dist/src/control-plane/remote-worker-runner.js.map +1 -1
  89. package/dist/src/control-plane/remote-workers.js +2 -2
  90. package/dist/src/control-plane/remote-workers.js.map +1 -1
  91. package/dist/src/control-plane/rerun-recovery.js +1 -1
  92. package/dist/src/control-plane/rerun-recovery.js.map +1 -1
  93. package/dist/src/control-plane/rerun-scheduler.js +21 -14
  94. package/dist/src/control-plane/rerun-scheduler.js.map +1 -1
  95. package/dist/src/control-plane/rerun-source.js +5 -2
  96. package/dist/src/control-plane/rerun-source.js.map +1 -1
  97. package/dist/src/control-plane/rerun-submission.js +7 -5
  98. package/dist/src/control-plane/rerun-submission.js.map +1 -1
  99. package/dist/src/control-plane/synthetic-result.js +3 -2
  100. package/dist/src/control-plane/synthetic-result.js.map +1 -1
  101. package/dist/src/control-plane/worker-model-relay.js +1 -1
  102. package/dist/src/control-plane/worker-model-relay.js.map +1 -1
  103. package/dist/src/controller-runtime/hash.js +6 -0
  104. package/dist/src/controller-runtime/hash.js.map +1 -1
  105. package/dist/src/daemon/client.js +12 -35
  106. package/dist/src/daemon/client.js.map +1 -1
  107. package/dist/src/daemon/evaluation-binding.js +33 -0
  108. package/dist/src/daemon/evaluation-binding.js.map +1 -0
  109. package/dist/src/daemon/evaluation-submission.js +27 -0
  110. package/dist/src/daemon/evaluation-submission.js.map +1 -0
  111. package/dist/src/daemon/index.js +1 -0
  112. package/dist/src/daemon/index.js.map +1 -1
  113. package/dist/src/daemon/json-stream.js +98 -0
  114. package/dist/src/daemon/json-stream.js.map +1 -0
  115. package/dist/src/daemon/scheduler.js +1 -4
  116. package/dist/src/daemon/scheduler.js.map +1 -1
  117. package/dist/src/daemon/server.js +14 -54
  118. package/dist/src/daemon/server.js.map +1 -1
  119. package/dist/src/domain/endpoints.js +2 -0
  120. package/dist/src/domain/endpoints.js.map +1 -0
  121. package/dist/src/domain/evaluation-binding.js +5 -0
  122. package/dist/src/domain/evaluation-binding.js.map +1 -0
  123. package/dist/src/domain/index.js +2 -0
  124. package/dist/src/domain/index.js.map +1 -1
  125. package/dist/src/domain/runs.js.map +1 -1
  126. package/dist/src/evals/benchmark-adapter-manifest.js +95 -32
  127. package/dist/src/evals/benchmark-adapter-manifest.js.map +1 -1
  128. package/dist/src/evals/benchmark-verification.js +106 -0
  129. package/dist/src/evals/benchmark-verification.js.map +1 -0
  130. package/dist/src/evals/collect-only-rerun.js +3 -2
  131. package/dist/src/evals/collect-only-rerun.js.map +1 -1
  132. package/dist/src/evals/directory.js +9 -1
  133. package/dist/src/evals/directory.js.map +1 -1
  134. package/dist/src/evals/endpoint-attempts.js +112 -0
  135. package/dist/src/evals/endpoint-attempts.js.map +1 -0
  136. package/dist/src/evals/endpoint-usage.js +8 -0
  137. package/dist/src/evals/endpoint-usage.js.map +1 -0
  138. package/dist/src/evals/environment-binding.js +9 -0
  139. package/dist/src/evals/environment-binding.js.map +1 -0
  140. package/dist/src/evals/eval-artifact-planning.js +10 -1
  141. package/dist/src/evals/eval-artifact-planning.js.map +1 -1
  142. package/dist/src/evals/eval-finalization.js +2 -3
  143. package/dist/src/evals/eval-finalization.js.map +1 -1
  144. package/dist/src/evals/eval-logical-plan.js +2 -0
  145. package/dist/src/evals/eval-logical-plan.js.map +1 -1
  146. package/dist/src/evals/eval-result-builder.js +4 -0
  147. package/dist/src/evals/eval-result-builder.js.map +1 -1
  148. package/dist/src/evals/eval-state.js +22 -0
  149. package/dist/src/evals/eval-state.js.map +1 -0
  150. package/dist/src/evals/execution-plan.js +6 -2
  151. package/dist/src/evals/execution-plan.js.map +1 -1
  152. package/dist/src/evals/index.js +5 -1
  153. package/dist/src/evals/index.js.map +1 -1
  154. package/dist/src/evals/infrastructure-retry.js +2 -0
  155. package/dist/src/evals/infrastructure-retry.js.map +1 -1
  156. package/dist/src/evals/model-capture-runtime.js +9 -13
  157. package/dist/src/evals/model-capture-runtime.js.map +1 -1
  158. package/dist/src/evals/native-phase-evidence.js +8 -2
  159. package/dist/src/evals/native-phase-evidence.js.map +1 -1
  160. package/dist/src/evals/planned-execution.js +7 -2
  161. package/dist/src/evals/planned-execution.js.map +1 -1
  162. package/dist/src/evals/planned-progress-publisher.js +18 -5
  163. package/dist/src/evals/planned-progress-publisher.js.map +1 -1
  164. package/dist/src/evals/planned-retry-execution.js +1 -0
  165. package/dist/src/evals/planned-retry-execution.js.map +1 -1
  166. package/dist/src/evals/preparation-rerun.js +0 -1
  167. package/dist/src/evals/preparation-rerun.js.map +1 -1
  168. package/dist/src/evals/progress.js +92 -7
  169. package/dist/src/evals/progress.js.map +1 -1
  170. package/dist/src/evals/records.js +12 -2
  171. package/dist/src/evals/records.js.map +1 -1
  172. package/dist/src/evals/recovery.js +2 -0
  173. package/dist/src/evals/recovery.js.map +1 -1
  174. package/dist/src/evals/remote-rerun-execution.js +1 -3
  175. package/dist/src/evals/remote-rerun-execution.js.map +1 -1
  176. package/dist/src/evals/remote-verifier-rerun.js +1 -1
  177. package/dist/src/evals/remote-verifier-rerun.js.map +1 -1
  178. package/dist/src/evals/request.js +14 -45
  179. package/dist/src/evals/request.js.map +1 -1
  180. package/dist/src/evals/rerun-observation.js +31 -0
  181. package/dist/src/evals/rerun-observation.js.map +1 -0
  182. package/dist/src/evals/rerun.js +23 -28
  183. package/dist/src/evals/rerun.js.map +1 -1
  184. package/dist/src/evals/service.js +45 -42
  185. package/dist/src/evals/service.js.map +1 -1
  186. package/dist/src/evals/shared-runtime.js +125 -0
  187. package/dist/src/evals/shared-runtime.js.map +1 -0
  188. package/dist/src/evals/trial-import.js +10 -2
  189. package/dist/src/evals/trial-import.js.map +1 -1
  190. package/dist/src/evals/trial-publication-recovery.js +1 -1
  191. package/dist/src/evals/trial-publication-recovery.js.map +1 -1
  192. package/dist/src/evals/verifier-artifacts.js +1 -0
  193. package/dist/src/evals/verifier-artifacts.js.map +1 -1
  194. package/dist/src/evals/verifier-only-rerun.js +1 -1
  195. package/dist/src/evals/verifier-only-rerun.js.map +1 -1
  196. package/dist/src/evals/verifier-rerun-result.js +3 -2
  197. package/dist/src/evals/verifier-rerun-result.js.map +1 -1
  198. package/dist/src/evals/verifier-score-artifacts.js +9 -1
  199. package/dist/src/evals/verifier-score-artifacts.js.map +1 -1
  200. package/dist/src/foundation/config.js +0 -5
  201. package/dist/src/foundation/config.js.map +1 -1
  202. package/dist/src/inference/index.js +3 -22
  203. package/dist/src/inference/index.js.map +1 -1
  204. package/dist/src/inference/lock.js +44 -130
  205. package/dist/src/inference/lock.js.map +1 -1
  206. package/dist/src/model-access/endpoint-contract.js +127 -0
  207. package/dist/src/model-access/endpoint-contract.js.map +1 -0
  208. package/dist/src/model-access/endpoint-credentials.js +54 -0
  209. package/dist/src/model-access/endpoint-credentials.js.map +1 -0
  210. package/dist/src/model-access/endpoint-probe-stream.js +66 -0
  211. package/dist/src/model-access/endpoint-probe-stream.js.map +1 -0
  212. package/dist/src/model-access/endpoint-verification.js +76 -0
  213. package/dist/src/model-access/endpoint-verification.js.map +1 -0
  214. package/dist/src/model-access/endpoints.js +105 -0
  215. package/dist/src/model-access/endpoints.js.map +1 -0
  216. package/dist/src/model-access/index.js +5 -1
  217. package/dist/src/model-access/index.js.map +1 -1
  218. package/dist/src/model-access/proxy.js +57 -14
  219. package/dist/src/model-access/proxy.js.map +1 -1
  220. package/dist/src/model-access/remote-model.js +21 -29
  221. package/dist/src/model-access/remote-model.js.map +1 -1
  222. package/dist/src/model-access/usage.js +30 -0
  223. package/dist/src/model-access/usage.js.map +1 -0
  224. package/dist/src/runs/endpoint-runtime.js +40 -0
  225. package/dist/src/runs/endpoint-runtime.js.map +1 -0
  226. package/dist/src/runs/executor.js +28 -42
  227. package/dist/src/runs/executor.js.map +1 -1
  228. package/dist/src/runs/local-inference-environment.js +5 -53
  229. package/dist/src/runs/local-inference-environment.js.map +1 -1
  230. package/dist/src/runs/manifest.js +1 -0
  231. package/dist/src/runs/manifest.js.map +1 -1
  232. package/dist/src/runs/outcome.js +3 -10
  233. package/dist/src/runs/outcome.js.map +1 -1
  234. package/dist/src/runs/queued.js +3 -2
  235. package/dist/src/runs/queued.js.map +1 -1
  236. package/dist/src/runs/records.js +3 -0
  237. package/dist/src/runs/records.js.map +1 -1
  238. package/dist/src/runs/request.js +10 -38
  239. package/dist/src/runs/request.js.map +1 -1
  240. package/dist/src/workers/remote-harbor-work-spec.js +8 -10
  241. package/dist/src/workers/remote-harbor-work-spec.js.map +1 -1
  242. package/dist/src/workers/remote-harbor-worker.js +3 -0
  243. package/dist/src/workers/remote-harbor-worker.js.map +1 -1
  244. package/docs/evidence/eval-preparation-2026-10-09.json +393 -0
  245. package/docs/evidence/eval-preparation-2026-10-09.md +178 -0
  246. package/docs/local-model-inference-spec.zh-CN.md +4 -663
  247. package/docs/resource-storage.zh-CN.md +116 -0
  248. package/docs/schemas/endpoint-evaluation-attempt.schema.json +29 -0
  249. package/docs/schemas/eval-artifact-pin.schema.json +14 -0
  250. package/docs/schemas/eval-execution-plan.schema.json +486 -105
  251. package/docs/schemas/eval-progress.schema.json +1 -1
  252. package/docs/schemas/eval-request.schema.json +149 -18
  253. package/docs/schemas/eval-rerun-submission.schema.json +8 -1
  254. package/docs/schemas/eval-result.schema.json +310 -56
  255. package/docs/schemas/evaluation-binding.schema.json +15 -0
  256. package/docs/schemas/external-endpoint-binding.schema.json +153 -0
  257. package/docs/schemas/external-endpoint-config.schema.json +93 -0
  258. package/docs/schemas/inference-execution-evidence.schema.json +253 -3
  259. package/docs/schemas/inference-lock-v3.schema.json +6 -0
  260. package/docs/schemas/inference-lock.schema.json +842 -341
  261. package/docs/schemas/ordered-eval-control.schema.json +7 -1
  262. package/docs/schemas/ready-endpoint-binding.schema.json +201 -0
  263. package/docs/schemas/run-manifest.schema.json +6 -0
  264. package/docs/schemas/run-request.schema.json +122 -20
  265. package/examples/external-inference/.env.example +16 -0
  266. package/examples/external-inference/README.md +203 -0
  267. package/examples/external-inference/accept.mjs +297 -0
  268. package/examples/external-inference/compose.gateway.yaml +119 -0
  269. package/examples/external-inference/compose.single.yaml +64 -0
  270. package/examples/external-inference/compose.tp2.yaml +15 -0
  271. package/examples/external-inference/deployment.example.json +58 -0
  272. package/examples/external-inference/endpoint.chat-completions.example.json +13 -0
  273. package/examples/external-inference/endpoint.responses.example.json +13 -0
  274. package/integrations/harbor/hitch_build_inputs.py +213 -0
  275. package/integrations/harbor/hitch_build_process.py +126 -0
  276. package/integrations/harbor/hitch_compose_preparation.py +274 -0
  277. package/integrations/harbor/hitch_harbor_agent.py +25 -39
  278. package/integrations/harbor/hitch_harbor_environment.py +96 -3
  279. package/integrations/harbor/hitch_image_cache.py +357 -0
  280. package/integrations/harbor/hitch_python_runtime.py +303 -0
  281. package/integrations/harbor/hitch_shared_runtime.py +123 -0
  282. package/integrations/model-call/cli.js +11 -10
  283. package/integrations/training-tool/cli.js +5 -3
  284. package/node_modules/smol-toml/LICENSE +24 -0
  285. package/node_modules/smol-toml/README.md +418 -0
  286. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  287. package/node_modules/smol-toml/dist/date.js +127 -0
  288. package/node_modules/smol-toml/dist/error.d.ts +38 -0
  289. package/node_modules/smol-toml/dist/error.js +63 -0
  290. package/node_modules/smol-toml/dist/extract.js +69 -0
  291. package/node_modules/smol-toml/dist/index.cjs +734 -0
  292. package/node_modules/smol-toml/dist/index.d.ts +43 -0
  293. package/node_modules/smol-toml/dist/index.js +33 -0
  294. package/node_modules/smol-toml/dist/parse.d.ts +36 -0
  295. package/node_modules/smol-toml/dist/parse.js +149 -0
  296. package/node_modules/smol-toml/dist/primitive.js +238 -0
  297. package/node_modules/smol-toml/dist/stringify.d.ts +31 -0
  298. package/node_modules/smol-toml/dist/stringify.js +181 -0
  299. package/node_modules/smol-toml/dist/struct.js +179 -0
  300. package/node_modules/smol-toml/dist/util.d.ts +38 -0
  301. package/node_modules/smol-toml/dist/util.js +89 -0
  302. package/node_modules/smol-toml/package.json +68 -0
  303. package/package.json +15 -4
  304. package/dist/src/cli/commands/local.js +0 -130
  305. package/dist/src/cli/commands/local.js.map +0 -1
  306. package/dist/src/cli/commands/model-node.js +0 -26
  307. package/dist/src/cli/commands/model-node.js.map +0 -1
  308. package/dist/src/control-plane/inference-manager.js +0 -357
  309. package/dist/src/control-plane/inference-manager.js.map +0 -1
  310. package/dist/src/control-plane/managed-gateway-receipt.js +0 -26
  311. package/dist/src/control-plane/managed-gateway-receipt.js.map +0 -1
  312. package/dist/src/control-plane/managed-service-recovery.js +0 -158
  313. package/dist/src/control-plane/managed-service-recovery.js.map +0 -1
  314. package/dist/src/daemon/inference-prepare.js +0 -47
  315. package/dist/src/daemon/inference-prepare.js.map +0 -1
  316. package/dist/src/inference/daemon-capacity.js +0 -41
  317. package/dist/src/inference/daemon-capacity.js.map +0 -1
  318. package/dist/src/inference/device-reservation.js +0 -58
  319. package/dist/src/inference/device-reservation.js.map +0 -1
  320. package/dist/src/inference/doctor.js +0 -95
  321. package/dist/src/inference/doctor.js.map +0 -1
  322. package/dist/src/inference/generation-recovery.js +0 -83
  323. package/dist/src/inference/generation-recovery.js.map +0 -1
  324. package/dist/src/inference/managed-launcher.js +0 -35
  325. package/dist/src/inference/managed-launcher.js.map +0 -1
  326. package/dist/src/inference/materialize.js +0 -66
  327. package/dist/src/inference/materialize.js.map +0 -1
  328. package/dist/src/inference/model-location.js +0 -34
  329. package/dist/src/inference/model-location.js.map +0 -1
  330. package/dist/src/inference/node-client.js +0 -167
  331. package/dist/src/inference/node-client.js.map +0 -1
  332. package/dist/src/inference/node-registry.js +0 -79
  333. package/dist/src/inference/node-registry.js.map +0 -1
  334. package/dist/src/inference/observation.js +0 -79
  335. package/dist/src/inference/observation.js.map +0 -1
  336. package/dist/src/inference/preflight.js +0 -132
  337. package/dist/src/inference/preflight.js.map +0 -1
  338. package/dist/src/inference/process-sglang.js +0 -169
  339. package/dist/src/inference/process-sglang.js.map +0 -1
  340. package/dist/src/inference/protocol-probe.js +0 -29
  341. package/dist/src/inference/protocol-probe.js.map +0 -1
  342. package/dist/src/inference/remote-model-store.js +0 -24
  343. package/dist/src/inference/remote-model-store.js.map +0 -1
  344. package/dist/src/inference/runtime-catalog.js +0 -41
  345. package/dist/src/inference/runtime-catalog.js.map +0 -1
  346. package/dist/src/inference/runtime-store.js +0 -93
  347. package/dist/src/inference/runtime-store.js.map +0 -1
  348. package/dist/src/inference/service-attachment.js +0 -46
  349. package/dist/src/inference/service-attachment.js.map +0 -1
  350. package/dist/src/inference/service-inspection.js +0 -38
  351. package/dist/src/inference/service-inspection.js.map +0 -1
  352. package/dist/src/inference/sglang.js +0 -279
  353. package/dist/src/inference/sglang.js.map +0 -1
  354. package/dist/src/inference/supervisor.js +0 -460
  355. package/dist/src/inference/supervisor.js.map +0 -1
  356. package/dist/src/model-access/local-gateway-request.js +0 -139
  357. package/dist/src/model-access/local-gateway-request.js.map +0 -1
  358. package/dist/src/model-access/local-gateway.js +0 -323
  359. package/dist/src/model-access/local-gateway.js.map +0 -1
  360. package/dist/src/runs/local-inference-run.js +0 -53
  361. package/dist/src/runs/local-inference-run.js.map +0 -1
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env node
2
+ // One-shot standard HTTP acceptance tool. No SDK, retry, engine management, or GPU accounting.
3
+ import { readFile, mkdir, writeFile } from 'node:fs/promises';
4
+ import { resolve, dirname } from 'node:path';
5
+ import { createHash, randomUUID } from 'node:crypto';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const help = `Usage: node accept.mjs --endpoint endpoint.json --deployment deployment.json --out artifacts/acceptance.json
9
+ --budget 1024 Output budget, passed unchanged to every generation
10
+ --concurrent 4 Number of simultaneous independent requests (at least 2)
11
+ --timeout-ms 180000 Per-request deadline, including stream consumption
12
+ --worker-metrics URL Repeat for each worker's /metrics endpoint
13
+ --gateway-metrics URL Optional official Gateway Prometheus endpoint
14
+ --expect-workers 2 Require positive measured request-count deltas on this many workers
15
+
16
+ Requires Node.js 22+. Endpoint api is explicitly responses or chat-completions.
17
+ The report contains prompts, generated output, tool arguments/results, and usage.
18
+ An HTTP report does not attest GPU topology, loaded weight identity, or real Harness behavior.
19
+ `;
20
+ const args = { budget: 1024, concurrent: 4, timeoutMs: 180000, workerMetrics: [] };
21
+ const names = { '--endpoint': 'endpoint', '--deployment': 'deployment', '--out': 'out', '--budget': 'budget', '--concurrent': 'concurrent', '--timeout-ms': 'timeoutMs', '--expect-workers': 'expectWorkers', '--gateway-metrics': 'gatewayMetrics' };
22
+ for (let i = 2; i < process.argv.length; i++) {
23
+ const flag = process.argv[i];
24
+ if (flag === '--help') { console.log(help); process.exit(0); }
25
+ const value = process.argv[++i];
26
+ if (!value || value.startsWith('--')) throw new Error(`Missing value for ${flag}`);
27
+ if (flag === '--worker-metrics') args.workerMetrics.push(value);
28
+ else if (names[flag]) args[names[flag]] = value;
29
+ else throw new Error(`Unknown option ${flag}`);
30
+ }
31
+ for (const field of ['endpoint', 'deployment', 'out']) if (!args[field]) throw new Error(`--${field} is required.\n${help}`);
32
+ for (const field of ['budget', 'concurrent', 'timeoutMs', 'expectWorkers']) {
33
+ if (args[field] === undefined) continue;
34
+ args[field] = Number(args[field]);
35
+ if (!Number.isSafeInteger(args[field]) || args[field] < (field === 'concurrent' ? 2 : 1)) throw new Error(`Invalid ${field}`);
36
+ }
37
+ if (args.expectWorkers && args.workerMetrics.length !== args.expectWorkers) throw new Error('--expect-workers must equal the number of --worker-metrics URLs');
38
+
39
+ const endpoint = JSON.parse(await readFile(args.endpoint, 'utf8'));
40
+ const deployment = JSON.parse(await readFile(args.deployment, 'utf8'));
41
+ function check(condition, message) { if (!condition) throw new Error(message); }
42
+ check(['responses', 'chat-completions'].includes(endpoint.api), 'Endpoint must explicitly select responses or chat-completions');
43
+ check(typeof endpoint.wire_model === 'string' && endpoint.wire_model.length > 0, 'Missing wire_model');
44
+ const responses = endpoint.api === 'responses';
45
+ const base = endpoint.base_url.replace(/\/+$/, '');
46
+ for (const url of [base, ...args.workerMetrics, args.gatewayMetrics].filter(Boolean)) {
47
+ const parsed = new URL(url);
48
+ check(['http:', 'https:'].includes(parsed.protocol) && !parsed.username && !parsed.password, 'Use HTTP(S) URLs without embedded credentials');
49
+ }
50
+ let apiKey;
51
+ if (endpoint.auth?.kind === 'env') {
52
+ apiKey = process.env[endpoint.auth.name];
53
+ check(typeof apiKey === 'string' && apiKey.length > 0, `Missing credential environment variable ${endpoint.auth.name}`);
54
+ } else check(endpoint.auth?.kind === 'none', 'This probe supports auth kind env or none');
55
+ function redact(value, key = '') {
56
+ if (/^(api_?key|authorization|password|secret|credential|access_token)$/i.test(key)) return '[REDACTED]';
57
+ if (typeof value === 'string') return apiKey ? value.split(apiKey).join('[REDACTED]') : value;
58
+ if (Array.isArray(value)) return value.map(item => redact(item));
59
+ if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, redact(item, name)]));
60
+ return value;
61
+ }
62
+ const report = {
63
+ schema_version: '1', observation_kind: 'standard-http-endpoint-acceptance',
64
+ started_at: new Date().toISOString(), passed: false,
65
+ hardware_validation: 'NOT PERFORMED BY THIS HTTP PROBE',
66
+ identity_validation: 'operator metadata only; not proof of loaded weights',
67
+ harness_validation: 'NOT PERFORMED; run the selected real Harness separately',
68
+ configuration: { endpoint, deployment, budget: args.budget, temperature: 0, concurrent_requests: args.concurrent, request_timeout_ms: args.timeoutMs, transparent_retries: 0 },
69
+ probe: { node_version: process.version, sha256: createHash('sha256').update(await readFile(fileURLToPath(import.meta.url))).digest('hex') },
70
+ checks: [], exchanges: [],
71
+ };
72
+ const generationUrl = `${base}/${responses ? 'responses' : 'chat/completions'}`;
73
+ const user = text => ({ role: 'user', content: text });
74
+ function body(history, { stream = false, tools, toolChoice } = {}) {
75
+ return {
76
+ model: endpoint.wire_model, temperature: 0,
77
+ ...(responses ? { input: history, max_output_tokens: args.budget, store: false } : { messages: history, max_tokens: args.budget }),
78
+ stream,
79
+ ...(!responses && stream ? { stream_options: { include_usage: true } } : {}),
80
+ ...(tools ? { tools, parallel_tool_calls: false, tool_choice: toolChoice ?? 'auto' } : {}),
81
+ };
82
+ }
83
+ function view(json) {
84
+ if (responses) {
85
+ check(['completed', 'incomplete'].includes(json.status), `Unexpected Responses status ${json.status}`);
86
+ if (json.status === 'incomplete') check(json.incomplete_details?.reason === 'max_output_tokens', 'Unexpected incomplete response reason');
87
+ return { text: (json.output ?? []).flatMap(item => item.type === 'message' ? (item.content ?? []).filter(part => part.type === 'output_text').map(part => part.text) : []).join(''), tools: (json.output ?? []).filter(item => item.type === 'function_call').map(item => ({ id: item.call_id, name: item.name, arguments: item.arguments })), history: json.output ?? [], usage: json.usage, terminal: json.status };
88
+ }
89
+ const choice = json.choices?.[0];
90
+ check(choice && choice.finish_reason !== null && choice.finish_reason !== undefined, 'Missing chat finish_reason');
91
+ const message = choice.message;
92
+ check(message?.role === 'assistant', 'Missing assistant chat message');
93
+ return { text: message.content ?? '', tools: (message.tool_calls ?? []).map(item => ({ id: item.id, name: item.function?.name, arguments: item.function?.arguments })), history: [{ role: 'assistant', content: message.content ?? null, ...(message.tool_calls?.length ? { tool_calls: message.tool_calls } : {}) }], usage: json.usage, terminal: choice.finish_reason };
94
+ }
95
+ function verifyUsage(result) {
96
+ const usage = result.usage;
97
+ check(usage && typeof usage === 'object' && !Array.isArray(usage), 'Missing or invalid usage; missing values are not replaced with zero');
98
+ function count(name, aliases) {
99
+ const fields = aliases.filter(field => Object.hasOwn(usage, field));
100
+ check(fields.length > 0 && fields.every(field => Number.isSafeInteger(usage[field]) && usage[field] >= 0), `Missing or invalid ${name} usage; missing values are not replaced with zero`);
101
+ const value = usage[fields[0]];
102
+ check(fields.every(field => usage[field] === value), `Inconsistent ${name} usage aliases: ${fields.join(', ')}`);
103
+ return { value, fields };
104
+ }
105
+ // Verify recognized wire variants without rewriting the captured upstream usage.
106
+ const input = count('input', ['input_tokens', 'prompt_tokens']);
107
+ const output = count('output', ['output_tokens', 'completion_tokens']);
108
+ check(output.value <= args.budget, `Reported output usage ${output.value} exceeds the unchanged budget ${args.budget}`);
109
+ return { input_tokens: input.value, output_tokens: output.value, source_fields: { input_tokens: input.fields, output_tokens: output.fields } };
110
+ }
111
+ async function exchange(name, url, { data, mode = 'json', signal, onContent, expectedAbort = false, acceptedStatus, key = apiKey } = {}) {
112
+ const started = performance.now();
113
+ const record = { name, started_at: new Date().toISOString(), request: { method: data ? 'POST' : 'GET', url, ...(data ? { body: data } : {}), authentication: key === apiKey ? endpoint.auth.kind : 'intentionally-invalid-key' } };
114
+ report.exchanges.push(record);
115
+ try {
116
+ const response = await fetch(url, { method: data ? 'POST' : 'GET', headers: { ...(data ? { 'Content-Type': 'application/json' } : {}), ...(key ? { Authorization: `Bearer ${key}` } : {}), 'X-Request-ID': `hitch-accept-${randomUUID()}` }, ...(data ? { body: JSON.stringify(data) } : {}), signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(args.timeoutMs)]) : AbortSignal.timeout(args.timeoutMs) });
117
+ record.response = { status: response.status, headers: Object.fromEntries(['content-type', 'x-request-id', 'retry-after'].flatMap(key => response.headers.has(key) ? [[key, response.headers.get(key)]] : [])) };
118
+ if (!response.ok || acceptedStatus || mode === 'text') {
119
+ record.response.body = await response.text();
120
+ check(acceptedStatus ? acceptedStatus.includes(response.status) : response.ok, `HTTP ${response.status} for ${name}`);
121
+ return record.response.body;
122
+ }
123
+ if (mode === 'json') {
124
+ const text = await response.text();
125
+ record.response.body = text;
126
+ const json = JSON.parse(text);
127
+ record.response.body = json;
128
+ const result = view(json);
129
+ record.response.usage_verification = verifyUsage(result);
130
+ return result;
131
+ }
132
+ check(response.headers.get('content-type')?.includes('text/event-stream'), 'Streaming response is not SSE');
133
+ const events = [];
134
+ record.response.events = events;
135
+ let buffer = '', totalBytes = 0, streamedText = '', finalResponse, usage, finishReason, done = false;
136
+ const calls = new Map();
137
+ function event(block) {
138
+ const lines = block.split(/\r?\n/);
139
+ const payload = lines.filter(line => line.startsWith('data:')).map(line => line.slice(5).replace(/^ /, '')).join('\n');
140
+ if (!payload) return;
141
+ if (payload === '[DONE]') { done = true; events.push({ data: '[DONE]', elapsed_ms: performance.now() - started }); return; }
142
+ const json = JSON.parse(payload);
143
+ events.push({ event: lines.find(line => line.startsWith('event:'))?.slice(6).trim(), data: json, elapsed_ms: performance.now() - started });
144
+ check(json.type !== 'error' && json.type !== 'response.failed' && !json.error, 'Upstream reported a streaming error');
145
+ if (responses) {
146
+ if (json.type === 'response.output_text.delta' && json.delta) { streamedText += json.delta; onContent?.(); }
147
+ if (['response.completed', 'response.incomplete'].includes(json.type)) finalResponse = json.response;
148
+ } else {
149
+ if (json.usage) usage = json.usage;
150
+ for (const choice of json.choices ?? []) {
151
+ if (choice.delta?.content) { streamedText += choice.delta.content; onContent?.(); }
152
+ if (choice.finish_reason) finishReason = choice.finish_reason;
153
+ for (const delta of choice.delta?.tool_calls ?? []) {
154
+ const call = calls.get(delta.index) ?? { id: '', type: 'function', function: { name: '', arguments: '' } };
155
+ call.id += delta.id ?? '';
156
+ call.function.name += delta.function?.name ?? '';
157
+ call.function.arguments += delta.function?.arguments ?? '';
158
+ calls.set(delta.index, call);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ const decoder = new TextDecoder();
164
+ for await (const chunk of response.body) {
165
+ totalBytes += chunk.byteLength;
166
+ check(totalBytes <= 8 * 1024 * 1024, 'SSE response exceeds probe capture limit');
167
+ buffer += decoder.decode(chunk, { stream: true });
168
+ let boundary;
169
+ while ((boundary = /\r?\n\r?\n/.exec(buffer))) {
170
+ event(buffer.slice(0, boundary.index));
171
+ buffer = buffer.slice(boundary.index + boundary[0].length);
172
+ }
173
+ }
174
+ buffer += decoder.decode();
175
+ if (buffer.trim()) event(buffer);
176
+ if (responses) check(finalResponse, 'SSE ended without a terminal Responses snapshot');
177
+ else check(done && finishReason, 'SSE ended without chat finish_reason and [DONE]');
178
+ const result = view(responses ? finalResponse : { choices: [{ finish_reason: finishReason, message: { role: 'assistant', content: streamedText || null, ...(calls.size ? { tool_calls: [...calls.values()] } : {}) } }], usage });
179
+ record.response.usage_verification = verifyUsage(result);
180
+ result.streamed_text = streamedText;
181
+ result.streamed_tool_arguments = responses ? events.filter(item => item.data?.type === 'response.function_call_arguments.delta').map(item => item.data.delta ?? '').join('') : [...calls.values()].map(item => item.function.arguments).join('');
182
+ record.response.terminal = result.terminal;
183
+ record.response.usage = result.usage;
184
+ return result;
185
+ } catch (error) {
186
+ record.error = { name: error.name, message: error.message };
187
+ if (expectedAbort && signal?.aborted) { record.expected_client_abort = true; return { aborted: true }; }
188
+ throw error;
189
+ } finally { record.duration_ms = performance.now() - started; }
190
+ }
191
+ async function checkStep(name, run) {
192
+ const item = { name, passed: false };
193
+ report.checks.push(item);
194
+ try { await run(); item.passed = true; }
195
+ catch (error) { item.error = error.message; throw error; }
196
+ }
197
+ async function generate(name, history, options = {}) { return exchange(name, generationUrl, { data: body(history, options), mode: options.stream ? 'sse' : 'json', ...options }); }
198
+ const marker = prefix => `${prefix}_${randomUUID().replaceAll('-', '')}`;
199
+ const assertText = (result, expected) => check(typeof result.text === 'string' && result.text.includes(expected), `Generated output did not contain expected marker ${expected}`);
200
+ async function toolRoundtrip(stream) {
201
+ const parcel = marker('PARCEL'), delivery = marker('DELIVERED');
202
+ const definition = { name: 'lookup_delivery_code', description: 'Look up the delivery confirmation code for a parcel.', parameters: { type: 'object', properties: { parcel_id: { type: 'string' } }, required: ['parcel_id'], additionalProperties: false }, strict: true };
203
+ const tools = [responses ? { type: 'function', ...definition } : { type: 'function', function: definition }];
204
+ const toolChoice = responses ? { type: 'function', name: definition.name } : { type: 'function', function: { name: definition.name } };
205
+ const history = [user(`Call lookup_delivery_code with parcel_id ${parcel}. Then tell me the delivery_code returned by the tool. Do not invent the result.`)];
206
+ const first = await generate(`tools-${stream ? 'stream' : 'json'}-call`, history, { tools, toolChoice, stream });
207
+ check(first.tools.length === 1, 'Expected exactly one function call');
208
+ if (stream) check(first.streamed_tool_arguments.length > 0, 'Streaming tool response did not emit function arguments');
209
+ const call = first.tools[0];
210
+ check(call.id && call.name === definition.name, 'Model returned an undeclared function or missing call id');
211
+ const input = JSON.parse(call.arguments);
212
+ check(input.parcel_id === parcel && Object.keys(input).length === 1, 'Tool arguments do not match the requested parcel');
213
+ // Execute this one allowlisted, pure local tool; then send its actual output back.
214
+ const output = { parcel_id: input.parcel_id, delivery_code: delivery };
215
+ report.exchanges.push({ name: 'local-tool-execution', tool: call.name, call_id: call.id, input, output });
216
+ const resultItem = responses ? { type: 'function_call_output', call_id: call.id, output: JSON.stringify(output) } : { role: 'tool', tool_call_id: call.id, content: JSON.stringify(output) };
217
+ const final = await generate(`tools-${stream ? 'stream' : 'json'}-continue`, [...history, ...first.history, resultItem], { tools, toolChoice: 'none', stream });
218
+ assertText(final, delivery);
219
+ if (stream) check(final.streamed_text.includes(delivery), 'Tool continuation text was not delivered in SSE deltas');
220
+ check(final.tools.length === 0, 'Model did not finish after the tool result');
221
+ }
222
+ async function metricsSnapshot(label) {
223
+ return Promise.all(args.workerMetrics.map(async (url, index) => {
224
+ const text = await exchange(`${label}-worker-${index + 1}-metrics`, url, { mode: 'text' });
225
+ const matches = [...text.matchAll(/^sglang:num_requests_total(?:\{[^}]*\})?\s+([0-9eE.+-]+)/gm)];
226
+ return { url, request_count: matches.length ? matches.reduce((sum, match) => sum + Number(match[1]), 0) : null };
227
+ }));
228
+ }
229
+ async function cancellationIsolation() {
230
+ const cancelled = new AbortController();
231
+ const cancelledFirst = Promise.withResolvers(), survivorFirst = Promise.withResolvers();
232
+ let cancelledEnded = false, survivorEnded = false;
233
+ const cancelMarker = marker('CANCEL'), survivorMarker = marker('SURVIVE');
234
+ const prompt = value => [user(`Begin with ${value}. Then write a long tutorial with at least 150 numbered sentences about arithmetic, explaining each sentence in detail.`)];
235
+ const cancelRequest = generate('cancel-one-active-stream', prompt(cancelMarker), { stream: true, signal: cancelled.signal, expectedAbort: true, onContent: () => cancelledFirst.resolve() }).finally(() => { cancelledEnded = true; cancelledFirst.reject(new Error('Cancelled request ended before content')); });
236
+ const survivorRequest = generate('independent-surviving-stream', prompt(survivorMarker), { stream: true, onContent: () => survivorFirst.resolve() }).finally(() => { survivorEnded = true; survivorFirst.reject(new Error('Survivor ended before content')); });
237
+ // Attach handlers immediately so a failure cannot leave an unhandled rejection.
238
+ const settled = Promise.allSettled([cancelRequest, survivorRequest]);
239
+ try {
240
+ await Promise.all([cancelledFirst.promise, survivorFirst.promise]);
241
+ check(!cancelledEnded && !survivorEnded, 'Streams did not overlap long enough to observe cancellation isolation');
242
+ report.cancellation = { aborted_at: new Date().toISOString(), both_streams_had_content: true, both_streams_incomplete_at_abort: true, scope: 'only the cancelled request AbortController; no stop, drain, flush, or release API' };
243
+ cancelled.abort(new DOMException('Acceptance cancels this request only', 'AbortError'));
244
+ } catch (error) { cancelled.abort(); await settled; throw error; }
245
+ const [a, b] = await settled;
246
+ check(a.status === 'fulfilled' && a.value.aborted, 'Cancelled stream did not observe client abort');
247
+ if (b.status === 'rejected') throw b.reason;
248
+ assertText(b.value, survivorMarker);
249
+ const after = marker('STILL_SERVING');
250
+ assertText(await generate('service-remains-available-after-cancel', [user(`Reply with exactly ${after}.`)]), after);
251
+ }
252
+ try {
253
+ if (apiKey) await checkStep('authentication rejects an invalid key on the generation endpoint', () => exchange('invalid-auth', generationUrl, { data: body([user('Say hello.')]), acceptedStatus: [401, 403], key: `invalid-${randomUUID()}` }));
254
+ await checkStep('normal-budget generation and usage', async () => {
255
+ const expected = marker('GENERATED');
256
+ assertText(await generate('plain-generation', [user(`Reply with exactly ${expected}.`)]), expected);
257
+ });
258
+ await checkStep('complete multi-turn history without previous_response_id', async () => {
259
+ const expected = marker('REMEMBER');
260
+ const history = [user(`Remember this marker: ${expected}. Reply with Remembered.`)];
261
+ const first = await generate('history-first-turn', history);
262
+ assertText(await generate('history-second-turn', [...history, ...first.history, user('Repeat the marker from my first message exactly.')]), expected);
263
+ });
264
+ await checkStep('SSE terminal event and usage', async () => {
265
+ const expected = marker('STREAMED');
266
+ const result = await generate('stream-generation', [user(`Reply with exactly ${expected}.`)], { stream: true });
267
+ assertText(result, expected);
268
+ check(result.streamed_text.includes(expected), 'Text was not delivered in SSE deltas');
269
+ });
270
+ await checkStep('JSON tool call, local execution, result, and continuation', () => toolRoundtrip(false));
271
+ await checkStep('streaming tool call, local execution, result, and continuation', () => toolRoundtrip(true));
272
+ const before = await metricsSnapshot('before-concurrency');
273
+ if (args.gatewayMetrics) await exchange('before-gateway-metrics', args.gatewayMetrics, { mode: 'text' });
274
+ await checkStep('independent concurrent requests', async () => {
275
+ await Promise.all(Array.from({ length: args.concurrent }, async (_, index) => {
276
+ const expected = marker(`CONCURRENT_${index}`);
277
+ assertText(await generate(`concurrent-${index + 1}`, [user(`Reply with exactly ${expected}.`)]), expected);
278
+ }));
279
+ });
280
+ await checkStep('abort an active stream while another completes and service remains usable', cancellationIsolation);
281
+ const after = await metricsSnapshot('after-concurrency');
282
+ if (args.gatewayMetrics) await exchange('after-gateway-metrics', args.gatewayMetrics, { mode: 'text' });
283
+ report.worker_distribution = before.map((entry, index) => ({ url: entry.url, before: entry.request_count, after: after[index].request_count, delta: entry.request_count !== null && after[index].request_count !== null ? after[index].request_count - entry.request_count : null }));
284
+ if (args.expectWorkers) await checkStep('server metrics show requests on every expected worker', async () => {
285
+ check(report.worker_distribution.every(entry => Number.isFinite(entry.delta) && entry.delta > 0), 'Missing worker request counters, reset counters, or no positive request delta on a worker');
286
+ });
287
+ report.passed = true;
288
+ } catch (error) {
289
+ report.error = { name: error.name, message: error.message };
290
+ } finally {
291
+ report.finished_at = new Date().toISOString();
292
+ const output = resolve(args.out);
293
+ await mkdir(dirname(output), { recursive: true });
294
+ await writeFile(output, `${JSON.stringify(redact(report), null, 2)}\n`, { mode: 0o600 });
295
+ console.log(`${report.passed ? 'PASS' : 'FAIL'}: HTTP acceptance report written to ${output}`);
296
+ if (!report.passed) { console.error(redact(report.error?.message ?? 'Acceptance failed')); process.exitCode = 1; }
297
+ }
@@ -0,0 +1,119 @@
1
+ name: hitch-external-inference
2
+
3
+ x-worker: &worker
4
+ image: ${SGLANG_IMAGE:-lmsysorg/sglang:v0.5.21}
5
+ init: true
6
+ restart: unless-stopped
7
+ shm_size: 8gb
8
+ entrypoint: ["/bin/sh", "-ec"]
9
+ command:
10
+ - |
11
+ key="$$(cat /run/secrets/api_key)"
12
+ test -n "$$key"
13
+ exec python3 -m sglang.launch_server \
14
+ --model-path /model --tokenizer-path /model \
15
+ --served-model-name "$$WIRE_MODEL" \
16
+ --host 0.0.0.0 --port 30000 --api-key "$$key" \
17
+ --tp-size 1 --dtype bfloat16 --disable-cuda-graph \
18
+ --context-length "$$CONTEXT_LENGTH" \
19
+ --mem-fraction-static "$$MEM_FRACTION_STATIC" \
20
+ --max-running-requests "$$ENGINE_MAX_RUNNING" \
21
+ --max-queued-requests "$$ENGINE_MAX_QUEUED" \
22
+ --tool-call-parser qwen25 --enable-metrics
23
+ environment:
24
+ WIRE_MODEL: ${WIRE_MODEL:?Set a versioned wire model name}
25
+ CONTEXT_LENGTH: ${CONTEXT_LENGTH:-16384}
26
+ MEM_FRACTION_STATIC: ${MEM_FRACTION_STATIC:-0.82}
27
+ ENGINE_MAX_RUNNING: ${ENGINE_MAX_RUNNING:-4}
28
+ ENGINE_MAX_QUEUED: ${ENGINE_MAX_QUEUED:-8}
29
+ HF_HUB_OFFLINE: "1"
30
+ TRANSFORMERS_OFFLINE: "1"
31
+ secrets: [api_key]
32
+ volumes:
33
+ - type: bind
34
+ source: ${HF_SNAPSHOT_DIR:?Set a self-contained immutable HF snapshot directory}
35
+ target: /model
36
+ read_only: true
37
+ bind:
38
+ create_host_path: false
39
+ healthcheck:
40
+ test: ["CMD-SHELL", 'curl -fsS -H "Authorization: Bearer $$(cat /run/secrets/api_key)" http://127.0.0.1:30000/health >/dev/null']
41
+ interval: 10s
42
+ timeout: 5s
43
+ start_period: 10m
44
+ retries: 60
45
+ networks: [model]
46
+
47
+ services:
48
+ worker-1:
49
+ <<: *worker
50
+ ports: ["127.0.0.1:30001:30000"]
51
+ deploy:
52
+ resources:
53
+ reservations:
54
+ devices:
55
+ - driver: nvidia
56
+ device_ids: ["${INFERENCE_GPU_0:?Set the first GPU UUID reserved for inference}"]
57
+ capabilities: [gpu]
58
+
59
+ worker-2:
60
+ <<: *worker
61
+ ports: ["127.0.0.1:30002:30000"]
62
+ deploy:
63
+ resources:
64
+ reservations:
65
+ devices:
66
+ - driver: nvidia
67
+ device_ids: ["${INFERENCE_GPU_1:?Set a different GPU UUID reserved for inference}"]
68
+ capabilities: [gpu]
69
+
70
+ gateway:
71
+ # Upstream gateway-only release; workers retain their separate SGLang image.
72
+ image: ${GATEWAY_IMAGE:-lightseekorg/smg:1.12.0@sha256:d08b509491f222c2c9786323a3df90136127364c25b9b9b48e91b4b3e9e695d6}
73
+ # Read the host-owned 0600 Compose secret without relaxing its permissions.
74
+ user: "0:0"
75
+ init: true
76
+ restart: unless-stopped
77
+ entrypoint: ["/bin/sh", "-ec"]
78
+ command:
79
+ - |
80
+ key="$$(cat /run/secrets/api_key)"
81
+ test -n "$$key"
82
+ exec smg launch \
83
+ --host 0.0.0.0 --port 30000 \
84
+ --worker-urls http://worker-1:30000 http://worker-2:30000 \
85
+ --api-key "$$key" --policy round_robin --disable-retries \
86
+ --disable-tokenizer-autoload \
87
+ --max-concurrent-requests "$$GATEWAY_MAX_CONCURRENT" \
88
+ --queue-size "$$GATEWAY_QUEUE_SIZE" --queue-timeout-secs 30 \
89
+ --request-timeout-secs 180 --worker-startup-timeout-secs 1800 \
90
+ --history-backend none --prometheus-host 0.0.0.0 --prometheus-port 10001
91
+ environment:
92
+ NVIDIA_VISIBLE_DEVICES: "void"
93
+ CUDA_VISIBLE_DEVICES: ""
94
+ GATEWAY_MAX_CONCURRENT: ${GATEWAY_MAX_CONCURRENT:-8}
95
+ GATEWAY_QUEUE_SIZE: ${GATEWAY_QUEUE_SIZE:-16}
96
+ secrets: [api_key]
97
+ ports:
98
+ - "127.0.0.1:${INFERENCE_PORT:-30000}:30000"
99
+ - "127.0.0.1:10001:10001"
100
+ depends_on:
101
+ worker-1:
102
+ condition: service_healthy
103
+ worker-2:
104
+ condition: service_healthy
105
+ healthcheck:
106
+ test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:30000/readiness', timeout=4).close()"]
107
+ interval: 10s
108
+ timeout: 5s
109
+ start_period: 1m
110
+ retries: 60
111
+ networks: [model]
112
+
113
+ secrets:
114
+ api_key:
115
+ file: ${INFERENCE_API_KEY_FILE:?Set an absolute path to a private API key file}
116
+
117
+ networks:
118
+ model:
119
+ internal: true
@@ -0,0 +1,64 @@
1
+ name: hitch-external-inference
2
+
3
+ services:
4
+ engine:
5
+ image: ${SGLANG_IMAGE:-lmsysorg/sglang:v0.5.21}
6
+ init: true
7
+ restart: unless-stopped
8
+ shm_size: 8gb
9
+ entrypoint: ["/bin/sh", "-ec"]
10
+ command:
11
+ - |
12
+ key="$$(cat /run/secrets/api_key)"
13
+ test -n "$$key"
14
+ exec python3 -m sglang.launch_server \
15
+ --model-path /model --tokenizer-path /model \
16
+ --served-model-name "$$WIRE_MODEL" \
17
+ --host 0.0.0.0 --port 30000 --api-key "$$key" \
18
+ --tp-size "$$TP_SIZE" --dtype bfloat16 --disable-cuda-graph \
19
+ --context-length "$$CONTEXT_LENGTH" \
20
+ --mem-fraction-static "$$MEM_FRACTION_STATIC" \
21
+ --max-running-requests "$$ENGINE_MAX_RUNNING" \
22
+ --max-queued-requests "$$ENGINE_MAX_QUEUED" \
23
+ --tool-call-parser qwen25 --enable-metrics
24
+ environment:
25
+ WIRE_MODEL: ${WIRE_MODEL:?Set a versioned wire model name}
26
+ TP_SIZE: "1"
27
+ CONTEXT_LENGTH: ${CONTEXT_LENGTH:-16384}
28
+ MEM_FRACTION_STATIC: ${MEM_FRACTION_STATIC:-0.82}
29
+ ENGINE_MAX_RUNNING: ${ENGINE_MAX_RUNNING:-4}
30
+ ENGINE_MAX_QUEUED: ${ENGINE_MAX_QUEUED:-8}
31
+ HF_HUB_OFFLINE: "1"
32
+ TRANSFORMERS_OFFLINE: "1"
33
+ secrets: [api_key]
34
+ volumes:
35
+ - type: bind
36
+ source: ${HF_SNAPSHOT_DIR:?Set a self-contained immutable HF snapshot directory}
37
+ target: /model
38
+ read_only: true
39
+ bind:
40
+ create_host_path: false
41
+ ports:
42
+ - "127.0.0.1:${INFERENCE_PORT:-30000}:30000"
43
+ healthcheck:
44
+ test: ["CMD-SHELL", 'curl -fsS -H "Authorization: Bearer $$(cat /run/secrets/api_key)" http://127.0.0.1:30000/health >/dev/null']
45
+ interval: 10s
46
+ timeout: 5s
47
+ start_period: 10m
48
+ retries: 60
49
+ deploy:
50
+ resources:
51
+ reservations:
52
+ devices:
53
+ - driver: nvidia
54
+ device_ids: ["${INFERENCE_GPU_0:?Set the GPU UUID reserved for inference}"]
55
+ capabilities: [gpu]
56
+ networks: [model]
57
+
58
+ secrets:
59
+ api_key:
60
+ file: ${INFERENCE_API_KEY_FILE:?Set an absolute path to a private API key file}
61
+
62
+ networks:
63
+ model:
64
+ internal: true
@@ -0,0 +1,15 @@
1
+ # Apply only with compose.single.yaml. Requires Docker Compose >= 2.24.4.
2
+ services:
3
+ engine:
4
+ shm_size: 16gb
5
+ environment:
6
+ TP_SIZE: "2"
7
+ deploy:
8
+ resources:
9
+ reservations:
10
+ devices: !override
11
+ - driver: nvidia
12
+ device_ids:
13
+ - "${INFERENCE_GPU_0:?Set the first inference GPU UUID}"
14
+ - "${INFERENCE_GPU_1:?Set the second inference GPU UUID}"
15
+ capabilities: [gpu]
@@ -0,0 +1,58 @@
1
+ {
2
+ "record_kind": "operator-declared-example-not-a-trusted-deployment-receipt",
3
+ "deployment_ref": "REPLACE_WITH_DEPLOYMENT_PIPELINE_RUN_OR_CHANGE_REF",
4
+ "subject": {
5
+ "artifact_digest": "COPY_FROM_EXISTING_CAS_MODEL_MANIFEST",
6
+ "hf_repository": "Qwen/Qwen2.5-7B-Instruct",
7
+ "hf_revision": "a09a35458c702b33eeacc393d103063234e8bc28"
8
+ },
9
+ "engine": {
10
+ "image": "lmsysorg/sglang:v0.5.21",
11
+ "resolved_image_digest": null,
12
+ "source_commit": "e00930c5489053f26d86b179cee0d087f846acbb",
13
+ "configured_sglang_version": "0.5.21",
14
+ "observed_package_versions": null
15
+ },
16
+ "gateway": {
17
+ "enabled": false,
18
+ "image": "lightseekorg/smg:1.12.0@sha256:d08b509491f222c2c9786323a3df90136127364c25b9b9b48e91b4b3e9e695d6",
19
+ "resolved_image_digest": null,
20
+ "source_commit": "dcc5cb0b8f2a64694938c10f02c6caaa0c40d5ef",
21
+ "configured_smg_version": "1.12.0",
22
+ "observed_package_versions": null
23
+ },
24
+ "configuration": {
25
+ "compose_files": [
26
+ "compose.single.yaml"
27
+ ],
28
+ "wire_model": "qwen25-7b-a09a35458c70",
29
+ "tensor_parallel_size": 1,
30
+ "replicas": 1,
31
+ "dtype": "bfloat16",
32
+ "quantization": "none",
33
+ "context_length": 16384,
34
+ "tool_parser": "qwen25",
35
+ "tokenizer_and_template": "from-the-same-read-only-HF-snapshot",
36
+ "radix_cache": "upstream-default-enabled",
37
+ "max_running_requests_per_worker": 4,
38
+ "max_queued_requests_per_worker": 8,
39
+ "gateway": null,
40
+ "request_sampling": {
41
+ "temperature": 0,
42
+ "output_budget": 1024
43
+ },
44
+ "mem_fraction_static": 0.82,
45
+ "disable_cuda_graph": true
46
+ },
47
+ "isolation": {
48
+ "policy": "static-exclusive-device-allocation-by-deployment-owner",
49
+ "inference_gpu_uuids": [],
50
+ "training_gpu_uuids": [],
51
+ "allocation_record_ref": null
52
+ },
53
+ "hardware_validation": {
54
+ "status": "NOT RUN",
55
+ "evidence_ref": null
56
+ },
57
+ "receipt_source": null
58
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schema_version": "1",
3
+ "base_url": "http://127.0.0.1:30000/v1",
4
+ "wire_model": "qwen25-7b-a09a35458c70",
5
+ "api": "chat-completions",
6
+ "auth": {"kind": "env", "name": "INFERENCE_API_KEY"},
7
+ "capabilities": {
8
+ "streaming": true,
9
+ "tool_calls": true,
10
+ "parallel_tool_calls": false,
11
+ "input_modalities": ["text"]
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "schema_version": "1",
3
+ "base_url": "http://127.0.0.1:30000/v1",
4
+ "wire_model": "qwen25-7b-a09a35458c70",
5
+ "api": "responses",
6
+ "auth": {"kind": "env", "name": "INFERENCE_API_KEY"},
7
+ "capabilities": {
8
+ "streaming": true,
9
+ "tool_calls": true,
10
+ "parallel_tool_calls": false,
11
+ "input_modalities": ["text"]
12
+ }
13
+ }