Files
wehub-resource-sync 0418dc5cf9
CI / Python tests (3.10) (push) Has been cancelled
CI / Python tests (3.11) (push) Has been cancelled
CI / Python quality (push) Has been cancelled
CI / Frontend typecheck (push) Has been cancelled
Autopilot Pages / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:45:00 +08:00

219 lines
7.6 KiB
Python

"""Runtime helpers for ohmo."""
from __future__ import annotations
import asyncio
import json
import os
import sys
from pathlib import Path
from openharness.api.client import SupportsStreamingMessages
from openharness.engine.stream_events import AssistantTextDelta, AssistantTurnComplete, CompactProgressEvent, ErrorEvent, StatusEvent
from openharness.ui.backend_host import run_backend_host
from openharness.ui.runtime import build_runtime, close_runtime, handle_line, start_runtime
from openharness.ui.react_launcher import _resolve_npm, _resolve_tsx, get_frontend_dir
from ohmo.memory import create_memory_command_backend
from ohmo.prompts import build_ohmo_system_prompt
from ohmo.session_storage import OhmoSessionBackend
from ohmo.workspace import get_memory_dir, get_plugins_dir, get_sessions_dir, get_skills_dir, initialize_workspace
def _ohmo_extra_roots(workspace: str | Path | None) -> tuple[tuple[str, ...], tuple[str, ...]]:
root = initialize_workspace(workspace)
return ((str(get_skills_dir(root)),), (str(get_plugins_dir(root)),))
async def run_ohmo_backend(
*,
cwd: str | None = None,
workspace: str | Path | None = None,
model: str | None = None,
max_turns: int | None = None,
provider_profile: str | None = None,
api_client: SupportsStreamingMessages | None = None,
restore_messages: list[dict] | None = None,
restore_tool_metadata: dict[str, object] | None = None,
backend_only: bool = True,
) -> int:
"""Run the shared React backend host with ohmo workspace semantics."""
del backend_only
cwd_path = str(Path(cwd or Path.cwd()).resolve())
workspace_root = initialize_workspace(workspace)
extra_skill_dirs, extra_plugin_roots = _ohmo_extra_roots(workspace_root)
return await run_backend_host(
cwd=cwd_path,
model=model,
max_turns=max_turns,
system_prompt=build_ohmo_system_prompt(cwd_path, workspace=workspace_root),
active_profile=provider_profile,
api_client=api_client,
restore_messages=restore_messages,
restore_tool_metadata=restore_tool_metadata,
enforce_max_turns=max_turns is not None,
session_backend=OhmoSessionBackend(workspace_root),
extra_skill_dirs=extra_skill_dirs,
extra_plugin_roots=extra_plugin_roots,
memory_backend=create_memory_command_backend(workspace_root),
include_project_memory=False,
autodream_context={
"memory_dir": str(get_memory_dir(workspace_root)),
"session_dir": str(get_sessions_dir(workspace_root)),
"app_label": "ohmo personal memory",
"runner_module": "ohmo",
},
)
def build_ohmo_backend_command(
*,
cwd: str | None = None,
workspace: str | Path | None = None,
model: str | None = None,
max_turns: int | None = None,
provider_profile: str | None = None,
) -> list[str]:
"""Return the backend command for the React terminal UI."""
command = [sys.executable, "-m", "ohmo", "--backend-only"]
if cwd:
command.extend(["--cwd", cwd])
if workspace:
command.extend(["--workspace", str(workspace)])
if model:
command.extend(["--model", model])
if max_turns is not None:
command.extend(["--max-turns", str(max_turns)])
if provider_profile:
command.extend(["--profile", provider_profile])
return command
async def launch_ohmo_react_tui(
*,
cwd: str | None = None,
workspace: str | Path | None = None,
model: str | None = None,
max_turns: int | None = None,
provider_profile: str | None = None,
) -> int:
"""Launch the shared React terminal UI with an ohmo backend."""
frontend_dir = get_frontend_dir()
package_json = frontend_dir / "package.json"
if not package_json.exists():
raise RuntimeError(f"React terminal frontend is missing: {package_json}")
npm = _resolve_npm()
if not (frontend_dir / "node_modules").exists():
install = await asyncio.create_subprocess_exec(
npm,
"install",
"--no-fund",
"--no-audit",
cwd=str(frontend_dir),
)
if await install.wait() != 0:
raise RuntimeError("Failed to install React terminal frontend dependencies")
cwd_path = str(Path(cwd or Path.cwd()).resolve())
workspace_root = initialize_workspace(workspace)
env = os.environ.copy()
env["OPENHARNESS_FRONTEND_CONFIG"] = json.dumps(
{
"backend_command": build_ohmo_backend_command(
cwd=cwd_path,
workspace=workspace_root,
model=model,
max_turns=max_turns,
provider_profile=provider_profile,
),
"initial_prompt": None,
"theme": "default",
}
)
tsx_cmd = _resolve_tsx(frontend_dir)
process = await asyncio.create_subprocess_exec(
*tsx_cmd,
"src/index.tsx",
cwd=str(frontend_dir),
env=env,
stdin=None,
stdout=None,
stderr=None,
)
return await process.wait()
async def run_ohmo_print_mode(
*,
prompt: str,
cwd: str | None = None,
workspace: str | Path | None = None,
model: str | None = None,
max_turns: int | None = None,
provider_profile: str | None = None,
) -> int:
"""Run a single ohmo prompt and print the assistant output."""
cwd_path = str(Path(cwd or Path.cwd()).resolve())
workspace_root = initialize_workspace(workspace)
extra_skill_dirs, extra_plugin_roots = _ohmo_extra_roots(workspace_root)
previous_cwd = Path.cwd()
os.chdir(cwd_path)
try:
bundle = await build_runtime(
model=model,
max_turns=max_turns,
system_prompt=build_ohmo_system_prompt(cwd_path, workspace=workspace_root),
active_profile=provider_profile,
session_backend=OhmoSessionBackend(workspace_root),
enforce_max_turns=max_turns is not None,
extra_skill_dirs=extra_skill_dirs,
extra_plugin_roots=extra_plugin_roots,
memory_backend=create_memory_command_backend(workspace_root),
include_project_memory=False,
autodream_context={
"memory_dir": str(get_memory_dir(workspace_root)),
"session_dir": str(get_sessions_dir(workspace_root)),
"app_label": "ohmo personal memory",
"runner_module": "ohmo",
},
)
await start_runtime(bundle)
async def _print_system(message: str) -> None:
print(message, file=sys.stderr)
saw_error = False
async def _render_event(event) -> None:
nonlocal saw_error
if isinstance(event, AssistantTextDelta):
sys.stdout.write(event.text)
sys.stdout.flush()
elif isinstance(event, AssistantTurnComplete):
sys.stdout.write("\n")
sys.stdout.flush()
elif isinstance(event, ErrorEvent):
saw_error = True
print(event.message, file=sys.stderr)
elif isinstance(event, CompactProgressEvent):
if event.message:
print(event.message, file=sys.stderr)
elif isinstance(event, StatusEvent):
print(event.message, file=sys.stderr)
async def _clear_output() -> None:
return None
await handle_line(
bundle,
prompt,
print_system=_print_system,
render_event=_render_event,
clear_output=_clear_output,
)
await close_runtime(bundle)
return 1 if saw_error else 0
finally:
os.chdir(previous_cwd)