Skip to content

Commit 2324937

Browse files
authored
Merge pull request #22 from Sankhya-AI/alpha
v3.0.1: Fix pip install — pure-Python fallbacks for dhee-accel
2 parents bda06bf + 20631a6 commit 2324937

41 files changed

Lines changed: 4481 additions & 5110 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,20 @@ pip install dhee[ollama,mcp] # Ollama (local inference, no API costs)
276276
```bash
277277
git clone https://github.com/Sankhya-AI/Dhee.git
278278
cd Dhee
279-
pip install -e ".[dev]"
279+
280+
./scripts/bootstrap_dev_env.sh
281+
source .venv-dhee/bin/activate
282+
283+
# optional if you prefer manual bootstrap:
284+
# python3 -m venv .venv-dhee
285+
# .venv-dhee/bin/python -m pip install -e ./dhee-accel -e ./engram-bus -e ".[dev]"
286+
280287
pytest
288+
289+
# live vendor-backed suites are explicit opt-in:
290+
# DHEE_RUN_LIVE_TESTS=1 pytest -q tests/test_e2e_all_features.py tests/test_power_packages.py
291+
292+
# manual smoke scripts live under scripts/manual/
281293
```
282294

283295
---

dhee/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
d.checkpoint("Fixed it", what_worked="git blame first")
1414
1515
Memory Classes:
16+
Engram — batteries-included memory interface with sensible defaults
1617
CoreMemory — lightweight: add/search/delete + decay (no LLM)
1718
SmartMemory — + echo encoding, categories, knowledge graph (needs LLM)
1819
FullMemory — + scenes, profiles, orchestration, cognition (everything)
@@ -22,7 +23,7 @@
2223
from dhee.memory.core import CoreMemory
2324
from dhee.memory.smart import SmartMemory
2425
from dhee.memory.main import FullMemory
25-
from dhee.simple import Dhee
26+
from dhee.simple import Dhee, Engram
2627
from dhee.adapters.base import DheePlugin
2728
from dhee.core.category import CategoryProcessor, Category, CategoryType, CategoryMatch
2829
from dhee.core.echo import EchoProcessor, EchoDepth, EchoResult
@@ -31,9 +32,10 @@
3132
# Default: CoreMemory (lightest, zero-config)
3233
Memory = CoreMemory
3334

34-
__version__ = "3.0.0"
35+
__version__ = "3.0.1"
3536
__all__ = [
3637
# Memory classes
38+
"Engram",
3739
"CoreMemory",
3840
"SmartMemory",
3941
"FullMemory",

dhee/adapters/base.py

Lines changed: 106 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
from pathlib import Path
3737
from typing import Any, Callable, Dict, List, Optional, Union
3838

39+
from dhee.checkpoint_runtime import run_checkpoint_common
40+
3941
logger = logging.getLogger(__name__)
4042

4143

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

123+
@property
124+
def memory(self):
125+
"""Expose the configured runtime memory engine for advanced integrations."""
126+
return self._engram.memory
127+
121128
# ------------------------------------------------------------------
122129
# Hook registry
123130
# ------------------------------------------------------------------
@@ -153,6 +160,13 @@ def _fire_hooks(self, event: str, data: Any) -> None:
153160
except Exception:
154161
logger.debug("Hook %s failed", event)
155162

163+
@staticmethod
164+
def _health_error(component: str, exc: Exception) -> Dict[str, str]:
165+
return {
166+
"component": component,
167+
"error": f"{type(exc).__name__}: {exc}",
168+
}
169+
156170
# ------------------------------------------------------------------
157171
# Tool 1: remember
158172
# ------------------------------------------------------------------
@@ -257,7 +271,7 @@ def context(
257271
hyper_ctx = self._buddhi.get_hyper_context(
258272
user_id=uid,
259273
task_description=task_description,
260-
memory=self._engram._memory,
274+
memory=self.memory,
261275
)
262276
if operational:
263277
result = hyper_ctx.to_operational_dict()
@@ -325,62 +339,29 @@ def checkpoint(
325339
if not what_worked:
326340
what_worked = outcome.get("what_worked")
327341

328-
result: Dict[str, Any] = {}
329-
score = max(0.0, min(1.0, float(outcome_score))) if outcome_score is not None else None
330-
331-
# 1. Session digest
332-
try:
333-
from dhee.core.kernel import save_session_digest
334-
digest = save_session_digest(
335-
task_summary=summary, agent_id=agent_id, repo=repo,
336-
status=status, decisions_made=decisions,
337-
files_touched=files_touched, todos_remaining=todos,
338-
)
339-
result["session_saved"] = True
340-
if isinstance(digest, dict):
341-
result["session_id"] = digest.get("session_id")
342-
except Exception:
343-
result["session_saved"] = False
344-
345-
# 2. Batch enrichment
346-
memory = self._engram._memory
347-
if hasattr(memory, "enrich_pending"):
348-
try:
349-
enrich_result = memory.enrich_pending(
350-
user_id=uid, batch_size=10, max_batches=5,
351-
)
352-
enriched = enrich_result.get("enriched_count", 0)
353-
if enriched > 0:
354-
result["memories_enriched"] = enriched
355-
except Exception:
356-
pass
357-
358-
# 3. Outcome recording
359-
if task_type and score is not None:
360-
insight = self._buddhi.record_outcome(
361-
user_id=uid, task_type=task_type, score=score,
362-
)
363-
result["outcome_recorded"] = True
364-
if insight:
365-
result["auto_insight"] = insight.to_dict()
366-
367-
# 4. Insight synthesis
368-
if any([what_worked, what_failed, key_decision]):
369-
insights = self._buddhi.reflect(
370-
user_id=uid, task_type=task_type or "general",
371-
what_worked=what_worked, what_failed=what_failed,
372-
key_decision=key_decision,
373-
outcome_score=score if score is not None else None,
374-
)
375-
result["insights_created"] = len(insights)
376-
377-
# 5. Intention storage
378-
if remember_to:
379-
intention = self._buddhi.store_intention(
380-
user_id=uid, description=remember_to,
381-
trigger_keywords=trigger_keywords,
382-
)
383-
result["intention_stored"] = intention.to_dict()
342+
result = run_checkpoint_common(
343+
logger=logger,
344+
log_prefix="Plugin checkpoint",
345+
user_id=uid,
346+
summary=summary,
347+
status=status,
348+
agent_id=agent_id,
349+
repo=repo,
350+
decisions=decisions,
351+
files_touched=files_touched,
352+
todos=todos,
353+
task_type=task_type,
354+
outcome_score=outcome_score,
355+
what_worked=what_worked,
356+
what_failed=what_failed,
357+
key_decision=key_decision,
358+
remember_to=remember_to,
359+
trigger_keywords=trigger_keywords,
360+
enrich_pending_fn=self._engram.enrich_pending,
361+
record_outcome_fn=self._buddhi.record_outcome,
362+
reflect_fn=self._buddhi.reflect,
363+
store_intention_fn=self._buddhi.store_intention,
364+
)
384365

385366
# 6. Episode closure (via kernel)
386367
ep_result = self._kernel.record_checkpoint_event(
@@ -441,8 +422,8 @@ def session_start(
441422
task_description=task_description or "session",
442423
task_type=task_type or "general",
443424
)
444-
except Exception:
445-
pass
425+
except Exception as exc:
426+
logger.warning("Session start episode initialization failed: %s", exc, exc_info=True)
446427

447428
ctx = self.context(task_description=task_description, user_id=uid)
448429
return self._render_system_prompt(ctx, task_description)
@@ -479,17 +460,19 @@ def _handle_tracker_signals(self, signals: Dict[str, Any], user_id: str) -> None
479460
if signals.get("needs_auto_checkpoint"):
480461
args = signals.get("auto_checkpoint_args", {})
481462
try:
482-
self.checkpoint(user_id=user_id, **args)
483-
except Exception:
484-
pass
463+
checkpoint_result = self.checkpoint(user_id=user_id, **args)
464+
for warning in checkpoint_result.get("warnings", []):
465+
logger.warning("Plugin auto-checkpoint warning: %s", warning)
466+
except Exception as exc:
467+
logger.warning("Plugin auto-checkpoint failed: %s", exc, exc_info=True)
485468

486469
# Auto-context for new session
487470
if signals.get("needs_auto_context"):
488471
task = signals.get("inferred_task")
489472
try:
490473
self.context(task_description=task, user_id=user_id)
491-
except Exception:
492-
pass
474+
except Exception as exc:
475+
logger.warning("Plugin auto-context failed: %s", exc, exc_info=True)
493476

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

507491
health["kernel"] = self._kernel.get_stats()
508492
health["buddhi"] = self._buddhi.get_stats()
@@ -513,16 +497,45 @@ def cognition_health(self, user_id: Optional[str] = None) -> Dict[str, Any]:
513497
low_util = [p for p in policies if p.utility < -0.2 and p.apply_count >= 3]
514498
if low_util:
515499
warnings.append(f"{len(low_util)} policies with negative utility")
500+
except Exception as exc:
501+
logger.warning(
502+
"Cognition health derivation failed for policies: %s",
503+
exc,
504+
exc_info=True,
505+
)
506+
errors.append(self._health_error("policies.get_user_policies", exc))
507+
508+
try:
516509
active_intentions = self._kernel.intentions.get_active(uid)
517510
if len(active_intentions) > 20:
518-
warnings.append(f"{len(active_intentions)} active intentions (consider cleanup)")
511+
warnings.append(
512+
f"{len(active_intentions)} active intentions (consider cleanup)"
513+
)
514+
except Exception as exc:
515+
logger.warning(
516+
"Cognition health derivation failed for intentions: %s",
517+
exc,
518+
exc_info=True,
519+
)
520+
errors.append(self._health_error("intentions.get_active", exc))
521+
522+
try:
519523
contradictions = self._kernel.beliefs.get_contradictions(uid)
520524
if len(contradictions) > 5:
521-
warnings.append(f"{len(contradictions)} unresolved belief contradictions")
522-
except Exception:
523-
pass
525+
warnings.append(
526+
f"{len(contradictions)} unresolved belief contradictions"
527+
)
528+
except Exception as exc:
529+
logger.warning(
530+
"Cognition health derivation failed for contradictions: %s",
531+
exc,
532+
exc_info=True,
533+
)
534+
errors.append(self._health_error("beliefs.get_contradictions", exc))
524535

525536
health["warnings"] = warnings
537+
if errors:
538+
health["errors"] = errors
526539
return health
527540

528541
# ------------------------------------------------------------------
@@ -640,14 +653,37 @@ def end_trajectory(
640653
# Store trajectory as memory for skill mining
641654
try:
642655
from dhee.skills.trajectory import TrajectoryStore
643-
store = TrajectoryStore(memory=self._engram._memory)
656+
store = TrajectoryStore(memory=self.memory)
644657
store.save(trajectory)
645658
result["stored"] = True
646-
except Exception:
659+
except Exception as exc:
660+
logger.warning("Trajectory persistence failed: %s", exc, exc_info=True)
647661
result["stored"] = False
662+
result["storage_error"] = str(exc)
648663

649664
return result
650665

666+
def close(self) -> None:
667+
"""Flush cognition state and release runtime resources."""
668+
errors: List[str] = []
669+
670+
try:
671+
self._buddhi.flush()
672+
except Exception as exc:
673+
logger.exception("DheePlugin close failed for buddhi.flush")
674+
errors.append(f"buddhi.flush: {type(exc).__name__}: {exc}")
675+
676+
try:
677+
self._engram.close()
678+
except Exception as exc:
679+
logger.exception("DheePlugin close failed for engram.close")
680+
errors.append(f"engram.close: {type(exc).__name__}: {exc}")
681+
682+
if errors:
683+
raise RuntimeError(
684+
"Failed to close DheePlugin resources: " + "; ".join(errors)
685+
)
686+
651687
# ------------------------------------------------------------------
652688
# Framework export: OpenAI function calling
653689
# ------------------------------------------------------------------

dhee/api/app.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ async def handoff_checkpoint(request: CheckpointRequest):
9595
bus = _get_bus()
9696

9797
# Find or create a session for this agent
98-
session = bus.get_session(agent_id=request.agent_id)
98+
session = bus.get_session(
99+
agent_id=request.agent_id,
100+
repo=request.repo_path,
101+
)
99102
if session is None:
100103
sid = bus.save_session(
101104
agent_id=request.agent_id,

0 commit comments

Comments
 (0)