@mcoda/mswarm 0.1.133 → 0.1.135-codali.2

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 (250) hide show
  1. package/README.md +44 -0
  2. package/dist/codali-executor.d.ts +66 -1
  3. package/dist/codali-executor.d.ts.map +1 -1
  4. package/dist/codali-executor.js +83 -8
  5. package/dist/codali-executor.js.map +1 -1
  6. package/dist/invocation-token.d.ts.map +1 -1
  7. package/dist/invocation-token.js +1 -0
  8. package/dist/invocation-token.js.map +1 -1
  9. package/dist/native-reasoning.d.ts +8 -0
  10. package/dist/native-reasoning.d.ts.map +1 -0
  11. package/dist/native-reasoning.js +18 -0
  12. package/dist/native-reasoning.js.map +1 -0
  13. package/dist/runtime.d.ts +25 -1
  14. package/dist/runtime.d.ts.map +1 -1
  15. package/dist/runtime.js +220 -20
  16. package/dist/runtime.js.map +1 -1
  17. package/dist/server.d.ts +3 -1
  18. package/dist/server.d.ts.map +1 -1
  19. package/dist/server.js +58 -8
  20. package/dist/server.js.map +1 -1
  21. package/dist/vendor/codali/agents/RoleResolution.d.ts.map +1 -1
  22. package/dist/vendor/codali/agents/RoleResolution.js +4 -4
  23. package/dist/vendor/codali/api/CodaliApi.d.ts +16 -0
  24. package/dist/vendor/codali/api/CodaliApi.d.ts.map +1 -1
  25. package/dist/vendor/codali/api/CodaliApi.js +39 -10
  26. package/dist/vendor/codali/cli/AskCommand.d.ts +3 -0
  27. package/dist/vendor/codali/cli/AskCommand.d.ts.map +1 -1
  28. package/dist/vendor/codali/cli/AskCommand.js +76 -13
  29. package/dist/vendor/codali/cli/RunCommand.d.ts +4 -0
  30. package/dist/vendor/codali/cli/RunCommand.d.ts.map +1 -1
  31. package/dist/vendor/codali/cli/RunCommand.js +110 -24
  32. package/dist/vendor/codali/cli/TerminalCommand.d.ts +35 -0
  33. package/dist/vendor/codali/cli/TerminalCommand.d.ts.map +1 -0
  34. package/dist/vendor/codali/cli/TerminalCommand.js +616 -0
  35. package/dist/vendor/codali/cli/TerminalCredentials.d.ts +3 -0
  36. package/dist/vendor/codali/cli/TerminalCredentials.d.ts.map +1 -0
  37. package/dist/vendor/codali/cli/TerminalCredentials.js +89 -0
  38. package/dist/vendor/codali/cli/TerminalInput.d.ts +24 -0
  39. package/dist/vendor/codali/cli/TerminalInput.d.ts.map +1 -0
  40. package/dist/vendor/codali/cli/TerminalInput.js +98 -0
  41. package/dist/vendor/codali/cli/TerminalText.d.ts +10 -0
  42. package/dist/vendor/codali/cli/TerminalText.d.ts.map +1 -0
  43. package/dist/vendor/codali/cli/TerminalText.js +53 -0
  44. package/dist/vendor/codali/cli.d.ts.map +1 -1
  45. package/dist/vendor/codali/cli.js +33 -4
  46. package/dist/vendor/codali/cognitive/ConfiguredPipeline.d.ts +34 -0
  47. package/dist/vendor/codali/cognitive/ConfiguredPipeline.d.ts.map +1 -0
  48. package/dist/vendor/codali/cognitive/ConfiguredPipeline.js +50 -0
  49. package/dist/vendor/codali/cognitive/ValidationRunner.d.ts +4 -0
  50. package/dist/vendor/codali/cognitive/ValidationRunner.d.ts.map +1 -1
  51. package/dist/vendor/codali/cognitive/ValidationRunner.js +18 -41
  52. package/dist/vendor/codali/config/NodeOrchestration.d.ts +21 -0
  53. package/dist/vendor/codali/config/NodeOrchestration.d.ts.map +1 -0
  54. package/dist/vendor/codali/config/NodeOrchestration.js +54 -0
  55. package/dist/vendor/codali/config/OrchestrationConfig.d.ts +100 -0
  56. package/dist/vendor/codali/config/OrchestrationConfig.d.ts.map +1 -0
  57. package/dist/vendor/codali/config/OrchestrationConfig.js +245 -0
  58. package/dist/vendor/codali/connectors/mcp/McpClient.d.ts +3 -1
  59. package/dist/vendor/codali/connectors/mcp/McpClient.d.ts.map +1 -1
  60. package/dist/vendor/codali/connectors/mcp/McpClient.js +3 -2
  61. package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts +2 -2
  62. package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts.map +1 -1
  63. package/dist/vendor/codali/connectors/mcp/McpServerRegistry.js +39 -16
  64. package/dist/vendor/codali/connectors/mcp/McpToolAdapter.js +2 -2
  65. package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.d.ts +6 -0
  66. package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.d.ts.map +1 -0
  67. package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.js +41 -0
  68. package/dist/vendor/codali/connectors/media/ImageGenerationTool.js +3 -3
  69. package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.d.ts +5 -0
  70. package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.d.ts.map +1 -0
  71. package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.js +65 -0
  72. package/dist/vendor/codali/connectors/media/MediaJobStore.d.ts +68 -0
  73. package/dist/vendor/codali/connectors/media/MediaJobStore.d.ts.map +1 -0
  74. package/dist/vendor/codali/connectors/media/MediaJobStore.js +199 -0
  75. package/dist/vendor/codali/connectors/media/MediaJobWorker.d.ts +2 -0
  76. package/dist/vendor/codali/connectors/media/MediaJobWorker.d.ts.map +1 -0
  77. package/dist/vendor/codali/connectors/media/MediaJobWorker.js +65 -0
  78. package/dist/vendor/codali/connectors/media/RemoteMediaJob.d.ts +6 -0
  79. package/dist/vendor/codali/connectors/media/RemoteMediaJob.d.ts.map +1 -0
  80. package/dist/vendor/codali/connectors/media/RemoteMediaJob.js +80 -0
  81. package/dist/vendor/codali/docdex/DocdexClient.d.ts +18 -1
  82. package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -1
  83. package/dist/vendor/codali/docdex/DocdexClient.js +135 -19
  84. package/dist/vendor/codali/docdex/ManagedDocdex.d.ts +29 -0
  85. package/dist/vendor/codali/docdex/ManagedDocdex.d.ts.map +1 -0
  86. package/dist/vendor/codali/docdex/ManagedDocdex.js +215 -0
  87. package/dist/vendor/codali/docdex/ManagedRuntime.d.ts +28 -0
  88. package/dist/vendor/codali/docdex/ManagedRuntime.d.ts.map +1 -0
  89. package/dist/vendor/codali/docdex/ManagedRuntime.js +212 -0
  90. package/dist/vendor/codali/eval/EvaluationProcess.d.ts +13 -0
  91. package/dist/vendor/codali/eval/EvaluationProcess.d.ts.map +1 -0
  92. package/dist/vendor/codali/eval/EvaluationProcess.js +76 -0
  93. package/dist/vendor/codali/eval/LongWorkflowJourney.d.ts +34 -0
  94. package/dist/vendor/codali/eval/LongWorkflowJourney.d.ts.map +1 -0
  95. package/dist/vendor/codali/eval/LongWorkflowJourney.js +243 -0
  96. package/dist/vendor/codali/eval/PlanningCorpus.d.ts +45 -0
  97. package/dist/vendor/codali/eval/PlanningCorpus.d.ts.map +1 -0
  98. package/dist/vendor/codali/eval/PlanningCorpus.js +189 -0
  99. package/dist/vendor/codali/eval/PlanningCorpusRunner.d.ts +13 -0
  100. package/dist/vendor/codali/eval/PlanningCorpusRunner.d.ts.map +1 -0
  101. package/dist/vendor/codali/eval/PlanningCorpusRunner.js +89 -0
  102. package/dist/vendor/codali/eval/TerminalCorpusRunner.d.ts +36 -0
  103. package/dist/vendor/codali/eval/TerminalCorpusRunner.d.ts.map +1 -0
  104. package/dist/vendor/codali/eval/TerminalCorpusRunner.js +222 -0
  105. package/dist/vendor/codali/eval/TerminalTaskCorpus.d.ts +26 -0
  106. package/dist/vendor/codali/eval/TerminalTaskCorpus.d.ts.map +1 -0
  107. package/dist/vendor/codali/eval/TerminalTaskCorpus.js +105 -0
  108. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts +11 -0
  109. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts.map +1 -0
  110. package/dist/vendor/codali/eval/TerminalWorkflowCorpus.js +116 -0
  111. package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.d.ts +4 -0
  112. package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.d.ts.map +1 -0
  113. package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.js +132 -0
  114. package/dist/vendor/codali/gateway/CodaliGateway.d.ts +2 -0
  115. package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
  116. package/dist/vendor/codali/gateway/CodaliGateway.js +16 -4
  117. package/dist/vendor/codali/gateway/ConfiguredGateway.d.ts +23 -0
  118. package/dist/vendor/codali/gateway/ConfiguredGateway.d.ts.map +1 -0
  119. package/dist/vendor/codali/gateway/ConfiguredGateway.js +44 -0
  120. package/dist/vendor/codali/gateway/GatewayTracer.d.ts +1 -1
  121. package/dist/vendor/codali/gateway/GatewayTracer.d.ts.map +1 -1
  122. package/dist/vendor/codali/gateway/GatewayTracer.js +2 -27
  123. package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.d.ts.map +1 -1
  124. package/dist/vendor/codali/gateway/LocalGatewayTaskRunner.js +13 -1
  125. package/dist/vendor/codali/index.d.ts +23 -0
  126. package/dist/vendor/codali/index.d.ts.map +1 -1
  127. package/dist/vendor/codali/index.js +12 -0
  128. package/dist/vendor/codali/permissions/PermissionGate.d.ts +44 -0
  129. package/dist/vendor/codali/permissions/PermissionGate.d.ts.map +1 -0
  130. package/dist/vendor/codali/permissions/PermissionGate.js +97 -0
  131. package/dist/vendor/codali/providers/ConfiguredEmbeddings.d.ts +43 -0
  132. package/dist/vendor/codali/providers/ConfiguredEmbeddings.d.ts.map +1 -0
  133. package/dist/vendor/codali/providers/ConfiguredEmbeddings.js +87 -0
  134. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts +69 -0
  135. package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts.map +1 -0
  136. package/dist/vendor/codali/providers/ConfiguredModelScope.js +113 -0
  137. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts +79 -0
  138. package/dist/vendor/codali/providers/ConfiguredProvider.d.ts.map +1 -0
  139. package/dist/vendor/codali/providers/ConfiguredProvider.js +238 -0
  140. package/dist/vendor/codali/providers/ModelServiceError.d.ts +10 -0
  141. package/dist/vendor/codali/providers/ModelServiceError.d.ts.map +1 -0
  142. package/dist/vendor/codali/providers/ModelServiceError.js +45 -0
  143. package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts.map +1 -1
  144. package/dist/vendor/codali/providers/MswarmWorkerProvider.js +4 -0
  145. package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
  146. package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +134 -52
  147. package/dist/vendor/codali/providers/ProviderTypes.d.ts +14 -0
  148. package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
  149. package/dist/vendor/codali/runtime/CodaliJobRuntime.d.ts +6 -0
  150. package/dist/vendor/codali/runtime/CodaliJobRuntime.d.ts.map +1 -1
  151. package/dist/vendor/codali/runtime/CodaliJobRuntime.js +36 -1
  152. package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +30 -0
  153. package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -1
  154. package/dist/vendor/codali/runtime/CodaliRuntime.js +71 -1
  155. package/dist/vendor/codali/runtime/CompletionReport.d.ts +5 -0
  156. package/dist/vendor/codali/runtime/CompletionReport.d.ts.map +1 -0
  157. package/dist/vendor/codali/runtime/CompletionReport.js +12 -0
  158. package/dist/vendor/codali/runtime/ProgressTracker.d.ts +17 -0
  159. package/dist/vendor/codali/runtime/ProgressTracker.d.ts.map +1 -0
  160. package/dist/vendor/codali/runtime/ProgressTracker.js +27 -0
  161. package/dist/vendor/codali/runtime/RequestedTestEvidence.d.ts +12 -0
  162. package/dist/vendor/codali/runtime/RequestedTestEvidence.d.ts.map +1 -0
  163. package/dist/vendor/codali/runtime/RequestedTestEvidence.js +16 -0
  164. package/dist/vendor/codali/runtime/RequiredRetrieval.d.ts +13 -0
  165. package/dist/vendor/codali/runtime/RequiredRetrieval.d.ts.map +1 -0
  166. package/dist/vendor/codali/runtime/RequiredRetrieval.js +25 -0
  167. package/dist/vendor/codali/runtime/ReviewSource.d.ts +6 -0
  168. package/dist/vendor/codali/runtime/ReviewSource.d.ts.map +1 -0
  169. package/dist/vendor/codali/runtime/ReviewSource.js +38 -0
  170. package/dist/vendor/codali/runtime/Runner.d.ts +13 -0
  171. package/dist/vendor/codali/runtime/Runner.d.ts.map +1 -1
  172. package/dist/vendor/codali/runtime/Runner.js +220 -158
  173. package/dist/vendor/codali/runtime/SecretRedaction.d.ts +11 -0
  174. package/dist/vendor/codali/runtime/SecretRedaction.d.ts.map +1 -0
  175. package/dist/vendor/codali/runtime/SecretRedaction.js +85 -0
  176. package/dist/vendor/codali/runtime/SessionController.d.ts +148 -0
  177. package/dist/vendor/codali/runtime/SessionController.d.ts.map +1 -0
  178. package/dist/vendor/codali/runtime/SessionController.js +1356 -0
  179. package/dist/vendor/codali/runtime/StructuralEvidence.d.ts +22 -0
  180. package/dist/vendor/codali/runtime/StructuralEvidence.d.ts.map +1 -0
  181. package/dist/vendor/codali/runtime/StructuralEvidence.js +87 -0
  182. package/dist/vendor/codali/runtime/TaskReviewContext.d.ts +34 -0
  183. package/dist/vendor/codali/runtime/TaskReviewContext.d.ts.map +1 -0
  184. package/dist/vendor/codali/runtime/TaskReviewContext.js +34 -0
  185. package/dist/vendor/codali/runtime/ValidationOutput.d.ts +5 -0
  186. package/dist/vendor/codali/runtime/ValidationOutput.d.ts.map +1 -0
  187. package/dist/vendor/codali/runtime/ValidationOutput.js +32 -0
  188. package/dist/vendor/codali/runtime/VerificationCommand.d.ts +3 -0
  189. package/dist/vendor/codali/runtime/VerificationCommand.d.ts.map +1 -0
  190. package/dist/vendor/codali/runtime/VerificationCommand.js +48 -0
  191. package/dist/vendor/codali/runtime/WorkStatusPage.d.ts +15 -0
  192. package/dist/vendor/codali/runtime/WorkStatusPage.d.ts.map +1 -0
  193. package/dist/vendor/codali/runtime/WorkStatusPage.js +58 -0
  194. package/dist/vendor/codali/runtime/WorkspaceLedger.d.ts +13 -0
  195. package/dist/vendor/codali/runtime/WorkspaceLedger.d.ts.map +1 -0
  196. package/dist/vendor/codali/runtime/WorkspaceLedger.js +145 -0
  197. package/dist/vendor/codali/runtime/WorkspaceLock.d.ts.map +1 -1
  198. package/dist/vendor/codali/runtime/WorkspaceLock.js +20 -17
  199. package/dist/vendor/codali/runtime/WorkspaceSnapshot.d.ts +13 -0
  200. package/dist/vendor/codali/runtime/WorkspaceSnapshot.d.ts.map +1 -0
  201. package/dist/vendor/codali/runtime/WorkspaceSnapshot.js +88 -0
  202. package/dist/vendor/codali/session/AtomicState.d.ts +13 -0
  203. package/dist/vendor/codali/session/AtomicState.d.ts.map +1 -0
  204. package/dist/vendor/codali/session/AtomicState.js +109 -0
  205. package/dist/vendor/codali/session/SessionStoragePath.d.ts +15 -0
  206. package/dist/vendor/codali/session/SessionStoragePath.d.ts.map +1 -0
  207. package/dist/vendor/codali/session/SessionStoragePath.js +89 -0
  208. package/dist/vendor/codali/session/SessionStore.d.ts +9 -0
  209. package/dist/vendor/codali/session/SessionStore.d.ts.map +1 -1
  210. package/dist/vendor/codali/session/SessionStore.js +181 -47
  211. package/dist/vendor/codali/session/WorkState.d.ts +23 -0
  212. package/dist/vendor/codali/session/WorkState.d.ts.map +1 -0
  213. package/dist/vendor/codali/session/WorkState.js +367 -0
  214. package/dist/vendor/codali/session/WorkTypes.d.ts +94 -0
  215. package/dist/vendor/codali/session/WorkTypes.d.ts.map +1 -0
  216. package/dist/vendor/codali/session/WorkTypes.js +1 -0
  217. package/dist/vendor/codali/tools/ToolRegistry.d.ts +2 -0
  218. package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -1
  219. package/dist/vendor/codali/tools/ToolRegistry.js +12 -2
  220. package/dist/vendor/codali/tools/ToolTypes.d.ts +16 -0
  221. package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -1
  222. package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +2 -0
  223. package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -1
  224. package/dist/vendor/codali/tools/docdex/DocdexTools.js +17 -2
  225. package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -1
  226. package/dist/vendor/codali/tools/filesystem/FileTools.js +30 -10
  227. package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts +16 -0
  228. package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts.map +1 -0
  229. package/dist/vendor/codali/tools/shell/AsyncProcess.js +94 -0
  230. package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.d.ts +13 -0
  231. package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.d.ts.map +1 -0
  232. package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.js +130 -0
  233. package/dist/vendor/codali/tools/shell/HostToolchain.d.ts +19 -0
  234. package/dist/vendor/codali/tools/shell/HostToolchain.d.ts.map +1 -0
  235. package/dist/vendor/codali/tools/shell/HostToolchain.js +264 -0
  236. package/dist/vendor/codali/tools/shell/IsolatedExecutor.d.ts +15 -0
  237. package/dist/vendor/codali/tools/shell/IsolatedExecutor.d.ts.map +1 -0
  238. package/dist/vendor/codali/tools/shell/IsolatedExecutor.js +134 -0
  239. package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -1
  240. package/dist/vendor/codali/tools/shell/ShellTool.js +9 -29
  241. package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.d.ts +3 -0
  242. package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.d.ts.map +1 -0
  243. package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.js +30 -0
  244. package/package.json +31 -29
  245. package/scripts/backend-http-drain.mjs +152 -0
  246. package/scripts/media_gpu_lock.py +238 -0
  247. package/scripts/stable-audio-3-openai-server.py +45 -14
  248. package/scripts/stable-diffusion-cpp-openai-server.py +34 -4
  249. package/scripts/stable-diffusion-cpp-webm-duration.patch +22 -0
  250. package/scripts/wan22-openai-server.py +97 -18
@@ -0,0 +1,238 @@
1
+ """Serialize GPU1 media inference across the independent HTTP services."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import fcntl
6
+ import json
7
+ import logging
8
+ import math
9
+ import os
10
+ import stat
11
+ import threading
12
+ import time
13
+ import uuid
14
+ from contextlib import contextmanager
15
+ from pathlib import Path
16
+ from typing import Callable, Iterator
17
+
18
+
19
+ class MediaGpuError(RuntimeError):
20
+ status = 503
21
+ code = "media_gpu_unavailable"
22
+
23
+
24
+ class MediaGpuBusy(MediaGpuError):
25
+ status = 429
26
+ code = "media_gpu_busy"
27
+
28
+
29
+ class MediaGpuCancelled(MediaGpuError):
30
+ code = "service_shutting_down"
31
+
32
+
33
+ class MediaGpuRecoveryRequired(MediaGpuError):
34
+ code = "media_gpu_recovery_required"
35
+
36
+
37
+ def _open_scheduler() -> int:
38
+ runtime_dir = Path(os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}"))
39
+ path = os.environ.get("MCODA_MEDIA_GPU_LOCK_PATH", str(runtime_dir / "mcoda-media-gpu1.lock"))
40
+ fd = -1
41
+ try:
42
+ fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_CLOEXEC | os.O_NOFOLLOW, 0o600)
43
+ metadata = os.fstat(fd)
44
+ if (not stat.S_ISREG(metadata.st_mode) or metadata.st_uid != os.geteuid()
45
+ or metadata.st_nlink != 1 or stat.S_IMODE(metadata.st_mode) & 0o077):
46
+ os.close(fd)
47
+ raise MediaGpuError("shared media GPU lock must be a private, owner-owned regular file")
48
+ return fd
49
+ except OSError as error:
50
+ if fd >= 0:
51
+ os.close(fd)
52
+ raise MediaGpuError("cannot access the shared media GPU scheduler") from error
53
+
54
+
55
+ def _read_lease(fd: int) -> dict | None:
56
+ raw = os.pread(fd, 16_385, 0)
57
+ if not raw:
58
+ return None
59
+ try:
60
+ if len(raw) > 16_384:
61
+ raise ValueError("oversized lease")
62
+ value = json.loads(raw)
63
+ if (not isinstance(value, dict) or value.get("version") != 1
64
+ or not isinstance(value.get("id"), str)
65
+ or value.get("status") not in ("active", "uncertain")):
66
+ raise ValueError("invalid lease")
67
+ return value
68
+ except (ValueError, UnicodeDecodeError) as error:
69
+ raise MediaGpuRecoveryRequired("media GPU lease is unreadable; reconcile native inference before recovery") from error
70
+
71
+
72
+ def _write_lease(fd: int, value: dict | None) -> None:
73
+ data = b"" if value is None else json.dumps(value, separators=(",", ":")).encode("utf-8")
74
+ os.lseek(fd, 0, os.SEEK_SET)
75
+ offset = 0
76
+ while offset < len(data):
77
+ written = os.write(fd, data[offset:])
78
+ if written <= 0:
79
+ raise MediaGpuError("cannot persist the shared media GPU lease")
80
+ offset += written
81
+ os.ftruncate(fd, len(data))
82
+ os.fsync(fd)
83
+
84
+
85
+ class MediaGpuLease:
86
+ """Host-owned receipt; never store prompts, credentials or client URLs."""
87
+
88
+ def __init__(self, fd: int, label: str):
89
+ self._fd = fd
90
+ self._closed = False
91
+ self._uncertain = False
92
+ self.record = {"version": 1, "id": str(uuid.uuid4()), "status": "active",
93
+ "label": label[:128], "pid": os.getpid(), "started_at": time.time()}
94
+ _write_lease(fd, self.record)
95
+
96
+ def mark_uncertain(self, reason: str) -> None:
97
+ if self._closed:
98
+ raise MediaGpuError("media GPU lease is already closed")
99
+ self._uncertain = True
100
+ self.record.update(status="uncertain", reason=reason[:128])
101
+ _write_lease(self._fd, self.record)
102
+
103
+ def record_native_job(self, job_id: str) -> None:
104
+ if self._closed or not isinstance(job_id, str) or not job_id or len(job_id) > 256:
105
+ raise MediaGpuError("invalid native media job receipt")
106
+ self.record["native_job_id"] = job_id
107
+ _write_lease(self._fd, self.record)
108
+
109
+ def record_native_process(self, pid: int) -> None:
110
+ """Pin a Linux native child's identity without recording its arguments."""
111
+ if self._closed or not isinstance(pid, int) or pid <= 0:
112
+ raise MediaGpuError("invalid native media process receipt")
113
+ try:
114
+ fields = Path(f"/proc/{pid}/stat").read_text().rsplit(")", 1)[1].split()
115
+ boot_id = Path("/proc/sys/kernel/random/boot_id").read_text().strip()
116
+ if int(fields[2]) != pid or not boot_id:
117
+ raise ValueError("native process must own its process group")
118
+ identity = {"pid": pid, "process_group": pid,
119
+ "start_ticks": int(fields[19]), "boot_id": boot_id}
120
+ except (OSError, ValueError, IndexError) as error:
121
+ raise MediaGpuError("cannot establish native media process identity") from error
122
+ self.record["native_process"] = identity
123
+ _write_lease(self._fd, self.record)
124
+
125
+ def confirm_finished(self) -> None:
126
+ if self._closed:
127
+ raise MediaGpuError("media GPU lease is already closed")
128
+ self._uncertain = False
129
+ self.record.update(status="active")
130
+ self.record.pop("reason", None)
131
+ _write_lease(self._fd, self.record)
132
+
133
+
134
+ def reconcile_media_gpu_slot(expected_id: str, terminal_probe: Callable[[dict], bool]) -> bool:
135
+ """Clear one abandoned receipt only after a trusted backend terminal probe.
136
+
137
+ Process death, elapsed time, HTTP disconnection and a cancel acknowledgement
138
+ are not terminal proof. The caller supplies a backend-specific probe or
139
+ evidence that the native engine was stopped and cannot retain that job.
140
+ """
141
+ fd = _open_scheduler()
142
+ try:
143
+ try:
144
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
145
+ except BlockingIOError as error:
146
+ raise MediaGpuBusy("media GPU lease is still owned by a running worker") from error
147
+ current = _read_lease(fd)
148
+ if current is None or current["id"] != expected_id:
149
+ return False
150
+ if terminal_probe(dict(current)) is not True:
151
+ return False
152
+ _write_lease(fd, None)
153
+ return True
154
+ finally:
155
+ os.close(fd)
156
+
157
+
158
+ def media_gpu_health() -> str:
159
+ """Report admission state without exposing a receipt or clearing uncertainty."""
160
+ fd = -1
161
+ try:
162
+ fd = _open_scheduler()
163
+ try:
164
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
165
+ except BlockingIOError:
166
+ return "busy"
167
+ return "ready" if _read_lease(fd) is None else "recovery_required"
168
+ except MediaGpuRecoveryRequired:
169
+ return "recovery_required"
170
+ except MediaGpuError:
171
+ return "unavailable"
172
+ finally:
173
+ if fd >= 0:
174
+ os.close(fd)
175
+
176
+
177
+ @contextmanager
178
+ def media_gpu_slot(
179
+ label: str, *, cancelled: threading.Event | None = None,
180
+ shutdown: threading.Event | None = None,
181
+ ) -> Iterator[MediaGpuLease]:
182
+ """Hold a kernel lock until native inference finishes, even if a client leaves.
183
+
184
+ Never unlink the lock file: waiters must keep referring to the same inode.
185
+ Each call opens its own file description so threads also exclude each other.
186
+ """
187
+ try:
188
+ timeout = float(os.environ.get("MCODA_MEDIA_GPU_WAIT_SECONDS", "600"))
189
+ except ValueError as error:
190
+ raise MediaGpuError("media GPU wait limit must be a number") from error
191
+ if not math.isfinite(timeout) or timeout < 0:
192
+ raise MediaGpuError("media GPU wait limit must be finite and nonnegative")
193
+ fd = _open_scheduler()
194
+ acquired = False
195
+ lease: MediaGpuLease | None = None
196
+ started = time.monotonic()
197
+ try:
198
+ while True:
199
+ if any(event is not None and event.is_set() for event in (cancelled, shutdown)):
200
+ raise MediaGpuCancelled("media generation stopped while waiting for the GPU")
201
+ try:
202
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
203
+ acquired = True
204
+ break
205
+ except BlockingIOError:
206
+ remaining = timeout - (time.monotonic() - started)
207
+ if remaining <= 0:
208
+ raise MediaGpuBusy("the media GPU is busy; retry the request")
209
+ if cancelled is not None:
210
+ cancelled.wait(min(0.1, remaining))
211
+ else:
212
+ time.sleep(min(0.1, remaining))
213
+ if any(event is not None and event.is_set() for event in (cancelled, shutdown)):
214
+ raise MediaGpuCancelled("media generation stopped before GPU inference")
215
+ if _read_lease(fd) is not None:
216
+ raise MediaGpuRecoveryRequired("an earlier media GPU lease has no confirmed outcome; reconcile native inference before retrying")
217
+ lease = MediaGpuLease(fd, label)
218
+ logging.info(
219
+ "media_gpu_acquired label=%s pid=%d wait_ms=%d monotonic=%.6f",
220
+ label, os.getpid(), int((time.monotonic() - started) * 1000), time.monotonic(),
221
+ )
222
+ yield lease
223
+ finally:
224
+ try:
225
+ if acquired:
226
+ if lease is not None:
227
+ try:
228
+ if not lease._uncertain:
229
+ _write_lease(fd, None)
230
+ finally:
231
+ lease._closed = True
232
+ logging.info(
233
+ "media_gpu_released label=%s pid=%d monotonic=%.6f",
234
+ label, os.getpid(), time.monotonic(),
235
+ )
236
+ fcntl.flock(fd, fcntl.LOCK_UN)
237
+ finally:
238
+ os.close(fd)
@@ -25,6 +25,8 @@ from pathlib import Path
25
25
  from socketserver import TCPServer
26
26
  from typing import Any, Protocol
27
27
 
28
+ from media_gpu_lock import MediaGpuError, MediaGpuRecoveryRequired, media_gpu_health, media_gpu_slot
29
+
28
30
 
29
31
  DEFAULT_HOST = "127.0.0.1"
30
32
  DEFAULT_PORT = 11446
@@ -165,7 +167,7 @@ class SubprocessBackend:
165
167
  seed: int | None,
166
168
  ) -> tuple[bytes, dict[str, Any]]:
167
169
  effective_seed = seed if seed is not None else secrets.randbelow(2**31)
168
- with tempfile.TemporaryDirectory(prefix="stable-audio-3-") as temp_dir:
170
+ with media_gpu_slot("audio") as lease, tempfile.TemporaryDirectory(prefix="stable-audio-3-") as temp_dir:
169
171
  output_path = Path(temp_dir) / "generation.wav"
170
172
  command = [
171
173
  str(self.python),
@@ -189,26 +191,48 @@ class SubprocessBackend:
189
191
  command.extend(("--precision", self.precision))
190
192
  if self.models_dir:
191
193
  command.extend(("--models-dir", str(self.models_dir)))
192
- process = subprocess.Popen(
194
+ lease.mark_uncertain("audio_launch_pending")
195
+ try:
196
+ process = subprocess.Popen(
193
197
  command,
194
198
  cwd=self.runtime_root,
195
199
  stdin=subprocess.DEVNULL,
196
200
  stdout=subprocess.DEVNULL,
197
201
  stderr=subprocess.DEVNULL,
198
202
  start_new_session=True,
199
- )
203
+ )
204
+ except OSError:
205
+ # Popen reports failed exec only after reaping its failed child.
206
+ lease.confirm_finished()
207
+ raise
200
208
  try:
201
- return_code = process.wait(timeout=self.timeout_seconds)
202
- except subprocess.TimeoutExpired as error:
209
+ lease.record_native_process(process.pid)
203
210
  try:
204
- os.killpg(process.pid, signal.SIGTERM)
205
- process.wait(timeout=5)
206
- except subprocess.TimeoutExpired:
207
- os.killpg(process.pid, signal.SIGKILL)
208
- process.wait()
211
+ return_code = process.wait(timeout=self.timeout_seconds)
212
+ except subprocess.TimeoutExpired as error:
213
+ try:
214
+ os.killpg(process.pid, signal.SIGTERM)
215
+ process.wait(timeout=5)
216
+ except subprocess.TimeoutExpired:
217
+ os.killpg(process.pid, signal.SIGKILL)
218
+ process.wait()
219
+ except ProcessLookupError:
220
+ process.wait()
221
+ raise RuntimeError("Stable Audio CLI timed out") from error
222
+ finally:
223
+ # A reaped wrapper does not prove that its process group stopped.
224
+ # Any unexpected wait/identity failure keeps the durable fence.
225
+ group_alive = True
226
+ try:
227
+ os.killpg(process.pid, 0)
209
228
  except ProcessLookupError:
229
+ group_alive = False
230
+ except PermissionError:
210
231
  pass
211
- raise RuntimeError("Stable Audio CLI timed out") from error
232
+ if process.poll() is not None and not group_alive:
233
+ lease.confirm_finished()
234
+ if lease.record["status"] == "uncertain":
235
+ raise MediaGpuRecoveryRequired("audio process group has no confirmed terminal outcome")
212
236
  if return_code != 0:
213
237
  raise RuntimeError(
214
238
  f"Stable Audio CLI exited with status {return_code}"
@@ -244,7 +268,8 @@ class ServerState:
244
268
 
245
269
 
246
270
  class AudioHttpServer(ThreadingHTTPServer):
247
- daemon_threads = True
271
+ # Let active inference finish before process exit releases the shared GPU.
272
+ daemon_threads = False
248
273
  request_queue_size = 8
249
274
 
250
275
  def server_bind(self) -> None:
@@ -438,8 +463,9 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
438
463
 
439
464
  def do_GET(self) -> None:
440
465
  if self.path == "/healthz":
441
- if state.fatal_error.is_set():
442
- self._send_json(503, {"status": "unhealthy", "model": state.model_id})
466
+ gpu = media_gpu_health()
467
+ if state.fatal_error.is_set() or gpu not in ("ready", "busy"):
468
+ self._send_json(503, {"status": "unhealthy", "model": state.model_id, "media_gpu": gpu})
443
469
  return
444
470
  self._send_json(
445
471
  200,
@@ -447,6 +473,7 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
447
473
  "status": "ok",
448
474
  "model": state.model_id,
449
475
  "backend": state.backend_name,
476
+ "media_gpu": gpu,
450
477
  "dit": state.dit,
451
478
  "decoder": state.decoder,
452
479
  },
@@ -568,6 +595,10 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
568
595
  )
569
596
  except ApiError as error:
570
597
  self._send_error(error)
598
+ except MediaGpuError as error:
599
+ self._send_error(
600
+ ApiError(error.status, str(error), error_type="server_error", code=error.code)
601
+ )
571
602
  except Exception as error:
572
603
  logging.error("generation failed error_type=%s", type(error).__name__)
573
604
  self._send_error(
@@ -6,6 +6,7 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import base64
8
8
  import binascii
9
+ import http.client
9
10
  import ipaddress
10
11
  import json
11
12
  import logging
@@ -28,6 +29,8 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
28
29
  from socketserver import TCPServer
29
30
  from typing import Any
30
31
 
32
+ from media_gpu_lock import MediaGpuError, media_gpu_health, media_gpu_slot
33
+
31
34
 
32
35
  DEFAULT_HOST = "127.0.0.1"
33
36
  DEFAULT_PORT = 11449
@@ -116,7 +119,11 @@ class UpstreamError(Exception):
116
119
  pass
117
120
 
118
121
 
119
- class UpstreamTimeout(UpstreamError):
122
+ class UpstreamTransportError(UpstreamError):
123
+ pass
124
+
125
+
126
+ class UpstreamTimeout(UpstreamTransportError):
120
127
  pass
121
128
 
122
129
 
@@ -459,10 +466,10 @@ def _bounded_json_request(
459
466
  raw = error.read(max_bytes + 1)
460
467
  status = error.code
461
468
  content_type = error.headers.get_content_type()
462
- except (OSError, urllib.error.URLError) as error:
469
+ except (OSError, urllib.error.URLError, http.client.HTTPException) as error:
463
470
  if _is_timeout_error(error):
464
471
  raise UpstreamTimeout("stable-diffusion.cpp request timed out") from error
465
- raise UpstreamError("stable-diffusion.cpp is unreachable") from error
472
+ raise UpstreamTransportError("stable-diffusion.cpp is unreachable") from error
466
473
  if len(raw) > max_bytes:
467
474
  raise UpstreamError("stable-diffusion.cpp response exceeds the configured limit")
468
475
  if content_type != "application/json":
@@ -507,6 +514,19 @@ def _native_request(request: dict[str, Any]) -> dict[str, Any]:
507
514
 
508
515
 
509
516
  def _generate_image(state: ServerState, request: dict[str, Any]) -> dict[str, Any]:
517
+ with media_gpu_slot("image", cancelled=state.shutdown_requested) as lease:
518
+ try:
519
+ return _generate_image_locked(state, request)
520
+ except (UpstreamTransportError, OSError, urllib.error.URLError, http.client.HTTPException) as error:
521
+ # A lost HTTP connection does not prove the synchronous native
522
+ # renderer stopped. Keep a durable fence after this worker exits.
523
+ lease.mark_uncertain("image_transport_lost")
524
+ if isinstance(error, UpstreamError):
525
+ raise
526
+ raise UpstreamTransportError("native image outcome is uncertain") from error
527
+
528
+
529
+ def _generate_image_locked(state: ServerState, request: dict[str, Any]) -> dict[str, Any]:
510
530
  max_json_bytes = math.ceil(state.max_output_bytes / 3) * 4 + 131_072
511
531
  status, payload = _bounded_json_request(
512
532
  f"{state.upstream_base_url}/v1/images/generations",
@@ -776,6 +796,10 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
776
796
  ApiError(404, "not found", error_type="not_found_error")
777
797
  )
778
798
  return
799
+ gpu = media_gpu_health() if self.path == "/healthz" else None
800
+ if self.path == "/healthz" and gpu not in ("ready", "busy"):
801
+ self._send_json(503, {"status": "unhealthy", "model": state.model_id, "media_gpu": gpu})
802
+ return
779
803
  try:
780
804
  _check_upstream_model(state)
781
805
  except UpstreamError:
@@ -801,6 +825,7 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
801
825
  "status": "ok",
802
826
  "model": state.model_id,
803
827
  "upstream_model": state.upstream_model_id,
828
+ "media_gpu": gpu,
804
829
  "upstream": "stable-diffusion.cpp",
805
830
  },
806
831
  )
@@ -921,7 +946,8 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
921
946
  threading.Thread(
922
947
  target=run_generation,
923
948
  name="sd-cpp-image-generation",
924
- daemon=True,
949
+ # Preserve the GPU lease until non-cancellable inference ends.
950
+ daemon=False,
925
951
  ).start()
926
952
  if not completed.wait(state.inference_timeout_seconds):
927
953
  logging.error("generation exceeded client deadline; native request still active")
@@ -947,6 +973,10 @@ def _handler_for(state: ServerState) -> type[BaseHTTPRequestHandler]:
947
973
  )
948
974
  elif isinstance(error, ApiError):
949
975
  self._send_error(error)
976
+ elif isinstance(error, MediaGpuError):
977
+ self._send_error(
978
+ ApiError(error.status, str(error), error_type="server_error", code=error.code)
979
+ )
950
980
  elif isinstance(error, UpstreamTimeout):
951
981
  logging.error("generation failed reason=upstream timeout")
952
982
  self._send_error(
@@ -0,0 +1,22 @@
1
+ --- a/examples/common/media_io.cpp
2
+ +++ b/examples/common/media_io.cpp
3
+ @@ -1212,6 +1212,9 @@
4
+
5
+ const uint64_t frame_duration_ns = std::max<uint64_t>(
6
+ 1, static_cast<uint64_t>(std::llround(1000000000.0 / static_cast<double>(fps))));
7
+ + if (video_track != nullptr) {
8
+ + video_track->set_default_duration(frame_duration_ns);
9
+ + }
10
+ uint64_t timestamp_ns = 0;
11
+
12
+ for (int i = 0; i < num_images; ++i) {
13
+ @@ -1256,6 +1259,9 @@
14
+ timestamp_ns += frame_duration_ns;
15
+ }
16
+
17
+ + // Include the final frame's display interval, not only its timestamp.
18
+ + segment.set_duration(static_cast<double>(timestamp_ns) /
19
+ + segment.GetSegmentInfo()->timecode_scale());
20
+ if (!segment.Finalize()) {
21
+ fprintf(stderr, "Error: Failed to finalize WebM output.\n");
22
+ return -1;