@mcoda/mswarm 0.1.134 → 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.
- package/README.md +40 -0
- package/dist/codali-executor.d.ts +63 -0
- package/dist/codali-executor.d.ts.map +1 -1
- package/dist/codali-executor.js +78 -8
- package/dist/codali-executor.js.map +1 -1
- package/dist/invocation-token.d.ts.map +1 -1
- package/dist/invocation-token.js +1 -0
- package/dist/invocation-token.js.map +1 -1
- package/dist/native-reasoning.d.ts +8 -0
- package/dist/native-reasoning.d.ts.map +1 -0
- package/dist/native-reasoning.js +18 -0
- package/dist/native-reasoning.js.map +1 -0
- package/dist/runtime.d.ts +23 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +181 -15
- package/dist/runtime.js.map +1 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +58 -8
- package/dist/server.js.map +1 -1
- package/dist/vendor/codali/agents/RoleResolution.d.ts.map +1 -1
- package/dist/vendor/codali/agents/RoleResolution.js +4 -4
- package/dist/vendor/codali/api/CodaliApi.d.ts +16 -0
- package/dist/vendor/codali/api/CodaliApi.d.ts.map +1 -1
- package/dist/vendor/codali/api/CodaliApi.js +39 -10
- package/dist/vendor/codali/cli/AskCommand.d.ts +3 -0
- package/dist/vendor/codali/cli/AskCommand.d.ts.map +1 -1
- package/dist/vendor/codali/cli/AskCommand.js +76 -13
- package/dist/vendor/codali/cli/RunCommand.d.ts +4 -0
- package/dist/vendor/codali/cli/RunCommand.d.ts.map +1 -1
- package/dist/vendor/codali/cli/RunCommand.js +110 -24
- package/dist/vendor/codali/cli/TerminalCommand.d.ts +35 -0
- package/dist/vendor/codali/cli/TerminalCommand.d.ts.map +1 -0
- package/dist/vendor/codali/cli/TerminalCommand.js +616 -0
- package/dist/vendor/codali/cli/TerminalCredentials.d.ts +3 -0
- package/dist/vendor/codali/cli/TerminalCredentials.d.ts.map +1 -0
- package/dist/vendor/codali/cli/TerminalCredentials.js +89 -0
- package/dist/vendor/codali/cli/TerminalInput.d.ts +24 -0
- package/dist/vendor/codali/cli/TerminalInput.d.ts.map +1 -0
- package/dist/vendor/codali/cli/TerminalInput.js +98 -0
- package/dist/vendor/codali/cli/TerminalText.d.ts +10 -0
- package/dist/vendor/codali/cli/TerminalText.d.ts.map +1 -0
- package/dist/vendor/codali/cli/TerminalText.js +53 -0
- package/dist/vendor/codali/cli.d.ts.map +1 -1
- package/dist/vendor/codali/cli.js +33 -4
- package/dist/vendor/codali/cognitive/ConfiguredPipeline.d.ts +34 -0
- package/dist/vendor/codali/cognitive/ConfiguredPipeline.d.ts.map +1 -0
- package/dist/vendor/codali/cognitive/ConfiguredPipeline.js +50 -0
- package/dist/vendor/codali/cognitive/ValidationRunner.d.ts +4 -0
- package/dist/vendor/codali/cognitive/ValidationRunner.d.ts.map +1 -1
- package/dist/vendor/codali/cognitive/ValidationRunner.js +18 -41
- package/dist/vendor/codali/config/NodeOrchestration.d.ts +21 -0
- package/dist/vendor/codali/config/NodeOrchestration.d.ts.map +1 -0
- package/dist/vendor/codali/config/NodeOrchestration.js +54 -0
- package/dist/vendor/codali/config/OrchestrationConfig.d.ts +100 -0
- package/dist/vendor/codali/config/OrchestrationConfig.d.ts.map +1 -0
- package/dist/vendor/codali/config/OrchestrationConfig.js +245 -0
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts +3 -1
- package/dist/vendor/codali/connectors/mcp/McpClient.d.ts.map +1 -1
- package/dist/vendor/codali/connectors/mcp/McpClient.js +3 -2
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts +2 -2
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.d.ts.map +1 -1
- package/dist/vendor/codali/connectors/mcp/McpServerRegistry.js +39 -16
- package/dist/vendor/codali/connectors/mcp/McpToolAdapter.js +2 -2
- package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.d.ts +6 -0
- package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/ConfiguredMediaTools.js +41 -0
- package/dist/vendor/codali/connectors/media/ImageGenerationTool.js +3 -3
- package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.d.ts +5 -0
- package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/MediaArtifactReceipt.js +65 -0
- package/dist/vendor/codali/connectors/media/MediaJobStore.d.ts +68 -0
- package/dist/vendor/codali/connectors/media/MediaJobStore.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/MediaJobStore.js +199 -0
- package/dist/vendor/codali/connectors/media/MediaJobWorker.d.ts +2 -0
- package/dist/vendor/codali/connectors/media/MediaJobWorker.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/MediaJobWorker.js +65 -0
- package/dist/vendor/codali/connectors/media/RemoteMediaJob.d.ts +6 -0
- package/dist/vendor/codali/connectors/media/RemoteMediaJob.d.ts.map +1 -0
- package/dist/vendor/codali/connectors/media/RemoteMediaJob.js +80 -0
- package/dist/vendor/codali/docdex/DocdexClient.d.ts +18 -1
- package/dist/vendor/codali/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/vendor/codali/docdex/DocdexClient.js +135 -19
- package/dist/vendor/codali/docdex/ManagedDocdex.d.ts +29 -0
- package/dist/vendor/codali/docdex/ManagedDocdex.d.ts.map +1 -0
- package/dist/vendor/codali/docdex/ManagedDocdex.js +215 -0
- package/dist/vendor/codali/docdex/ManagedRuntime.d.ts +28 -0
- package/dist/vendor/codali/docdex/ManagedRuntime.d.ts.map +1 -0
- package/dist/vendor/codali/docdex/ManagedRuntime.js +212 -0
- package/dist/vendor/codali/eval/EvaluationProcess.d.ts +13 -0
- package/dist/vendor/codali/eval/EvaluationProcess.d.ts.map +1 -0
- package/dist/vendor/codali/eval/EvaluationProcess.js +76 -0
- package/dist/vendor/codali/eval/LongWorkflowJourney.d.ts +34 -0
- package/dist/vendor/codali/eval/LongWorkflowJourney.d.ts.map +1 -0
- package/dist/vendor/codali/eval/LongWorkflowJourney.js +243 -0
- package/dist/vendor/codali/eval/PlanningCorpus.d.ts +45 -0
- package/dist/vendor/codali/eval/PlanningCorpus.d.ts.map +1 -0
- package/dist/vendor/codali/eval/PlanningCorpus.js +189 -0
- package/dist/vendor/codali/eval/PlanningCorpusRunner.d.ts +13 -0
- package/dist/vendor/codali/eval/PlanningCorpusRunner.d.ts.map +1 -0
- package/dist/vendor/codali/eval/PlanningCorpusRunner.js +89 -0
- package/dist/vendor/codali/eval/TerminalCorpusRunner.d.ts +36 -0
- package/dist/vendor/codali/eval/TerminalCorpusRunner.d.ts.map +1 -0
- package/dist/vendor/codali/eval/TerminalCorpusRunner.js +222 -0
- package/dist/vendor/codali/eval/TerminalTaskCorpus.d.ts +26 -0
- package/dist/vendor/codali/eval/TerminalTaskCorpus.d.ts.map +1 -0
- package/dist/vendor/codali/eval/TerminalTaskCorpus.js +105 -0
- package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts +11 -0
- package/dist/vendor/codali/eval/TerminalWorkflowCorpus.d.ts.map +1 -0
- package/dist/vendor/codali/eval/TerminalWorkflowCorpus.js +116 -0
- package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.d.ts +4 -0
- package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.d.ts.map +1 -0
- package/dist/vendor/codali/eval/fixtures/TerminalRecoveryJourney.js +132 -0
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts +2 -0
- package/dist/vendor/codali/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/CodaliGateway.js +11 -1
- package/dist/vendor/codali/gateway/ConfiguredGateway.d.ts +23 -0
- package/dist/vendor/codali/gateway/ConfiguredGateway.d.ts.map +1 -0
- package/dist/vendor/codali/gateway/ConfiguredGateway.js +44 -0
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts +1 -1
- package/dist/vendor/codali/gateway/GatewayTracer.d.ts.map +1 -1
- package/dist/vendor/codali/gateway/GatewayTracer.js +2 -27
- package/dist/vendor/codali/index.d.ts +23 -0
- package/dist/vendor/codali/index.d.ts.map +1 -1
- package/dist/vendor/codali/index.js +12 -0
- package/dist/vendor/codali/permissions/PermissionGate.d.ts +44 -0
- package/dist/vendor/codali/permissions/PermissionGate.d.ts.map +1 -0
- package/dist/vendor/codali/permissions/PermissionGate.js +97 -0
- package/dist/vendor/codali/providers/ConfiguredEmbeddings.d.ts +43 -0
- package/dist/vendor/codali/providers/ConfiguredEmbeddings.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ConfiguredEmbeddings.js +87 -0
- package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts +69 -0
- package/dist/vendor/codali/providers/ConfiguredModelScope.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ConfiguredModelScope.js +113 -0
- package/dist/vendor/codali/providers/ConfiguredProvider.d.ts +79 -0
- package/dist/vendor/codali/providers/ConfiguredProvider.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ConfiguredProvider.js +238 -0
- package/dist/vendor/codali/providers/ModelServiceError.d.ts +10 -0
- package/dist/vendor/codali/providers/ModelServiceError.d.ts.map +1 -0
- package/dist/vendor/codali/providers/ModelServiceError.js +45 -0
- package/dist/vendor/codali/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/MswarmWorkerProvider.js +4 -0
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/vendor/codali/providers/OpenAiCompatibleProvider.js +91 -50
- package/dist/vendor/codali/providers/ProviderTypes.d.ts +12 -0
- package/dist/vendor/codali/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliJobRuntime.d.ts +6 -0
- package/dist/vendor/codali/runtime/CodaliJobRuntime.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliJobRuntime.js +36 -1
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts +28 -0
- package/dist/vendor/codali/runtime/CodaliRuntime.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/CodaliRuntime.js +69 -1
- package/dist/vendor/codali/runtime/CompletionReport.d.ts +5 -0
- package/dist/vendor/codali/runtime/CompletionReport.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/CompletionReport.js +12 -0
- package/dist/vendor/codali/runtime/ProgressTracker.d.ts +17 -0
- package/dist/vendor/codali/runtime/ProgressTracker.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/ProgressTracker.js +27 -0
- package/dist/vendor/codali/runtime/RequestedTestEvidence.d.ts +12 -0
- package/dist/vendor/codali/runtime/RequestedTestEvidence.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RequestedTestEvidence.js +16 -0
- package/dist/vendor/codali/runtime/RequiredRetrieval.d.ts +13 -0
- package/dist/vendor/codali/runtime/RequiredRetrieval.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/RequiredRetrieval.js +25 -0
- package/dist/vendor/codali/runtime/ReviewSource.d.ts +6 -0
- package/dist/vendor/codali/runtime/ReviewSource.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/ReviewSource.js +38 -0
- package/dist/vendor/codali/runtime/Runner.d.ts +7 -0
- package/dist/vendor/codali/runtime/Runner.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/Runner.js +215 -171
- package/dist/vendor/codali/runtime/SecretRedaction.d.ts +11 -0
- package/dist/vendor/codali/runtime/SecretRedaction.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/SecretRedaction.js +85 -0
- package/dist/vendor/codali/runtime/SessionController.d.ts +148 -0
- package/dist/vendor/codali/runtime/SessionController.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/SessionController.js +1356 -0
- package/dist/vendor/codali/runtime/StructuralEvidence.d.ts +22 -0
- package/dist/vendor/codali/runtime/StructuralEvidence.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/StructuralEvidence.js +87 -0
- package/dist/vendor/codali/runtime/TaskReviewContext.d.ts +34 -0
- package/dist/vendor/codali/runtime/TaskReviewContext.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/TaskReviewContext.js +34 -0
- package/dist/vendor/codali/runtime/ValidationOutput.d.ts +5 -0
- package/dist/vendor/codali/runtime/ValidationOutput.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/ValidationOutput.js +32 -0
- package/dist/vendor/codali/runtime/VerificationCommand.d.ts +3 -0
- package/dist/vendor/codali/runtime/VerificationCommand.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/VerificationCommand.js +48 -0
- package/dist/vendor/codali/runtime/WorkStatusPage.d.ts +15 -0
- package/dist/vendor/codali/runtime/WorkStatusPage.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/WorkStatusPage.js +58 -0
- package/dist/vendor/codali/runtime/WorkspaceLedger.d.ts +13 -0
- package/dist/vendor/codali/runtime/WorkspaceLedger.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/WorkspaceLedger.js +145 -0
- package/dist/vendor/codali/runtime/WorkspaceLock.d.ts.map +1 -1
- package/dist/vendor/codali/runtime/WorkspaceLock.js +20 -17
- package/dist/vendor/codali/runtime/WorkspaceSnapshot.d.ts +13 -0
- package/dist/vendor/codali/runtime/WorkspaceSnapshot.d.ts.map +1 -0
- package/dist/vendor/codali/runtime/WorkspaceSnapshot.js +88 -0
- package/dist/vendor/codali/session/AtomicState.d.ts +13 -0
- package/dist/vendor/codali/session/AtomicState.d.ts.map +1 -0
- package/dist/vendor/codali/session/AtomicState.js +109 -0
- package/dist/vendor/codali/session/SessionStoragePath.d.ts +15 -0
- package/dist/vendor/codali/session/SessionStoragePath.d.ts.map +1 -0
- package/dist/vendor/codali/session/SessionStoragePath.js +89 -0
- package/dist/vendor/codali/session/SessionStore.d.ts +9 -0
- package/dist/vendor/codali/session/SessionStore.d.ts.map +1 -1
- package/dist/vendor/codali/session/SessionStore.js +181 -47
- package/dist/vendor/codali/session/WorkState.d.ts +23 -0
- package/dist/vendor/codali/session/WorkState.d.ts.map +1 -0
- package/dist/vendor/codali/session/WorkState.js +367 -0
- package/dist/vendor/codali/session/WorkTypes.d.ts +94 -0
- package/dist/vendor/codali/session/WorkTypes.d.ts.map +1 -0
- package/dist/vendor/codali/session/WorkTypes.js +1 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts +2 -0
- package/dist/vendor/codali/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/vendor/codali/tools/ToolRegistry.js +12 -2
- package/dist/vendor/codali/tools/ToolTypes.d.ts +16 -0
- package/dist/vendor/codali/tools/ToolTypes.d.ts.map +1 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts +2 -0
- package/dist/vendor/codali/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/docdex/DocdexTools.js +17 -2
- package/dist/vendor/codali/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/vendor/codali/tools/filesystem/FileTools.js +30 -10
- package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts +16 -0
- package/dist/vendor/codali/tools/shell/AsyncProcess.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/AsyncProcess.js +94 -0
- package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.d.ts +13 -0
- package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/DarwinRuntimeLibraries.js +130 -0
- package/dist/vendor/codali/tools/shell/HostToolchain.d.ts +19 -0
- package/dist/vendor/codali/tools/shell/HostToolchain.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/HostToolchain.js +264 -0
- package/dist/vendor/codali/tools/shell/IsolatedExecutor.d.ts +15 -0
- package/dist/vendor/codali/tools/shell/IsolatedExecutor.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/IsolatedExecutor.js +134 -0
- package/dist/vendor/codali/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/vendor/codali/tools/shell/ShellTool.js +9 -29
- package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.d.ts +3 -0
- package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.d.ts.map +1 -0
- package/dist/vendor/codali/tools/shell/ShellWorkingDirectory.js +30 -0
- package/package.json +31 -29
- package/scripts/backend-http-drain.mjs +152 -0
- package/scripts/media_gpu_lock.py +238 -0
- package/scripts/stable-audio-3-openai-server.py +45 -14
- package/scripts/stable-diffusion-cpp-openai-server.py +34 -4
- package/scripts/stable-diffusion-cpp-webm-duration.patch +22 -0
- 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
|
-
|
|
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
|
-
|
|
202
|
-
except subprocess.TimeoutExpired as error:
|
|
209
|
+
lease.record_native_process(process.pid)
|
|
203
210
|
try:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
442
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|