Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,20 @@ pip install dhee[ollama,mcp] # Ollama (local inference, no API costs)
```bash
git clone https://github.com/Sankhya-AI/Dhee.git
cd Dhee
pip install -e ".[dev]"

./scripts/bootstrap_dev_env.sh
source .venv-dhee/bin/activate

# optional if you prefer manual bootstrap:
# python3 -m venv .venv-dhee
# .venv-dhee/bin/python -m pip install -e ./dhee-accel -e ./engram-bus -e ".[dev]"

pytest

# live vendor-backed suites are explicit opt-in:
# DHEE_RUN_LIVE_TESTS=1 pytest -q tests/test_e2e_all_features.py tests/test_power_packages.py

# manual smoke scripts live under scripts/manual/
```

---
Expand Down
6 changes: 4 additions & 2 deletions dhee/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
d.checkpoint("Fixed it", what_worked="git blame first")

Memory Classes:
Engram — batteries-included memory interface with sensible defaults
CoreMemory — lightweight: add/search/delete + decay (no LLM)
SmartMemory — + echo encoding, categories, knowledge graph (needs LLM)
FullMemory — + scenes, profiles, orchestration, cognition (everything)
Expand All @@ -22,7 +23,7 @@
from dhee.memory.core import CoreMemory
from dhee.memory.smart import SmartMemory
from dhee.memory.main import FullMemory
from dhee.simple import Dhee
from dhee.simple import Dhee, Engram
from dhee.adapters.base import DheePlugin
from dhee.core.category import CategoryProcessor, Category, CategoryType, CategoryMatch
from dhee.core.echo import EchoProcessor, EchoDepth, EchoResult
Expand All @@ -31,9 +32,10 @@
# Default: CoreMemory (lightest, zero-config)
Memory = CoreMemory

__version__ = "3.0.0"
__version__ = "3.0.1"
__all__ = [
# Memory classes
"Engram",
"CoreMemory",
"SmartMemory",
"FullMemory",
Expand Down
176 changes: 106 additions & 70 deletions dhee/adapters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Union

from dhee.checkpoint_runtime import run_checkpoint_common

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -118,6 +120,11 @@ def kernel(self):
"""Access the CognitionKernel for direct state manipulation."""
return self._kernel

@property
def memory(self):
"""Expose the configured runtime memory engine for advanced integrations."""
return self._engram.memory

# ------------------------------------------------------------------
# Hook registry
# ------------------------------------------------------------------
Expand Down Expand Up @@ -153,6 +160,13 @@ def _fire_hooks(self, event: str, data: Any) -> None:
except Exception:
logger.debug("Hook %s failed", event)

@staticmethod
def _health_error(component: str, exc: Exception) -> Dict[str, str]:
return {
"component": component,
"error": f"{type(exc).__name__}: {exc}",
}

# ------------------------------------------------------------------
# Tool 1: remember
# ------------------------------------------------------------------
Expand Down Expand Up @@ -257,7 +271,7 @@ def context(
hyper_ctx = self._buddhi.get_hyper_context(
user_id=uid,
task_description=task_description,
memory=self._engram._memory,
memory=self.memory,
)
if operational:
result = hyper_ctx.to_operational_dict()
Expand Down Expand Up @@ -325,62 +339,29 @@ def checkpoint(
if not what_worked:
what_worked = outcome.get("what_worked")

result: Dict[str, Any] = {}
score = max(0.0, min(1.0, float(outcome_score))) if outcome_score is not None else None

# 1. Session digest
try:
from dhee.core.kernel import save_session_digest
digest = save_session_digest(
task_summary=summary, agent_id=agent_id, repo=repo,
status=status, decisions_made=decisions,
files_touched=files_touched, todos_remaining=todos,
)
result["session_saved"] = True
if isinstance(digest, dict):
result["session_id"] = digest.get("session_id")
except Exception:
result["session_saved"] = False

# 2. Batch enrichment
memory = self._engram._memory
if hasattr(memory, "enrich_pending"):
try:
enrich_result = memory.enrich_pending(
user_id=uid, batch_size=10, max_batches=5,
)
enriched = enrich_result.get("enriched_count", 0)
if enriched > 0:
result["memories_enriched"] = enriched
except Exception:
pass

# 3. Outcome recording
if task_type and score is not None:
insight = self._buddhi.record_outcome(
user_id=uid, task_type=task_type, score=score,
)
result["outcome_recorded"] = True
if insight:
result["auto_insight"] = insight.to_dict()

# 4. Insight synthesis
if any([what_worked, what_failed, key_decision]):
insights = self._buddhi.reflect(
user_id=uid, task_type=task_type or "general",
what_worked=what_worked, what_failed=what_failed,
key_decision=key_decision,
outcome_score=score if score is not None else None,
)
result["insights_created"] = len(insights)

# 5. Intention storage
if remember_to:
intention = self._buddhi.store_intention(
user_id=uid, description=remember_to,
trigger_keywords=trigger_keywords,
)
result["intention_stored"] = intention.to_dict()
result = run_checkpoint_common(
logger=logger,
log_prefix="Plugin checkpoint",
user_id=uid,
summary=summary,
status=status,
agent_id=agent_id,
repo=repo,
decisions=decisions,
files_touched=files_touched,
todos=todos,
task_type=task_type,
outcome_score=outcome_score,
what_worked=what_worked,
what_failed=what_failed,
key_decision=key_decision,
remember_to=remember_to,
trigger_keywords=trigger_keywords,
enrich_pending_fn=self._engram.enrich_pending,
record_outcome_fn=self._buddhi.record_outcome,
reflect_fn=self._buddhi.reflect,
store_intention_fn=self._buddhi.store_intention,
)

# 6. Episode closure (via kernel)
ep_result = self._kernel.record_checkpoint_event(
Expand Down Expand Up @@ -441,8 +422,8 @@ def session_start(
task_description=task_description or "session",
task_type=task_type or "general",
)
except Exception:
pass
except Exception as exc:
logger.warning("Session start episode initialization failed: %s", exc, exc_info=True)

ctx = self.context(task_description=task_description, user_id=uid)
return self._render_system_prompt(ctx, task_description)
Expand Down Expand Up @@ -479,17 +460,19 @@ def _handle_tracker_signals(self, signals: Dict[str, Any], user_id: str) -> None
if signals.get("needs_auto_checkpoint"):
args = signals.get("auto_checkpoint_args", {})
try:
self.checkpoint(user_id=user_id, **args)
except Exception:
pass
checkpoint_result = self.checkpoint(user_id=user_id, **args)
for warning in checkpoint_result.get("warnings", []):
logger.warning("Plugin auto-checkpoint warning: %s", warning)
except Exception as exc:
logger.warning("Plugin auto-checkpoint failed: %s", exc, exc_info=True)

# Auto-context for new session
if signals.get("needs_auto_context"):
task = signals.get("inferred_task")
try:
self.context(task_description=task, user_id=user_id)
except Exception:
pass
except Exception as exc:
logger.warning("Plugin auto-context failed: %s", exc, exc_info=True)

# ------------------------------------------------------------------
# Cognition health (harness monitoring)
Expand All @@ -503,6 +486,7 @@ def cognition_health(self, user_id: Optional[str] = None) -> Dict[str, Any]:
"""
uid = user_id or self._user_id
health: Dict[str, Any] = {}
errors: List[Dict[str, str]] = []

health["kernel"] = self._kernel.get_stats()
health["buddhi"] = self._buddhi.get_stats()
Expand All @@ -513,16 +497,45 @@ def cognition_health(self, user_id: Optional[str] = None) -> Dict[str, Any]:
low_util = [p for p in policies if p.utility < -0.2 and p.apply_count >= 3]
if low_util:
warnings.append(f"{len(low_util)} policies with negative utility")
except Exception as exc:
logger.warning(
"Cognition health derivation failed for policies: %s",
exc,
exc_info=True,
)
errors.append(self._health_error("policies.get_user_policies", exc))

try:
active_intentions = self._kernel.intentions.get_active(uid)
if len(active_intentions) > 20:
warnings.append(f"{len(active_intentions)} active intentions (consider cleanup)")
warnings.append(
f"{len(active_intentions)} active intentions (consider cleanup)"
)
except Exception as exc:
logger.warning(
"Cognition health derivation failed for intentions: %s",
exc,
exc_info=True,
)
errors.append(self._health_error("intentions.get_active", exc))

try:
contradictions = self._kernel.beliefs.get_contradictions(uid)
if len(contradictions) > 5:
warnings.append(f"{len(contradictions)} unresolved belief contradictions")
except Exception:
pass
warnings.append(
f"{len(contradictions)} unresolved belief contradictions"
)
except Exception as exc:
logger.warning(
"Cognition health derivation failed for contradictions: %s",
exc,
exc_info=True,
)
errors.append(self._health_error("beliefs.get_contradictions", exc))

health["warnings"] = warnings
if errors:
health["errors"] = errors
return health

# ------------------------------------------------------------------
Expand Down Expand Up @@ -640,14 +653,37 @@ def end_trajectory(
# Store trajectory as memory for skill mining
try:
from dhee.skills.trajectory import TrajectoryStore
store = TrajectoryStore(memory=self._engram._memory)
store = TrajectoryStore(memory=self.memory)
store.save(trajectory)
result["stored"] = True
except Exception:
except Exception as exc:
logger.warning("Trajectory persistence failed: %s", exc, exc_info=True)
result["stored"] = False
result["storage_error"] = str(exc)

return result

def close(self) -> None:
"""Flush cognition state and release runtime resources."""
errors: List[str] = []

try:
self._buddhi.flush()
except Exception as exc:
logger.exception("DheePlugin close failed for buddhi.flush")
errors.append(f"buddhi.flush: {type(exc).__name__}: {exc}")

try:
self._engram.close()
except Exception as exc:
logger.exception("DheePlugin close failed for engram.close")
errors.append(f"engram.close: {type(exc).__name__}: {exc}")

if errors:
raise RuntimeError(
"Failed to close DheePlugin resources: " + "; ".join(errors)
)

# ------------------------------------------------------------------
# Framework export: OpenAI function calling
# ------------------------------------------------------------------
Expand Down
5 changes: 4 additions & 1 deletion dhee/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ async def handoff_checkpoint(request: CheckpointRequest):
bus = _get_bus()

# Find or create a session for this agent
session = bus.get_session(agent_id=request.agent_id)
session = bus.get_session(
agent_id=request.agent_id,
repo=request.repo_path,
)
if session is None:
sid = bus.save_session(
agent_id=request.agent_id,
Expand Down
Loading
Loading