Files
Constant/constant/state.py
T
2026-04-06 07:20:14 +02:00

330 lines
9.9 KiB
Python

from __future__ import annotations
import json
import hashlib
import uuid
from copy import deepcopy
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
from . import __version__
from .paths import (
cache_root,
chat_root,
config_root,
data_root,
fleet_config_path,
indexes_dir,
memory_config_path,
memory_sources_dir,
missions_dir,
models_config_path,
)
DEFAULT_FLEET: dict[str, Any] = {
"version": 1,
"local_machine": "command-center",
"repo_dir": "$HOME/constant",
"machines": [
{
"label": "command-center",
"target": "local",
"auto_clis": ["codex", "vibe", "claude"],
"manual_clis": ["copilot"],
"backends": ["omc", "cli-local", "cockpit"],
},
{
"label": "builder-a",
"target": "dev@builder-a",
"auto_clis": ["codex", "vibe", "claude"],
"manual_clis": ["copilot"],
"backends": ["cli-ssh", "cockpit"],
},
{
"label": "builder-b",
"target": "dev@builder-b",
"auto_clis": ["codex", "vibe", "claude"],
"manual_clis": ["copilot"],
"backends": ["cli-ssh", "cockpit"],
},
{
"label": "edge-a",
"target": "dev@edge-a",
"auto_clis": ["codex", "vibe", "claude"],
"manual_clis": ["copilot"],
"backends": ["cli-ssh", "cockpit"],
},
{
"label": "lab-a",
"target": "dev@lab-a",
"auto_clis": ["codex", "vibe", "claude"],
"manual_clis": ["copilot"],
"backends": ["cli-ssh", "cockpit"],
},
],
}
DEFAULT_MODELS: dict[str, Any] = {
"version": 1,
"enable_mlx": "auto",
"planner": {
"role": "planner",
"model_id": "mlx-community-staging/Llama-3.2-3B-Instruct-mlx-4Bit",
"max_tokens": 900,
},
"buddy": {
"role": "buddy",
"model_id": "mlx-community/Qwen2.5-Coder-3B-Instruct-4bit",
"max_tokens": 900,
},
"verify": {
"role": "verify",
"model_id": "mlx-community-staging/Llama-3.2-3B-Instruct-mlx-4Bit",
"max_tokens": 700,
},
"fallback_mode": "heuristic",
}
DEFAULT_MEMORY: dict[str, Any] = {
"version": 1,
"local_store_path": str(data_root() / "memory.sqlite"),
"qdrant_url": "",
"qdrant_collection": "constant_memory",
"workspace_enrollments": [],
"instruction_weights": {
"workspace": 1.0,
"repo": 0.85,
"ancestor": 0.65,
"user": 0.45,
"default": 0.2,
},
"max_chunks_per_query": 8,
"vector_dimensions": 96,
}
def now_utc() -> str:
return datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def ensure_runtime_dirs() -> None:
for path in (cache_root(), chat_root(), config_root(), data_root(), missions_dir(), indexes_dir(), memory_sources_dir()):
path.mkdir(parents=True, exist_ok=True)
def _legacy_config_path(path: Path) -> Path:
if path.suffix == ".json":
return path.with_suffix(".yaml")
return path
def _read_json_yaml(path: Path, default: dict[str, Any]) -> dict[str, Any]:
ensure_runtime_dirs()
legacy_path = _legacy_config_path(path)
if not path.exists() and legacy_path != path and legacy_path.exists():
return json.loads(legacy_path.read_text(encoding="utf-8"))
if not path.exists():
_write_json_yaml(path, default)
return deepcopy(default)
return json.loads(path.read_text(encoding="utf-8"))
def _write_json_yaml(path: Path, payload: dict[str, Any]) -> None:
ensure_runtime_dirs()
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
def load_fleet_config() -> dict[str, Any]:
payload = _read_json_yaml(fleet_config_path(), DEFAULT_FLEET)
for machine in payload.get("machines", []):
backends = []
for backend in machine.get("backends", []):
backends.append("cockpit" if backend == "zellij" else backend)
machine["backends"] = backends
return payload
def load_models_config() -> dict[str, Any]:
payload = _read_json_yaml(models_config_path(), DEFAULT_MODELS)
merged = deepcopy(DEFAULT_MODELS)
for key, value in payload.items():
if isinstance(value, dict) and isinstance(merged.get(key), dict):
merged[key].update(value)
else:
merged[key] = value
return merged
def load_memory_config() -> dict[str, Any]:
return _read_json_yaml(memory_config_path(), DEFAULT_MEMORY)
def save_memory_config(payload: dict[str, Any]) -> None:
_write_json_yaml(memory_config_path(), payload)
def fleet_machine(label: str) -> dict[str, Any]:
fleet = load_fleet_config()
for machine in fleet["machines"]:
if machine["label"] == label or machine["target"] == label:
return machine
raise KeyError(f"Unknown machine: {label}")
def mission_dir(mission_id: str) -> Path:
return missions_dir() / mission_id
def mission_file(mission_id: str) -> Path:
return mission_dir(mission_id) / "mission.json"
def mission_events_file(mission_id: str) -> Path:
return mission_dir(mission_id) / "events.ndjson"
def mission_artifacts_dir(mission_id: str) -> Path:
return mission_dir(mission_id) / "artifacts"
def _workspace_chat_slug(workspace: str) -> str:
normalized = str(Path(workspace).expanduser().resolve())
name = Path(normalized).name or "workspace"
digest = hashlib.sha1(normalized.encode("utf-8")).hexdigest()[:10]
return f"{name}-{digest}"
def chat_file(workspace: str, mission_id: str | None = None) -> Path:
ensure_runtime_dirs()
if mission_id:
return chat_root() / "missions" / f"{mission_id}.ndjson"
return chat_root() / "workspaces" / f"{_workspace_chat_slug(workspace)}.ndjson"
def read_chat_history(workspace: str, mission_id: str | None = None, limit: int = 80) -> list[dict[str, Any]]:
path = chat_file(workspace, mission_id=mission_id)
if not path.exists():
return []
entries: list[dict[str, Any]] = []
for raw in path.read_text(encoding="utf-8").splitlines()[-limit:]:
try:
entries.append(json.loads(raw))
except json.JSONDecodeError:
continue
return entries
def append_chat_message(
role: str,
content: str,
*,
workspace: str,
mission_id: str | None = None,
intent: str | None = None,
machine: str | None = None,
pane: str | None = None,
meta: dict[str, Any] | None = None,
) -> dict[str, Any]:
path = chat_file(workspace, mission_id=mission_id)
path.parent.mkdir(parents=True, exist_ok=True)
entry = {
"timestamp": now_utc(),
"role": role,
"content": content,
"intent": intent or "plain_chat",
"workspace": str(Path(workspace).expanduser().resolve()),
"mission_id": mission_id,
"machine": machine,
"pane": pane,
"meta": meta or {},
}
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(entry, sort_keys=True) + "\n")
return entry
def create_mission(goal: str, workspace: str, routing_overrides: dict[str, Any] | None = None) -> dict[str, Any]:
mission_id = uuid.uuid4().hex[:12]
mission = {
"mission_id": mission_id,
"title": goal.strip().splitlines()[0][:80] or f"mission-{mission_id}",
"goal": goal,
"workspace": workspace,
"status": "draft",
"priority": "normal",
"created_at": now_utc(),
"updated_at": now_utc(),
"planner_model": load_models_config()["planner"]["model_id"],
"buddy_model": load_models_config()["buddy"]["model_id"],
"verify_model": load_models_config()["verify"]["model_id"],
"owner": "Constant",
"routing_overrides": routing_overrides or {},
"steps": [],
"artifacts": [],
"meta": {
"schema_version": 1,
"tool_version": __version__,
},
}
save_mission(mission)
append_event(mission_id, "mission.created", {"goal": goal, "workspace": workspace})
return mission
def save_mission(mission: dict[str, Any]) -> None:
path = mission_file(mission["mission_id"])
path.parent.mkdir(parents=True, exist_ok=True)
mission["updated_at"] = now_utc()
path.write_text(json.dumps(mission, indent=2, sort_keys=True) + "\n", encoding="utf-8")
def load_mission(mission_id: str) -> dict[str, Any]:
return json.loads(mission_file(mission_id).read_text(encoding="utf-8"))
def list_missions() -> list[dict[str, Any]]:
ensure_runtime_dirs()
missions: list[dict[str, Any]] = []
for path in sorted(missions_dir().glob("*/mission.json")):
missions.append(json.loads(path.read_text(encoding="utf-8")))
return missions
def append_event(mission_id: str, event_type: str, payload: dict[str, Any]) -> None:
path = mission_events_file(mission_id)
path.parent.mkdir(parents=True, exist_ok=True)
event = {
"timestamp": now_utc(),
"type": event_type,
"payload": payload,
}
with path.open("a", encoding="utf-8") as handle:
handle.write(json.dumps(event, sort_keys=True) + "\n")
def write_artifact(mission_id: str, name: str, payload: dict[str, Any]) -> str:
artifact_dir = mission_artifacts_dir(mission_id)
artifact_dir.mkdir(parents=True, exist_ok=True)
path = artifact_dir / name
path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
mission = load_mission(mission_id)
mission["artifacts"].append(str(path))
save_mission(mission)
return str(path)
def first_active_step(mission: dict[str, Any]) -> dict[str, Any] | None:
for step in mission["steps"]:
if step["status"] not in {"done", "failed", "needs_human"}:
return step
return None