cluxion-agentplugin-preprocessing 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cluxion_agentplugin_adapters/claude/.claude-plugin/plugin.json +8 -0
- cluxion_agentplugin_adapters/claude/skills/preprocess/SKILL.md +33 -0
- cluxion_agentplugin_adapters/codex/config-snippet.toml +5 -0
- cluxion_agentplugin_docs/cluxion-Docs/README.md +22 -0
- cluxion_agentplugin_docs/cluxion-Docs/architecture.md +36 -0
- cluxion_agentplugin_docs/cluxion-Docs/harness-logic.md +51 -0
- cluxion_agentplugin_docs/cluxion-Docs/honesty-preprocessing.md +40 -0
- cluxion_agentplugin_docs/cluxion-Docs/install-and-operations.md +36 -0
- cluxion_agentplugin_docs/cluxion-Docs/security.md +27 -0
- cluxion_agentplugin_docs/github-profile/README.md +67 -0
- cluxion_agentplugin_preprocessing/__init__.py +7 -0
- cluxion_agentplugin_preprocessing/cli.py +124 -0
- cluxion_agentplugin_preprocessing/hermes_config.py +163 -0
- cluxion_agentplugin_preprocessing/plugin.py +135 -0
- cluxion_agentplugin_preprocessing/plugin.yaml +13 -0
- cluxion_agentplugin_preprocessing/runner.py +241 -0
- cluxion_agentplugin_preprocessing/schemas.py +148 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/METADATA +115 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/RECORD +48 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/WHEEL +4 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/entry_points.txt +8 -0
- cluxion_agentplugin_preprocessing-0.2.0.dist-info/licenses/LICENSE +197 -0
- cluxion_runtime/__init__.py +16 -0
- cluxion_runtime/__main__.py +5 -0
- cluxion_runtime/adapters/__init__.py +25 -0
- cluxion_runtime/adapters/contract.py +82 -0
- cluxion_runtime/adapters/grok_build.py +35 -0
- cluxion_runtime/adapters/hermes.py +161 -0
- cluxion_runtime/adapters/spec.py +35 -0
- cluxion_runtime/bootstrap.py +270 -0
- cluxion_runtime/cli.py +282 -0
- cluxion_runtime/core/__init__.py +36 -0
- cluxion_runtime/core/clarification.py +192 -0
- cluxion_runtime/core/dispatch_store.py +270 -0
- cluxion_runtime/core/harness.py +320 -0
- cluxion_runtime/core/intent.py +55 -0
- cluxion_runtime/core/ledger.py +189 -0
- cluxion_runtime/core/ledger_codec.py +38 -0
- cluxion_runtime/core/plan_codec.py +121 -0
- cluxion_runtime/core/preprocess.py +497 -0
- cluxion_runtime/core/types.py +220 -0
- cluxion_runtime/core/work_queue.py +73 -0
- cluxion_runtime/models/__init__.py +15 -0
- cluxion_runtime/models/supervisor.py +156 -0
- cluxion_runtime/models/vllm_mlx.py +87 -0
- cluxion_runtime/resources/__init__.py +7 -0
- cluxion_runtime/resources/queue_bridge.py +128 -0
- cluxion_runtime/resources/rust_bridge.py +82 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Runtime dependency bootstrap for local model backends."""
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from collections.abc import Callable, Sequence
|
|
9
|
+
from contextlib import suppress
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
LOCAL_RUNTIME_PACKAGES: tuple[str, ...] = ("vllm-mlx",)
|
|
14
|
+
LOCAL_RUNTIME_COMMANDS: tuple[str, ...] = ("vllm-mlx",)
|
|
15
|
+
RUNTIME_VENV_ENV = "CLUXION_PREPROCESS_RUNTIME_VENV"
|
|
16
|
+
RUNTIME_HOME_ENV = "CLUXION_PREPROCESS_RUNTIME_HOME"
|
|
17
|
+
_LEGACY_RUNTIME_VENV_ENV = "HERMES_CLUXION_RUNTIME_VENV"
|
|
18
|
+
_LEGACY_RUNTIME_HOME_ENV = "HERMES_CLUXION_RUNTIME_HOME"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class BootstrapResult:
|
|
23
|
+
"""Dependency bootstrap result."""
|
|
24
|
+
|
|
25
|
+
ok: bool
|
|
26
|
+
changed: bool
|
|
27
|
+
packages: tuple[str, ...]
|
|
28
|
+
commands: tuple[str, ...]
|
|
29
|
+
install_command: tuple[str, ...]
|
|
30
|
+
runtime_dir: str = ""
|
|
31
|
+
command_paths: dict[str, str] | None = None
|
|
32
|
+
setup_command: tuple[str, ...] = ()
|
|
33
|
+
returncode: int = 0
|
|
34
|
+
stdout: str = ""
|
|
35
|
+
stderr: str = ""
|
|
36
|
+
reason: str = ""
|
|
37
|
+
|
|
38
|
+
def to_dict(self) -> dict[str, object]:
|
|
39
|
+
return {
|
|
40
|
+
"ok": self.ok,
|
|
41
|
+
"changed": self.changed,
|
|
42
|
+
"packages": list(self.packages),
|
|
43
|
+
"commands": list(self.commands),
|
|
44
|
+
"install_command": list(self.install_command),
|
|
45
|
+
"runtime_dir": self.runtime_dir,
|
|
46
|
+
"command_paths": dict(self.command_paths or {}),
|
|
47
|
+
"setup_command": list(self.setup_command),
|
|
48
|
+
"returncode": self.returncode,
|
|
49
|
+
"stdout": self.stdout[-4_000:],
|
|
50
|
+
"stderr": self.stderr[-4_000:],
|
|
51
|
+
"reason": self.reason,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
CommandRunner = Callable[[Sequence[str], float], subprocess.CompletedProcess[str]]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def ensure_local_runtime(
|
|
59
|
+
*,
|
|
60
|
+
packages: Sequence[str] = LOCAL_RUNTIME_PACKAGES,
|
|
61
|
+
commands: Sequence[str] = LOCAL_RUNTIME_COMMANDS,
|
|
62
|
+
upgrade: bool = False,
|
|
63
|
+
dry_run: bool = False,
|
|
64
|
+
python: str | None = None,
|
|
65
|
+
runtime_dir: str | Path | None = None,
|
|
66
|
+
timeout_sec: float = 900.0,
|
|
67
|
+
command_runner: CommandRunner | None = None,
|
|
68
|
+
) -> BootstrapResult:
|
|
69
|
+
"""Install or upgrade packages required by the local model runtime."""
|
|
70
|
+
package_tuple = tuple(_validate_package_name(package) for package in packages)
|
|
71
|
+
command_tuple = tuple(commands)
|
|
72
|
+
runtime_path = _runtime_dir(runtime_dir)
|
|
73
|
+
runtime_python = Path(python) if python else _runtime_python(runtime_path)
|
|
74
|
+
command_paths = _command_paths(command_tuple, runtime_python)
|
|
75
|
+
missing = tuple(command for command, path in command_paths.items() if not Path(path).exists())
|
|
76
|
+
setup_command = _setup_command(runtime_path) if not python and not runtime_python.exists() else ()
|
|
77
|
+
install_command = _install_command(package_tuple, upgrade=upgrade, python=str(runtime_python))
|
|
78
|
+
if not missing and not upgrade:
|
|
79
|
+
return BootstrapResult(
|
|
80
|
+
True,
|
|
81
|
+
False,
|
|
82
|
+
package_tuple,
|
|
83
|
+
command_tuple,
|
|
84
|
+
install_command,
|
|
85
|
+
str(runtime_path),
|
|
86
|
+
command_paths,
|
|
87
|
+
setup_command,
|
|
88
|
+
reason="already_available",
|
|
89
|
+
)
|
|
90
|
+
if dry_run:
|
|
91
|
+
reason = "upgrade_requested" if upgrade else f"missing_commands:{','.join(missing)}"
|
|
92
|
+
return BootstrapResult(
|
|
93
|
+
True,
|
|
94
|
+
False,
|
|
95
|
+
package_tuple,
|
|
96
|
+
command_tuple,
|
|
97
|
+
install_command,
|
|
98
|
+
str(runtime_path),
|
|
99
|
+
command_paths,
|
|
100
|
+
setup_command,
|
|
101
|
+
reason=reason,
|
|
102
|
+
)
|
|
103
|
+
runner = _run_command if command_runner is None else command_runner
|
|
104
|
+
try:
|
|
105
|
+
setup_completed = _completed_ok(setup_command)
|
|
106
|
+
if setup_command:
|
|
107
|
+
setup_completed = runner(setup_command, timeout_sec)
|
|
108
|
+
if setup_completed.returncode != 0:
|
|
109
|
+
return BootstrapResult(
|
|
110
|
+
False,
|
|
111
|
+
False,
|
|
112
|
+
package_tuple,
|
|
113
|
+
command_tuple,
|
|
114
|
+
install_command,
|
|
115
|
+
str(runtime_path),
|
|
116
|
+
command_paths,
|
|
117
|
+
setup_command,
|
|
118
|
+
setup_completed.returncode,
|
|
119
|
+
setup_completed.stdout,
|
|
120
|
+
setup_completed.stderr,
|
|
121
|
+
"runtime_venv_failed",
|
|
122
|
+
)
|
|
123
|
+
completed = runner(install_command, timeout_sec)
|
|
124
|
+
except OSError as exc:
|
|
125
|
+
return BootstrapResult(
|
|
126
|
+
False,
|
|
127
|
+
False,
|
|
128
|
+
package_tuple,
|
|
129
|
+
command_tuple,
|
|
130
|
+
install_command,
|
|
131
|
+
str(runtime_path),
|
|
132
|
+
command_paths,
|
|
133
|
+
setup_command,
|
|
134
|
+
1,
|
|
135
|
+
"",
|
|
136
|
+
str(exc),
|
|
137
|
+
"install_failed",
|
|
138
|
+
)
|
|
139
|
+
ok = completed.returncode == 0 and all(Path(path).exists() for path in command_paths.values())
|
|
140
|
+
reason = "installed" if ok and missing else "upgraded" if ok else "install_failed"
|
|
141
|
+
return BootstrapResult(
|
|
142
|
+
ok,
|
|
143
|
+
completed.returncode == 0,
|
|
144
|
+
package_tuple,
|
|
145
|
+
command_tuple,
|
|
146
|
+
install_command,
|
|
147
|
+
str(runtime_path),
|
|
148
|
+
command_paths,
|
|
149
|
+
setup_command,
|
|
150
|
+
completed.returncode,
|
|
151
|
+
_join_output(setup_completed.stdout, completed.stdout),
|
|
152
|
+
_join_output(setup_completed.stderr, completed.stderr),
|
|
153
|
+
reason,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _runtime_dir(runtime_dir: str | Path | None) -> Path:
|
|
158
|
+
if runtime_dir is not None:
|
|
159
|
+
return Path(runtime_dir).expanduser()
|
|
160
|
+
for env_name in (RUNTIME_VENV_ENV, _LEGACY_RUNTIME_VENV_ENV):
|
|
161
|
+
env_venv = os_environ(env_name)
|
|
162
|
+
if env_venv:
|
|
163
|
+
return Path(env_venv).expanduser()
|
|
164
|
+
home_value = ""
|
|
165
|
+
for env_name in (RUNTIME_HOME_ENV, _LEGACY_RUNTIME_HOME_ENV):
|
|
166
|
+
home_value = os_environ(env_name)
|
|
167
|
+
if home_value:
|
|
168
|
+
break
|
|
169
|
+
home = Path(home_value or Path.home() / ".local" / "share" / "cluxion-agentplugin-preprocessing")
|
|
170
|
+
return home.expanduser() / "runtime-venv"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _runtime_python(runtime_dir: Path) -> Path:
|
|
174
|
+
return _bin_dir(runtime_dir) / ("python.exe" if sys.platform == "win32" else "python")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _bin_dir(runtime_dir: Path) -> Path:
|
|
178
|
+
return runtime_dir / ("Scripts" if sys.platform == "win32" else "bin")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _command_paths(commands: tuple[str, ...], runtime_python: Path) -> dict[str, str]:
|
|
182
|
+
return {command: str(runtime_python.parent / command) for command in commands}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _setup_command(runtime_dir: Path) -> tuple[str, ...]:
|
|
186
|
+
uv = shutil.which("uv")
|
|
187
|
+
if uv is not None:
|
|
188
|
+
return (uv, "venv", "--seed", str(runtime_dir))
|
|
189
|
+
return (sys.executable, "-m", "venv", str(runtime_dir))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _install_command(packages: tuple[str, ...], *, upgrade: bool, python: str | None) -> tuple[str, ...]:
|
|
193
|
+
python_executable = python or sys.executable
|
|
194
|
+
if _python_module_available(python_executable, "pip"):
|
|
195
|
+
command = [python_executable, "-m", "pip", "install", "--disable-pip-version-check"]
|
|
196
|
+
if upgrade:
|
|
197
|
+
command.append("--upgrade")
|
|
198
|
+
command.extend(packages)
|
|
199
|
+
return tuple(command)
|
|
200
|
+
|
|
201
|
+
uv = shutil.which("uv")
|
|
202
|
+
if uv is not None:
|
|
203
|
+
command = [uv, "pip", "install", "--python", python_executable]
|
|
204
|
+
if upgrade:
|
|
205
|
+
command.append("--upgrade")
|
|
206
|
+
command.extend(packages)
|
|
207
|
+
return tuple(command)
|
|
208
|
+
|
|
209
|
+
command = [python_executable, "-m", "pip", "install", "--disable-pip-version-check"]
|
|
210
|
+
if upgrade:
|
|
211
|
+
command.append("--upgrade")
|
|
212
|
+
command.extend(packages)
|
|
213
|
+
return tuple(command)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _python_module_available(python: str, module: str) -> bool:
|
|
217
|
+
with suppress(OSError, subprocess.TimeoutExpired):
|
|
218
|
+
completed = subprocess.run(
|
|
219
|
+
[python, "-m", module, "--version"],
|
|
220
|
+
text=True,
|
|
221
|
+
capture_output=True,
|
|
222
|
+
check=False,
|
|
223
|
+
timeout=10,
|
|
224
|
+
)
|
|
225
|
+
return completed.returncode == 0
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _run_command(command: Sequence[str], timeout_sec: float) -> subprocess.CompletedProcess[str]:
|
|
230
|
+
return subprocess.run(
|
|
231
|
+
list(command),
|
|
232
|
+
text=True,
|
|
233
|
+
capture_output=True,
|
|
234
|
+
check=False,
|
|
235
|
+
timeout=timeout_sec,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _completed_ok(command: Sequence[str]) -> subprocess.CompletedProcess[str]:
|
|
240
|
+
return subprocess.CompletedProcess(list(command), 0, stdout="", stderr="")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _join_output(*parts: str) -> str:
|
|
244
|
+
return "\n".join(part for part in parts if part)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def os_environ(key: str) -> str | None:
|
|
248
|
+
import os
|
|
249
|
+
|
|
250
|
+
return os.environ.get(key)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _validate_package_name(package: str) -> str:
|
|
254
|
+
cleaned = package.strip()
|
|
255
|
+
if not cleaned:
|
|
256
|
+
raise ValueError("package name must not be empty")
|
|
257
|
+
allowed = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-<>=!~")
|
|
258
|
+
if any(ch not in allowed for ch in cleaned):
|
|
259
|
+
raise ValueError(f"unsafe package specifier: {package!r}")
|
|
260
|
+
return cleaned
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
__all__ = [
|
|
264
|
+
"LOCAL_RUNTIME_COMMANDS",
|
|
265
|
+
"LOCAL_RUNTIME_PACKAGES",
|
|
266
|
+
"RUNTIME_HOME_ENV",
|
|
267
|
+
"RUNTIME_VENV_ENV",
|
|
268
|
+
"BootstrapResult",
|
|
269
|
+
"ensure_local_runtime",
|
|
270
|
+
]
|
cluxion_runtime/cli.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""cluxion-runtime 공통 CLI."""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from cluxion_runtime.adapters.contract import work_item_from_adapter_payload
|
|
11
|
+
from cluxion_runtime.adapters.hermes import (
|
|
12
|
+
build_hermes_local_endpoint_patch,
|
|
13
|
+
hermes_config_patch_to_dict,
|
|
14
|
+
hermes_config_set_commands,
|
|
15
|
+
render_hermes_yaml_fragment,
|
|
16
|
+
)
|
|
17
|
+
from cluxion_runtime.bootstrap import ensure_local_runtime
|
|
18
|
+
from cluxion_runtime.core.dispatch_store import (
|
|
19
|
+
DispatchStoreError,
|
|
20
|
+
build_briefing_payload,
|
|
21
|
+
next_dispatch_step,
|
|
22
|
+
persist_dispatch_bundle,
|
|
23
|
+
record_dispatch_result,
|
|
24
|
+
)
|
|
25
|
+
from cluxion_runtime.core.harness import build_harness_plan
|
|
26
|
+
from cluxion_runtime.core.plan_codec import plan_to_dict
|
|
27
|
+
from cluxion_runtime.core.types import AgentSurface, ModelRuntimeProfile
|
|
28
|
+
from cluxion_runtime.models import LocalModelSupervisor, build_vllm_mlx_profile
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from collections.abc import Sequence
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
35
|
+
"""CLI 진입점."""
|
|
36
|
+
parser = _build_parser()
|
|
37
|
+
args = parser.parse_args(argv)
|
|
38
|
+
if args.command == "bootstrap":
|
|
39
|
+
return _run_bootstrap(args)
|
|
40
|
+
if args.command == "plan":
|
|
41
|
+
return _run_plan(args)
|
|
42
|
+
if args.command == "serve-local":
|
|
43
|
+
return _run_serve_local(args)
|
|
44
|
+
if args.command == "hermes-local-config":
|
|
45
|
+
return _run_hermes_local_config(args)
|
|
46
|
+
if args.command == "queue-next":
|
|
47
|
+
return _run_queue_next(args)
|
|
48
|
+
if args.command == "queue-record":
|
|
49
|
+
return _run_queue_record(args)
|
|
50
|
+
if args.command == "queue-brief":
|
|
51
|
+
return _run_queue_brief(args)
|
|
52
|
+
parser.print_help(sys.stderr)
|
|
53
|
+
return 2
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
57
|
+
parser = argparse.ArgumentParser(prog="cluxion-runtime")
|
|
58
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
59
|
+
bootstrap = subparsers.add_parser("bootstrap", help="로컬 모델 런타임 의존성을 자동 설치 또는 업그레이드")
|
|
60
|
+
bootstrap.add_argument("--upgrade", action="store_true", help="이미 설치되어도 최신 버전으로 업그레이드")
|
|
61
|
+
bootstrap.add_argument("--dry-run", action="store_true", help="설치하지 않고 실행할 installer 명령만 출력")
|
|
62
|
+
bootstrap.add_argument("--package", action="append", default=None, help="설치할 패키지 spec. 기본값: vllm-mlx")
|
|
63
|
+
plan = subparsers.add_parser("plan", help="외부 agent 작업을 Cluxion 하네스 계획으로 변환")
|
|
64
|
+
plan.add_argument("--surface", default=AgentSurface.API.value)
|
|
65
|
+
plan.add_argument("--work-id", default="")
|
|
66
|
+
plan.add_argument("--prompt", default="")
|
|
67
|
+
plan.add_argument("--model-route", default="host/default")
|
|
68
|
+
plan.add_argument("--priority", default="normal")
|
|
69
|
+
plan.add_argument("--expected-ram-mb", type=int, default=0)
|
|
70
|
+
plan.add_argument("--context-tokens", type=int, default=0)
|
|
71
|
+
plan.add_argument("--cwd", default="")
|
|
72
|
+
plan.add_argument("--json-stdin", action="store_true")
|
|
73
|
+
serve = subparsers.add_parser("serve-local", help="로컬 모델용 vLLM-MLX 서버 endpoint를 준비")
|
|
74
|
+
serve.add_argument("--model", required=True)
|
|
75
|
+
serve.add_argument("--host", default="127.0.0.1")
|
|
76
|
+
serve.add_argument("--port", type=int, default=8000)
|
|
77
|
+
serve.add_argument("--max-tokens", type=int, default=128_000)
|
|
78
|
+
serve.add_argument("--dry-run", action="store_true")
|
|
79
|
+
serve.add_argument("--auto-install", action=argparse.BooleanOptionalAction, default=True)
|
|
80
|
+
serve.add_argument("--upgrade-runtime", action="store_true")
|
|
81
|
+
hermes = subparsers.add_parser("hermes-local-config", help="Hermes custom provider용 로컬 endpoint 패치 출력")
|
|
82
|
+
hermes.add_argument("--model", required=True)
|
|
83
|
+
hermes.add_argument("--host", default="127.0.0.1")
|
|
84
|
+
hermes.add_argument("--port", type=int, default=8000)
|
|
85
|
+
hermes.add_argument("--max-tokens", type=int, default=128_000)
|
|
86
|
+
hermes.add_argument("--context-length", type=int, default=128_000)
|
|
87
|
+
hermes.add_argument("--provider-key", default="cluxion-local")
|
|
88
|
+
hermes.add_argument("--display-name", default="Cluxion Local")
|
|
89
|
+
queue_next = subparsers.add_parser("queue-next", help="저장된 dispatch queue에서 다음 segment를 반환")
|
|
90
|
+
queue_next.add_argument("--work-id", required=True)
|
|
91
|
+
queue_record = subparsers.add_parser("queue-record", help="segment 실행 결과를 dispatch queue에 기록")
|
|
92
|
+
queue_record.add_argument("--work-id", required=True)
|
|
93
|
+
queue_record.add_argument("--step-id", required=True)
|
|
94
|
+
queue_record.add_argument("--result", default="")
|
|
95
|
+
queue_record.add_argument("--error", default="")
|
|
96
|
+
queue_record.add_argument("--failed", action="store_true")
|
|
97
|
+
queue_record.add_argument("--json-stdin", action="store_true")
|
|
98
|
+
queue_brief = subparsers.add_parser("queue-brief", help="저장된 segment 결과를 최종 briefing prompt로 변환")
|
|
99
|
+
queue_brief.add_argument("--work-id", required=True)
|
|
100
|
+
return parser
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _run_bootstrap(args: argparse.Namespace) -> int:
|
|
104
|
+
result = ensure_local_runtime(
|
|
105
|
+
packages=tuple(args.package) if args.package else ("vllm-mlx",),
|
|
106
|
+
upgrade=bool(args.upgrade),
|
|
107
|
+
dry_run=bool(args.dry_run),
|
|
108
|
+
)
|
|
109
|
+
print(json.dumps(result.to_dict(), ensure_ascii=False, sort_keys=True))
|
|
110
|
+
return 0 if result.ok else 1
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _run_plan(args: argparse.Namespace) -> int:
|
|
114
|
+
surface = AgentSurface(str(args.surface))
|
|
115
|
+
payload = _payload_from_stdin() if args.json_stdin else _payload_from_args(args)
|
|
116
|
+
item = work_item_from_adapter_payload(payload, default_surface=surface)
|
|
117
|
+
plan = build_harness_plan(item)
|
|
118
|
+
persisted = persist_dispatch_bundle(plan)
|
|
119
|
+
output = plan_to_dict(plan)
|
|
120
|
+
if persisted is not None:
|
|
121
|
+
item_output = output.get("item")
|
|
122
|
+
if isinstance(item_output, dict):
|
|
123
|
+
item_output["original_prompt_stored"] = True
|
|
124
|
+
output["dispatch_store"] = {"stored": True, "path": str(persisted)}
|
|
125
|
+
print(json.dumps(output, ensure_ascii=False, sort_keys=True))
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _run_queue_next(args: argparse.Namespace) -> int:
|
|
130
|
+
try:
|
|
131
|
+
payload = next_dispatch_step(str(args.work_id))
|
|
132
|
+
except DispatchStoreError as exc:
|
|
133
|
+
payload = {"ok": False, "error": str(exc)}
|
|
134
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
135
|
+
return 1
|
|
136
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _run_queue_record(args: argparse.Namespace) -> int:
|
|
141
|
+
payload = _payload_from_stdin() if args.json_stdin else {}
|
|
142
|
+
result = str(payload.get("result", args.result))
|
|
143
|
+
error = str(payload.get("error", args.error))
|
|
144
|
+
failed = bool(payload.get("failed", args.failed))
|
|
145
|
+
try:
|
|
146
|
+
recorded = record_dispatch_result(
|
|
147
|
+
str(args.work_id),
|
|
148
|
+
str(args.step_id),
|
|
149
|
+
result=result,
|
|
150
|
+
error=error,
|
|
151
|
+
succeeded=not failed,
|
|
152
|
+
)
|
|
153
|
+
except DispatchStoreError as exc:
|
|
154
|
+
recorded = {"ok": False, "error": str(exc)}
|
|
155
|
+
print(json.dumps(recorded, ensure_ascii=False, sort_keys=True))
|
|
156
|
+
return 1
|
|
157
|
+
print(json.dumps(recorded, ensure_ascii=False, sort_keys=True))
|
|
158
|
+
return 0
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _run_queue_brief(args: argparse.Namespace) -> int:
|
|
162
|
+
try:
|
|
163
|
+
payload = build_briefing_payload(str(args.work_id))
|
|
164
|
+
except DispatchStoreError as exc:
|
|
165
|
+
payload = {"ok": False, "error": str(exc)}
|
|
166
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
167
|
+
return 1
|
|
168
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
169
|
+
return 0
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _run_serve_local(args: argparse.Namespace) -> int:
|
|
173
|
+
bootstrap = None
|
|
174
|
+
if args.auto_install and (not args.dry_run or args.upgrade_runtime):
|
|
175
|
+
bootstrap = ensure_local_runtime(upgrade=bool(args.upgrade_runtime))
|
|
176
|
+
if not bootstrap.ok:
|
|
177
|
+
profile = build_vllm_mlx_profile(
|
|
178
|
+
str(args.model),
|
|
179
|
+
host=str(args.host),
|
|
180
|
+
port=int(args.port),
|
|
181
|
+
max_tokens=int(args.max_tokens),
|
|
182
|
+
)
|
|
183
|
+
payload = _serve_payload(profile)
|
|
184
|
+
payload["bootstrap"] = bootstrap.to_dict()
|
|
185
|
+
payload.update({"started": False, "pid": 0, "reason": "bootstrap_failed"})
|
|
186
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
187
|
+
return 1
|
|
188
|
+
elif args.auto_install and args.dry_run:
|
|
189
|
+
bootstrap = ensure_local_runtime(upgrade=bool(args.upgrade_runtime), dry_run=True)
|
|
190
|
+
executable = _runtime_executable(bootstrap) or "vllm-mlx"
|
|
191
|
+
profile = build_vllm_mlx_profile(
|
|
192
|
+
str(args.model),
|
|
193
|
+
host=str(args.host),
|
|
194
|
+
port=int(args.port),
|
|
195
|
+
max_tokens=int(args.max_tokens),
|
|
196
|
+
executable=executable,
|
|
197
|
+
)
|
|
198
|
+
payload = _serve_payload(profile)
|
|
199
|
+
if bootstrap is not None:
|
|
200
|
+
payload["bootstrap"] = bootstrap.to_dict()
|
|
201
|
+
if not args.dry_run:
|
|
202
|
+
result = LocalModelSupervisor(profile).start()
|
|
203
|
+
payload.update({"started": result.started, "pid": result.pid, "reason": result.reason})
|
|
204
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
205
|
+
return 0
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _runtime_executable(bootstrap: object) -> str | None:
|
|
209
|
+
if bootstrap is None:
|
|
210
|
+
return None
|
|
211
|
+
command_paths = getattr(bootstrap, "command_paths", None) or {}
|
|
212
|
+
value = command_paths.get("vllm-mlx")
|
|
213
|
+
return str(value) if value else None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _serve_payload(profile: ModelRuntimeProfile) -> dict[str, object]:
|
|
217
|
+
return {
|
|
218
|
+
"provider": "openai-compatible",
|
|
219
|
+
"runtime": profile.kind.value,
|
|
220
|
+
"model": profile.model,
|
|
221
|
+
"base_url": profile.base_url,
|
|
222
|
+
"command": list(profile.command),
|
|
223
|
+
"health_path": profile.health_path,
|
|
224
|
+
"started": False,
|
|
225
|
+
"pid": 0,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _run_hermes_local_config(args: argparse.Namespace) -> int:
|
|
230
|
+
profile = build_vllm_mlx_profile(
|
|
231
|
+
str(args.model),
|
|
232
|
+
host=str(args.host),
|
|
233
|
+
port=int(args.port),
|
|
234
|
+
max_tokens=int(args.max_tokens),
|
|
235
|
+
)
|
|
236
|
+
patch = build_hermes_local_endpoint_patch(
|
|
237
|
+
profile.model,
|
|
238
|
+
profile.base_url,
|
|
239
|
+
provider_key=str(args.provider_key),
|
|
240
|
+
display_name=str(args.display_name),
|
|
241
|
+
context_length=int(args.context_length),
|
|
242
|
+
)
|
|
243
|
+
payload = {
|
|
244
|
+
"provider": patch.provider_id,
|
|
245
|
+
"model": patch.model,
|
|
246
|
+
"base_url": patch.base_url,
|
|
247
|
+
"slash_model": patch.slash_model,
|
|
248
|
+
"serve_command": list(profile.command),
|
|
249
|
+
"config_patch": hermes_config_patch_to_dict(patch),
|
|
250
|
+
"config_set_commands": list(hermes_config_set_commands(patch)),
|
|
251
|
+
"yaml_fragment": render_hermes_yaml_fragment(patch),
|
|
252
|
+
}
|
|
253
|
+
print(json.dumps(payload, ensure_ascii=False, sort_keys=True))
|
|
254
|
+
return 0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _payload_from_stdin() -> dict[str, object]:
|
|
258
|
+
raw = sys.stdin.read()
|
|
259
|
+
payload = json.loads(raw)
|
|
260
|
+
if not isinstance(payload, dict):
|
|
261
|
+
raise ValueError("stdin JSON은 object여야 한다.")
|
|
262
|
+
return dict(payload)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _payload_from_args(args: argparse.Namespace) -> dict[str, object]:
|
|
266
|
+
return {
|
|
267
|
+
"surface": str(args.surface),
|
|
268
|
+
"work_id": str(args.work_id),
|
|
269
|
+
"prompt": str(args.prompt),
|
|
270
|
+
"model_route": str(args.model_route),
|
|
271
|
+
"priority": str(args.priority),
|
|
272
|
+
"expected_ram_mb": int(args.expected_ram_mb),
|
|
273
|
+
"context_tokens": int(args.context_tokens),
|
|
274
|
+
"cwd": str(args.cwd),
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
if __name__ == "__main__":
|
|
279
|
+
raise SystemExit(main())
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
__all__ = ["main"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""하네스 코어 공개 API."""
|
|
4
|
+
|
|
5
|
+
from cluxion_runtime.core.harness import build_harness_plan
|
|
6
|
+
from cluxion_runtime.core.intent import classify_intent
|
|
7
|
+
from cluxion_runtime.core.ledger import DurableWorkLedger, LedgerEntry, RetryDecision, WorkStatus
|
|
8
|
+
from cluxion_runtime.core.preprocess import preprocess_work
|
|
9
|
+
from cluxion_runtime.core.types import (
|
|
10
|
+
AgentSurface,
|
|
11
|
+
AnswerPolicy,
|
|
12
|
+
HarnessPlan,
|
|
13
|
+
RuntimeKind,
|
|
14
|
+
WorkIntent,
|
|
15
|
+
WorkItem,
|
|
16
|
+
WorkPriority,
|
|
17
|
+
)
|
|
18
|
+
from cluxion_runtime.core.work_queue import AgentWorkQueue
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AgentSurface",
|
|
22
|
+
"AgentWorkQueue",
|
|
23
|
+
"AnswerPolicy",
|
|
24
|
+
"DurableWorkLedger",
|
|
25
|
+
"HarnessPlan",
|
|
26
|
+
"LedgerEntry",
|
|
27
|
+
"RetryDecision",
|
|
28
|
+
"RuntimeKind",
|
|
29
|
+
"WorkIntent",
|
|
30
|
+
"WorkItem",
|
|
31
|
+
"WorkPriority",
|
|
32
|
+
"WorkStatus",
|
|
33
|
+
"build_harness_plan",
|
|
34
|
+
"classify_intent",
|
|
35
|
+
"preprocess_work",
|
|
36
|
+
]
|