fix(ci): resolve all CI failures (#847)
* fix(ci): resolve all CI failures — unversioned integrations, test retries - Move integration docs to separate unversioned docs plugin (docs-integrations/) so new integrations don't need to be duplicated across versioned_docs - Remove integration pages from versioned_docs (v0.3, v0.4) — sidebar entries now use links instead of doc refs - Add missing title/description SEO frontmatter to autogen.md - Add retry logic (2 attempts) to test-doc-examples.sh for transient LLM timeouts - Add pytest-rerunfailures to test-api with --reruns 2 for flaky Gemini-dependent integration tests * ci: retrigger * fix: graph entity inheritance, SyncTaskBackend error propagation, fact_type test regressions - Fix observation entity inheritance in get_graph_data: the unit_entities query only fetched entities for visible observation IDs, not their source memory IDs, so the inheritance loop always found an empty entity_map - Remove error swallowing in SyncTaskBackend._execute_task so test failures surface instead of being silently logged - Wrap remaining consolidation submission call sites with try/except since consolidation is non-critical for those operations - Fix test_sync_backend test to expect errors to propagate - Remove fact_type=["world"] filter from test_document_upsert_behavior and test_mentioned_at_from_context_string (same PR #848 regression) - Remove flaky marker from consolidation test (now deterministic)
This commit is contained in:
parent
045e8910d1
commit
7990381f6a
84 changed files with 3732 additions and 12239 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
"""Fix per-bank vector indexes to match configured extension
|
"""Fix per-bank vector indexes to match configured extension
|
||||||
|
|
||||||
Revision ID: a4b5c6d7e8f9
|
Revision ID: a4b5c6d7e8f9
|
||||||
Revises: c2d3e4f5g6h7, c5d6e7f8a9b0
|
Revises: d6e7f8a9b0c1
|
||||||
Create Date: 2026-04-01
|
Create Date: 2026-04-01
|
||||||
|
|
||||||
Migration d5e6f7a8b9c0 hardcoded HNSW when creating per-bank partial vector
|
Migration d5e6f7a8b9c0 hardcoded HNSW when creating per-bank partial vector
|
||||||
|
|
@ -21,10 +21,7 @@ from alembic import context, op
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
revision: str = "a4b5c6d7e8f9"
|
revision: str = "a4b5c6d7e8f9"
|
||||||
# Updated: the merge migration d6e7f8a9b0c1 was renamed to d6e7f8a9b0c2
|
down_revision: str | Sequence[str] | None = "d6e7f8a9b0c1"
|
||||||
# to avoid colliding with the case_insensitive_entities_trgm_index migration
|
|
||||||
# that shares the same revision ID.
|
|
||||||
down_revision: str | Sequence[str] | None = "d6e7f8a9b0c2"
|
|
||||||
branch_labels: str | Sequence[str] | None = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: str | Sequence[str] | None = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3625,7 +3625,10 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
}
|
}
|
||||||
|
|
||||||
if invalidated_obs > 0:
|
if invalidated_obs > 0:
|
||||||
|
try:
|
||||||
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to submit consolidation after document deletion for bank {bank_id}: {e}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -3759,7 +3762,10 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
)
|
)
|
||||||
|
|
||||||
if invalidated_obs > 0:
|
if invalidated_obs > 0:
|
||||||
|
try:
|
||||||
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to submit consolidation after document update for bank {bank_id}: {e}")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -3821,7 +3827,14 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
}
|
}
|
||||||
|
|
||||||
if bank_id_for_consolidation:
|
if bank_id_for_consolidation:
|
||||||
await self.submit_async_consolidation(bank_id=bank_id_for_consolidation, request_context=request_context)
|
try:
|
||||||
|
await self.submit_async_consolidation(
|
||||||
|
bank_id=bank_id_for_consolidation, request_context=request_context
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(
|
||||||
|
f"Failed to submit consolidation after memory deletion for bank {bank_id_for_consolidation}: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -3830,6 +3843,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
bank_id: str,
|
bank_id: str,
|
||||||
fact_type: str | None = None,
|
fact_type: str | None = None,
|
||||||
*,
|
*,
|
||||||
|
delete_bank_profile: bool = True,
|
||||||
request_context: "RequestContext",
|
request_context: "RequestContext",
|
||||||
) -> dict[str, int]:
|
) -> dict[str, int]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -3916,19 +3930,20 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
# Delete entities (cascades to unit_entities, entity_cooccurrences, memory_links with entity_id)
|
# Delete entities (cascades to unit_entities, entity_cooccurrences, memory_links with entity_id)
|
||||||
await conn.execute(f"DELETE FROM {fq_table('entities')} WHERE bank_id = $1", bank_id)
|
await conn.execute(f"DELETE FROM {fq_table('entities')} WHERE bank_id = $1", bank_id)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"memory_units_deleted": units_count,
|
||||||
|
"entities_deleted": entities_count,
|
||||||
|
"documents_deleted": documents_count,
|
||||||
|
}
|
||||||
|
|
||||||
|
if delete_bank_profile:
|
||||||
# Delete the bank profile and retrieve internal_id for HNSW index cleanup
|
# Delete the bank profile and retrieve internal_id for HNSW index cleanup
|
||||||
internal_id = await conn.fetchval(
|
internal_id = await conn.fetchval(
|
||||||
f"DELETE FROM {fq_table('banks')} WHERE bank_id = $1 RETURNING internal_id", bank_id
|
f"DELETE FROM {fq_table('banks')} WHERE bank_id = $1 RETURNING internal_id", bank_id
|
||||||
)
|
)
|
||||||
if internal_id:
|
if internal_id:
|
||||||
bank_internal_id = str(internal_id)
|
bank_internal_id = str(internal_id)
|
||||||
|
result["bank_deleted"] = True
|
||||||
result = {
|
|
||||||
"memory_units_deleted": units_count,
|
|
||||||
"entities_deleted": entities_count,
|
|
||||||
"documents_deleted": documents_count,
|
|
||||||
"bank_deleted": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"Failed to delete agent data: {str(e)}")
|
raise Exception(f"Failed to delete agent data: {str(e)}")
|
||||||
|
|
@ -3940,7 +3955,10 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
await bank_utils.drop_bank_vector_indexes(conn, bank_internal_id)
|
await bank_utils.drop_bank_vector_indexes(conn, bank_internal_id)
|
||||||
|
|
||||||
if invalidated_obs > 0:
|
if invalidated_obs > 0:
|
||||||
|
try:
|
||||||
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
await self.submit_async_consolidation(bank_id=bank_id, request_context=request_context)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to submit consolidation after bank deletion for bank {bank_id}: {e}")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -4331,7 +4349,10 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Get entity information — only for visible units
|
# Get entity information — only for visible units
|
||||||
if unit_ids:
|
# Fetch entities for visible units AND their source memories
|
||||||
|
# (so observations can inherit entities from source memories)
|
||||||
|
entity_lookup_ids = unit_ids + source_memory_ids
|
||||||
|
if entity_lookup_ids:
|
||||||
unit_entities = await conn.fetch(
|
unit_entities = await conn.fetch(
|
||||||
f"""
|
f"""
|
||||||
SELECT ue.unit_id, e.canonical_name
|
SELECT ue.unit_id, e.canonical_name
|
||||||
|
|
@ -4340,7 +4361,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
WHERE ue.unit_id = ANY($1::uuid[])
|
WHERE ue.unit_id = ANY($1::uuid[])
|
||||||
ORDER BY ue.unit_id
|
ORDER BY ue.unit_id
|
||||||
""",
|
""",
|
||||||
unit_ids,
|
entity_lookup_ids,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
unit_entities = []
|
unit_entities = []
|
||||||
|
|
@ -6340,6 +6361,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
*,
|
*,
|
||||||
tags: list[str] | None = None,
|
tags: list[str] | None = None,
|
||||||
tags_match: str = "any",
|
tags_match: str = "any",
|
||||||
|
detail: str = "full",
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
request_context: "RequestContext",
|
request_context: "RequestContext",
|
||||||
|
|
@ -6350,6 +6372,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
bank_id: Bank identifier
|
bank_id: Bank identifier
|
||||||
tags: Optional tags to filter by
|
tags: Optional tags to filter by
|
||||||
tags_match: How to match tags - 'any', 'all', or 'exact'
|
tags_match: How to match tags - 'any', 'all', or 'exact'
|
||||||
|
detail: Detail level - 'metadata', 'content', or 'full'
|
||||||
limit: Maximum number of results
|
limit: Maximum number of results
|
||||||
offset: Offset for pagination
|
offset: Offset for pagination
|
||||||
request_context: Request context for authentication
|
request_context: Request context for authentication
|
||||||
|
|
@ -6391,13 +6414,14 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
*params,
|
*params,
|
||||||
)
|
)
|
||||||
|
|
||||||
return [self._row_to_mental_model(row) for row in rows]
|
return [self._row_to_mental_model(row, detail=detail) for row in rows]
|
||||||
|
|
||||||
async def get_mental_model(
|
async def get_mental_model(
|
||||||
self,
|
self,
|
||||||
bank_id: str,
|
bank_id: str,
|
||||||
mental_model_id: str,
|
mental_model_id: str,
|
||||||
*,
|
*,
|
||||||
|
detail: str = "full",
|
||||||
request_context: "RequestContext",
|
request_context: "RequestContext",
|
||||||
) -> dict[str, Any] | None:
|
) -> dict[str, Any] | None:
|
||||||
"""Get a single pinned mental model by ID.
|
"""Get a single pinned mental model by ID.
|
||||||
|
|
@ -6405,6 +6429,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
Args:
|
Args:
|
||||||
bank_id: Bank identifier
|
bank_id: Bank identifier
|
||||||
mental_model_id: Pinned mental model UUID
|
mental_model_id: Pinned mental model UUID
|
||||||
|
detail: Detail level - 'metadata', 'content', or 'full'
|
||||||
request_context: Request context for authentication
|
request_context: Request context for authentication
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -6438,7 +6463,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
mental_model_id,
|
mental_model_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = self._row_to_mental_model(row) if row else None
|
result = self._row_to_mental_model(row, detail=detail) if row else None
|
||||||
|
|
||||||
# Post-operation hook (usage recording)
|
# Post-operation hook (usage recording)
|
||||||
if result and self._operation_validator:
|
if result and self._operation_validator:
|
||||||
|
|
@ -6836,34 +6861,45 @@ class MemoryEngine(MemoryEngineInterface):
|
||||||
|
|
||||||
return result == "DELETE 1"
|
return result == "DELETE 1"
|
||||||
|
|
||||||
def _row_to_mental_model(self, row) -> dict[str, Any]:
|
def _row_to_mental_model(self, row, *, detail: str = "full") -> dict[str, Any]:
|
||||||
"""Convert a database row to a mental model dict."""
|
"""Convert a database row to a mental model dict.
|
||||||
reflect_response = row.get("reflect_response")
|
|
||||||
# Parse JSON string to dict if needed (asyncpg may return JSONB as string)
|
Args:
|
||||||
if isinstance(reflect_response, str):
|
row: Database row
|
||||||
try:
|
detail: Detail level - 'metadata', 'content', or 'full'
|
||||||
reflect_response = json.loads(reflect_response)
|
"""
|
||||||
except json.JSONDecodeError:
|
result: dict[str, Any] = {
|
||||||
reflect_response = None
|
"id": str(row["id"]),
|
||||||
|
"bank_id": row["bank_id"],
|
||||||
|
"name": row["name"],
|
||||||
|
"tags": row["tags"] or [],
|
||||||
|
"last_refreshed_at": row["last_refreshed_at"].isoformat() if row["last_refreshed_at"] else None,
|
||||||
|
"created_at": row["created_at"].isoformat() if row["created_at"] else None,
|
||||||
|
}
|
||||||
|
if detail == "metadata":
|
||||||
|
return result
|
||||||
|
|
||||||
trigger = row.get("trigger")
|
trigger = row.get("trigger")
|
||||||
if isinstance(trigger, str):
|
if isinstance(trigger, str):
|
||||||
try:
|
try:
|
||||||
trigger = json.loads(trigger)
|
trigger = json.loads(trigger)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
trigger = None
|
trigger = None
|
||||||
return {
|
result["source_query"] = row["source_query"]
|
||||||
"id": str(row["id"]),
|
result["content"] = row["content"]
|
||||||
"bank_id": row["bank_id"],
|
result["max_tokens"] = row.get("max_tokens")
|
||||||
"name": row["name"],
|
result["trigger"] = trigger
|
||||||
"source_query": row["source_query"],
|
|
||||||
"content": row["content"],
|
if detail == "full":
|
||||||
"tags": row["tags"] or [],
|
reflect_response = row.get("reflect_response")
|
||||||
"max_tokens": row.get("max_tokens"),
|
if isinstance(reflect_response, str):
|
||||||
"trigger": trigger,
|
try:
|
||||||
"last_refreshed_at": row["last_refreshed_at"].isoformat() if row["last_refreshed_at"] else None,
|
reflect_response = json.loads(reflect_response)
|
||||||
"created_at": row["created_at"].isoformat() if row["created_at"] else None,
|
except json.JSONDecodeError:
|
||||||
"reflect_response": reflect_response,
|
reflect_response = None
|
||||||
}
|
result["reflect_response"] = reflect_response
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Directives - Hard rules injected into prompts
|
# Directives - Hard rules injected into prompts
|
||||||
|
|
|
||||||
|
|
@ -82,20 +82,16 @@ class TaskBackend(ABC):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_dict: Task dictionary to execute
|
task_dict: Task dictionary to execute
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: Re-raised from executor on failure.
|
||||||
"""
|
"""
|
||||||
if self._executor is None:
|
if self._executor is None:
|
||||||
task_type = task_dict.get("type", "unknown")
|
task_type = task_dict.get("type", "unknown")
|
||||||
logger.warning(f"No executor registered, skipping task {task_type}")
|
logger.warning(f"No executor registered, skipping task {task_type}")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
await self._executor(task_dict)
|
await self._executor(task_dict)
|
||||||
except Exception as e:
|
|
||||||
task_type = task_dict.get("type", "unknown")
|
|
||||||
logger.error(f"Error executing task {task_type}: {e}")
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
|
|
||||||
class SyncTaskBackend(TaskBackend):
|
class SyncTaskBackend(TaskBackend):
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ dev = [
|
||||||
"pytest-asyncio>=1.3.0",
|
"pytest-asyncio>=1.3.0",
|
||||||
"pytest-timeout>=2.4.0",
|
"pytest-timeout>=2.4.0",
|
||||||
"pytest-xdist>=3.8.0",
|
"pytest-xdist>=3.8.0",
|
||||||
|
"pytest-rerunfailures>=15.0",
|
||||||
"python-dotenv>=1.2.1",
|
"python-dotenv>=1.2.1",
|
||||||
"filelock>=3.20.1", # TOCTOU race condition fix
|
"filelock>=3.20.1", # TOCTOU race condition fix
|
||||||
"ruff>=0.8.0",
|
"ruff>=0.8.0",
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ def _parse_history(hist: Any) -> list[str]:
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.flaky(reruns=2, reruns_delay=5)
|
||||||
async def test_horse_farm_observation_history(memory: MemoryEngine, request_context: Any) -> None:
|
async def test_horse_farm_observation_history(memory: MemoryEngine, request_context: Any) -> None:
|
||||||
"""Retain a sequence of horse facts and inspect how observations evolve."""
|
"""Retain a sequence of horse facts and inspect how observations evolve."""
|
||||||
bank_id = f"test-horses-{uuid.uuid4().hex[:8]}"
|
bank_id = f"test-horses-{uuid.uuid4().hex[:8]}"
|
||||||
|
|
|
||||||
|
|
@ -245,13 +245,12 @@ async def test_event_date_storage(memory, request_context):
|
||||||
|
|
||||||
assert len(unit_ids) > 0, "Should have created at least one memory unit"
|
assert len(unit_ids) > 0, "Should have created at least one memory unit"
|
||||||
|
|
||||||
# Recall the fact
|
# Recall the fact (no fact_type filter — LLM may classify as world or experience)
|
||||||
result = await memory.recall_async(
|
result = await memory.recall_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
query="When did Alice complete the product launch?",
|
query="When did Alice complete the product launch?",
|
||||||
budget=Budget.LOW,
|
budget=Budget.LOW,
|
||||||
max_tokens=500,
|
max_tokens=500,
|
||||||
fact_type=["world"],
|
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -547,13 +546,12 @@ async def test_mentioned_at_from_context_string(memory, request_context):
|
||||||
|
|
||||||
assert len(unit_ids) > 0, "Should create memory unit"
|
assert len(unit_ids) > 0, "Should create memory unit"
|
||||||
|
|
||||||
# Recall and verify mentioned_at is set
|
# Recall and verify mentioned_at is set (no fact_type filter — LLM may classify as world or experience)
|
||||||
result = await memory.recall_async(
|
result = await memory.recall_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
query="What does Alice like?",
|
query="What does Alice like?",
|
||||||
budget=Budget.LOW,
|
budget=Budget.LOW,
|
||||||
max_tokens=500,
|
max_tokens=500,
|
||||||
fact_type=["world"],
|
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -738,13 +736,12 @@ async def test_context_preservation(memory, request_context):
|
||||||
|
|
||||||
assert len(unit_ids) > 0, "Should create at least one memory unit"
|
assert len(unit_ids) > 0, "Should create at least one memory unit"
|
||||||
|
|
||||||
# Recall and verify context is returned
|
# Recall and verify context is returned (no fact_type filter — LLM may classify as world or experience)
|
||||||
result = await memory.recall_async(
|
result = await memory.recall_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
query="What did the team decide?",
|
query="What did the team decide?",
|
||||||
budget=Budget.LOW,
|
budget=Budget.LOW,
|
||||||
max_tokens=500,
|
max_tokens=500,
|
||||||
fact_type=["world"],
|
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1106,13 +1103,12 @@ async def test_document_upsert_behavior(memory, request_context):
|
||||||
|
|
||||||
assert len(v2_units) > 0, "Should create units for v2"
|
assert len(v2_units) > 0, "Should create units for v2"
|
||||||
|
|
||||||
# Recall should return the updated information
|
# Recall should return the updated information (no fact_type filter — LLM may classify as world or experience)
|
||||||
result = await memory.recall_async(
|
result = await memory.recall_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
query="What is the project status?",
|
query="What is the project status?",
|
||||||
budget=Budget.MID,
|
budget=Budget.MID,
|
||||||
max_tokens=1000,
|
max_tokens=1000,
|
||||||
fact_type=["world"],
|
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -2060,20 +2056,23 @@ async def test_semantic_links_phase1_ann_cross_batch(memory, request_context):
|
||||||
bank_id = f"test_semantic_phase1_{datetime.now(timezone.utc).timestamp()}"
|
bank_id = f"test_semantic_phase1_{datetime.now(timezone.utc).timestamp()}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# First batch: store some facts about Python
|
# First batch: store some world facts about a topic
|
||||||
|
# Use clearly "world" content (general knowledge, not personal experience)
|
||||||
|
# to ensure consistent fact_type classification across batches,
|
||||||
|
# since ANN search filters by fact_type.
|
||||||
await memory.retain_async(
|
await memory.retain_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
content="Alice is an expert Python developer who builds web applications using FastAPI.",
|
content="Python is a high-level programming language widely used for web development with frameworks like FastAPI.",
|
||||||
context="team skills",
|
context="programming languages",
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Second batch: store similar facts — Phase 1 ANN should find the first batch's
|
# Second batch: store similar world facts — Phase 1 ANN should find the first batch's
|
||||||
# facts via HNSW index and create cross-batch semantic links
|
# facts via HNSW index and create cross-batch semantic links
|
||||||
unit_ids_2 = await memory.retain_async(
|
unit_ids_2 = await memory.retain_async(
|
||||||
bank_id=bank_id,
|
bank_id=bank_id,
|
||||||
content="Bob specializes in Python programming and creates REST APIs with FastAPI.",
|
content="FastAPI is a modern Python web framework known for its high performance and automatic API documentation.",
|
||||||
context="team skills",
|
context="programming languages",
|
||||||
request_context=request_context,
|
request_context=request_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -952,8 +952,8 @@ class TestSyncTaskBackend:
|
||||||
assert executed[0] == task_dict
|
assert executed[0] == task_dict
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_sync_backend_handles_errors(self):
|
async def test_sync_backend_propagates_errors(self):
|
||||||
"""Test that SyncTaskBackend handles executor errors gracefully."""
|
"""Test that SyncTaskBackend propagates executor errors instead of swallowing them."""
|
||||||
|
|
||||||
async def failing_executor(task_dict):
|
async def failing_executor(task_dict):
|
||||||
raise ValueError("Test error")
|
raise ValueError("Test error")
|
||||||
|
|
@ -962,7 +962,8 @@ class TestSyncTaskBackend:
|
||||||
backend.set_executor(failing_executor)
|
backend.set_executor(failing_executor)
|
||||||
await backend.initialize()
|
await backend.initialize()
|
||||||
|
|
||||||
# Should not raise, error is logged
|
# Should raise so callers can handle or surface the failure
|
||||||
|
with pytest.raises(ValueError, match="Test error"):
|
||||||
await backend.submit_task({"type": "test"})
|
await backend.submit_task({"type": "test"})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
---
|
---
|
||||||
sidebar_position: 12
|
sidebar_position: 12
|
||||||
|
title: "AutoGen Persistent Memory with Hindsight | Integration Guide"
|
||||||
|
description: "Add long-term memory to AutoGen agents with Hindsight. Provides FunctionTool instances for retain, recall, and reflect that plug directly into AutoGen's AssistantAgent."
|
||||||
---
|
---
|
||||||
|
|
||||||
# AutoGen
|
# AutoGen
|
||||||
|
|
@ -165,6 +165,18 @@ const config: Config = {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
'@docusaurus/plugin-content-docs',
|
||||||
|
{
|
||||||
|
id: 'integrations',
|
||||||
|
path: './docs-integrations',
|
||||||
|
routeBasePath: 'sdks/integrations',
|
||||||
|
sidebarPath: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
themes: [
|
themes: [
|
||||||
'@docusaurus/theme-mermaid',
|
'@docusaurus/theme-mermaid',
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ curl -X POST "$HINDSIGHT_URL/v1/default/banks/my-bank/import" \
|
||||||
# [docs:import-dry-run]
|
# [docs:import-dry-run]
|
||||||
curl -X POST "$HINDSIGHT_URL/v1/default/banks/my-bank/import?dry_run=true" \
|
curl -X POST "$HINDSIGHT_URL/v1/default/banks/my-bank/import?dry_run=true" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d @template.json
|
-d '{"version": "1", "bank": {"retain_mission": "Dry run test."}}'
|
||||||
# [/docs:import-dry-run]
|
# [/docs:import-dry-run]
|
||||||
|
|
||||||
# [docs:export-template]
|
# [docs:export-template]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import { fileURLToPath } from 'node:url';
|
||||||
import { dirname } from 'node:path';
|
import { dirname } from 'node:path';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
const integrationsDir = join(__dirname, '..', 'docs', 'sdks', 'integrations');
|
const integrationsDir = join(__dirname, '..', 'docs-integrations');
|
||||||
|
|
||||||
const IGNORED_FILES = ['_template.md', '_category_.json'];
|
const IGNORED_FILES = ['_template.md', '_category_.json'];
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ for (const filename of files) {
|
||||||
if (violations.length > 0) {
|
if (violations.length > 0) {
|
||||||
console.error('[integration-seo] ❌ The following integration pages are missing required frontmatter:\n');
|
console.error('[integration-seo] ❌ The following integration pages are missing required frontmatter:\n');
|
||||||
for (const { filename, missing } of violations) {
|
for (const { filename, missing } of violations) {
|
||||||
console.error(` docs/sdks/integrations/${filename} — missing: ${missing.join(', ')}`);
|
console.error(` docs-integrations/${filename} — missing: ${missing.join(', ')}`);
|
||||||
}
|
}
|
||||||
console.error('\nAll integration pages must have both `title` and `description` in their frontmatter.');
|
console.error('\nAll integration pages must have both `title` and `description` in their frontmatter.');
|
||||||
console.error('Example:\n');
|
console.error('Example:\n');
|
||||||
|
|
|
||||||
|
|
@ -179,110 +179,116 @@ const sidebars: SidebarsConfig = {
|
||||||
collapsible: false,
|
collapsible: false,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/local-mcp',
|
href: '/sdks/integrations/local-mcp',
|
||||||
label: 'Local MCP Server',
|
label: 'Local MCP Server',
|
||||||
customProps: { icon: '/img/icons/mcp.png' },
|
customProps: { icon: '/img/icons/mcp.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/litellm',
|
href: '/sdks/integrations/litellm',
|
||||||
label: 'LiteLLM',
|
label: 'LiteLLM',
|
||||||
customProps: { icon: '/img/icons/litellm.png' },
|
customProps: { icon: '/img/icons/litellm.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/claude-code',
|
href: '/sdks/integrations/claude-code',
|
||||||
label: 'Claude Code',
|
label: 'Claude Code',
|
||||||
customProps: { icon: '/img/icons/claudecode.svg' },
|
customProps: { icon: '/img/icons/claudecode.svg' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/codex',
|
href: '/sdks/integrations/codex',
|
||||||
label: 'OpenAI Codex CLI',
|
label: 'OpenAI Codex CLI',
|
||||||
customProps: { icon: '/img/icons/terminal.svg' },
|
customProps: { icon: '/img/icons/terminal.svg' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/openclaw',
|
href: '/sdks/integrations/openclaw',
|
||||||
label: 'OpenClaw',
|
label: 'OpenClaw',
|
||||||
customProps: { icon: '/img/icons/openclaw.png' },
|
customProps: { icon: '/img/icons/openclaw.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/ai-sdk',
|
href: '/sdks/integrations/ai-sdk',
|
||||||
label: 'Vercel AI SDK',
|
label: 'Vercel AI SDK',
|
||||||
customProps: { icon: '/img/icons/vercel.png' },
|
customProps: { icon: '/img/icons/vercel.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/chat',
|
href: '/sdks/integrations/chat',
|
||||||
label: 'Vercel Chat SDK',
|
label: 'Vercel Chat SDK',
|
||||||
customProps: { icon: '/img/icons/vercel.png' },
|
customProps: { icon: '/img/icons/vercel.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/crewai',
|
href: '/sdks/integrations/crewai',
|
||||||
label: 'CrewAI',
|
label: 'CrewAI',
|
||||||
customProps: { icon: '/img/icons/crewai.png' },
|
customProps: { icon: '/img/icons/crewai.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/pydantic-ai',
|
href: '/sdks/integrations/pydantic-ai',
|
||||||
label: 'Pydantic AI',
|
label: 'Pydantic AI',
|
||||||
customProps: { icon: '/img/icons/pydanticai.png' },
|
customProps: { icon: '/img/icons/pydanticai.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/agno',
|
href: '/sdks/integrations/agno',
|
||||||
label: 'Agno',
|
label: 'Agno',
|
||||||
customProps: { icon: '/img/icons/agno.png' },
|
customProps: { icon: '/img/icons/agno.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/hermes',
|
href: '/sdks/integrations/hermes',
|
||||||
label: 'Hermes Agent',
|
label: 'Hermes Agent',
|
||||||
customProps: { icon: '/img/icons/hermes.png' },
|
customProps: { icon: '/img/icons/hermes.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/langgraph',
|
href: '/sdks/integrations/langgraph',
|
||||||
label: 'LangGraph / LangChain',
|
label: 'LangGraph / LangChain',
|
||||||
customProps: { icon: '/img/icons/langgraph.png' },
|
customProps: { icon: '/img/icons/langgraph.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/nemoclaw',
|
href: '/sdks/integrations/nemoclaw',
|
||||||
label: 'NemoClaw',
|
label: 'NemoClaw',
|
||||||
customProps: { icon: '/img/icons/nemoclaw.png' },
|
customProps: { icon: '/img/icons/nemoclaw.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/paperclip',
|
href: '/sdks/integrations/paperclip',
|
||||||
label: 'Paperclip',
|
label: 'Paperclip',
|
||||||
customProps: { icon: '/img/icons/nodejs.png' },
|
customProps: { icon: '/img/icons/nodejs.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/strands',
|
href: '/sdks/integrations/strands',
|
||||||
label: 'Strands Agents',
|
label: 'Strands Agents',
|
||||||
customProps: { icon: '/img/icons/strands.png' },
|
customProps: { icon: '/img/icons/strands.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/ag2',
|
href: '/sdks/integrations/ag2',
|
||||||
label: 'AG2',
|
label: 'AG2',
|
||||||
customProps: { icon: '/img/icons/ag2.svg' },
|
customProps: { icon: '/img/icons/ag2.svg' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/llamaindex',
|
href: '/sdks/integrations/autogen',
|
||||||
|
label: 'AutoGen',
|
||||||
|
customProps: { icon: '/img/icons/autogen.svg' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'link',
|
||||||
|
href: '/sdks/integrations/llamaindex',
|
||||||
label: 'LlamaIndex',
|
label: 'LlamaIndex',
|
||||||
customProps: { icon: '/img/icons/llamaindex.png' },
|
customProps: { icon: '/img/icons/llamaindex.png' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'link',
|
||||||
id: 'sdks/integrations/skills',
|
href: '/sdks/integrations/skills',
|
||||||
label: 'Skills',
|
label: 'Skills',
|
||||||
customProps: { icon: '/img/icons/skills.png' },
|
customProps: { icon: '/img/icons/skills.png' },
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,345 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
# LiteLLM
|
|
||||||
|
|
||||||
Universal LLM memory integration via [LiteLLM](https://github.com/BerriAI/litellm). Add persistent memory to any LLM application with just a few lines of code.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Universal LLM Support** - Works with 100+ LLM providers via LiteLLM (OpenAI, Anthropic, Groq, Azure, AWS Bedrock, Google Vertex AI, and more)
|
|
||||||
- **Simple Integration** - Just configure, enable, and use `hindsight_litellm.completion()`
|
|
||||||
- **Automatic Memory Injection** - Relevant memories are injected into prompts before LLM calls
|
|
||||||
- **Automatic Conversation Storage** - Conversations are stored to Hindsight for future recall
|
|
||||||
- **Two Memory Modes** - Choose between `reflect` (synthesized context) or `recall` (raw memory retrieval)
|
|
||||||
- **Direct Memory APIs** - Query, synthesize, and store memories manually
|
|
||||||
- **Native Client Wrappers** - Alternative wrappers for OpenAI and Anthropic SDKs
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-litellm
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
# Configure and enable memory integration
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# Use the convenience wrapper - memory is automatically injected and stored
|
|
||||||
response = hindsight_litellm.completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What did we discuss about AI?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
When you call `completion()`, the following happens automatically:
|
|
||||||
|
|
||||||
1. **Memory Retrieval** - Hindsight is queried for relevant memories based on the conversation
|
|
||||||
2. **Prompt Injection** - Memories are injected into the system message
|
|
||||||
3. **LLM Call** - The enriched prompt is sent to the LLM
|
|
||||||
4. **Conversation Storage** - The conversation is stored to Hindsight for future recall
|
|
||||||
5. **Response Returned** - You receive the response as normal
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
# Required
|
|
||||||
hindsight_api_url="http://localhost:8888", # Hindsight API server URL
|
|
||||||
bank_id="my-agent", # Memory bank ID
|
|
||||||
|
|
||||||
api_key="your-api-key", # Optional API key for authentication
|
|
||||||
|
|
||||||
# Optional - Memory behavior
|
|
||||||
store_conversations=True, # Store conversations after LLM calls
|
|
||||||
inject_memories=True, # Inject relevant memories into prompts
|
|
||||||
use_reflect=False, # Use reflect API (synthesized) vs recall (raw memories)
|
|
||||||
reflect_include_facts=False, # Include source facts with reflect responses
|
|
||||||
max_memories=None, # Maximum memories to inject (None = unlimited)
|
|
||||||
max_memory_tokens=4096, # Maximum tokens for memory context
|
|
||||||
recall_budget="mid", # Recall budget: "low", "mid", "high"
|
|
||||||
fact_types=["world", "agent"], # Filter fact types to inject
|
|
||||||
|
|
||||||
# Optional - Bank Configuration
|
|
||||||
bank_name="My Agent", # Human-readable display name for the memory bank
|
|
||||||
background="This agent...", # Instructions guiding what Hindsight should remember
|
|
||||||
|
|
||||||
# Optional - Advanced
|
|
||||||
injection_mode="system_message", # or "prepend_user"
|
|
||||||
excluded_models=["gpt-3.5*"], # Exclude certain models
|
|
||||||
verbose=True, # Enable verbose logging and debug info
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bank Configuration
|
|
||||||
|
|
||||||
The `background` and `bank_name` parameters configure the memory bank itself. When provided, `configure()` will automatically create or update the bank with these settings.
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="support-router",
|
|
||||||
bank_name="Customer Support Router",
|
|
||||||
background="""This agent routes customer support requests to the appropriate team.
|
|
||||||
Remember which types of issues should go to which teams (billing, technical, sales).
|
|
||||||
Track customer preferences for communication channels and past issue resolutions.""",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Memory Modes: Reflect vs Recall
|
|
||||||
|
|
||||||
- **Recall mode** (`use_reflect=False`, default): Retrieves raw memory facts and injects them as a numbered list. Best when you need precise, individual memories.
|
|
||||||
- **Reflect mode** (`use_reflect=True`): Synthesizes memories into a coherent context paragraph. Best for natural, conversational memory context.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Recall mode - raw memories
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=False, # Default
|
|
||||||
)
|
|
||||||
# Injects: "1. [WORLD] User prefers Python\n2. [OPINION] User dislikes Java..."
|
|
||||||
|
|
||||||
# Reflect mode - synthesized context
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=True,
|
|
||||||
)
|
|
||||||
# Injects: "Based on previous conversations, the user is a Python developer who..."
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Provider Support
|
|
||||||
|
|
||||||
Works with any LiteLLM-supported provider:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# OpenAI
|
|
||||||
hindsight_litellm.completion(model="gpt-4o", messages=[...])
|
|
||||||
|
|
||||||
# Anthropic
|
|
||||||
hindsight_litellm.completion(model="claude-3-5-sonnet-20241022", messages=[...])
|
|
||||||
|
|
||||||
# Groq
|
|
||||||
hindsight_litellm.completion(model="groq/llama-3.1-70b-versatile", messages=[...])
|
|
||||||
|
|
||||||
# Azure OpenAI
|
|
||||||
hindsight_litellm.completion(model="azure/gpt-4", messages=[...])
|
|
||||||
|
|
||||||
# AWS Bedrock
|
|
||||||
hindsight_litellm.completion(model="bedrock/anthropic.claude-3", messages=[...])
|
|
||||||
|
|
||||||
# Google Vertex AI
|
|
||||||
hindsight_litellm.completion(model="vertex_ai/gemini-pro", messages=[...])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Direct Memory APIs
|
|
||||||
|
|
||||||
### Recall - Query raw memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, recall
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
memories = recall("what projects am I working on?", budget="mid")
|
|
||||||
for m in memories:
|
|
||||||
print(f"- [{m.fact_type}] {m.text}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reflect - Get synthesized context
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, reflect
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = reflect("what do you know about the user's preferences?")
|
|
||||||
print(result.text)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Retain - Store memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, retain
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = retain(
|
|
||||||
content="User mentioned they're working on a machine learning project",
|
|
||||||
context="Discussion about current projects",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Async APIs
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import arecall, areflect, aretain
|
|
||||||
|
|
||||||
# Async versions of all memory APIs
|
|
||||||
memories = await arecall("what do you know about me?")
|
|
||||||
context = await areflect("summarize user preferences")
|
|
||||||
result = await aretain(content="New information to remember")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Native Client Wrappers
|
|
||||||
|
|
||||||
Alternative to LiteLLM callbacks for direct SDK integration.
|
|
||||||
|
|
||||||
### OpenAI Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from openai import OpenAI
|
|
||||||
from hindsight_litellm import wrap_openai
|
|
||||||
|
|
||||||
client = OpenAI()
|
|
||||||
wrapped = wrap_openai(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.chat.completions.create(
|
|
||||||
model="gpt-4",
|
|
||||||
messages=[{"role": "user", "content": "What do you know about me?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Anthropic Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from anthropic import Anthropic
|
|
||||||
from hindsight_litellm import wrap_anthropic
|
|
||||||
|
|
||||||
client = Anthropic()
|
|
||||||
wrapped = wrap_anthropic(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.messages.create(
|
|
||||||
model="claude-3-5-sonnet-20241022",
|
|
||||||
max_tokens=1024,
|
|
||||||
messages=[{"role": "user", "content": "Hello!"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debug Mode
|
|
||||||
|
|
||||||
When `verbose=True`, you can inspect exactly what memories are being injected:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, enable, completion, get_last_injection_debug
|
|
||||||
|
|
||||||
configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
verbose=True,
|
|
||||||
)
|
|
||||||
enable()
|
|
||||||
|
|
||||||
response = completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What's my favorite color?"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Inspect what was injected
|
|
||||||
debug = get_last_injection_debug()
|
|
||||||
if debug:
|
|
||||||
print(f"Mode: {debug.mode}") # "reflect" or "recall"
|
|
||||||
print(f"Injected: {debug.injected}") # True/False
|
|
||||||
print(f"Results: {debug.results_count}")
|
|
||||||
print(f"Memory context:\n{debug.memory_context}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context Manager
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import hindsight_memory
|
|
||||||
import litellm
|
|
||||||
|
|
||||||
with hindsight_memory(bank_id="user-123"):
|
|
||||||
response = litellm.completion(model="gpt-4", messages=[...])
|
|
||||||
# Memory integration automatically disabled after context
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disabling and Cleanup
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import disable, cleanup
|
|
||||||
|
|
||||||
# Temporarily disable memory integration
|
|
||||||
disable()
|
|
||||||
|
|
||||||
# Clean up all resources (call when shutting down)
|
|
||||||
cleanup()
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Main Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Configure global Hindsight settings |
|
|
||||||
| `enable()` | Enable memory integration with LiteLLM |
|
|
||||||
| `disable()` | Disable memory integration |
|
|
||||||
| `is_enabled()` | Check if memory integration is enabled |
|
|
||||||
| `cleanup()` | Clean up all resources |
|
|
||||||
|
|
||||||
### Configuration Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `is_configured()` | Check if Hindsight is configured |
|
|
||||||
| `reset_config()` | Reset configuration to defaults |
|
|
||||||
|
|
||||||
### Memory Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `recall(query, ...)` | Synchronously query raw memories |
|
|
||||||
| `arecall(query, ...)` | Asynchronously query raw memories |
|
|
||||||
| `reflect(query, ...)` | Synchronously get synthesized memory context |
|
|
||||||
| `areflect(query, ...)` | Asynchronously get synthesized memory context |
|
|
||||||
| `retain(content, ...)` | Synchronously store a memory |
|
|
||||||
| `aretain(content, ...)` | Asynchronously store a memory |
|
|
||||||
|
|
||||||
### Debug Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_last_injection_debug()` | Get debug info from last memory injection |
|
|
||||||
| `clear_injection_debug()` | Clear stored debug info |
|
|
||||||
|
|
||||||
### Client Wrappers
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `wrap_openai(client, ...)` | Wrap OpenAI client with memory |
|
|
||||||
| `wrap_anthropic(client, ...)` | Wrap Anthropic client with memory |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- litellm >= 1.40.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
# Local MCP Server
|
|
||||||
|
|
||||||
Hindsight provides a fully local MCP server that runs entirely on your machine with an embedded PostgreSQL database. No external server or database setup required.
|
|
||||||
|
|
||||||
This is ideal for:
|
|
||||||
- **Personal use with Claude Desktop** — Give Claude long-term memory across conversations
|
|
||||||
- **Development and testing** — Quick setup without infrastructure
|
|
||||||
- **Privacy-focused setups** — All data stays on your machine
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
|
||||||
--app claude-desktop \
|
|
||||||
--set HINDSIGHT_API_LLM_API_KEY=sk-...
|
|
||||||
```
|
|
||||||
|
|
||||||
This script will:
|
|
||||||
1. Install [uv](https://docs.astral.sh/uv/) if not already installed
|
|
||||||
2. Configure Claude Desktop to use the Hindsight MCP server
|
|
||||||
3. Set the provided environment variables in the MCP configuration
|
|
||||||
|
|
||||||
:::info Other MCP Applications
|
|
||||||
The quick install script currently supports Claude Desktop only. For other MCP-compatible applications (Cursor, Cline, etc.), follow the [Manual Configuration](#manual-configuration) steps below.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Manual Configuration
|
|
||||||
|
|
||||||
Add the following to your MCP client's configuration. For Claude Desktop:
|
|
||||||
|
|
||||||
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
||||||
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
||||||
|
|
||||||
For other MCP clients, refer to their documentation for the configuration file location.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"hindsight": {
|
|
||||||
"command": "uvx",
|
|
||||||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
|
||||||
"env": {
|
|
||||||
"HINDSIGHT_API_LLM_API_KEY": "sk-..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### With Custom Bank ID
|
|
||||||
|
|
||||||
By default, memories are stored in a bank called `mcp`. To use a different bank:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"hindsight": {
|
|
||||||
"command": "uvx",
|
|
||||||
"args": ["--from", "hindsight-api", "hindsight-local-mcp"],
|
|
||||||
"env": {
|
|
||||||
"HINDSIGHT_API_LLM_API_KEY": "sk-...",
|
|
||||||
"HINDSIGHT_API_MCP_LOCAL_BANK_ID": "my-personal-memory"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
All standard [Hindsight configuration variables](/developer/configuration) are supported.
|
|
||||||
|
|
||||||
### Local MCP Specific
|
|
||||||
|
|
||||||
| Variable | Required | Default | Description |
|
|
||||||
|----------|----------|---------|-------------|
|
|
||||||
| `HINDSIGHT_API_MCP_LOCAL_BANK_ID` | No | `mcp` | Memory bank ID to use |
|
|
||||||
| `HINDSIGHT_API_MCP_INSTRUCTIONS` | No | - | Additional instructions appended to both `retain` and `recall` tools |
|
|
||||||
|
|
||||||
### Customizing Tool Behavior
|
|
||||||
|
|
||||||
You can customize what gets stored by adding instructions to the tools. Re-run the install script with the additional `--set` flag:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-mcp | bash -s -- \
|
|
||||||
--app claude-desktop \
|
|
||||||
--set HINDSIGHT_API_LLM_API_KEY=sk-... \
|
|
||||||
--set HINDSIGHT_API_MCP_INSTRUCTIONS="Also store every action you take, code you write, and files you modify."
|
|
||||||
```
|
|
||||||
|
|
||||||
These instructions are appended to the default tool descriptions, guiding Claude on when and how to use the memory tools.
|
|
||||||
|
|
||||||
## Available Tools
|
|
||||||
|
|
||||||
### retain
|
|
||||||
|
|
||||||
Store information to long-term memory. This is a **fire-and-forget** operation — it returns immediately while processing happens in the background.
|
|
||||||
|
|
||||||
| Parameter | Type | Required | Description |
|
|
||||||
|-----------|------|----------|-------------|
|
|
||||||
| `content` | string | Yes | The fact or memory to store |
|
|
||||||
| `context` | string | No | Category for the memory (default: `general`) |
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "retain",
|
|
||||||
"arguments": {
|
|
||||||
"content": "User's favorite color is blue",
|
|
||||||
"context": "preferences"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"status": "accepted",
|
|
||||||
"message": "Memory storage initiated"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### recall
|
|
||||||
|
|
||||||
Search memories to provide personalized responses.
|
|
||||||
|
|
||||||
| Parameter | Type | Required | Description |
|
|
||||||
|-----------|------|----------|-------------|
|
|
||||||
| `query` | string | Yes | Natural language search query |
|
|
||||||
| `max_tokens` | integer | No | Maximum tokens to return (default: 4096) |
|
|
||||||
| `budget` | string | No | Search depth: `low`, `mid`, or `high` (default: `low`) |
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "recall",
|
|
||||||
"arguments": {
|
|
||||||
"query": "What are the user's color preferences?",
|
|
||||||
"max_tokens": 2048,
|
|
||||||
"budget": "mid"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The local MCP server:
|
|
||||||
|
|
||||||
1. **Starts an embedded PostgreSQL** (pg0) on an automatically assigned port
|
|
||||||
2. **Initializes the Hindsight memory engine** with local embeddings
|
|
||||||
3. **Connects via stdio** to Claude Code using the MCP protocol
|
|
||||||
|
|
||||||
Data is persisted in the pg0 data directory (`~/.pg0/hindsight-mcp/`), so your memories survive restarts.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### "HINDSIGHT_API_LLM_API_KEY required"
|
|
||||||
|
|
||||||
Make sure you've set the API key in your MCP configuration:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"HINDSIGHT_API_LLM_API_KEY": "sk-..."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Slow startup
|
|
||||||
|
|
||||||
The first startup may take longer as it:
|
|
||||||
- Downloads the embedding model (~100MB)
|
|
||||||
- Initializes the PostgreSQL database
|
|
||||||
|
|
||||||
Subsequent starts are faster.
|
|
||||||
|
|
||||||
### Checking logs
|
|
||||||
|
|
||||||
Set `HINDSIGHT_API_LOG_LEVEL=debug` for verbose output:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"HINDSIGHT_API_LOG_LEVEL": "debug"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Logs are written to stderr and visible in Claude Code's MCP server output.
|
|
||||||
|
|
@ -1,323 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 3
|
|
||||||
---
|
|
||||||
|
|
||||||
# Skills
|
|
||||||
|
|
||||||
Hindsight provides an Agent Skill that gives AI coding assistants persistent memory across sessions. Skills are reusable prompt templates that agents can load when needed to gain specialized capabilities.
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
| Platform | Skills Directory |
|
|
||||||
|----------|-----------------|
|
|
||||||
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `~/.claude/skills/` |
|
|
||||||
| [OpenCode](https://github.com/opencode-ai/opencode) | `~/.opencode/skills/` |
|
|
||||||
| [Codex CLI](https://github.com/openai/codex) | `~/.codex/skills/` |
|
|
||||||
|
|
||||||
## Deployment Modes
|
|
||||||
|
|
||||||
The skill supports two deployment modes:
|
|
||||||
|
|
||||||
| Mode | Best For | Data Location |
|
|
||||||
|------|----------|---------------|
|
|
||||||
| **Local** | Individual developers | Your machine (`~/.pg0/`) |
|
|
||||||
| **Cloud** | Teams sharing knowledge | Hindsight Cloud |
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
|
|
||||||
### Option 1: Interactive Installer (Recommended)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer will:
|
|
||||||
1. Prompt you to select your AI coding assistant
|
|
||||||
2. Select deployment mode (local or cloud)
|
|
||||||
3. Configure the appropriate settings
|
|
||||||
4. Install the skill to the appropriate directory
|
|
||||||
|
|
||||||
### Install for a Specific Platform
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Claude Code (interactive mode selection)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude
|
|
||||||
|
|
||||||
# OpenCode
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app opencode
|
|
||||||
|
|
||||||
# Codex CLI
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app codex
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install with Cloud Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Direct cloud setup (skips interactive prompts for mode)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Using add-skill
|
|
||||||
|
|
||||||
If you use [add-skill](https://add-skill.org/) to manage your agent skills:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# For local mode (individual developers)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-local
|
|
||||||
|
|
||||||
# For Hindsight Cloud (teams)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-cloud
|
|
||||||
|
|
||||||
# For self-hosted Hindsight servers
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-self-hosted
|
|
||||||
```
|
|
||||||
|
|
||||||
On first use, the AI will guide you through the remaining setup:
|
|
||||||
- **Local**: Run `uvx hindsight-embed configure` to set up your LLM provider
|
|
||||||
- **Cloud**: Provide your API key and bank ID
|
|
||||||
- **Self-hosted**: Provide your server URL, API key, and bank ID
|
|
||||||
|
|
||||||
## What the Skill Provides
|
|
||||||
|
|
||||||
Once installed, your AI assistant gains the ability to:
|
|
||||||
|
|
||||||
- **Retain** - Store user preferences, learnings, and procedure outcomes
|
|
||||||
- **Recall** - Search for relevant context before starting tasks
|
|
||||||
- **Reflect** - Synthesize memories into contextual answers
|
|
||||||
|
|
||||||
The skill uses the `hindsight-embed` CLI which runs a lightweight local daemon with an embedded database.
|
|
||||||
|
|
||||||
## How Skills Work
|
|
||||||
|
|
||||||
Skills are **model-invoked**, meaning the AI assistant automatically decides when to use them based on the context of your conversation. You don't need to explicitly trigger the skill.
|
|
||||||
|
|
||||||
The assistant will:
|
|
||||||
- **Store** when you share preferences, when tasks succeed/fail, or when learnings emerge
|
|
||||||
- **Recall** before starting non-trivial tasks to get relevant context
|
|
||||||
|
|
||||||
### What Gets Stored
|
|
||||||
|
|
||||||
The skill is optimized to store:
|
|
||||||
|
|
||||||
| Category | Examples |
|
|
||||||
|----------|----------|
|
|
||||||
| **User Preferences** | Coding style, tool preferences, language choices |
|
|
||||||
| **Procedure Outcomes** | Commands that worked, configurations that resolved issues |
|
|
||||||
| **Learnings** | Bug solutions, workarounds, architecture decisions |
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-embed CLI
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Local Daemon (auto-started)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Embedded PostgreSQL (~/.pg0/hindsight-embed/)
|
|
||||||
```
|
|
||||||
|
|
||||||
All data stays on your machine. The daemon auto-starts when needed and shuts down after inactivity.
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-cli
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Cloud API (https://api.hindsight.vectorize.io)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Shared Memory Bank (team-accessible)
|
|
||||||
```
|
|
||||||
|
|
||||||
Data is stored in Hindsight Cloud and shared across your team. All team members with the same bank ID can access shared memories.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Local Mode Setup
|
|
||||||
|
|
||||||
The skill uses configuration stored in `~/.hindsight/config.env`. Reconfigure anytime:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Cloud Mode Setup
|
|
||||||
|
|
||||||
Cloud mode connects to [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup), allowing teams to share memories about a codebase. When one team member learns something, everyone benefits.
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
1. A Hindsight Cloud account ([sign up](https://ui.hindsight.vectorize.io/signup))
|
|
||||||
2. An API key from your team admin
|
|
||||||
3. A bank ID for your project (e.g., `team-acme-frontend`)
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
Run the installer with cloud mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll be prompted for:
|
|
||||||
|
|
||||||
| Setting | Description | Example |
|
|
||||||
|---------|-------------|---------|
|
|
||||||
| **Cloud API URL** | Hindsight Cloud endpoint | `https://api.hindsight.vectorize.io` |
|
|
||||||
| **API Key** | Your authentication key | `hs_xxx...` |
|
|
||||||
| **Bank ID** | Shared memory bank for your team | `team-acme-frontend` |
|
|
||||||
|
|
||||||
### Configuration Files
|
|
||||||
|
|
||||||
Cloud mode creates two files:
|
|
||||||
|
|
||||||
**`~/.hindsight/config`** — API connection settings (TOML format):
|
|
||||||
```toml
|
|
||||||
api_url = "https://api.hindsight.vectorize.io"
|
|
||||||
api_key = "hs_xxx..."
|
|
||||||
```
|
|
||||||
|
|
||||||
**`~/.claude/skills/hindsight/SKILL.md`** — Skill definition with your bank ID baked in.
|
|
||||||
|
|
||||||
### Team Setup
|
|
||||||
|
|
||||||
To set up cloud mode for your team:
|
|
||||||
|
|
||||||
1. **Team admin** creates a bank in Hindsight Cloud (e.g., `team-acme-frontend`)
|
|
||||||
2. **Team admin** generates API keys for each team member
|
|
||||||
3. **Each developer** runs the installer with their API key and the shared bank ID
|
|
||||||
4. All team members now share the same memory bank
|
|
||||||
|
|
||||||
### What to Store in Team Banks
|
|
||||||
|
|
||||||
Cloud mode uses a **shared team bank**. Be thoughtful about what goes in:
|
|
||||||
|
|
||||||
| Type | Examples | How to Store |
|
|
||||||
|------|----------|--------------|
|
|
||||||
| **Project conventions** | Linting rules, testing requirements, Node version | `"Project uses ESLint with Airbnb config"` |
|
|
||||||
| **Team knowledge** | Architecture decisions, common pitfalls, domain logic | `"Auth module requires Redis 7+"` |
|
|
||||||
| **Individual preferences** | Personal coding style, communication preferences | `"Alice prefers verbose commit messages"` |
|
|
||||||
|
|
||||||
**Key distinction**: Project conventions apply to everyone. Individual preferences should include the person's name so the AI knows when to apply them.
|
|
||||||
|
|
||||||
### Example Workflow
|
|
||||||
|
|
||||||
```
|
|
||||||
Day 1: Alice discovers a requirement
|
|
||||||
─────────────────────────────────────
|
|
||||||
Alice's AI assistant stores:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
"Alice prefers explicit error handling over silent failures"
|
|
||||||
|
|
||||||
Day 2: Bob starts working on auth
|
|
||||||
─────────────────────────────────
|
|
||||||
Bob's AI assistant recalls:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
|
|
||||||
Bob avoids the same issue Alice hit!
|
|
||||||
(Alice's personal preference is stored but won't be applied to Bob)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Cloud Connection
|
|
||||||
|
|
||||||
After installation, verify the connection:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Store a test memory
|
|
||||||
hindsight memory retain team-acme-frontend "Alice works at Google as a software engineer"
|
|
||||||
|
|
||||||
# Recall it
|
|
||||||
hindsight memory recall team-acme-frontend "Alice"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Switching Between Banks
|
|
||||||
|
|
||||||
If you work on multiple projects, you can have different skills installed for each AI assistant, or manually switch banks:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Environment variable override (temporary)
|
|
||||||
HINDSIGHT_API_URL=https://api.hindsight.vectorize.io \
|
|
||||||
HINDSIGHT_API_KEY=hs_xxx \
|
|
||||||
hindsight memory recall different-bank "query"
|
|
||||||
```
|
|
||||||
|
|
||||||
For permanent multi-bank setups, reinstall the skill with a different bank ID.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Skill not activating
|
|
||||||
|
|
||||||
The skill activates based on its description matching your request. Try being explicit:
|
|
||||||
- "Remember that..." triggers storage
|
|
||||||
- "What do you know about..." triggers recall
|
|
||||||
|
|
||||||
### Local Mode Issues
|
|
||||||
|
|
||||||
**Daemon not starting:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed daemon status
|
|
||||||
uvx hindsight-embed daemon logs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Reconfigure LLM provider:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cloud Mode Issues
|
|
||||||
|
|
||||||
**Authentication errors:**
|
|
||||||
```bash
|
|
||||||
# Verify your config
|
|
||||||
cat ~/.hindsight/config
|
|
||||||
|
|
||||||
# Test connection manually
|
|
||||||
hindsight bank list
|
|
||||||
```
|
|
||||||
|
|
||||||
**Wrong bank ID:**
|
|
||||||
|
|
||||||
Check your SKILL.md file to see which bank ID is configured:
|
|
||||||
```bash
|
|
||||||
cat ~/.claude/skills/hindsight/SKILL.md | grep "memory retain"
|
|
||||||
```
|
|
||||||
|
|
||||||
To change the bank ID, reinstall the skill:
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
**Network/firewall issues:**
|
|
||||||
```bash
|
|
||||||
# Test connectivity to cloud API
|
|
||||||
curl -I https://api.hindsight.vectorize.io/health
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- An LLM API key (OpenAI, Anthropic, Groq, etc.)
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- Hindsight Cloud API key
|
|
||||||
- Network access to `https://api.hindsight.vectorize.io`
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 8
|
|
||||||
title: "AG2 (AutoGen) Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add long-term persistent memory to your AG2 (AutoGen) agents with Hindsight. Automatic fact extraction, entity tracking, and recall tools that persist across conversations."
|
|
||||||
---
|
|
||||||
|
|
||||||
# AG2
|
|
||||||
|
|
||||||
Persistent long-term memory for [AG2](https://ag2.ai) agents (community AutoGen fork). Give your agents retain/recall/reflect tools that persist across conversations.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/ag2)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Drop-in Tools** — `register_hindsight_tools()` registers retain, recall, and reflect in one line
|
|
||||||
- **AG2-native** — Uses `Annotated` type hints compatible with AG2's `@register_for_llm` / `@register_for_execution` pattern
|
|
||||||
- **GroupChat Support** — Multiple agents can share a single memory bank
|
|
||||||
- **Selective Tools** — Include only the tools you need (`include_retain`, `include_recall`, `include_reflect`)
|
|
||||||
- **Simple Configuration** — Configure once globally or override per tool set
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-ag2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from autogen import AssistantAgent, UserProxyAgent, LLMConfig
|
|
||||||
from hindsight_ag2 import register_hindsight_tools
|
|
||||||
|
|
||||||
llm_config = LLMConfig(api_type="openai", model="gpt-4o-mini")
|
|
||||||
|
|
||||||
with llm_config:
|
|
||||||
assistant = AssistantAgent(
|
|
||||||
name="assistant",
|
|
||||||
system_message="You are a helpful assistant with long-term memory.",
|
|
||||||
)
|
|
||||||
user_proxy = UserProxyAgent(
|
|
||||||
name="user",
|
|
||||||
human_input_mode="NEVER",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Register Hindsight memory tools on both agents
|
|
||||||
register_hindsight_tools(
|
|
||||||
assistant, user_proxy,
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
# The assistant can now use hindsight_retain, hindsight_recall, hindsight_reflect
|
|
||||||
result = user_proxy.initiate_chat(
|
|
||||||
assistant,
|
|
||||||
message="Remember that I prefer Python over JavaScript.",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it. The assistant can now store and retrieve memories across conversations.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The integration provides three AG2-compatible tool functions backed by Hindsight's API:
|
|
||||||
|
|
||||||
| Tool | Hindsight | What happens |
|
|
||||||
|------|-----------|--------------|
|
|
||||||
| `hindsight_retain(content)` | `retain(bank_id, content, ...)` | Content is stored. Hindsight extracts facts, entities, and relationships from the raw text. |
|
|
||||||
| `hindsight_recall(query)` | `recall(bank_id, query, ...)` | Hindsight runs semantic search, BM25, graph traversal, and reranking. Returns a numbered list of matching memories. |
|
|
||||||
| `hindsight_reflect(query)` | `reflect(bank_id, query, ...)` | Hindsight synthesizes a reasoned answer from all relevant memories, using the bank's disposition traits. |
|
|
||||||
|
|
||||||
Tools are plain Python functions with `Annotated` type hints. AG2 uses these hints to generate the tool schema that the LLM sees.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Global Configuration
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-key", # or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # low / mid / high
|
|
||||||
max_tokens=4096,
|
|
||||||
tags=["source:ag2"], # default tags for retain
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Per-Tool Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
budget="high",
|
|
||||||
max_tokens=8192,
|
|
||||||
tags=["team:alpha"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## GroupChat with Shared Memory
|
|
||||||
|
|
||||||
Multiple agents can share a single memory bank in a GroupChat:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager, LLMConfig
|
|
||||||
from hindsight_ag2 import register_hindsight_tools
|
|
||||||
|
|
||||||
llm_config = LLMConfig(api_type="openai", model="gpt-4o-mini")
|
|
||||||
|
|
||||||
with llm_config:
|
|
||||||
researcher = AssistantAgent(name="researcher", system_message="You research topics.")
|
|
||||||
writer = AssistantAgent(name="writer", system_message="You write content.")
|
|
||||||
executor = UserProxyAgent(name="executor", human_input_mode="NEVER")
|
|
||||||
|
|
||||||
# All agents share the same memory bank
|
|
||||||
for agent in [researcher, writer]:
|
|
||||||
register_hindsight_tools(agent, executor, bank_id="team-memory")
|
|
||||||
|
|
||||||
group_chat = GroupChat(agents=[researcher, writer, executor], messages=[])
|
|
||||||
manager = GroupChatManager(groupchat=group_chat)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Registration
|
|
||||||
|
|
||||||
For full control over how tools are registered:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
for tool_fn in tools:
|
|
||||||
assistant.register_for_llm(description=tool_fn.__doc__)(tool_fn)
|
|
||||||
user_proxy.register_for_execution()(tool_fn)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Set global connection and default settings |
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `reset_config()` | Reset configuration to None |
|
|
||||||
|
|
||||||
### create_hindsight_tools
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured `Hindsight` client |
|
|
||||||
| `hindsight_api_url` | from config | Hindsight API URL |
|
|
||||||
| `api_key` | from config | API key |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode (any/all/any_strict/all_strict) |
|
|
||||||
| `retain_metadata` | `None` | Metadata dict for retain operations |
|
|
||||||
| `retain_document_id` | `None` | Document ID for retain (groups/upserts memories) |
|
|
||||||
| `recall_types` | `None` | Fact types to filter (world, experience, opinion, observation) |
|
|
||||||
| `recall_include_entities` | `False` | Include entity information in recall results |
|
|
||||||
| `reflect_context` | `None` | Additional context for reflect operations |
|
|
||||||
| `reflect_max_tokens` | `max_tokens` | Max tokens for reflect results |
|
|
||||||
| `reflect_response_schema` | `None` | JSON schema to constrain reflect output format |
|
|
||||||
| `reflect_tags` | `recall_tags` | Tags to filter memories used in reflect |
|
|
||||||
| `reflect_tags_match` | `recall_tags_match` | Tag matching for reflect |
|
|
||||||
| `include_retain` | `True` | Include the retain tool |
|
|
||||||
| `include_recall` | `True` | Include the recall tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect tool |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- ag2 >= 0.9.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 9
|
|
||||||
title: "Agno Agent Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to Agno agents using Hindsight's retain, recall, and reflect tools. Plug into Agno's native Toolkit pattern for long-term memory across sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Agno
|
|
||||||
|
|
||||||
Persistent memory tools for [Agno](https://github.com/agno-agi/agno) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — using Agno's native Toolkit pattern.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Native Toolkit** - Extends Agno's `Toolkit` base class, just like `Mem0Tools`
|
|
||||||
- **Memory Instructions** - Pre-recall memories for injection into `Agent(instructions=[...])`
|
|
||||||
- **Three Memory Tools** - Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Flexible Bank Resolution** - Static bank ID, `RunContext.user_id`, or custom resolver
|
|
||||||
- **Simple Configuration** - Configure once globally, or pass a client directly
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-agno
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from agno.agent import Agent
|
|
||||||
from agno.models.openai import OpenAIChat
|
|
||||||
from hindsight_agno import HindsightTools
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
|
|
||||||
agent.print_response("Remember that I prefer dark mode")
|
|
||||||
agent.print_response("What are my preferences?")
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`retain_memory`** — Store information to long-term memory
|
|
||||||
- **`recall_memory`** — Search long-term memory for relevant facts
|
|
||||||
- **`reflect_on_memory`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## With Memory Instructions
|
|
||||||
|
|
||||||
Pre-recall relevant memories and inject them into the system prompt:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_agno import HindsightTools, memory_instructions
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
instructions=[memory_instructions(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = [HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
enable_retain=True,
|
|
||||||
enable_recall=True,
|
|
||||||
enable_reflect=False, # Omit reflect
|
|
||||||
)]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bank Resolution
|
|
||||||
|
|
||||||
The bank ID is resolved in order:
|
|
||||||
|
|
||||||
1. **`bank_resolver`** — Custom callable `(RunContext) -> str`
|
|
||||||
2. **`bank_id`** — Static bank ID passed to constructor
|
|
||||||
3. **`run_context.user_id`** — Automatic per-user banks
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Per-user banks from RunContext
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(hindsight_api_url="http://localhost:8888")],
|
|
||||||
user_id="user-123", # Used as bank_id
|
|
||||||
)
|
|
||||||
|
|
||||||
# Custom resolver
|
|
||||||
def resolve_bank(ctx):
|
|
||||||
return f"team-{ctx.user_id}"
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_resolver=resolve_bank,
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing connection details to every toolkit, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_agno import configure, HindsightTools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create toolkit without passing connection details
|
|
||||||
tools = [HindsightTools(bank_id="user-123")]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### `HindsightTools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static Hindsight memory bank ID |
|
|
||||||
| `bank_resolver` | `None` | Callable `(RunContext) -> str` for dynamic bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `enable_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `enable_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `enable_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- agno
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 4
|
|
||||||
title: "Vercel AI SDK Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to any Vercel AI SDK application with five ready-to-use Hindsight tools. Retain conversations, recall context, and reflect on past interactions — works with any model."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vercel AI SDK
|
|
||||||
|
|
||||||
The `@vectorize-io/hindsight-ai-sdk` package integrates [Hindsight](https://hindsight.vectorize.io) memory with the [Vercel AI SDK](https://ai-sdk.dev). It provides five ready-to-use tools for retaining, recalling, and reflecting on long-term memories.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/ai-sdk)
|
|
||||||
|
|
||||||
import CodeSnippet from '@site/src/components/CodeSnippet';
|
|
||||||
|
|
||||||
import aiSdkTs from '!!raw-loader!@site/examples/integrations/ai-sdk.ts';
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Create a Hindsight client and pass it to `createHindsightTools` along with a `bankId`. The `bankId` identifies the memory store for this session—typically a user ID.
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="setup" language="typescript" />
|
|
||||||
|
|
||||||
:::tip Per-request bank IDs
|
|
||||||
In multi-user applications, create `tools` inside your request handler so each request closes over the correct `bankId`. See the [Next.js example](#in-a-nextjs-route-handler) below.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### With `generateText`
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="generate-text" language="typescript" />
|
|
||||||
|
|
||||||
### With `streamText`
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="stream-text" language="typescript" />
|
|
||||||
|
|
||||||
### With `ToolLoopAgent`
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="tool-loop-agent" language="typescript" />
|
|
||||||
|
|
||||||
### In a Next.js Route Handler
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="next-api-route" language="typescript" />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tools Reference
|
|
||||||
|
|
||||||
Five tools are registered. The `bankId` is fixed at creation time—the agent cannot change it.
|
|
||||||
|
|
||||||
| Tool | What the agent provides | What the constructor controls |
|
|
||||||
|------|------------------------|-------------------------------|
|
|
||||||
| `retain` | `content`, `documentId`, `timestamp`, `context` | `async`, `tags`, `metadata` |
|
|
||||||
| `recall` | `query`, `queryTimestamp` | `budget`, `types`, `maxTokens`, `includeEntities`, `includeChunks` |
|
|
||||||
| `reflect` | `query`, `context` | `budget` |
|
|
||||||
| `getMentalModel` | `mentalModelId` | — |
|
|
||||||
| `getDocument` | `documentId` | — |
|
|
||||||
|
|
||||||
**Why this split?** Semantic inputs (what to remember, what to search for) belong to the agent. Infrastructure concerns (cost budget, tagging strategy, async mode) belong to the application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Constructor Options
|
|
||||||
|
|
||||||
All options except `client` and `bankId` are optional. Each tool's options are grouped under the tool name.
|
|
||||||
|
|
||||||
<CodeSnippet code={aiSdkTs} section="constructor-options" language="typescript" />
|
|
||||||
|
|
||||||
### `retain`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `async` | `boolean` | `false` | Fire-and-forget — do not wait for ingestion to complete |
|
|
||||||
| `tags` | `string[]` | — | Tags attached to every retained memory |
|
|
||||||
| `metadata` | `Record<string, string>` | — | Metadata attached to every retained memory |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
||||||
### `recall`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls retrieval depth and latency |
|
|
||||||
| `types` | `('world' \| 'experience' \| 'observation')[]` | all | Restrict results to these fact types |
|
|
||||||
| `maxTokens` | `number` | API default | Cap the total tokens returned |
|
|
||||||
| `includeEntities` | `boolean` | `false` | Include entity observations in results |
|
|
||||||
| `includeChunks` | `boolean` | `false` | Include raw source chunks in results |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
||||||
### `reflect`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls synthesis depth and latency |
|
|
||||||
| `maxTokens` | `number` | API default | Maximum tokens for the response |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "Vercel Chat SDK Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Give your Vercel Chat SDK bot persistent, per-user memory across Slack, Discord, Teams, and more. Single handler wrapper, no custom plumbing required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vercel Chat SDK
|
|
||||||
|
|
||||||
We built `@vectorize-io/hindsight-chat` to give [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear — no custom plumbing required.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/chat)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @vectorize-io/hindsight-chat
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Chat } from 'chat';
|
|
||||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
||||||
import { withHindsightChat } from '@vectorize-io/hindsight-chat';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
|
|
||||||
const chat = new Chat({ connectors: [/* your connectors */] });
|
|
||||||
const hindsight = new HindsightClient({ apiKey: process.env.HINDSIGHT_API_KEY });
|
|
||||||
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId, // per-user memory
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
await thread.subscribe();
|
|
||||||
|
|
||||||
const result = await streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stream the response
|
|
||||||
const chunks: string[] = [];
|
|
||||||
for await (const chunk of result.textStream) {
|
|
||||||
chunks.push(chunk);
|
|
||||||
}
|
|
||||||
const fullResponse = chunks.join('');
|
|
||||||
await thread.post(fullResponse);
|
|
||||||
|
|
||||||
// Store the conversation in memory
|
|
||||||
await ctx.retain(
|
|
||||||
`User: ${message.text}\nAssistant: ${fullResponse}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### `withHindsightChat(options, handler)`
|
|
||||||
|
|
||||||
`withHindsightChat` wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler `(thread, message) => Promise<void>` so it drops in without changing your handler signature.
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `client` | `HindsightClient` | *required* | Hindsight client instance |
|
|
||||||
| `bankId` | `string \| (msg) => string` | *required* | Memory bank ID or resolver function |
|
|
||||||
| `recall.enabled` | `boolean` | `true` | Auto-recall memories before handler |
|
|
||||||
| `recall.budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Processing budget for recall |
|
|
||||||
| `recall.maxTokens` | `number` | API default | Max tokens for recall results |
|
|
||||||
| `recall.types` | `FactType[]` | all | Filter to specific fact types |
|
|
||||||
| `recall.includeEntities` | `boolean` | `true` | Include entity observations |
|
|
||||||
| `retain.enabled` | `boolean` | `false` | Auto-retain inbound messages |
|
|
||||||
| `retain.async` | `boolean` | `true` | Fire-and-forget retain |
|
|
||||||
| `retain.tags` | `string[]` | – | Tags for retained memories |
|
|
||||||
| `retain.metadata` | `Record<string, string>` | – | Metadata for retained memories |
|
|
||||||
|
|
||||||
### Context (`ctx`)
|
|
||||||
|
|
||||||
We inject a third `ctx` argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank:
|
|
||||||
|
|
||||||
| Property/Method | Description |
|
|
||||||
|----------------|-------------|
|
|
||||||
| `ctx.bankId` | Resolved bank ID |
|
|
||||||
| `ctx.memories` | Array of recalled memories |
|
|
||||||
| `ctx.entities` | Entity observations (or null) |
|
|
||||||
| `ctx.memoriesAsSystemPrompt(options?)` | Format memories for LLM system prompt |
|
|
||||||
| `ctx.retain(content, options?)` | Store content in memory |
|
|
||||||
| `ctx.recall(query, options?)` | Search memories |
|
|
||||||
| `ctx.reflect(query, options?)` | Reason over memories |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Subscribed Message Handler
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
chat.onSubscribedMessage(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId,
|
|
||||||
recall: { budget: 'high', maxTokens: 1000 },
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
const result = await generateText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
await thread.post(result.text);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Auto-Retain Inbound Messages
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId,
|
|
||||||
retain: { enabled: true, tags: ['slack', 'inbound'] },
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
// Inbound message is already being retained automatically
|
|
||||||
const result = await generateText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
await thread.post(result.text);
|
|
||||||
|
|
||||||
// Retain the assistant response separately
|
|
||||||
await ctx.retain(`Assistant: ${result.text}`, {
|
|
||||||
tags: ['slack', 'outbound'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Static Bank ID (Shared Memory)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// All users share the same memory bank
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{ client: hindsight, bankId: 'shared-team-memory' },
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed.
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "Claude Code Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Claude Code with Hindsight. Automatically captures conversations and recalls relevant context across sessions using Claude Code's hook-based architecture."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Claude Code
|
|
||||||
|
|
||||||
Biomimetic long-term memory for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) using [Hindsight](https://vectorize.io/hindsight). Automatically captures conversations and intelligently recalls relevant context — a complete port of [`hindsight-openclaw`](./openclaw) adapted to Claude Code's hook-based plugin architecture.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/claude-code)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Add the Hindsight marketplace and install the plugin
|
|
||||||
claude plugin marketplace add vectorize-io/hindsight
|
|
||||||
claude plugin install hindsight-memory
|
|
||||||
|
|
||||||
# 2. Configure your LLM provider for memory extraction
|
|
||||||
# Option A: OpenAI (auto-detected)
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option B: Anthropic (auto-detected)
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option C: No API key needed (uses Claude Code's own model — personal/local use only)
|
|
||||||
export HINDSIGHT_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option D: Connect to an external Hindsight server instead of running locally
|
|
||||||
mkdir -p ~/.hindsight
|
|
||||||
echo '{"hindsightApiUrl": "https://your-hindsight-server.com"}' > ~/.hindsight/claude-code.json
|
|
||||||
|
|
||||||
# 3. Start Claude Code — the plugin activates automatically
|
|
||||||
claude
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! The plugin will automatically start capturing and recalling memories.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every user prompt, queries Hindsight for relevant memories and injects them as context (invisible to the chat transcript, visible to Claude)
|
|
||||||
- **Auto-retain** — after every response (or every N turns), extracts and retains conversation content to Hindsight for long-term storage
|
|
||||||
- **Daemon management** — can auto-start/stop `hindsight-embed` locally or connect to an external Hindsight server
|
|
||||||
- **Dynamic bank IDs** — supports per-agent, per-project, or per-session memory isolation
|
|
||||||
- **Channel-agnostic** — works with Claude Code Channels (Telegram, Discord, Slack) or interactive sessions
|
|
||||||
- **Zero dependencies** — pure Python stdlib, no pip install required
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin uses all four Claude Code hook events:
|
|
||||||
|
|
||||||
| Hook | Event | Purpose |
|
|
||||||
|------|-------|---------|
|
|
||||||
| `session_start.py` | `SessionStart` | Health check — verify Hindsight is reachable |
|
|
||||||
| `recall.py` | `UserPromptSubmit` | **Auto-recall** — query memories, inject as `additionalContext` |
|
|
||||||
| `retain.py` | `Stop` | **Auto-retain** — extract transcript, POST to Hindsight (async) |
|
|
||||||
| `session_end.py` | `SessionEnd` | Cleanup — stop auto-managed daemon if started |
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended for production)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted). No local LLM needed — the server handles fact extraction.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-token"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon (auto-managed)
|
|
||||||
|
|
||||||
The plugin automatically starts and stops `hindsight-embed` via `uvx`. Requires an LLM provider API key for local fact extraction.
|
|
||||||
|
|
||||||
Set an LLM provider:
|
|
||||||
```bash
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
# or
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
# or
|
|
||||||
export HINDSIGHT_LLM_PROVIDER=claude-code # No API key needed
|
|
||||||
```
|
|
||||||
|
|
||||||
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_LLM_MODEL`.
|
|
||||||
|
|
||||||
### 3. Existing Local Server
|
|
||||||
|
|
||||||
If you already have `hindsight-embed` running, leave `hindsightApiUrl` empty and set `apiPort` to match your server's port. The plugin will detect it automatically.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
All settings live in `~/.hindsight/claude-code.json`. Every setting can also be overridden via environment variables. The plugin ships with sensible defaults — you only need to configure what you want to change.
|
|
||||||
|
|
||||||
**Loading order** (later entries win):
|
|
||||||
1. Built-in defaults (hardcoded in the plugin)
|
|
||||||
2. Plugin `settings.json` (ships with the plugin, at `CLAUDE_PLUGIN_ROOT/settings.json`)
|
|
||||||
3. User config (`~/.hindsight/claude-code.json` — recommended for your overrides)
|
|
||||||
4. Environment variables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Connection & Daemon
|
|
||||||
|
|
||||||
These settings control how the plugin connects to the Hindsight API.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | `HINDSIGHT_API_URL` | `""` (empty) | URL of an external Hindsight API server. When empty, the plugin uses a local daemon instead. |
|
|
||||||
| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | `null` | Authentication token for the external API. Only needed when `hindsightApiUrl` is set. |
|
|
||||||
| `apiPort` | `HINDSIGHT_API_PORT` | `9077` | Port used by the local `hindsight-embed` daemon. Change this if you run multiple instances or have a port conflict. |
|
|
||||||
| `daemonIdleTimeout` | `HINDSIGHT_DAEMON_IDLE_TIMEOUT` | `0` | Seconds of inactivity before the local daemon shuts itself down. `0` means the daemon stays running until the session ends. |
|
|
||||||
| `embedVersion` | `HINDSIGHT_EMBED_VERSION` | `"latest"` | Which version of `hindsight-embed` to install via `uvx`. Pin to a specific version (e.g. `"0.5.2"`) for reproducibility. |
|
|
||||||
| `embedPackagePath` | `HINDSIGHT_EMBED_PACKAGE_PATH` | `null` | Local filesystem path to a `hindsight-embed` checkout. When set, the plugin runs from this path instead of installing via `uvx`. Useful for development. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### LLM Provider (local daemon only)
|
|
||||||
|
|
||||||
These settings configure which LLM the local daemon uses for fact extraction. They are **ignored** when connecting to an external API (the server uses its own LLM configuration).
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `llmProvider` | `HINDSIGHT_LLM_PROVIDER` | auto-detect | Which LLM provider to use. Supported values: `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `openai-codex`, `claude-code`. When omitted, the plugin auto-detects by checking for API key env vars in order: `OPENAI_API_KEY` → `ANTHROPIC_API_KEY` → `GEMINI_API_KEY` → `GROQ_API_KEY`. |
|
|
||||||
| `llmModel` | `HINDSIGHT_LLM_MODEL` | provider default | Override the default model for the chosen provider (e.g. `"gpt-4o"`, `"claude-sonnet-4-20250514"`). When omitted, the Hindsight API picks a sensible default for each provider. |
|
|
||||||
| `llmApiKeyEnv` | — | provider standard | Name of the environment variable that holds the API key. Normally auto-detected (e.g. `OPENAI_API_KEY` for the `openai` provider). Set this only if your key is in a non-standard env var. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
A **bank** is an isolated memory store — like a separate "brain." These settings control which bank the plugin reads from and writes to.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | `HINDSIGHT_BANK_ID` | `"claude_code"` | The bank ID to use when `dynamicBankId` is `false`. All sessions share this single bank. |
|
|
||||||
| `bankMission` | `HINDSIGHT_BANK_MISSION` | generic assistant prompt | A short description of the agent's identity and purpose. Sent to Hindsight when creating or updating the bank, and used during recall to contextualize results. |
|
|
||||||
| `retainMission` | — | extraction prompt | Instructions for the fact extraction LLM — tells it *what* to extract from conversations (e.g. "Extract technical decisions and user preferences"). |
|
|
||||||
| `dynamicBankId` | `HINDSIGHT_DYNAMIC_BANK_ID` | `false` | When `true`, the plugin derives a unique bank ID from context fields (see `dynamicBankGranularity`), giving each combination its own isolated memory. |
|
|
||||||
| `dynamicBankGranularity` | — | `["agent", "project"]` | Which context fields to combine when building a dynamic bank ID. Available fields: `agent` (agent name), `project` (working directory), `session` (session ID), `channel` (channel ID), `user` (user ID). |
|
|
||||||
| `bankIdPrefix` | — | `""` | A string prepended to all bank IDs — both static and dynamic. Useful for namespacing (e.g. `"prod"` or `"staging"`). |
|
|
||||||
| `agentName` | `HINDSIGHT_AGENT_NAME` | `"claude-code"` | Name used for the `agent` field in dynamic bank ID derivation. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
Auto-recall runs on every user prompt. It queries Hindsight for relevant memories and injects them into Claude's context as invisible `additionalContext` (the user doesn't see them in the chat transcript).
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `HINDSIGHT_AUTO_RECALL` | `true` | Master switch for auto-recall. Set to `false` to disable memory retrieval entirely. |
|
|
||||||
| `recallBudget` | `HINDSIGHT_RECALL_BUDGET` | `"mid"` | Controls how hard Hindsight searches for memories. `"low"` = fast, fewer strategies; `"mid"` = balanced; `"high"` = thorough, slower. Affects latency directly. |
|
|
||||||
| `recallMaxTokens` | `HINDSIGHT_RECALL_MAX_TOKENS` | `1024` | Maximum number of tokens in the recalled memory block. Lower values reduce context usage but may truncate relevant memories. |
|
|
||||||
| `recallTypes` | — | `["world", "experience"]` | Which memory types to retrieve. `"world"` = general facts; `"experience"` = personal experiences; `"observation"` = raw observations. |
|
|
||||||
| `recallContextTurns` | `HINDSIGHT_RECALL_CONTEXT_TURNS` | `1` | How many prior conversation turns to include when composing the recall query. `1` = only the latest user message; higher values give more context but may dilute the query. |
|
|
||||||
| `recallMaxQueryChars` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | `800` | Maximum character length of the query sent to Hindsight. Longer queries are truncated. |
|
|
||||||
| `recallRoles` | — | `["user", "assistant"]` | Which message roles to include when building the recall query from prior turns. |
|
|
||||||
| `recallPromptPreamble` | — | built-in string | Text placed above the recalled memories in the injected context block. Customize this to change how Claude interprets the memories. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
Auto-retain runs after Claude responds. It extracts the conversation transcript and sends it to Hindsight for long-term storage and fact extraction.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `HINDSIGHT_AUTO_RETAIN` | `true` | Master switch for auto-retain. Set to `false` to disable memory storage entirely. |
|
|
||||||
| `retainMode` | `HINDSIGHT_RETAIN_MODE` | `"full-session"` | Retention strategy. `"full-session"` sends the full conversation transcript (with chunking). |
|
|
||||||
| `retainEveryNTurns` | — | `10` | How often to retain. `1` = every turn; `10` = every 10th turn. Higher values reduce API calls but delay memory capture. Values > 1 enable **chunked retention** with a sliding window. |
|
|
||||||
| `retainOverlapTurns` | — | `2` | When chunked retention fires, this many extra turns from the previous chunk are included for continuity. Total window size = `retainEveryNTurns + retainOverlapTurns`. |
|
|
||||||
| `retainRoles` | — | `["user", "assistant"]` | Which message roles to include in the retained transcript. |
|
|
||||||
| `retainToolCalls` | — | `true` | Whether to include tool calls (function invocations and results) in the retained transcript. Captures structured actions like file reads, searches, and code edits. |
|
|
||||||
| `retainTags` | — | `["{session_id}"]` | Tags attached to the retained document. Supports `{session_id}` placeholder which is replaced with the current session ID at runtime. |
|
|
||||||
| `retainMetadata` | — | `{}` | Arbitrary key-value metadata attached to the retained document. |
|
|
||||||
| `retainContext` | — | `"claude-code"` | A label attached to retained memories identifying their source. Useful when multiple integrations write to the same bank. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Debug
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `HINDSIGHT_DEBUG` | `false` | Enable verbose logging to stderr. All log lines are prefixed with `[Hindsight]`. Useful for diagnosing connection issues, recall/retain behavior, and bank ID derivation. |
|
|
||||||
|
|
||||||
## Claude Code Channels
|
|
||||||
|
|
||||||
With [Claude Code Channels](https://docs.anthropic.com/en/docs/claude-code), Claude Code can operate as a persistent background agent connected to Telegram, Discord, Slack, and other messaging platforms. This plugin gives Channel-based agents the same long-term memory that `hindsight-openclaw` provides for Openclaw agents.
|
|
||||||
|
|
||||||
For Channel agents, enable dynamic bank IDs for per-channel/per-user memory isolation:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dynamicBankId": true,
|
|
||||||
"dynamicBankGranularity": ["agent", "channel", "user"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And set channel context via environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_CHANNEL_ID="telegram-group-12345"
|
|
||||||
export HINDSIGHT_USER_ID="user-67890"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Plugin not activating**: Check Claude Code logs for `[Hindsight]` messages. Enable `"debug": true` in `~/.hindsight/claude-code.json`.
|
|
||||||
|
|
||||||
**Recall returning no memories**: Verify the Hindsight server is reachable (`curl http://localhost:9077/health`). Memories need at least one retain cycle before they're available.
|
|
||||||
|
|
||||||
**Daemon not starting**: Ensure an LLM API key is set (or use `HINDSIGHT_LLM_PROVIDER=claude-code`). Review daemon logs at `~/.hindsight/profiles/claude-code.log`.
|
|
||||||
|
|
||||||
**High latency on recall**: The recall hook has a 12-second timeout. Use `recallBudget: "low"` or reduce `recallMaxTokens` for faster responses.
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 6
|
|
||||||
title: "Codex CLI Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to OpenAI Codex CLI with Hindsight. Three Python hook scripts automatically recall context before each prompt and retain conversations — no workflow changes required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Codex
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/codex)
|
|
||||||
|
|
||||||
Persistent memory for [Codex CLI](https://github.com/openai/codex) using [Hindsight](https://vectorize.io/hindsight). Three Python hook scripts automatically recall relevant context before each prompt and retain conversations after each turn — no changes to your Codex workflow required.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-codex | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer will guide you through choosing local or cloud mode and configuring your connection. Once installed, start a new Codex session — memory is live.
|
|
||||||
|
|
||||||
To uninstall:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-codex | bash -s -- --uninstall
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every user prompt, queries Hindsight for relevant memories and injects them as `additionalContext` (invisible to the transcript, visible to Codex)
|
|
||||||
- **Auto-retain** — after each Codex response, stores the conversation transcript to Hindsight for future recall
|
|
||||||
- **Dynamic bank IDs** — supports per-project memory isolation based on the working directory
|
|
||||||
- **Session-level upsert** — uses the session ID as the document ID so re-running the same session updates rather than duplicates stored content
|
|
||||||
- **Zero dependencies** — pure Python stdlib, no pip install required
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin uses three Codex hook events:
|
|
||||||
|
|
||||||
| Hook | Event | Purpose |
|
|
||||||
|------|-------|---------|
|
|
||||||
| `session_start.py` | `SessionStart` | Warm up — verify Hindsight is reachable |
|
|
||||||
| `recall.py` | `UserPromptSubmit` | **Auto-recall** — query memories, inject as `additionalContext` |
|
|
||||||
| `retain.py` | `Stop` | **Auto-retain** — extract transcript, POST to Hindsight (async) |
|
|
||||||
|
|
||||||
On `UserPromptSubmit`, the hook reads the prompt, queries Hindsight for the most relevant memories, and outputs a `hookSpecificOutput.additionalContext` block. Codex prepends this to the conversation before sending it to the model:
|
|
||||||
|
|
||||||
```
|
|
||||||
<hindsight_memories>
|
|
||||||
Relevant memories from past conversations...
|
|
||||||
Current time - 2026-03-27 09:14
|
|
||||||
|
|
||||||
- Project uses FastAPI with asyncpg — not SQLAlchemy [world] (2026-03-26)
|
|
||||||
- Preferred testing framework: pytest with pytest-asyncio [experience] (2026-03-26)
|
|
||||||
</hindsight_memories>
|
|
||||||
```
|
|
||||||
|
|
||||||
On `Stop`, the hook reads the session transcript, strips previously injected memory tags (to prevent feedback loops), and POSTs the conversation to Hindsight asynchronously.
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://api.hindsight.vectorize.io",
|
|
||||||
"hindsightApiToken": "hsk_your_token"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon
|
|
||||||
|
|
||||||
Run `hindsight-embed` locally. The `session_start.py` hook will detect it on `apiPort` (default `9077`). The daemon is not auto-started by the Codex plugin — start it separately:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed
|
|
||||||
```
|
|
||||||
|
|
||||||
Then leave `hindsightApiUrl` empty in your config and the plugin will connect to `http://localhost:9077`.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Settings are loaded from `~/.hindsight/codex.json`. Every setting can also be overridden via environment variable.
|
|
||||||
|
|
||||||
**Loading order** (later entries win):
|
|
||||||
|
|
||||||
1. Built-in defaults
|
|
||||||
2. Plugin `settings.json` (at `~/.hindsight/codex/settings.json`)
|
|
||||||
3. User config (`~/.hindsight/codex.json`)
|
|
||||||
4. Environment variables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Connection
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | `HINDSIGHT_API_URL` | `""` | URL of the Hindsight API server. Required. |
|
|
||||||
| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | `null` | API token for authentication. Required for Hindsight Cloud. |
|
|
||||||
| `apiPort` | `HINDSIGHT_API_PORT` | `9077` | Port for the local `hindsight-embed` daemon. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | `HINDSIGHT_BANK_ID` | `"codex"` | The bank to read from and write to. All sessions share this bank unless `dynamicBankId` is enabled. |
|
|
||||||
| `bankMission` | `HINDSIGHT_BANK_MISSION` | coding assistant prompt | Describes the agent's purpose. Sent when creating or updating the bank. |
|
|
||||||
| `retainMission` | — | extraction prompt | Instructions for Hindsight's fact extraction — what to extract from coding conversations. |
|
|
||||||
| `dynamicBankId` | `HINDSIGHT_DYNAMIC_BANK_ID` | `false` | When `true`, derives a unique bank ID from `dynamicBankGranularity` fields — useful for per-project isolation. |
|
|
||||||
| `dynamicBankGranularity` | — | `["agent", "project"]` | Which fields to combine for dynamic bank IDs. `"project"` = working directory, `"agent"` = agent name. |
|
|
||||||
| `bankIdPrefix` | — | `""` | Prefix prepended to all bank IDs. |
|
|
||||||
| `agentName` | `HINDSIGHT_AGENT_NAME` | `"codex"` | Agent name used in dynamic bank ID derivation. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `HINDSIGHT_AUTO_RECALL` | `true` | Master switch for auto-recall. |
|
|
||||||
| `recallBudget` | `HINDSIGHT_RECALL_BUDGET` | `"mid"` | Search depth: `"low"` (fast), `"mid"` (balanced), `"high"` (thorough). |
|
|
||||||
| `recallMaxTokens` | `HINDSIGHT_RECALL_MAX_TOKENS` | `1024` | Max tokens in the recalled memory block. |
|
|
||||||
| `recallTypes` | — | `["world", "experience"]` | Memory types to retrieve. |
|
|
||||||
| `recallContextTurns` | `HINDSIGHT_RECALL_CONTEXT_TURNS` | `1` | Prior turns to include when building the recall query. `1` = latest prompt only. |
|
|
||||||
| `recallMaxQueryChars` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | `800` | Max characters in the query sent to Hindsight. |
|
|
||||||
| `recallRoles` | — | `["user", "assistant"]` | Which roles to include when building a multi-turn query. |
|
|
||||||
| `recallPromptPreamble` | — | built-in | Text placed above the recalled memories in the injected context block. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `HINDSIGHT_AUTO_RETAIN` | `true` | Master switch for auto-retain. |
|
|
||||||
| `retainMode` | `HINDSIGHT_RETAIN_MODE` | `"full-session"` | `"full-session"` sends the full transcript per session (upserted by session ID). `"chunked"` sends sliding windows every N turns. |
|
|
||||||
| `retainEveryNTurns` | — | `10` | Retain fires every N turns. `1` = every turn. Higher values reduce API calls. |
|
|
||||||
| `retainOverlapTurns` | — | `2` | Extra turns included from the previous chunk (chunked mode only). |
|
|
||||||
| `retainRoles` | — | `["user", "assistant"]` | Which roles to include in the retained transcript. |
|
|
||||||
| `retainTags` | — | `["{session_id}"]` | Tags attached to the stored document. `{session_id}` is replaced at runtime. |
|
|
||||||
| `retainMetadata` | — | `{}` | Arbitrary key-value metadata attached to the stored document. |
|
|
||||||
| `retainContext` | — | `"codex"` | Label identifying the source integration. Useful when multiple integrations write to the same bank. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Debug
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `HINDSIGHT_DEBUG` | `false` | Enable verbose logging to stderr. All log lines are prefixed with `[Hindsight]`. |
|
|
||||||
|
|
||||||
## Per-Project Memory
|
|
||||||
|
|
||||||
To give each project its own isolated memory bank, enable dynamic bank IDs:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dynamicBankId": true,
|
|
||||||
"dynamicBankGranularity": ["agent", "project"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With this config, running Codex in `~/projects/api` and `~/projects/frontend` stores and recalls memories separately. Bank IDs are derived from the working directory path.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Hooks not firing**: Check that `~/.codex/config.toml` contains `codex_hooks = true` under `[features]`. Re-run the installer to fix this automatically.
|
|
||||||
|
|
||||||
**No memories recalled**: Recall returns results only after something has been retained. Either complete one Codex session first, or seed your bank manually using the [cookbook example](https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/codex-memory).
|
|
||||||
|
|
||||||
**Memory not being stored**: `retainEveryNTurns` defaults to `10` — retain only fires every 10 turns. While testing, add `"retainEveryNTurns": 1` to `~/.hindsight/codex.json`.
|
|
||||||
|
|
||||||
**Debug mode**: Add `"debug": true` to `~/.hindsight/codex.json` to see what Hindsight is doing on each turn:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] Recalling from bank 'codex', query length: 42
|
|
||||||
[Hindsight] Injecting 3 memories
|
|
||||||
[Hindsight] Retaining to bank 'codex', doc 'sess-abc123', 2 messages, 847 chars
|
|
||||||
```
|
|
||||||
|
|
||||||
**High latency on recall**: Use `"recallBudget": "low"` or reduce `recallMaxTokens` to speed up recall queries.
|
|
||||||
|
|
@ -1,245 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "CrewAI Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add long-term memory to your CrewAI agent crews. Hindsight provides fact extraction, entity tracking, and temporal awareness — persisted automatically across all crew runs."
|
|
||||||
---
|
|
||||||
|
|
||||||
# CrewAI
|
|
||||||
|
|
||||||
Persistent memory for AI agent crews via [CrewAI](https://github.com/crewAIInc/crewAI). Give your crews long-term memory with fact extraction, entity tracking, and temporal awareness.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/crewai)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Drop-in Storage Backend** - Implements CrewAI's `Storage` interface for `ExternalMemory`
|
|
||||||
- **Automatic Memory Flow** - CrewAI automatically stores task outputs and retrieves relevant memories
|
|
||||||
- **Per-Agent Banks** - Optionally give each agent its own isolated memory bank
|
|
||||||
- **Reflect Tool** - Agents can explicitly reason over memories with disposition-aware synthesis
|
|
||||||
- **Simple Configuration** - Configure once, use everywhere
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-crewai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure, HindsightStorage
|
|
||||||
from crewai.memory.external.external_memory import ExternalMemory
|
|
||||||
from crewai import Agent, Crew, Task
|
|
||||||
|
|
||||||
configure(hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
crew = Crew(
|
|
||||||
agents=[Agent(role="Researcher", goal="Find information", backstory="...")],
|
|
||||||
tasks=[Task(description="Research AI trends", expected_output="Report")],
|
|
||||||
external_memory=ExternalMemory(
|
|
||||||
storage=HindsightStorage(bank_id="my-crew")
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
crew.kickoff()
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it. CrewAI will automatically:
|
|
||||||
- **Query memories** at the start of each task
|
|
||||||
- **Store task outputs** to Hindsight after each task completes
|
|
||||||
|
|
||||||
Memories persist across crew runs, so your crew learns over time.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The integration maps CrewAI's 3-method `Storage` interface to Hindsight's API:
|
|
||||||
|
|
||||||
| CrewAI | Hindsight | What happens |
|
|
||||||
|--------|-----------|--------------|
|
|
||||||
| `save(value, metadata, agent)` | `retain(bank_id, content, ...)` | Task output is stored. Hindsight extracts facts, entities, and relationships from the raw text. |
|
|
||||||
| `search(query, limit)` | `recall(bank_id, query, ...)` | CrewAI constructs a query from the task description. Hindsight runs semantic search, BM25, graph traversal, and reranking. |
|
|
||||||
| `reset()` | `delete_bank(bank_id)` | Wipes the bank and optionally recreates it with its original mission. |
|
|
||||||
|
|
||||||
CrewAI calls `search()` automatically at the start of each task and `save()` after each task completes.
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888", # Hindsight API URL
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: "low", "mid", "high"
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match: any/all/any_strict/all_strict
|
|
||||||
verbose=True, # Enable logging
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Per-Storage Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
budget="high",
|
|
||||||
max_tokens=8192,
|
|
||||||
tags=["team:alpha"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bank Missions
|
|
||||||
|
|
||||||
Set a mission to guide how Hindsight processes and organizes memories:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
mission="Track software architecture decisions, technical debt, and team preferences.",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Per-Agent Memory Banks
|
|
||||||
|
|
||||||
Give each agent its own isolated memory bank:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
per_agent_banks=True,
|
|
||||||
# Researcher -> "my-crew-researcher"
|
|
||||||
# Writer -> "my-crew-writer"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use a custom bank resolver for full control:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
bank_resolver=lambda base, agent: f"{base}-{agent.lower()}" if agent else base,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
:::info
|
|
||||||
When `per_agent_banks=True`, the automatic `search()` at task start queries the base bank (shared context), since CrewAI's `search()` method does not receive the agent parameter. For per-agent search isolation, create separate `HindsightStorage` instances per agent.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Reflect Tool
|
|
||||||
|
|
||||||
CrewAI's storage interface only supports save/search/reset. To give agents access to Hindsight's `reflect` (disposition-aware memory synthesis), add it as a tool:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import HindsightReflectTool
|
|
||||||
|
|
||||||
reflect_tool = HindsightReflectTool(
|
|
||||||
bank_id="my-crew",
|
|
||||||
budget="mid",
|
|
||||||
reflect_context="You are helping a software team track decisions.",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
role="Analyst",
|
|
||||||
goal="Analyze project history",
|
|
||||||
backstory="...",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
When the agent calls this tool, it gets a synthesized, contextual answer based on all relevant memories rather than raw fact snippets.
|
|
||||||
|
|
||||||
## Full Example
|
|
||||||
|
|
||||||
A research crew that remembers findings across runs:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure, HindsightStorage, HindsightReflectTool
|
|
||||||
from crewai.memory.external.external_memory import ExternalMemory
|
|
||||||
from crewai import Agent, Crew, Task
|
|
||||||
|
|
||||||
configure(hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="research-crew",
|
|
||||||
mission="Track technology research findings and comparisons.",
|
|
||||||
)
|
|
||||||
|
|
||||||
reflect_tool = HindsightReflectTool(bank_id="research-crew", budget="mid")
|
|
||||||
|
|
||||||
researcher = Agent(
|
|
||||||
role="Researcher",
|
|
||||||
goal="Research topics, building on prior knowledge.",
|
|
||||||
backstory="Before starting, use hindsight_reflect to check what you already know.",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
|
|
||||||
writer = Agent(
|
|
||||||
role="Writer",
|
|
||||||
goal="Write summaries incorporating prior findings.",
|
|
||||||
backstory="Use hindsight_reflect to recall prior research.",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
|
|
||||||
crew = Crew(
|
|
||||||
agents=[researcher, writer],
|
|
||||||
tasks=[
|
|
||||||
Task(description="Research the benefits of Rust", expected_output="Analysis", agent=researcher),
|
|
||||||
Task(description="Write an executive summary", expected_output="Summary", agent=writer),
|
|
||||||
],
|
|
||||||
external_memory=ExternalMemory(storage=storage),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run 1: researches Rust, stores findings
|
|
||||||
crew.kickoff()
|
|
||||||
|
|
||||||
# Run 2: recalls Rust research when comparing with Go
|
|
||||||
crew.tasks[0].description = "Compare Rust with Go"
|
|
||||||
crew.kickoff()
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Set global connection and default settings |
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `reset_config()` | Reset configuration to None |
|
|
||||||
|
|
||||||
### Storage
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `hindsight_api_url` | from config | Override API URL |
|
|
||||||
| `api_key` | from config | Override API key |
|
|
||||||
| `budget` | `"mid"` | Recall budget (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `per_agent_banks` | `False` | Give each agent its own bank |
|
|
||||||
| `bank_resolver` | `None` | Custom `(bank_id, agent) -> bank_id` |
|
|
||||||
| `mission` | `None` | Bank mission for memory organization |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
### Reflect Tool
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `budget` | `"mid"` | Reflect budget (low/mid/high) |
|
|
||||||
| `reflect_context` | `None` | Additional context for reasoning |
|
|
||||||
| `hindsight_api_url` | from config | Override API URL |
|
|
||||||
| `api_key` | from config | Override API key |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- crewai >= 0.86.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 10
|
|
||||||
title: "Hermes Agent Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Hermes Agent with Hindsight. Automatically recalls context before every LLM call and retains conversations for future sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hermes Agent
|
|
||||||
|
|
||||||
Persistent long-term memory for [Hermes Agent](https://github.com/NousResearch/hermes-agent) using [Hindsight](https://vectorize.io/hindsight). Automatically recalls relevant context before every LLM call and retains conversations for future sessions — plus explicit retain/recall/reflect tools.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Install the plugin into Hermes's Python environment
|
|
||||||
uv pip install hindsight-hermes --python $HOME/.hermes/hermes-agent/venv/bin/python
|
|
||||||
|
|
||||||
# 2. Configure (choose one)
|
|
||||||
# Option A: Config file (recommended)
|
|
||||||
mkdir -p ~/.hindsight
|
|
||||||
cat > ~/.hindsight/hermes.json << 'EOF'
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "http://localhost:9077",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Option B: Environment variables
|
|
||||||
export HINDSIGHT_API_URL=http://localhost:9077
|
|
||||||
export HINDSIGHT_BANK_ID=hermes
|
|
||||||
|
|
||||||
# 3. Start Hermes — the plugin activates automatically
|
|
||||||
hermes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every turn, queries Hindsight for relevant memories and injects them into the system prompt (via `pre_llm_call` hook)
|
|
||||||
- **Auto-retain** — after every response, retains the user/assistant exchange to Hindsight (via `post_llm_call` hook)
|
|
||||||
- **Explicit tools** — `hindsight_retain`, `hindsight_recall`, `hindsight_reflect` for direct model control
|
|
||||||
- **Config file** — `~/.hindsight/hermes.json` with the same field names as openclaw and claude-code integrations
|
|
||||||
- **Zero config overhead** — env vars still work as overrides for CI/automation
|
|
||||||
|
|
||||||
:::note
|
|
||||||
The lifecycle hooks (`pre_llm_call`/`post_llm_call`) require hermes-agent with [PR #2823](https://github.com/NousResearch/hermes-agent/pull/2823) or later. On older versions, only the three tools are registered — hooks are silently skipped.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin registers via Hermes's `hermes_agent.plugins` entry point system:
|
|
||||||
|
|
||||||
| Component | Purpose |
|
|
||||||
|-----------|---------|
|
|
||||||
| `pre_llm_call` hook | **Auto-recall** — query memories, inject as ephemeral system prompt context |
|
|
||||||
| `post_llm_call` hook | **Auto-retain** — store user/assistant exchange to Hindsight |
|
|
||||||
| `hindsight_retain` tool | Explicit memory storage (model-initiated) |
|
|
||||||
| `hindsight_recall` tool | Explicit memory search (model-initiated) |
|
|
||||||
| `hindsight_reflect` tool | LLM-synthesized answer from stored memories |
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended for production)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted). No local LLM needed — the server handles fact extraction.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-token",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon
|
|
||||||
|
|
||||||
If you're running `hindsight-embed` locally, point to it:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "http://localhost:9077",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Follow the [Quick Start](/developer/api/quickstart) guide to get the Hindsight API running.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
All settings are in `~/.hindsight/hermes.json`. Every setting can also be overridden via environment variables (env vars take priority).
|
|
||||||
|
|
||||||
### Connection & Daemon
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | — | `HINDSIGHT_API_URL` | Hindsight API URL |
|
|
||||||
| `hindsightApiToken` | `null` | `HINDSIGHT_API_TOKEN` / `HINDSIGHT_API_KEY` | Auth token for API |
|
|
||||||
| `apiPort` | `9077` | `HINDSIGHT_API_PORT` | Port for local Hindsight daemon |
|
|
||||||
| `daemonIdleTimeout` | `0` | `HINDSIGHT_DAEMON_IDLE_TIMEOUT` | Seconds before idle daemon shuts down (0 = never) |
|
|
||||||
| `embedVersion` | `"latest"` | `HINDSIGHT_EMBED_VERSION` | `hindsight-embed` version for `uvx` |
|
|
||||||
|
|
||||||
### LLM Provider (daemon mode only)
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `llmProvider` | auto-detect | `HINDSIGHT_LLM_PROVIDER` | LLM provider: `openai`, `anthropic`, `gemini`, `groq`, `ollama` |
|
|
||||||
| `llmModel` | provider default | `HINDSIGHT_LLM_MODEL` | Model override |
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | — | `HINDSIGHT_BANK_ID` | Memory bank ID |
|
|
||||||
| `bankMission` | `""` | `HINDSIGHT_BANK_MISSION` | Agent identity/purpose for the memory bank |
|
|
||||||
| `retainMission` | `null` | — | Custom retain mission (what to extract from conversations) |
|
|
||||||
| `bankIdPrefix` | `""` | — | Prefix for all bank IDs |
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `true` | `HINDSIGHT_AUTO_RECALL` | Enable automatic memory recall via `pre_llm_call` hook |
|
|
||||||
| `recallBudget` | `"mid"` | `HINDSIGHT_RECALL_BUDGET` | Recall effort: `low`, `mid`, `high` |
|
|
||||||
| `recallMaxTokens` | `4096` | `HINDSIGHT_RECALL_MAX_TOKENS` | Max tokens in recall response |
|
|
||||||
| `recallMaxQueryChars` | `800` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | Max chars of user message used as query |
|
|
||||||
| `recallPromptPreamble` | see below | — | Header text injected before recalled memories |
|
|
||||||
|
|
||||||
Default preamble:
|
|
||||||
> Relevant memories from past conversations (prioritize recent when conflicting). Only use memories that are directly useful to continue this conversation; ignore the rest:
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `true` | `HINDSIGHT_AUTO_RETAIN` | Enable automatic retention via `post_llm_call` hook |
|
|
||||||
| `retainEveryNTurns` | `1` | — | Retain every Nth turn |
|
|
||||||
| `retainOverlapTurns` | `2` | — | Extra overlap turns for continuity |
|
|
||||||
| `retainRoles` | `["user", "assistant"]` | — | Which message roles to retain |
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `false` | `HINDSIGHT_DEBUG` | Enable debug logging to stderr |
|
|
||||||
|
|
||||||
## Hermes Gateway (Telegram, Discord, Slack)
|
|
||||||
|
|
||||||
When using Hermes in gateway mode (multi-platform messaging), the plugin works across all platforms. Hermes creates a fresh `AIAgent` per message, and the plugin's `pre_llm_call` hook ensures relevant memories are recalled for each turn regardless of platform.
|
|
||||||
|
|
||||||
## Disabling Hermes's Built-in Memory
|
|
||||||
|
|
||||||
Hermes has a built-in `memory` tool that saves to local markdown files. If both are active, the LLM may prefer the built-in one. Disable it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hermes tools disable memory
|
|
||||||
```
|
|
||||||
|
|
||||||
Re-enable later with `hermes tools enable memory`.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Plugin not loading**: Verify the entry point is registered:
|
|
||||||
```bash
|
|
||||||
python -c "
|
|
||||||
import importlib.metadata
|
|
||||||
eps = importlib.metadata.entry_points(group='hermes_agent.plugins')
|
|
||||||
print(list(eps))
|
|
||||||
"
|
|
||||||
```
|
|
||||||
You should see `EntryPoint(name='hindsight', value='hindsight_hermes', ...)`.
|
|
||||||
|
|
||||||
**Tools don't appear in `/tools`**: Check that `hindsightApiUrl` (or `HINDSIGHT_API_URL`) is set. The plugin silently skips registration when unconfigured.
|
|
||||||
|
|
||||||
**Connection refused**: Verify the Hindsight API is running:
|
|
||||||
```bash
|
|
||||||
curl http://localhost:9077/health
|
|
||||||
```
|
|
||||||
|
|
||||||
**Recall returning no memories**: Memories need at least one retain cycle. Try storing a fact first, then asking about it in a new session.
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 7
|
|
||||||
title: "LangGraph & LangChain Persistent Memory with Hindsight"
|
|
||||||
description: "Add long-term memory to LangGraph and LangChain agents with Hindsight. Three integration patterns — tools, nodes, and BaseStore adapter — for persistent memory across agent runs."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LangGraph / LangChain
|
|
||||||
|
|
||||||
Persistent long-term memory for [LangGraph](https://langchain-ai.github.io/langgraph/) and [LangChain](https://python.langchain.com/) agents via Hindsight. Three integration patterns at different abstraction levels — the tools pattern works with both LangChain and LangGraph, while nodes and the BaseStore adapter are LangGraph-specific.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/langgraph)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Memory Tools** — retain, recall, and reflect as LangChain `@tool` functions compatible with `bind_tools()` and `ToolNode`. Works with **both LangChain and LangGraph** — no LangGraph dependency required for this pattern.
|
|
||||||
- **Graph Nodes** *(LangGraph)* — Pre-built nodes that auto-inject memories before LLM calls and auto-store after responses
|
|
||||||
- **BaseStore Adapter** *(LangGraph)* — Drop-in `BaseStore` implementation backed by Hindsight, for LangGraph's native memory patterns
|
|
||||||
- **Dynamic Banks** — Resolve bank IDs per-request from `RunnableConfig` for per-user memory
|
|
||||||
- **Async-Native** — Uses `aretain`, `arecall`, `areflect` directly — no thread-pool workarounds
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-langgraph
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start: Tools (LangChain & LangGraph)
|
|
||||||
|
|
||||||
The tools pattern creates standard LangChain `@tool` functions that work with any LangChain-compatible model via `bind_tools()`. You can use them with a LangGraph agent or with plain LangChain — no LangGraph required.
|
|
||||||
|
|
||||||
**With LangGraph (recommended):**
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_hindsight_tools
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools=tools)
|
|
||||||
|
|
||||||
result = await agent.ainvoke(
|
|
||||||
{"messages": [{"role": "user", "content": "Remember that I prefer dark mode"}]}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**With plain LangChain:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_hindsight_tools
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)
|
|
||||||
response = await model.ainvoke("Remember that I prefer dark mode")
|
|
||||||
```
|
|
||||||
|
|
||||||
When using plain LangChain, you handle the tool execution loop yourself — call the model, check for `tool_calls`, execute them, and feed results back. LangGraph automates this loop for you.
|
|
||||||
|
|
||||||
The agent gets three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## Quick Start: Memory Nodes (LangGraph)
|
|
||||||
|
|
||||||
Add recall and retain nodes to your graph for automatic memory injection and storage.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_recall_node, create_retain_node
|
|
||||||
from langgraph.graph import StateGraph, MessagesState, START, END
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
recall = create_recall_node(client=client, bank_id="user-123")
|
|
||||||
retain = create_retain_node(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
builder = StateGraph(MessagesState)
|
|
||||||
builder.add_node("recall", recall)
|
|
||||||
builder.add_node("agent", agent_node) # your LLM node
|
|
||||||
builder.add_node("retain", retain)
|
|
||||||
|
|
||||||
builder.add_edge(START, "recall")
|
|
||||||
builder.add_edge("recall", "agent")
|
|
||||||
builder.add_edge("agent", "retain")
|
|
||||||
builder.add_edge("retain", END)
|
|
||||||
|
|
||||||
graph = builder.compile()
|
|
||||||
```
|
|
||||||
|
|
||||||
The recall node extracts the latest user message, searches Hindsight, and injects matching memories as a `SystemMessage`. The retain node stores human messages (optionally AI messages too) after the response.
|
|
||||||
|
|
||||||
## Quick Start: BaseStore (LangGraph)
|
|
||||||
|
|
||||||
Use Hindsight as a LangGraph `BaseStore` for cross-thread persistent memory with semantic search.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import HindsightStore
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
store = HindsightStore(client=client)
|
|
||||||
|
|
||||||
graph = builder.compile(checkpointer=checkpointer, store=store)
|
|
||||||
|
|
||||||
# Store and search via the store API
|
|
||||||
await store.aput(("user", "123", "prefs"), "theme", {"value": "dark mode"})
|
|
||||||
results = await store.asearch(("user", "123", "prefs"), query="theme preference")
|
|
||||||
```
|
|
||||||
|
|
||||||
Namespace tuples are mapped to Hindsight bank IDs with `.` as separator (e.g., `("user", "123")` becomes bank `user.123`). Banks are auto-created on first access.
|
|
||||||
|
|
||||||
## Dynamic Bank IDs
|
|
||||||
|
|
||||||
Both nodes and the store support per-user bank resolution from `RunnableConfig`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
recall = create_recall_node(client=client, bank_id_from_config="user_id")
|
|
||||||
retain = create_retain_node(client=client, bank_id_from_config="user_id")
|
|
||||||
|
|
||||||
# Bank ID resolved at runtime from config
|
|
||||||
result = await graph.ainvoke(
|
|
||||||
{"messages": [{"role": "user", "content": "hello"}]},
|
|
||||||
config={"configurable": {"user_id": "user-456"}},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing a client to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_langgraph import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client — uses global config
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Retain Node Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
retain = create_retain_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
retain_human=True, # Store human messages (default: True)
|
|
||||||
retain_ai=False, # Store AI responses (default: False)
|
|
||||||
tags=["source:chat"], # Tags applied to stored memories
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recall Node Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
recall = create_recall_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
budget="low", # Recall budget: low/mid/high
|
|
||||||
max_results=10, # Max memories injected
|
|
||||||
max_tokens=4096, # Max tokens for recall
|
|
||||||
tags=["scope:user"], # Filter by tags
|
|
||||||
tags_match="all", # Tag match mode
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using `output_key` for Prompt Control
|
|
||||||
|
|
||||||
By default, the recall node appends a `SystemMessage` to `messages`. Use `output_key` to write memory text to a custom state field instead, giving you full control over prompt ordering:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import Optional
|
|
||||||
from langgraph.graph import MessagesState
|
|
||||||
|
|
||||||
class AgentState(MessagesState):
|
|
||||||
memory_context: Optional[str] = None
|
|
||||||
|
|
||||||
recall = create_recall_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
output_key="memory_context",
|
|
||||||
)
|
|
||||||
|
|
||||||
# In your agent node, read state["memory_context"] and prepend it
|
|
||||||
# to the system prompt before calling the model.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations and Notes
|
|
||||||
|
|
||||||
### HindsightStore
|
|
||||||
|
|
||||||
- **Async-only.** All sync methods (`batch`, `get`, `put`, `delete`, `search`, `list_namespaces`) raise `NotImplementedError`. Use the async variants (`abatch`, `aget`, `aput`, `adelete`, `asearch`, `alist_namespaces`) instead.
|
|
||||||
- **`get()` relies on recall.** There is no direct key lookup — the key is used as a recall query and only exact `document_id` matches are returned. Items that do not rank in the top recall results may appear missing.
|
|
||||||
- **`list_namespaces` is session-scoped.** It only tracks namespaces that have been written to via `aput()` during the current process. After a restart, `list_namespaces` returns empty even though data still exists in Hindsight.
|
|
||||||
- **`delete` is a no-op.** Calling `adelete()` logs a debug message but does not remove data from Hindsight. Hindsight's memory model is append-oriented; fact superseding is handled automatically during retain.
|
|
||||||
|
|
||||||
### Memory Nodes
|
|
||||||
|
|
||||||
- **SystemMessage ordering.** The recall node adds a `SystemMessage` with recalled memories. Because `MessagesState` uses `add_messages` (which appends), this message appears after existing messages rather than at position 0. The message has a stable ID (`hindsight_memory_context`) so it is updated rather than duplicated across invocations. If your LLM provider requires system messages first, sort or filter messages in your agent node before passing them to the model.
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
- **Tools** raise `HindsightError` on failure, which surfaces to the agent as a tool error.
|
|
||||||
- **Nodes** silently log errors and return empty messages, so a Hindsight outage does not crash your graph.
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode (any/all/any\_strict/all\_strict) |
|
|
||||||
| `retain_metadata` | `None` | Default metadata dict for retain operations |
|
|
||||||
| `retain_document_id` | `None` | Default document\_id for retain (groups/upserts memories) |
|
|
||||||
| `recall_types` | `None` | Fact types to filter (world, experience, opinion, observation) |
|
|
||||||
| `recall_include_entities` | `False` | Include entity information in recall results |
|
|
||||||
| `reflect_context` | `None` | Additional context for reflect operations |
|
|
||||||
| `reflect_max_tokens` | `None` | Max tokens for reflect results (defaults to `max_tokens`) |
|
|
||||||
| `reflect_response_schema` | `None` | JSON schema to constrain reflect output format |
|
|
||||||
| `reflect_tags` | `None` | Tags to filter memories used in reflect (defaults to `recall_tags`) |
|
|
||||||
| `reflect_tags_match` | `None` | Tag matching for reflect (defaults to `recall_tags_match`) |
|
|
||||||
| `include_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `include_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `create_recall_node()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static bank ID (or use `bank_id_from_config`) |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `max_results` | `10` | Max memories to inject |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `bank_id_from_config` | `"user_id"` | Config key to resolve bank ID at runtime |
|
|
||||||
| `output_key` | `None` | If set, write memory text to this state key instead of appending a SystemMessage to `messages` |
|
|
||||||
|
|
||||||
### `create_retain_node()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static bank ID (or use `bank_id_from_config`) |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `tags` | `None` | Tags applied to stored memories |
|
|
||||||
| `bank_id_from_config` | `"user_id"` | Config key to resolve bank ID at runtime |
|
|
||||||
| `retain_human` | `True` | Store human messages |
|
|
||||||
| `retain_ai` | `False` | Store AI responses |
|
|
||||||
|
|
||||||
### `HindsightStore()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `tags` | `None` | Tags applied to all retain operations |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- langchain-core >= 0.3.0
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- langgraph >= 0.3.0 *(only for nodes and store patterns — install with `pip install hindsight-langgraph[langgraph]`)*
|
|
||||||
|
|
@ -1,349 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
title: "LiteLLM Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to any LLM application via LiteLLM and Hindsight. Universal integration — works with any model or provider with just a few lines of code."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LiteLLM
|
|
||||||
|
|
||||||
Universal LLM memory integration via [LiteLLM](https://github.com/BerriAI/litellm). Add persistent memory to any LLM application with just a few lines of code.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/litellm)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Universal LLM Support** - Works with 100+ LLM providers via LiteLLM (OpenAI, Anthropic, Groq, Azure, AWS Bedrock, Google Vertex AI, and more)
|
|
||||||
- **Simple Integration** - Just configure, enable, and use `hindsight_litellm.completion()`
|
|
||||||
- **Automatic Memory Injection** - Relevant memories are injected into prompts before LLM calls
|
|
||||||
- **Automatic Conversation Storage** - Conversations are stored to Hindsight for future recall
|
|
||||||
- **Two Memory Modes** - Choose between `reflect` (synthesized context) or `recall` (raw memory retrieval)
|
|
||||||
- **Direct Memory APIs** - Query, synthesize, and store memories manually
|
|
||||||
- **Native Client Wrappers** - Alternative wrappers for OpenAI and Anthropic SDKs
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-litellm
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
# Configure and enable memory integration
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# Use the convenience wrapper - memory is automatically injected and stored
|
|
||||||
response = hindsight_litellm.completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What did we discuss about AI?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
When you call `completion()`, the following happens automatically:
|
|
||||||
|
|
||||||
1. **Memory Retrieval** - Hindsight is queried for relevant memories based on the conversation
|
|
||||||
2. **Prompt Injection** - Memories are injected into the system message
|
|
||||||
3. **LLM Call** - The enriched prompt is sent to the LLM
|
|
||||||
4. **Conversation Storage** - The conversation is stored to Hindsight for future recall
|
|
||||||
5. **Response Returned** - You receive the response as normal
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
# Required
|
|
||||||
hindsight_api_url="http://localhost:8888", # Hindsight API server URL
|
|
||||||
bank_id="my-agent", # Memory bank ID
|
|
||||||
|
|
||||||
api_key="your-api-key", # Optional API key for authentication
|
|
||||||
|
|
||||||
# Optional - Memory behavior
|
|
||||||
store_conversations=True, # Store conversations after LLM calls
|
|
||||||
inject_memories=True, # Inject relevant memories into prompts
|
|
||||||
use_reflect=False, # Use reflect API (synthesized) vs recall (raw memories)
|
|
||||||
reflect_include_facts=False, # Include source facts with reflect responses
|
|
||||||
max_memories=None, # Maximum memories to inject (None = unlimited)
|
|
||||||
max_memory_tokens=4096, # Maximum tokens for memory context
|
|
||||||
recall_budget="mid", # Recall budget: "low", "mid", "high"
|
|
||||||
fact_types=["world", "agent"], # Filter fact types to inject
|
|
||||||
|
|
||||||
# Optional - Bank Configuration
|
|
||||||
bank_name="My Agent", # Human-readable display name for the memory bank
|
|
||||||
mission="This agent...", # Instructions guiding what Hindsight should remember
|
|
||||||
|
|
||||||
# Optional - Advanced
|
|
||||||
injection_mode="system_message", # or "prepend_user"
|
|
||||||
excluded_models=["gpt-3.5*"], # Exclude certain models
|
|
||||||
verbose=True, # Enable verbose logging and debug info
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bank Configuration
|
|
||||||
|
|
||||||
The `mission` and `bank_name` parameters configure the memory bank itself. When provided, `configure()` will automatically create or update the bank with these settings.
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="support-router",
|
|
||||||
bank_name="Customer Support Router",
|
|
||||||
mission="""You're a customer support router - keep track of which types of issues
|
|
||||||
should go to which teams (billing, technical, sales), customer preferences for
|
|
||||||
communication channels, and past issue resolutions.""",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Memory Modes: Reflect vs Recall
|
|
||||||
|
|
||||||
- **Recall mode** (`use_reflect=False`, default): Retrieves raw memory facts and injects them as a numbered list. Best when you need precise, individual memories.
|
|
||||||
- **Reflect mode** (`use_reflect=True`): Synthesizes memories into a coherent context paragraph. Best for natural, conversational memory context.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Recall mode - raw memories
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=False, # Default
|
|
||||||
)
|
|
||||||
# Injects: "1. [WORLD] User prefers Python\n2. [MENTAL MODEL] User prefers simple code..."
|
|
||||||
|
|
||||||
# Reflect mode - synthesized context
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=True,
|
|
||||||
)
|
|
||||||
# Injects: "Based on previous conversations, the user is a Python developer who..."
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Provider Support
|
|
||||||
|
|
||||||
Works with any LiteLLM-supported provider:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# OpenAI
|
|
||||||
hindsight_litellm.completion(model="gpt-4o", messages=[...])
|
|
||||||
|
|
||||||
# Anthropic
|
|
||||||
hindsight_litellm.completion(model="claude-3-5-sonnet-20241022", messages=[...])
|
|
||||||
|
|
||||||
# Groq
|
|
||||||
hindsight_litellm.completion(model="groq/llama-3.1-70b-versatile", messages=[...])
|
|
||||||
|
|
||||||
# Azure OpenAI
|
|
||||||
hindsight_litellm.completion(model="azure/gpt-4", messages=[...])
|
|
||||||
|
|
||||||
# AWS Bedrock
|
|
||||||
hindsight_litellm.completion(model="bedrock/anthropic.claude-3", messages=[...])
|
|
||||||
|
|
||||||
# Google Vertex AI
|
|
||||||
hindsight_litellm.completion(model="vertex_ai/gemini-pro", messages=[...])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Direct Memory APIs
|
|
||||||
|
|
||||||
### Recall - Query raw memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, recall
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
memories = recall("what projects am I working on?", budget="mid")
|
|
||||||
for m in memories:
|
|
||||||
print(f"- [{m.fact_type}] {m.text}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reflect - Get synthesized context
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, reflect
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = reflect("what do you know about the user's preferences?")
|
|
||||||
print(result.text)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Retain - Store memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, retain
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = retain(
|
|
||||||
content="User mentioned they're working on a machine learning project",
|
|
||||||
context="Discussion about current projects",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Async APIs
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import arecall, areflect, aretain
|
|
||||||
|
|
||||||
# Async versions of all memory APIs
|
|
||||||
memories = await arecall("what do you know about me?")
|
|
||||||
context = await areflect("summarize user preferences")
|
|
||||||
result = await aretain(content="New information to remember")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Native Client Wrappers
|
|
||||||
|
|
||||||
Alternative to LiteLLM callbacks for direct SDK integration.
|
|
||||||
|
|
||||||
### OpenAI Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from openai import OpenAI
|
|
||||||
from hindsight_litellm import wrap_openai
|
|
||||||
|
|
||||||
client = OpenAI()
|
|
||||||
wrapped = wrap_openai(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.chat.completions.create(
|
|
||||||
model="gpt-4",
|
|
||||||
messages=[{"role": "user", "content": "What do you know about me?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Anthropic Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from anthropic import Anthropic
|
|
||||||
from hindsight_litellm import wrap_anthropic
|
|
||||||
|
|
||||||
client = Anthropic()
|
|
||||||
wrapped = wrap_anthropic(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.messages.create(
|
|
||||||
model="claude-3-5-sonnet-20241022",
|
|
||||||
max_tokens=1024,
|
|
||||||
messages=[{"role": "user", "content": "Hello!"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debug Mode
|
|
||||||
|
|
||||||
When `verbose=True`, you can inspect exactly what memories are being injected:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, enable, completion, get_last_injection_debug
|
|
||||||
|
|
||||||
configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
verbose=True,
|
|
||||||
)
|
|
||||||
enable()
|
|
||||||
|
|
||||||
response = completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What's my favorite color?"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Inspect what was injected
|
|
||||||
debug = get_last_injection_debug()
|
|
||||||
if debug:
|
|
||||||
print(f"Mode: {debug.mode}") # "reflect" or "recall"
|
|
||||||
print(f"Injected: {debug.injected}") # True/False
|
|
||||||
print(f"Results: {debug.results_count}")
|
|
||||||
print(f"Memory context:\n{debug.memory_context}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context Manager
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import hindsight_memory
|
|
||||||
import litellm
|
|
||||||
|
|
||||||
with hindsight_memory(bank_id="user-123"):
|
|
||||||
response = litellm.completion(model="gpt-4", messages=[...])
|
|
||||||
# Memory integration automatically disabled after context
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disabling and Cleanup
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import disable, cleanup
|
|
||||||
|
|
||||||
# Temporarily disable memory integration
|
|
||||||
disable()
|
|
||||||
|
|
||||||
# Clean up all resources (call when shutting down)
|
|
||||||
cleanup()
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Main Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Configure global Hindsight settings |
|
|
||||||
| `enable()` | Enable memory integration with LiteLLM |
|
|
||||||
| `disable()` | Disable memory integration |
|
|
||||||
| `is_enabled()` | Check if memory integration is enabled |
|
|
||||||
| `cleanup()` | Clean up all resources |
|
|
||||||
|
|
||||||
### Configuration Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `is_configured()` | Check if Hindsight is configured |
|
|
||||||
| `reset_config()` | Reset configuration to defaults |
|
|
||||||
|
|
||||||
### Memory Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `recall(query, ...)` | Synchronously query raw memories |
|
|
||||||
| `arecall(query, ...)` | Asynchronously query raw memories |
|
|
||||||
| `reflect(query, ...)` | Synchronously get synthesized memory context |
|
|
||||||
| `areflect(query, ...)` | Asynchronously get synthesized memory context |
|
|
||||||
| `retain(content, ...)` | Synchronously store a memory |
|
|
||||||
| `aretain(content, ...)` | Asynchronously store a memory |
|
|
||||||
|
|
||||||
### Debug Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_last_injection_debug()` | Get debug info from last memory injection |
|
|
||||||
| `clear_injection_debug()` | Clear stored debug info |
|
|
||||||
|
|
||||||
### Client Wrappers
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `wrap_openai(client, ...)` | Wrap OpenAI client with memory |
|
|
||||||
| `wrap_anthropic(client, ...)` | Wrap Anthropic client with memory |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- litellm >= 1.40.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,251 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 8
|
|
||||||
title: "LlamaIndex Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to LlamaIndex agents with Hindsight. Supports agent-driven tools (HindsightToolSpec) and automatic memory via the BaseMemory interface."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LlamaIndex
|
|
||||||
|
|
||||||
Persistent long-term memory for [LlamaIndex](https://docs.llamaindex.ai/) agents via Hindsight. The `hindsight-llamaindex` package provides two complementary patterns:
|
|
||||||
|
|
||||||
- **`HindsightToolSpec`** — Agent-driven memory tools (retain/recall/reflect)
|
|
||||||
- **`HindsightMemory`** — Automatic memory via LlamaIndex's `BaseMemory` interface
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-llamaindex
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Automatic Memory (BaseMemory)
|
|
||||||
|
|
||||||
The simplest way to add Hindsight memory to a LlamaIndex agent. Messages are automatically stored on each turn, and relevant memories are recalled and injected as context.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_llamaindex import HindsightMemory
|
|
||||||
from llama_index.core.agent import ReActAgent
|
|
||||||
from llama_index.llms.openai import OpenAI
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
memory = HindsightMemory.from_client(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences and project context",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=[], llm=OpenAI(model="gpt-4o"))
|
|
||||||
response = await agent.run("Remember that I prefer dark mode", memory=memory)
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
### How It Works
|
|
||||||
|
|
||||||
| Event | What Happens |
|
|
||||||
|-------|-------------|
|
|
||||||
| Agent receives input | `aget(input)` recalls relevant memories from Hindsight, prepends as system message |
|
|
||||||
| Agent produces output | `aput(message)` retains the message to Hindsight for future recall |
|
|
||||||
| New session starts | Previous memories are available via recall; local chat buffer starts empty |
|
|
||||||
|
|
||||||
### `HindsightMemory.from_client()`
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| `client` | `Hindsight` | *required* | Hindsight client instance |
|
|
||||||
| `bank_id` | `str` | *required* | Memory bank ID |
|
|
||||||
| `mission` | `str` | `None` | Bank mission — auto-creates bank on first use |
|
|
||||||
| `context` | `str` | `"llamaindex"` | Source label for retain operations |
|
|
||||||
| `budget` | `str` | `"mid"` | Recall budget level |
|
|
||||||
| `max_tokens` | `int` | `4096` | Max recall tokens |
|
|
||||||
| `tags` | `list[str]` | `None` | Tags for retain operations |
|
|
||||||
| `recall_tags` | `list[str]` | `None` | Tags to filter recall |
|
|
||||||
| `recall_tags_match` | `str` | `"any"` | Tag matching mode |
|
|
||||||
| `system_prompt` | `str` | *(built-in)* | Template for memory system message. Must contain `{memories}` |
|
|
||||||
| `chat_history_limit` | `int` | `100` | Max messages in local buffer |
|
|
||||||
|
|
||||||
Also available: `HindsightMemory.from_url(hindsight_api_url, bank_id, ...)` for creating without a pre-built client.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Agent-Driven Tools (BaseToolSpec)
|
|
||||||
|
|
||||||
For explicit control, expose retain/recall/reflect as tools the agent can choose to call.
|
|
||||||
|
|
||||||
### Quick Start: Tool Spec
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_llamaindex import HindsightToolSpec
|
|
||||||
from llama_index.llms.openai import OpenAI
|
|
||||||
from llama_index.core.agent import ReActAgent
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
tools = spec.to_tool_list()
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=tools, llm=OpenAI(model="gpt-4o"))
|
|
||||||
response = await agent.run("Remember that I prefer dark mode")
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
### Quick Start: Factory Function
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Selecting Tools
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Via to_tool_list()
|
|
||||||
tools = spec.to_tool_list(spec_functions=["recall_memory", "reflect_on_memory"])
|
|
||||||
|
|
||||||
# Via factory flags
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Set defaults via `configure()`, override per-call:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid",
|
|
||||||
tags=["source:llamaindex"],
|
|
||||||
context="my-app",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client/url
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
### `HindsightToolSpec()`
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| `bank_id` | `str` | *required* | Hindsight memory bank to operate on |
|
|
||||||
| `client` | `Hindsight` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `str` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `str` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `str` | `None` → `"mid"` | Recall/reflect budget: `low`, `mid`, `high` |
|
|
||||||
| `max_tokens` | `int` | `None` → `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `list[str]` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `list[str]` | `None` | Tags to filter recall results |
|
|
||||||
| `recall_tags_match` | `str` | `None` → `"any"` | Tag matching: `any`, `all`, `any_strict`, `all_strict` |
|
|
||||||
| `retain_metadata` | `dict[str, str]` | `None` | Default metadata for retain operations |
|
|
||||||
| `retain_document_id` | `str` | `None` | Document ID for retain. Auto-generates `{session}-{timestamp}` if not set |
|
|
||||||
| `retain_context` | `str` | `"llamaindex"` | Source label for retain operations |
|
|
||||||
| `recall_types` | `list[str]` | `None` | Fact types: `world`, `experience`, `opinion`, `observation` |
|
|
||||||
| `recall_include_entities` | `bool` | `False` | Include entity info in recall results |
|
|
||||||
| `reflect_context` | `str` | `None` | Additional context for reflect |
|
|
||||||
| `reflect_max_tokens` | `int` | `None` | Max tokens for reflect (defaults to `max_tokens`) |
|
|
||||||
| `reflect_response_schema` | `dict` | `None` | JSON schema to constrain reflect output |
|
|
||||||
| `reflect_tags` | `list[str]` | `None` | Tags for reflect (defaults to `recall_tags`) |
|
|
||||||
| `reflect_tags_match` | `str` | `None` | Tag matching for reflect (defaults to `recall_tags_match`) |
|
|
||||||
| `mission` | `str` | `None` | Bank mission — auto-creates bank on first use |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Production Patterns
|
|
||||||
|
|
||||||
### Bank Mission
|
|
||||||
|
|
||||||
Set a mission to give the memory engine context for fact extraction:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Tools
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user coding preferences, project context, and technical decisions",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Memory
|
|
||||||
memory = HindsightMemory.from_client(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user coding preferences, project context, and technical decisions",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
The bank is created automatically on first use. If it already exists, creation is silently skipped.
|
|
||||||
|
|
||||||
### Memory Scoping with Tags
|
|
||||||
|
|
||||||
```python
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
tags=["source:chat", "session:abc"], # applied to all retains
|
|
||||||
recall_tags=["source:chat"], # filter recalls to chat memories
|
|
||||||
recall_tags_match="any",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
Both patterns handle errors gracefully — operations are logged and return friendly messages instead of raising exceptions. Agents continue functioning even if memory is unavailable.
|
|
||||||
|
|
||||||
### Combining Tools + Memory
|
|
||||||
|
|
||||||
Use both patterns together for maximum flexibility:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import create_hindsight_tools, HindsightMemory
|
|
||||||
|
|
||||||
# Automatic memory for context enrichment
|
|
||||||
memory = HindsightMemory.from_client(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
# Explicit tools for agent-driven reflect
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=False, # memory handles retain automatically
|
|
||||||
include_recall=False, # memory handles recall automatically
|
|
||||||
include_reflect=True, # agent can still explicitly reflect
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=tools, llm=llm)
|
|
||||||
|
|
||||||
# Pass memory to run()
|
|
||||||
response = await agent.run("What should I prioritize?", memory=memory)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python 3.10+
|
|
||||||
- `llama-index-core >= 0.11.0`
|
|
||||||
- `hindsight-client >= 0.4.0`
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 2
|
|
||||||
title: "Hindsight Local MCP Server | Persistent Memory for Claude"
|
|
||||||
description: "Run Hindsight as a local MCP server with embedded PostgreSQL — no external setup required. Ideal for Claude Code and Claude Desktop for long-term memory across conversations."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Local MCP Server
|
|
||||||
|
|
||||||
Hindsight provides a local MCP server that runs entirely on your machine with an embedded PostgreSQL database. No external server or database setup required.
|
|
||||||
|
|
||||||
This is ideal for:
|
|
||||||
- **Personal use with Claude Code / Claude Desktop** — Give Claude long-term memory across conversations
|
|
||||||
- **Development and testing** — Quick setup without infrastructure
|
|
||||||
- **Privacy-focused setups** — All data stays on your machine
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
Running `hindsight-local-mcp` starts the full Hindsight API on `localhost:8888` with an embedded PostgreSQL database (pg0). You then connect your MCP client to it over HTTP.
|
|
||||||
|
|
||||||
- Starts an embedded PostgreSQL (pg0) automatically
|
|
||||||
- Runs database migrations on startup
|
|
||||||
- Exposes the full MCP endpoint at `http://localhost:8888/mcp/`
|
|
||||||
- Data persists in `~/.pg0/hindsight-mcp/` across restarts
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
### 1. Start the server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Or with Ollama (no API key needed):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_PROVIDER=ollama HINDSIGHT_API_LLM_MODEL=llama3.2 uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure your MCP client
|
|
||||||
|
|
||||||
**Claude Code:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/
|
|
||||||
```
|
|
||||||
|
|
||||||
**Other MCP clients** — add an HTTP transport entry pointing to `http://localhost:8888/mcp/`.
|
|
||||||
|
|
||||||
## Bank Modes
|
|
||||||
|
|
||||||
The local server supports the same two modes as the hosted API:
|
|
||||||
|
|
||||||
### Multi-bank mode (default)
|
|
||||||
|
|
||||||
Use `http://localhost:8888/mcp/` — exposes all tools including bank management. Bank is selected per-request via the `bank_id` tool parameter or the `X-Bank-Id` header.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Single-bank mode
|
|
||||||
|
|
||||||
Use `http://localhost:8888/mcp/<bank-id>/` — pins all tools to one bank, no `bank_id` parameter needed. This replaces the old `HINDSIGHT_API_MCP_LOCAL_BANK_ID` env var.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/my-bank/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Tools
|
|
||||||
|
|
||||||
The local server exposes the full tool set (29 tools in multi-bank mode, 26 in single-bank mode):
|
|
||||||
|
|
||||||
**Core Memory**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `retain` | Store information to long-term memory with optional tags, metadata, and document association |
|
|
||||||
| `recall` | Search memories with natural language, configurable budget, type filters, and tag filters |
|
|
||||||
| `reflect` | Synthesize memories into a reasoned answer with optional structured output |
|
|
||||||
|
|
||||||
**Mental Models**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_mental_models` | List pinned reflections for a bank |
|
|
||||||
| `get_mental_model` | Get a specific mental model |
|
|
||||||
| `create_mental_model` | Create a new mental model with optional auto-refresh trigger |
|
|
||||||
| `update_mental_model` | Update a mental model's metadata |
|
|
||||||
| `delete_mental_model` | Delete a mental model |
|
|
||||||
| `refresh_mental_model` | Regenerate a mental model's content |
|
|
||||||
|
|
||||||
**Directives**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_directives` | List directives that guide memory processing |
|
|
||||||
| `create_directive` | Create a new directive |
|
|
||||||
| `delete_directive` | Delete a directive |
|
|
||||||
|
|
||||||
**Memory Browsing**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_memories` | Browse memories with filtering and pagination |
|
|
||||||
| `get_memory` | Get a specific memory by ID |
|
|
||||||
| `delete_memory` | Delete a specific memory |
|
|
||||||
|
|
||||||
**Documents**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_documents` | List ingested documents |
|
|
||||||
| `get_document` | Get a specific document |
|
|
||||||
| `delete_document` | Delete a document and its linked memories |
|
|
||||||
|
|
||||||
**Operations**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_operations` | List async operations with status filtering |
|
|
||||||
| `get_operation` | Check operation status and progress |
|
|
||||||
| `cancel_operation` | Cancel a pending or running operation |
|
|
||||||
|
|
||||||
**Tags & Bank Management**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_tags` | List unique tags used in a bank |
|
|
||||||
| `get_bank` | Get bank profile (name, mission, disposition) |
|
|
||||||
| `get_bank_stats` | Get bank statistics (multi-bank only) |
|
|
||||||
| `update_bank` | Update bank name or mission |
|
|
||||||
| `delete_bank` | Delete an entire bank and all its data |
|
|
||||||
| `clear_memories` | Clear memories without deleting the bank |
|
|
||||||
| `list_banks` | List all memory banks (multi-bank only) |
|
|
||||||
| `create_bank` | Create or configure a memory bank (multi-bank only) |
|
|
||||||
|
|
||||||
For detailed parameter documentation, see the [MCP Server reference](/developer/mcp-server#available-tools).
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
All standard [Hindsight configuration variables](/developer/configuration) are supported. Key ones for local use:
|
|
||||||
|
|
||||||
| Variable | Required | Default | Description |
|
|
||||||
|----------|----------|---------|-------------|
|
|
||||||
| `HINDSIGHT_API_LLM_API_KEY` | Yes* | — | API key for your LLM provider |
|
|
||||||
| `HINDSIGHT_API_LLM_PROVIDER` | No | `openai` | LLM provider (`openai`, `anthropic`, `ollama`, etc.) |
|
|
||||||
| `HINDSIGHT_API_LLM_MODEL` | No | `gpt-4o-mini` | Model name |
|
|
||||||
| `HINDSIGHT_API_DATABASE_URL` | No | `pg0://hindsight-mcp` | Override the database URL |
|
|
||||||
| `HINDSIGHT_API_PORT` | No | `8888` | Port to listen on |
|
|
||||||
| `HINDSIGHT_API_LOG_LEVEL` | No | `info` | Log level |
|
|
||||||
|
|
||||||
*Not required when using a local provider like Ollama.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Slow first startup
|
|
||||||
|
|
||||||
The first startup downloads the local embedding model (~100MB) and initializes the database. Subsequent starts are faster.
|
|
||||||
|
|
||||||
### Port already in use
|
|
||||||
|
|
||||||
Set a different port:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... HINDSIGHT_API_PORT=9000 uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Then update your MCP client URL to `http://localhost:9000/mcp/`.
|
|
||||||
|
|
||||||
### Checking logs
|
|
||||||
|
|
||||||
Set `HINDSIGHT_API_LOG_LEVEL=debug` for verbose output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... HINDSIGHT_API_LOG_LEVEL=debug uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "NemoClaw Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to NemoClaw sandboxed agents with Hindsight. One command adds automated memory extraction and auto-recall to any NemoClaw sandbox — no code changes required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# NemoClaw
|
|
||||||
|
|
||||||
Persistent memory for [NemoClaw](https://nemoclaw.ai) sandboxed agents using [Hindsight](https://hindsight.vectorize.io).
|
|
||||||
|
|
||||||
NemoClaw runs [OpenClaw](https://openclaw.ai) inside an OpenShell sandbox with controlled filesystem, process, and network egress policies. The `hindsight-nemoclaw` package automates adding Hindsight memory to a sandbox in one command — no code changes required.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/nemoclaw)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx @vectorize-io/hindsight-nemoclaw setup \
|
|
||||||
--sandbox my-assistant \
|
|
||||||
--api-url https://api.hindsight.vectorize.io \
|
|
||||||
--api-token <your-api-key> \
|
|
||||||
--bank-prefix my-sandbox
|
|
||||||
```
|
|
||||||
|
|
||||||
Get an API key at [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup).
|
|
||||||
|
|
||||||
You'll see output like:
|
|
||||||
|
|
||||||
```
|
|
||||||
[0] Preflight checks...
|
|
||||||
✓ openshell found
|
|
||||||
✓ openclaw found
|
|
||||||
|
|
||||||
[1] Installing @vectorize-io/hindsight-openclaw plugin...
|
|
||||||
✓ Plugin installed
|
|
||||||
|
|
||||||
[2] Configuring plugin in ~/.openclaw/openclaw.json...
|
|
||||||
✓ Plugin config written (bank: my-sandbox-openclaw)
|
|
||||||
|
|
||||||
[3] Applying Hindsight network policy to sandbox "my-assistant"...
|
|
||||||
✓ Policy version 2 submitted
|
|
||||||
✓ Policy version 2 loaded (active version: 2)
|
|
||||||
|
|
||||||
[4] Restarting OpenClaw gateway...
|
|
||||||
✓ Gateway restarted
|
|
||||||
|
|
||||||
✓ Setup complete!
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### The sandbox problem
|
|
||||||
|
|
||||||
OpenShell enforces strict network egress — every outbound endpoint must be explicitly permitted in the sandbox policy. By default, the Hindsight API (`api.hindsight.vectorize.io`) is not in that list.
|
|
||||||
|
|
||||||
The `hindsight-openclaw` plugin supports **external API mode**, where it skips the local daemon entirely and makes direct HTTPS calls to Hindsight Cloud. This is the natural fit for sandboxed environments: the plugin becomes a thin HTTP client, and the only sandbox change needed is one egress rule.
|
|
||||||
|
|
||||||
### What the setup command does
|
|
||||||
|
|
||||||
1. **Preflight** — verifies `openshell` and `openclaw` are installed
|
|
||||||
2. **Install plugin** — runs `openclaw plugins install @vectorize-io/hindsight-openclaw`
|
|
||||||
3. **Configure plugin** — writes external API mode config to `~/.openclaw/openclaw.json`
|
|
||||||
4. **Apply policy** — reads the current sandbox policy, merges the Hindsight egress block, and re-applies via `openshell policy set`
|
|
||||||
5. **Restart gateway** — runs `openclaw gateway restart`
|
|
||||||
|
|
||||||
### Memory flow
|
|
||||||
|
|
||||||
Once set up, the `hindsight-openclaw` plugin hooks into the OpenClaw gateway lifecycle:
|
|
||||||
|
|
||||||
- **`before_agent_start`** — recalls relevant memories from past sessions and injects them into context
|
|
||||||
- **`agent_end`** — retains the conversation to the Hindsight memory bank
|
|
||||||
|
|
||||||
The sandbox doesn't interfere with either step — it sees the Hindsight calls as normal HTTPS egress to a permitted endpoint.
|
|
||||||
|
|
||||||
## CLI Reference
|
|
||||||
|
|
||||||
```
|
|
||||||
hindsight-nemoclaw setup [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--sandbox <name> NemoClaw sandbox name (required)
|
|
||||||
--api-url <url> Hindsight API URL (required)
|
|
||||||
--api-token <token> Hindsight API token (required)
|
|
||||||
--bank-prefix <prefix> Memory bank prefix (default: "nemoclaw")
|
|
||||||
--skip-policy Skip sandbox network policy update
|
|
||||||
--skip-plugin-install Skip openclaw plugin installation
|
|
||||||
--dry-run Preview changes without applying
|
|
||||||
--help Show help
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `--dry-run` to preview all changes before applying anything. Use `--skip-policy` if you manage sandbox policies manually.
|
|
||||||
|
|
||||||
## Manual Setup
|
|
||||||
|
|
||||||
If you prefer to apply the steps yourself instead of using the CLI:
|
|
||||||
|
|
||||||
### 1. Install the plugin
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure `~/.openclaw/openclaw.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"hindsightApiUrl": "https://api.hindsight.vectorize.io",
|
|
||||||
"hindsightApiToken": "<your-api-key>",
|
|
||||||
"llmProvider": "claude-code",
|
|
||||||
"dynamicBankId": false,
|
|
||||||
"bankIdPrefix": "my-sandbox"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`llmProvider: "claude-code"` uses the Claude Code process already present in the sandbox — no additional API key needed.
|
|
||||||
|
|
||||||
### 3. Add the Hindsight network policy
|
|
||||||
|
|
||||||
`openshell policy set` replaces the entire policy document. Export your current policy first, add the Hindsight block, then re-apply:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
network_policies:
|
|
||||||
hindsight:
|
|
||||||
name: hindsight
|
|
||||||
endpoints:
|
|
||||||
- host: api.hindsight.vectorize.io
|
|
||||||
port: 443
|
|
||||||
protocol: rest
|
|
||||||
tls: terminate
|
|
||||||
enforcement: enforce
|
|
||||||
rules:
|
|
||||||
- allow:
|
|
||||||
method: GET
|
|
||||||
path: /**
|
|
||||||
- allow:
|
|
||||||
method: POST
|
|
||||||
path: /**
|
|
||||||
- allow:
|
|
||||||
method: PUT
|
|
||||||
path: /**
|
|
||||||
binaries:
|
|
||||||
- path: /usr/local/bin/openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openshell policy set my-sandbox --policy /path/to/full-policy.yaml --wait
|
|
||||||
openclaw gateway restart
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `hindsightApiUrl` | string | — | Hindsight API base URL |
|
|
||||||
| `hindsightApiToken` | string | — | API token for authentication |
|
|
||||||
| `llmProvider` | string | auto-detect | LLM provider for memory extraction |
|
|
||||||
| `dynamicBankId` | boolean | `false` | Isolate memory per user (`true`) or share across sessions (`false`) |
|
|
||||||
| `bankIdPrefix` | string | `"nemoclaw"` | Prefix for the memory bank name |
|
|
||||||
|
|
||||||
### Bank naming
|
|
||||||
|
|
||||||
When `dynamicBankId: false`, all sessions write to a single bank named `{bankIdPrefix}-openclaw`. When `dynamicBankId: true`, each user gets an isolated bank — useful for multi-tenant deployments.
|
|
||||||
|
|
||||||
## Verifying It Works
|
|
||||||
|
|
||||||
After setup, check the gateway logs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
```
|
|
||||||
|
|
||||||
On startup you should see:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] Plugin loaded successfully
|
|
||||||
[Hindsight] ✓ Using external API: https://api.hindsight.vectorize.io
|
|
||||||
[Hindsight] External API health: {"status":"healthy","database":"connected"}
|
|
||||||
[Hindsight] Default bank: my-sandbox-openclaw
|
|
||||||
[Hindsight] ✓ Ready (external API mode)
|
|
||||||
```
|
|
||||||
|
|
||||||
After a conversation:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] before_agent_start - bank: my-sandbox-openclaw, channel: undefined/webchat
|
|
||||||
[Hindsight Hook] agent_end triggered - bank: my-sandbox-openclaw
|
|
||||||
[Hindsight] Retained 6 messages to bank my-sandbox-openclaw for session agent:main:...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pitfalls
|
|
||||||
|
|
||||||
### Policy replacement is full-document
|
|
||||||
|
|
||||||
`openshell policy set` replaces the entire policy document. The `hindsight-nemoclaw setup` command handles this automatically. If you're applying manually, export the current policy first so existing rules aren't lost.
|
|
||||||
|
|
||||||
### LaunchAgent can't follow symlinks on macOS
|
|
||||||
|
|
||||||
On macOS, the OpenClaw gateway runs as a LaunchAgent under a restricted security context. `openclaw plugins install --link` creates a symlink the LaunchAgent can't follow — the setup command installs as a copy instead. If you see `EPERM: operation not permitted, scandir` in gateway logs, this is the cause.
|
|
||||||
|
|
||||||
### Memory retention is asynchronous
|
|
||||||
|
|
||||||
Fact extraction and entity resolution happen in the background after `retain`. If you open a new session immediately after closing one, the most recent memories may not be indexed yet — typically a few seconds.
|
|
||||||
|
|
||||||
### Binary-scoped egress
|
|
||||||
|
|
||||||
The `binaries` field in the network policy restricts the egress rule to a specific executable path. If OpenClaw updates and the binary path changes, the rule silently stops working. Check your binary path after upgrades.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin not loading
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins list | grep hindsight
|
|
||||||
# Should show: ✓ enabled │ Hindsight Memory │ ...
|
|
||||||
|
|
||||||
# Reinstall
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Egress blocked
|
|
||||||
|
|
||||||
If calls to `api.hindsight.vectorize.io` are being blocked, check the active sandbox policy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openshell sandbox get my-assistant
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify the `hindsight` block is present and the `binaries` path matches your OpenClaw binary:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
which openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### External API not connecting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# If you see daemon startup messages instead of "Using external API",
|
|
||||||
# the plugin config isn't being read — check ~/.openclaw/openclaw.json
|
|
||||||
```
|
|
||||||
|
|
@ -1,374 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 4
|
|
||||||
title: "OpenClaw Persistent Memory with Hindsight | Plugin Integration"
|
|
||||||
description: "Add persistent, automated memory to your OpenClaw agent with Hindsight. Local-first, open source — one plugin install replaces built-in memory with structured knowledge extraction and auto-recall."
|
|
||||||
---
|
|
||||||
|
|
||||||
# OpenClaw
|
|
||||||
|
|
||||||
Local, long term memory for [OpenClaw](https://openclaw.ai) agents using [Hindsight](https://vectorize.io/hindsight).
|
|
||||||
|
|
||||||
This plugin integrates [hindsight-embed](https://vectorize.io/hindsight/cli), a standalone daemon that bundles Hindsight's memory engine (API + PostgreSQL) into a single command. Everything runs locally on your machine, reuses the LLM you're already paying for, and costs nothing extra.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/openclaw)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
**Step 1: Set up LLM for memory extraction**
|
|
||||||
|
|
||||||
Choose one provider and set its API key:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option A: OpenAI
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option B: Anthropic
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option C: Gemini
|
|
||||||
export GEMINI_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option D: Groq
|
|
||||||
export GROQ_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option E: Claude Code (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option F: OpenAI Codex (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 2: Install the plugin**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 3: Start OpenClaw**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw gateway
|
|
||||||
```
|
|
||||||
|
|
||||||
The plugin will automatically:
|
|
||||||
- Start a local Hindsight daemon (port 9077)
|
|
||||||
- Capture conversations after each turn
|
|
||||||
- Inject relevant memories before agent responses
|
|
||||||
|
|
||||||
**Important:** The LLM you configure above is **only for memory extraction** (background processing). Your main OpenClaw agent can use any model you configure separately.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
**Auto-Capture:** Every conversation is automatically stored after each turn. Facts, entities, and relationships are extracted in the background.
|
|
||||||
|
|
||||||
**Auto-Recall:** Before each agent response, relevant memories are automatically injected into the context (up to 1024 tokens). The agent uses past context without needing to call tools.
|
|
||||||
|
|
||||||
**Feedback Loop Prevention:** The plugin automatically strips injected memory tags (`<hindsight_memories>`) before storing conversations. This prevents recalled memories from being re-extracted as new facts, which would cause exponential memory growth and duplicate entries.
|
|
||||||
|
|
||||||
Traditional memory systems give agents a `search_memory` tool - but models don't use it consistently. Auto-recall solves this by injecting memories automatically before every turn.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Plugin Settings
|
|
||||||
|
|
||||||
Optional settings in `~/.openclaw/openclaw.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"apiPort": 9077,
|
|
||||||
"daemonIdleTimeout": 0,
|
|
||||||
"embedVersion": "latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Options:**
|
|
||||||
- `apiPort` - Port for the openclaw profile daemon (default: `9077`)
|
|
||||||
- `daemonIdleTimeout` - Seconds before daemon shuts down from inactivity (default: `0` = never)
|
|
||||||
- `embedVersion` - hindsight-embed version (default: `"latest"`)
|
|
||||||
- `bankMission` - Agent identity/purpose stored on the memory bank. Helps the memory engine understand context for better fact extraction during retain. Set once per bank on first use — not a recall prompt.
|
|
||||||
- `dynamicBankId` - Enable per-context memory banks (default: `true`)
|
|
||||||
- `bankIdPrefix` - Optional prefix for bank IDs (e.g. `"prod"` → `"prod-slack-C123"`)
|
|
||||||
- `dynamicBankGranularity` - Fields used to derive bank ID: `agent`, `channel`, `user`, `provider` (default: `["agent", "channel", "user"]`)
|
|
||||||
- `excludeProviders` - Message providers to skip for recall/retain (e.g. `["slack"]`, `["telegram"]`, `["discord"]`)
|
|
||||||
- `autoRecall` - Auto-inject memories before each turn (default: `true`). Set to `false` when the agent has its own recall tool.
|
|
||||||
- `autoRetain` - Auto-retain conversations after each turn (default: `true`)
|
|
||||||
- `retainRoles` - Which message roles to retain (default: `["user", "assistant"]`). Options: `user`, `assistant`, `system`, `tool`
|
|
||||||
- `recallBudget` - Recall effort: `"low"`, `"mid"`, or `"high"` (default: `"mid"`). Higher budgets use more retrieval strategies for better results.
|
|
||||||
- `recallMaxTokens` - Max tokens for recall response (default: `1024`). Controls how much memory context is injected per turn.
|
|
||||||
- `recallTopK` - Max number of memories to inject per turn (default: unlimited).
|
|
||||||
- `recallTypes` - Memory types to recall (default: `["world", "experience"]`). Options: `world`, `experience`, `observation`.
|
|
||||||
- `recallContextTurns` - Number of prior user turns to include in the recall query (default: `1`).
|
|
||||||
- `recallMaxQueryChars` - Max characters for the composed recall query (default: `800`).
|
|
||||||
- `recallPromptPreamble` - Custom preamble text placed above recalled memories. Overrides the built-in guidance text.
|
|
||||||
- `recallInjectionPosition` - Where to inject recalled memories: `"prepend"` (default), `"append"`, or `"user"`. Use `"append"` to preserve prompt caching with large static system prompts. Use `"user"` to inject before the user message instead of in the system prompt.
|
|
||||||
- `recallRoles` - Which message roles to include when composing the contextual recall query (default: `["user", "assistant"]`).
|
|
||||||
- `retainEveryNTurns` - Retain every Nth turn (default: `1` = every turn). Values > 1 enable chunked retention.
|
|
||||||
- `retainOverlapTurns` - Extra prior turns included when chunked retention fires (default: `0`).
|
|
||||||
- `debug` - Enable debug logging (default: `false`).
|
|
||||||
|
|
||||||
### Memory Isolation
|
|
||||||
|
|
||||||
The plugin creates separate memory banks based on conversation context. By default, banks are derived from the `agent`, `channel`, and `user` fields — so each unique combination gets its own isolated memory store.
|
|
||||||
|
|
||||||
You can customize which fields are used for bank segmentation with `dynamicBankGranularity`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"dynamicBankGranularity": ["provider", "user"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, memories are isolated per provider + user, meaning the same user shares memories across all channels within a provider.
|
|
||||||
|
|
||||||
Available isolation fields:
|
|
||||||
- `agent` - The agent/bot identity
|
|
||||||
- `channel` - The channel or conversation ID
|
|
||||||
- `user` - The user interacting with the agent
|
|
||||||
- `provider` - The message provider (e.g. Slack, Discord)
|
|
||||||
|
|
||||||
Use `bankIdPrefix` to namespace bank IDs across environments (e.g. `"prod"`, `"staging"`). Set `dynamicBankId` to `false` to use a single shared bank for all conversations.
|
|
||||||
|
|
||||||
### Retention Controls
|
|
||||||
|
|
||||||
By default, the plugin retains `user` and `assistant` messages after each turn. You can customize this behavior:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"autoRetain": true,
|
|
||||||
"retainRoles": ["user", "assistant", "system"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `autoRetain` - Set to `false` to disable automatic retention entirely (useful if you handle retention yourself)
|
|
||||||
- `retainRoles` - Controls which message roles are included in the retained transcript. Only messages from the last user message onward are retained each turn, preventing duplicate storage.
|
|
||||||
|
|
||||||
### LLM Configuration
|
|
||||||
|
|
||||||
The plugin auto-detects your LLM provider from these environment variables:
|
|
||||||
|
|
||||||
| Provider | Env Var | Notes |
|
|
||||||
|----------|---------|-------|
|
|
||||||
| OpenAI | `OPENAI_API_KEY` | |
|
|
||||||
| Anthropic | `ANTHROPIC_API_KEY` | |
|
|
||||||
| Gemini | `GEMINI_API_KEY` | |
|
|
||||||
| Groq | `GROQ_API_KEY` | |
|
|
||||||
| Claude Code | `HINDSIGHT_API_LLM_PROVIDER=claude-code` | No API key needed |
|
|
||||||
| OpenAI Codex | `HINDSIGHT_API_LLM_PROVIDER=openai-codex` | No API key needed |
|
|
||||||
|
|
||||||
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_API_LLM_MODEL`.
|
|
||||||
|
|
||||||
**Override with explicit config:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai
|
|
||||||
export HINDSIGHT_API_LLM_API_KEY=sk-your-key
|
|
||||||
|
|
||||||
# Optional: custom base URL (OpenRouter, Azure, vLLM, etc.)
|
|
||||||
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example: Free OpenRouter model**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai
|
|
||||||
export HINDSIGHT_API_LLM_MODEL=xiaomi/mimo-v2-flash # FREE!
|
|
||||||
export HINDSIGHT_API_LLM_API_KEY=sk-or-v1-your-openrouter-key
|
|
||||||
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
### External API (Advanced)
|
|
||||||
|
|
||||||
Connect to a remote Hindsight API server instead of running a local daemon. This is useful for:
|
|
||||||
|
|
||||||
- **Shared memory** across multiple OpenClaw instances
|
|
||||||
- **Production deployments** with centralized memory storage
|
|
||||||
- **Team environments** where agents share knowledge
|
|
||||||
|
|
||||||
#### Plugin Configuration
|
|
||||||
|
|
||||||
Configure in `~/.openclaw/openclaw.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-api-token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Options:**
|
|
||||||
- `hindsightApiUrl` - Full URL to external Hindsight API (e.g., `https://mcp.hindsight.example.com`)
|
|
||||||
- `hindsightApiToken` - API token for authentication (optional, only if API requires auth)
|
|
||||||
|
|
||||||
#### Environment Variables (Alternative)
|
|
||||||
|
|
||||||
You can also configure via environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_EMBED_API_URL=https://your-hindsight-server.com
|
|
||||||
export HINDSIGHT_EMBED_API_TOKEN=your-api-token # Optional
|
|
||||||
|
|
||||||
openclaw gateway
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Plugin config takes precedence over environment variables.
|
|
||||||
|
|
||||||
#### Behavior
|
|
||||||
|
|
||||||
When external API mode is enabled:
|
|
||||||
- **No local daemon** is started (no hindsight-embed process)
|
|
||||||
- **Health check** runs on startup to verify API connectivity
|
|
||||||
- **All memory operations** (retain, recall, reflect) go to the external API
|
|
||||||
- **Faster startup** since no local PostgreSQL or embedding models are needed
|
|
||||||
|
|
||||||
#### Verification
|
|
||||||
|
|
||||||
Check OpenClaw logs for external API mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# Should see on startup:
|
|
||||||
# [Hindsight] External API mode enabled: https://your-hindsight-server.com
|
|
||||||
# [Hindsight] External API health check passed
|
|
||||||
```
|
|
||||||
|
|
||||||
If you see daemon startup messages instead, verify your configuration is correct.
|
|
||||||
|
|
||||||
## Inspecting Memories
|
|
||||||
|
|
||||||
### Check Configuration
|
|
||||||
|
|
||||||
View the daemon config that was written by the plugin:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat ~/.hindsight/profiles/openclaw.env
|
|
||||||
```
|
|
||||||
|
|
||||||
This shows the LLM provider, model, port, and other settings the daemon is using.
|
|
||||||
|
|
||||||
### Check Daemon Status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if daemon is running
|
|
||||||
uvx hindsight-embed@latest -p openclaw daemon status
|
|
||||||
|
|
||||||
# View daemon logs
|
|
||||||
tail -f ~/.hindsight/profiles/openclaw.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Query Memories
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Search memories
|
|
||||||
uvx hindsight-embed@latest -p openclaw memory recall openclaw "user preferences"
|
|
||||||
|
|
||||||
# View recent memories
|
|
||||||
uvx hindsight-embed@latest -p openclaw memory list openclaw --limit 10
|
|
||||||
|
|
||||||
# Open web UI (uses openclaw profile's daemon)
|
|
||||||
uvx hindsight-embed@latest -p openclaw ui
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin not loading
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins list | grep hindsight
|
|
||||||
# Should show: ✓ enabled │ Hindsight Memory │ ...
|
|
||||||
|
|
||||||
# Reinstall if needed
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Daemon not starting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check daemon status (note: -p openclaw uses the openclaw profile)
|
|
||||||
uvx hindsight-embed@latest -p openclaw daemon status
|
|
||||||
|
|
||||||
# View logs for errors
|
|
||||||
tail -f ~/.hindsight/profiles/openclaw.log
|
|
||||||
|
|
||||||
# Check configuration
|
|
||||||
cat ~/.hindsight/profiles/openclaw.env
|
|
||||||
|
|
||||||
# List all profiles
|
|
||||||
uvx hindsight-embed@latest profile list
|
|
||||||
```
|
|
||||||
|
|
||||||
### No API key error
|
|
||||||
|
|
||||||
Make sure you've set one of the provider API keys (or use a provider that doesn't require one):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option 1: OpenAI
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option 2: Anthropic
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option 3: Claude Code (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option 4: OpenAI Codex (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
|
|
||||||
|
|
||||||
# Verify it's set
|
|
||||||
echo $OPENAI_API_KEY
|
|
||||||
# or
|
|
||||||
echo $HINDSIGHT_API_LLM_PROVIDER
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify it's working
|
|
||||||
|
|
||||||
Check gateway logs for memory operations:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# Should see on startup:
|
|
||||||
# [Hindsight] ✓ Using provider: openai, model: gpt-4o-mini
|
|
||||||
# or
|
|
||||||
# [Hindsight] ✓ Using provider: claude-code, model: claude-sonnet-4-20250514
|
|
||||||
|
|
||||||
# Should see after conversations:
|
|
||||||
# [Hindsight] Retained X messages for session ...
|
|
||||||
# [Hindsight] Auto-recall: Injecting X memories
|
|
||||||
```
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 6
|
|
||||||
title: "Pydantic AI Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Pydantic AI agents with Hindsight. Async-native retain, recall, and reflect tools — persistent memory across all agent runs with no thread-pool hacks."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pydantic AI
|
|
||||||
|
|
||||||
Persistent memory tools for [Pydantic AI](https://ai.pydantic.dev/) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — all async-native with no thread-pool hacks.
|
|
||||||
|
|
||||||
[View Changelog →](/changelog/integrations/pydantic-ai)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Async-Native Tools** — Uses Pydantic AI's async tool interface directly (`aretain`, `arecall`, `areflect`)
|
|
||||||
- **Memory Instructions** — Auto-inject relevant memories into every agent run via `instructions=[...]`
|
|
||||||
- **Three Memory Tools** — Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Simple Configuration** — Configure once globally, or pass a client directly
|
|
||||||
- **Lightweight** — Depends on `pydantic-ai-slim` to avoid pulling in all model providers
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-pydantic-ai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions
|
|
||||||
from pydantic_ai import Agent
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
tools=create_hindsight_tools(client=client, bank_id="user-123"),
|
|
||||||
instructions=[memory_instructions(client=client, bank_id="user-123")],
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await agent.run("What do you remember about my preferences?")
|
|
||||||
print(result.output)
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
The `memory_instructions` callable automatically recalls relevant memories and injects them into the system prompt on every run.
|
|
||||||
|
|
||||||
## Tools Only (No Auto-Injection)
|
|
||||||
|
|
||||||
If you want the agent to decide when to use memory rather than always injecting context:
|
|
||||||
|
|
||||||
```python
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
tools=create_hindsight_tools(client=client, bank_id="user-123"),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Instructions Only (No Tools)
|
|
||||||
|
|
||||||
If you just want memories auto-injected without giving the agent explicit memory tools:
|
|
||||||
|
|
||||||
```python
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
instructions=[memory_instructions(client=client, bank_id="user-123")],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing a client to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_pydantic_ai import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client — uses global config
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Per-Tool Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
budget="high", # Override global budget
|
|
||||||
max_tokens=8192, # Override global max_tokens
|
|
||||||
tags=["session:abc"], # Override global tags
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Memory Instructions Options
|
|
||||||
|
|
||||||
Customize what memories get injected and how:
|
|
||||||
|
|
||||||
```python
|
|
||||||
instructions_fn = memory_instructions(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
query="relevant context about the user", # What to search for
|
|
||||||
budget="low", # Keep it fast
|
|
||||||
max_results=5, # Limit injected memories
|
|
||||||
max_tokens=4096, # Max recall tokens
|
|
||||||
prefix="Relevant memories:\n", # Text before the memory list
|
|
||||||
tags=["scope:global"], # Filter by tags
|
|
||||||
tags_match="any", # Tag match mode
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `include_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `include_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
@ -1,325 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 3
|
|
||||||
title: "Hindsight Agent Memory Skill | AI Coding Assistant Integration"
|
|
||||||
description: "Give AI coding assistants like Claude Code and Codex persistent memory across sessions with Hindsight's Agent Skill — a reusable prompt template for long-term context retention."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Skills
|
|
||||||
|
|
||||||
Hindsight provides an Agent Skill that gives AI coding assistants persistent memory across sessions. Skills are reusable prompt templates that agents can load when needed to gain specialized capabilities.
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
| Platform | Skills Directory |
|
|
||||||
|----------|-----------------|
|
|
||||||
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `~/.claude/skills/` |
|
|
||||||
| [OpenCode](https://github.com/opencode-ai/opencode) | `~/.opencode/skills/` |
|
|
||||||
| [Codex CLI](https://github.com/openai/codex) | `~/.codex/skills/` |
|
|
||||||
|
|
||||||
## Deployment Modes
|
|
||||||
|
|
||||||
The skill supports two deployment modes:
|
|
||||||
|
|
||||||
| Mode | Best For | Data Location |
|
|
||||||
|------|----------|---------------|
|
|
||||||
| **Local** | Individual developers | Your machine (`~/.pg0/`) |
|
|
||||||
| **Cloud** | Teams sharing knowledge | Hindsight Cloud |
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
|
|
||||||
### Option 1: Interactive Installer (Recommended)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer will:
|
|
||||||
1. Prompt you to select your AI coding assistant
|
|
||||||
2. Select deployment mode (local or cloud)
|
|
||||||
3. Configure the appropriate settings
|
|
||||||
4. Install the skill to the appropriate directory
|
|
||||||
|
|
||||||
### Install for a Specific Platform
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Claude Code (interactive mode selection)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude
|
|
||||||
|
|
||||||
# OpenCode
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app opencode
|
|
||||||
|
|
||||||
# Codex CLI
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app codex
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install with Cloud Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Direct cloud setup (skips interactive prompts for mode)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Using add-skill
|
|
||||||
|
|
||||||
If you use [add-skill](https://add-skill.org/) to manage your agent skills:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# For local mode (individual developers)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-local
|
|
||||||
|
|
||||||
# For Hindsight Cloud (teams)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-cloud
|
|
||||||
|
|
||||||
# For self-hosted Hindsight servers
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-self-hosted
|
|
||||||
```
|
|
||||||
|
|
||||||
On first use, the AI will guide you through the remaining setup:
|
|
||||||
- **Local**: Run `uvx hindsight-embed configure` to set up your LLM provider
|
|
||||||
- **Cloud**: Provide your API key and bank ID
|
|
||||||
- **Self-hosted**: Provide your server URL, API key, and bank ID
|
|
||||||
|
|
||||||
## What the Skill Provides
|
|
||||||
|
|
||||||
Once installed, your AI assistant gains the ability to:
|
|
||||||
|
|
||||||
- **Retain** - Store user preferences, learnings, and procedure outcomes
|
|
||||||
- **Recall** - Search for relevant context before starting tasks
|
|
||||||
- **Reflect** - Synthesize memories into contextual answers
|
|
||||||
|
|
||||||
The skill uses the `hindsight-embed` CLI which runs a lightweight local daemon with an embedded database.
|
|
||||||
|
|
||||||
## How Skills Work
|
|
||||||
|
|
||||||
Skills are **model-invoked**, meaning the AI assistant automatically decides when to use them based on the context of your conversation. You don't need to explicitly trigger the skill.
|
|
||||||
|
|
||||||
The assistant will:
|
|
||||||
- **Store** when you share preferences, when tasks succeed/fail, or when learnings emerge
|
|
||||||
- **Recall** before starting non-trivial tasks to get relevant context
|
|
||||||
|
|
||||||
### What Gets Stored
|
|
||||||
|
|
||||||
The skill is optimized to store:
|
|
||||||
|
|
||||||
| Category | Examples |
|
|
||||||
|----------|----------|
|
|
||||||
| **User Preferences** | Coding style, tool preferences, language choices |
|
|
||||||
| **Procedure Outcomes** | Commands that worked, configurations that resolved issues |
|
|
||||||
| **Learnings** | Bug solutions, workarounds, architecture decisions |
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-embed CLI
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Local Daemon (auto-started)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Embedded PostgreSQL (~/.pg0/hindsight-embed/)
|
|
||||||
```
|
|
||||||
|
|
||||||
All data stays on your machine. The daemon auto-starts when needed and shuts down after inactivity.
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-cli
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Cloud API (https://api.hindsight.vectorize.io)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Shared Memory Bank (team-accessible)
|
|
||||||
```
|
|
||||||
|
|
||||||
Data is stored in Hindsight Cloud and shared across your team. All team members with the same bank ID can access shared memories.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Local Mode Setup
|
|
||||||
|
|
||||||
The skill uses configuration stored in `~/.hindsight/config.env`. Reconfigure anytime:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Cloud Mode Setup
|
|
||||||
|
|
||||||
Cloud mode connects to [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup), allowing teams to share memories about a codebase. When one team member learns something, everyone benefits.
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
1. A Hindsight Cloud account ([sign up](https://ui.hindsight.vectorize.io/signup))
|
|
||||||
2. An API key from your team admin
|
|
||||||
3. A bank ID for your project (e.g., `team-acme-frontend`)
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
Run the installer with cloud mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll be prompted for:
|
|
||||||
|
|
||||||
| Setting | Description | Example |
|
|
||||||
|---------|-------------|---------|
|
|
||||||
| **Cloud API URL** | Hindsight Cloud endpoint | `https://api.hindsight.vectorize.io` |
|
|
||||||
| **API Key** | Your authentication key | `hs_xxx...` |
|
|
||||||
| **Bank ID** | Shared memory bank for your team | `team-acme-frontend` |
|
|
||||||
|
|
||||||
### Configuration Files
|
|
||||||
|
|
||||||
Cloud mode creates two files:
|
|
||||||
|
|
||||||
**`~/.hindsight/config`** — API connection settings (TOML format):
|
|
||||||
```toml
|
|
||||||
api_url = "https://api.hindsight.vectorize.io"
|
|
||||||
api_key = "hs_xxx..."
|
|
||||||
```
|
|
||||||
|
|
||||||
**`~/.claude/skills/hindsight/SKILL.md`** — Skill definition with your bank ID baked in.
|
|
||||||
|
|
||||||
### Team Setup
|
|
||||||
|
|
||||||
To set up cloud mode for your team:
|
|
||||||
|
|
||||||
1. **Team admin** creates a bank in Hindsight Cloud (e.g., `team-acme-frontend`)
|
|
||||||
2. **Team admin** generates API keys for each team member
|
|
||||||
3. **Each developer** runs the installer with their API key and the shared bank ID
|
|
||||||
4. All team members now share the same memory bank
|
|
||||||
|
|
||||||
### What to Store in Team Banks
|
|
||||||
|
|
||||||
Cloud mode uses a **shared team bank**. Be thoughtful about what goes in:
|
|
||||||
|
|
||||||
| Type | Examples | How to Store |
|
|
||||||
|------|----------|--------------|
|
|
||||||
| **Project conventions** | Linting rules, testing requirements, Node version | `"Project uses ESLint with Airbnb config"` |
|
|
||||||
| **Team knowledge** | Architecture decisions, common pitfalls, domain logic | `"Auth module requires Redis 7+"` |
|
|
||||||
| **Individual preferences** | Personal coding style, communication preferences | `"Alice prefers verbose commit messages"` |
|
|
||||||
|
|
||||||
**Key distinction**: Project conventions apply to everyone. Individual preferences should include the person's name so the AI knows when to apply them.
|
|
||||||
|
|
||||||
### Example Workflow
|
|
||||||
|
|
||||||
```
|
|
||||||
Day 1: Alice discovers a requirement
|
|
||||||
─────────────────────────────────────
|
|
||||||
Alice's AI assistant stores:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
"Alice prefers explicit error handling over silent failures"
|
|
||||||
|
|
||||||
Day 2: Bob starts working on auth
|
|
||||||
─────────────────────────────────
|
|
||||||
Bob's AI assistant recalls:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
|
|
||||||
Bob avoids the same issue Alice hit!
|
|
||||||
(Alice's personal preference is stored but won't be applied to Bob)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Cloud Connection
|
|
||||||
|
|
||||||
After installation, verify the connection:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Store a test memory
|
|
||||||
hindsight memory retain team-acme-frontend "Alice works at Google as a software engineer"
|
|
||||||
|
|
||||||
# Recall it
|
|
||||||
hindsight memory recall team-acme-frontend "Alice"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Switching Between Banks
|
|
||||||
|
|
||||||
If you work on multiple projects, you can have different skills installed for each AI assistant, or manually switch banks:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Environment variable override (temporary)
|
|
||||||
HINDSIGHT_API_URL=https://api.hindsight.vectorize.io \
|
|
||||||
HINDSIGHT_API_KEY=hs_xxx \
|
|
||||||
hindsight memory recall different-bank "query"
|
|
||||||
```
|
|
||||||
|
|
||||||
For permanent multi-bank setups, reinstall the skill with a different bank ID.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Skill not activating
|
|
||||||
|
|
||||||
The skill activates based on its description matching your request. Try being explicit:
|
|
||||||
- "Remember that..." triggers storage
|
|
||||||
- "What do you know about..." triggers recall
|
|
||||||
|
|
||||||
### Local Mode Issues
|
|
||||||
|
|
||||||
**Daemon not starting:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed daemon status
|
|
||||||
uvx hindsight-embed daemon logs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Reconfigure LLM provider:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cloud Mode Issues
|
|
||||||
|
|
||||||
**Authentication errors:**
|
|
||||||
```bash
|
|
||||||
# Verify your config
|
|
||||||
cat ~/.hindsight/config
|
|
||||||
|
|
||||||
# Test connection manually
|
|
||||||
hindsight bank list
|
|
||||||
```
|
|
||||||
|
|
||||||
**Wrong bank ID:**
|
|
||||||
|
|
||||||
Check your SKILL.md file to see which bank ID is configured:
|
|
||||||
```bash
|
|
||||||
cat ~/.claude/skills/hindsight/SKILL.md | grep "memory retain"
|
|
||||||
```
|
|
||||||
|
|
||||||
To change the bank ID, reinstall the skill:
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
**Network/firewall issues:**
|
|
||||||
```bash
|
|
||||||
# Test connectivity to cloud API
|
|
||||||
curl -I https://api.hindsight.vectorize.io/health
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- An LLM API key (OpenAI, Anthropic, Groq, etc.)
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- Hindsight Cloud API key
|
|
||||||
- Network access to `https://api.hindsight.vectorize.io`
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 13
|
|
||||||
title: "Strands Agents Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Strands Agents SDK agents with Hindsight. Retain, recall, and reflect tools using Strands' native @tool pattern for persistent memory across sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Strands Agents
|
|
||||||
|
|
||||||
Persistent memory tools for [Strands Agents SDK](https://github.com/strands-agents/sdk-python) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — using Strands' native `@tool` pattern.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Native `@tool` Functions** - Tools are plain Python functions, compatible with `Agent(tools=[...])`
|
|
||||||
- **Memory Instructions** - Pre-recall memories for injection into agent system prompt
|
|
||||||
- **Three Memory Tools** - Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Simple Configuration** - Configure once globally, or pass a client directly
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-strands
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from strands import Agent
|
|
||||||
from hindsight_strands import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(tools=tools)
|
|
||||||
agent("Remember that I prefer dark mode")
|
|
||||||
agent("What are my preferences?")
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## With Memory Instructions
|
|
||||||
|
|
||||||
Pre-recall relevant memories and inject them into the system prompt:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_strands import create_hindsight_tools, memory_instructions
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
memories = memory_instructions(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
tools=tools,
|
|
||||||
system_prompt=f"You are a helpful assistant.\n\n{memories}",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
enable_retain=True,
|
|
||||||
enable_recall=True,
|
|
||||||
enable_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing connection details to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_strands import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing connection details
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `enable_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `enable_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `enable_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- strands-agents
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -5,14 +5,70 @@
|
||||||
"label": "Architecture",
|
"label": "Architecture",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "doc", "id": "developer/index", "label": "Overview", "customProps": { "icon": "lu-book" } },
|
{
|
||||||
{ "type": "doc", "id": "developer/retain", "label": "Retain", "customProps": { "icon": "lu-brain" } },
|
"type": "doc",
|
||||||
{ "type": "doc", "id": "developer/retrieval", "label": "Recall", "customProps": { "icon": "lu-search" } },
|
"id": "developer/index",
|
||||||
{ "type": "doc", "id": "developer/reflect", "label": "Reflect", "customProps": { "icon": "lu-message" } },
|
"label": "Overview",
|
||||||
{ "type": "doc", "id": "developer/multilingual", "label": "Multilingual", "customProps": { "icon": "lu-languages" } },
|
"customProps": {
|
||||||
{ "type": "doc", "id": "developer/performance", "label": "Performance", "customProps": { "icon": "lu-zap" } },
|
"icon": "lu-book"
|
||||||
{ "type": "doc", "id": "developer/storage", "label": "Storage", "customProps": { "icon": "lu-database" } },
|
}
|
||||||
{ "type": "doc", "id": "developer/rag-vs-hindsight", "label": "RAG vs Memory", "customProps": { "icon": "lu-compare" } }
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/retain",
|
||||||
|
"label": "Retain",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-brain"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/retrieval",
|
||||||
|
"label": "Recall",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-search"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/reflect",
|
||||||
|
"label": "Reflect",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-message"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/multilingual",
|
||||||
|
"label": "Multilingual",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-languages"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/performance",
|
||||||
|
"label": "Performance",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-zap"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/storage",
|
||||||
|
"label": "Storage",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-database"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/rag-vs-hindsight",
|
||||||
|
"label": "RAG vs Memory",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-compare"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -20,14 +76,70 @@
|
||||||
"label": "API",
|
"label": "API",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "doc", "id": "developer/api/quickstart", "label": "Quick Start", "customProps": { "icon": "lu-rocket" } },
|
{
|
||||||
{ "type": "doc", "id": "developer/api/retain", "label": "Retain", "customProps": { "icon": "lu-brain" } },
|
"type": "doc",
|
||||||
{ "type": "doc", "id": "developer/api/recall", "label": "Recall", "customProps": { "icon": "lu-search" } },
|
"id": "developer/api/quickstart",
|
||||||
{ "type": "doc", "id": "developer/api/reflect", "label": "Reflect", "customProps": { "icon": "lu-message" } },
|
"label": "Quick Start",
|
||||||
{ "type": "doc", "id": "developer/api/memory-banks", "label": "Memory Banks", "customProps": { "icon": "lu-memory" } },
|
"customProps": {
|
||||||
{ "type": "doc", "id": "developer/api/entities", "label": "Entities", "customProps": { "icon": "lu-network" } },
|
"icon": "lu-rocket"
|
||||||
{ "type": "doc", "id": "developer/api/documents", "label": "Documents", "customProps": { "icon": "lu-file" } },
|
}
|
||||||
{ "type": "doc", "id": "developer/api/operations", "label": "Operations", "customProps": { "icon": "lu-cpu" } }
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/retain",
|
||||||
|
"label": "Retain",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-brain"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/recall",
|
||||||
|
"label": "Recall",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-search"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/reflect",
|
||||||
|
"label": "Reflect",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-message"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/memory-banks",
|
||||||
|
"label": "Memory Banks",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-memory"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/entities",
|
||||||
|
"label": "Entities",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-network"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/documents",
|
||||||
|
"label": "Documents",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-file"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/api/operations",
|
||||||
|
"label": "Operations",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-cpu"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -35,9 +147,30 @@
|
||||||
"label": "Clients",
|
"label": "Clients",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "doc", "id": "sdks/python", "label": "Python", "customProps": { "icon": "si-python" } },
|
{
|
||||||
{ "type": "doc", "id": "sdks/nodejs", "label": "TypeScript", "customProps": { "icon": "/img/icons/typescript.png" } },
|
"type": "doc",
|
||||||
{ "type": "doc", "id": "sdks/cli", "label": "CLI", "customProps": { "icon": "lu-terminal" } }
|
"id": "sdks/python",
|
||||||
|
"label": "Python",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "si-python"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "sdks/nodejs",
|
||||||
|
"label": "TypeScript",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "/img/icons/typescript.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "sdks/cli",
|
||||||
|
"label": "CLI",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-terminal"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -45,9 +178,30 @@
|
||||||
"label": "Integrations",
|
"label": "Integrations",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "doc", "id": "sdks/integrations/local-mcp", "label": "Local MCP Server", "customProps": { "icon": "/img/icons/mcp.png" } },
|
{
|
||||||
{ "type": "doc", "id": "sdks/integrations/litellm", "label": "LiteLLM", "customProps": { "icon": "/img/icons/litellm.png" } },
|
"type": "link",
|
||||||
{ "type": "doc", "id": "sdks/integrations/skills", "label": "Skills", "customProps": { "icon": "/img/icons/skills.png" } }
|
"label": "Local MCP Server",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "/img/icons/mcp.png"
|
||||||
|
},
|
||||||
|
"href": "/sdks/integrations/local-mcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "link",
|
||||||
|
"label": "LiteLLM",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "/img/icons/litellm.png"
|
||||||
|
},
|
||||||
|
"href": "/sdks/integrations/litellm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "link",
|
||||||
|
"label": "Skills",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "/img/icons/skills.png"
|
||||||
|
},
|
||||||
|
"href": "/sdks/integrations/skills"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -55,14 +209,70 @@
|
||||||
"label": "Hosting",
|
"label": "Hosting",
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "doc", "id": "developer/installation", "label": "Installation", "customProps": { "icon": "lu-package" } },
|
{
|
||||||
{ "type": "doc", "id": "developer/services", "label": "Services", "customProps": { "icon": "lu-server" } },
|
"type": "doc",
|
||||||
{ "type": "doc", "id": "developer/configuration", "label": "Configuration", "customProps": { "icon": "lu-settings" } },
|
"id": "developer/installation",
|
||||||
{ "type": "doc", "id": "developer/admin-cli", "label": "Admin CLI", "customProps": { "icon": "lu-terminal" } },
|
"label": "Installation",
|
||||||
{ "type": "doc", "id": "developer/extensions", "label": "Extensions", "customProps": { "icon": "lu-plug" } },
|
"customProps": {
|
||||||
{ "type": "doc", "id": "developer/models", "label": "Models", "customProps": { "icon": "lu-cpu" } },
|
"icon": "lu-package"
|
||||||
{ "type": "doc", "id": "developer/monitoring", "label": "Monitoring", "customProps": { "icon": "lu-activity" } },
|
}
|
||||||
{ "type": "doc", "id": "developer/mcp-server", "label": "MCP Server", "customProps": { "icon": "lu-network" } }
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/services",
|
||||||
|
"label": "Services",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-server"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/configuration",
|
||||||
|
"label": "Configuration",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-settings"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/admin-cli",
|
||||||
|
"label": "Admin CLI",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-terminal"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/extensions",
|
||||||
|
"label": "Extensions",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-plug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/models",
|
||||||
|
"label": "Models",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-cpu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/monitoring",
|
||||||
|
"label": "Monitoring",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-activity"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doc",
|
||||||
|
"id": "developer/mcp-server",
|
||||||
|
"label": "MCP Server",
|
||||||
|
"customProps": {
|
||||||
|
"icon": "lu-network"
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -220,140 +220,140 @@
|
||||||
"collapsible": false,
|
"collapsible": false,
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/local-mcp",
|
|
||||||
"label": "Local MCP Server",
|
"label": "Local MCP Server",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/mcp.png"
|
"icon": "/img/icons/mcp.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/local-mcp"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/litellm",
|
|
||||||
"label": "LiteLLM",
|
"label": "LiteLLM",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/litellm.png"
|
"icon": "/img/icons/litellm.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/litellm"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/claude-code",
|
|
||||||
"label": "Claude Code",
|
"label": "Claude Code",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/claudecode.svg"
|
"icon": "/img/icons/claudecode.svg"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/claude-code"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/codex",
|
|
||||||
"label": "OpenAI Codex CLI",
|
"label": "OpenAI Codex CLI",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/terminal.svg"
|
"icon": "/img/icons/terminal.svg"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/codex"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/openclaw",
|
|
||||||
"label": "OpenClaw",
|
"label": "OpenClaw",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/openclaw.png"
|
"icon": "/img/icons/openclaw.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/openclaw"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/ai-sdk",
|
|
||||||
"label": "Vercel AI SDK",
|
"label": "Vercel AI SDK",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/vercel.png"
|
"icon": "/img/icons/vercel.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/ai-sdk"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/chat",
|
|
||||||
"label": "Vercel Chat SDK",
|
"label": "Vercel Chat SDK",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/vercel.png"
|
"icon": "/img/icons/vercel.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/chat"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/crewai",
|
|
||||||
"label": "CrewAI",
|
"label": "CrewAI",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/crewai.png"
|
"icon": "/img/icons/crewai.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/crewai"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/pydantic-ai",
|
|
||||||
"label": "Pydantic AI",
|
"label": "Pydantic AI",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/pydanticai.png"
|
"icon": "/img/icons/pydanticai.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/pydantic-ai"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/agno",
|
|
||||||
"label": "Agno",
|
"label": "Agno",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/agno.png"
|
"icon": "/img/icons/agno.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/agno"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/hermes",
|
|
||||||
"label": "Hermes Agent",
|
"label": "Hermes Agent",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/hermes.png"
|
"icon": "/img/icons/hermes.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/hermes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/langgraph",
|
|
||||||
"label": "LangGraph / LangChain",
|
"label": "LangGraph / LangChain",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/langgraph.png"
|
"icon": "/img/icons/langgraph.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/langgraph"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/nemoclaw",
|
|
||||||
"label": "NemoClaw",
|
"label": "NemoClaw",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/nemoclaw.png"
|
"icon": "/img/icons/nemoclaw.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/nemoclaw"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/strands",
|
|
||||||
"label": "Strands Agents",
|
"label": "Strands Agents",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/strands.png"
|
"icon": "/img/icons/strands.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/strands"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/ag2",
|
|
||||||
"label": "AG2",
|
"label": "AG2",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/ag2.svg"
|
"icon": "/img/icons/ag2.svg"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/ag2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/llamaindex",
|
|
||||||
"label": "LlamaIndex",
|
"label": "LlamaIndex",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/llamaindex.png"
|
"icon": "/img/icons/llamaindex.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/llamaindex"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "doc",
|
"type": "link",
|
||||||
"id": "sdks/integrations/skills",
|
|
||||||
"label": "Skills",
|
"label": "Skills",
|
||||||
"customProps": {
|
"customProps": {
|
||||||
"icon": "/img/icons/skills.png"
|
"icon": "/img/icons/skills.png"
|
||||||
}
|
},
|
||||||
|
"href": "/sdks/integrations/skills"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ fi
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Number of retry attempts for each example (handles transient LLM timeouts)
|
||||||
|
MAX_RETRIES=2
|
||||||
|
|
||||||
# Function to run a single example
|
# Function to run a single example
|
||||||
run_example() {
|
run_example() {
|
||||||
local file="$1"
|
local file="$1"
|
||||||
|
|
@ -54,19 +57,30 @@ run_example() {
|
||||||
echo -n "Running $basename... "
|
echo -n "Running $basename... "
|
||||||
|
|
||||||
pushd "$workdir" > /dev/null 2>&1
|
pushd "$workdir" > /dev/null 2>&1
|
||||||
|
local attempt=1
|
||||||
|
while [ $attempt -le $MAX_RETRIES ]; do
|
||||||
if $runner "$file" > "$logfile" 2>&1; then
|
if $runner "$file" > "$logfile" 2>&1; then
|
||||||
|
if [ $attempt -gt 1 ]; then
|
||||||
|
echo -e "${GREEN}✓ PASS${NC} (passed on retry $attempt)"
|
||||||
|
else
|
||||||
echo -e "${GREEN}✓ PASS${NC}"
|
echo -e "${GREEN}✓ PASS${NC}"
|
||||||
|
fi
|
||||||
TOTAL_PASSED=$((TOTAL_PASSED + 1))
|
TOTAL_PASSED=$((TOTAL_PASSED + 1))
|
||||||
rm -f "$logfile" # Clean up successful test logs
|
rm -f "$logfile" # Clean up successful test logs
|
||||||
popd > /dev/null 2>&1
|
popd > /dev/null 2>&1
|
||||||
return 0
|
return 0
|
||||||
else
|
fi
|
||||||
echo -e "${RED}✗ FAIL${NC}"
|
if [ $attempt -lt $MAX_RETRIES ]; then
|
||||||
|
echo -e -n "${YELLOW}(attempt $attempt failed, retrying)${NC} "
|
||||||
|
fi
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e "${RED}✗ FAIL${NC} (after $MAX_RETRIES attempts)"
|
||||||
TOTAL_FAILED=$((TOTAL_FAILED + 1))
|
TOTAL_FAILED=$((TOTAL_FAILED + 1))
|
||||||
FAILED_EXAMPLES+=("$basename:$logfile")
|
FAILED_EXAMPLES+=("$basename:$logfile")
|
||||||
popd > /dev/null 2>&1
|
popd > /dev/null 2>&1
|
||||||
return 1
|
return 1
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run Python examples
|
# Run Python examples
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="Vercel AI SDK Changelog" subtitle="@vectorize-io/hindsight-ai-sdk — memory integration for Vercel AI SDK." />
|
<PageHero title="Vercel AI SDK Changelog" subtitle="@vectorize-io/hindsight-ai-sdk — memory integration for Vercel AI SDK." />
|
||||||
|
|
||||||
[← Vercel AI SDK integration](../../sdks/integrations/ai-sdk.md)
|
← Vercel AI SDK integration
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="Vercel Chat SDK Changelog" subtitle="@vectorize-io/hindsight-chat — memory integration for Vercel Chat SDK." />
|
<PageHero title="Vercel Chat SDK Changelog" subtitle="@vectorize-io/hindsight-chat — memory integration for Vercel Chat SDK." />
|
||||||
|
|
||||||
[← Vercel Chat SDK integration](../../sdks/integrations/chat.md)
|
← Vercel Chat SDK integration
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="Claude Code Changelog" subtitle="hindsight-memory — Hindsight memory plugin for Claude Code." />
|
<PageHero title="Claude Code Changelog" subtitle="hindsight-memory — Hindsight memory plugin for Claude Code." />
|
||||||
|
|
||||||
[← Claude Code integration](../../sdks/integrations/claude-code.md)
|
← Claude Code integration
|
||||||
|
|
||||||
## [0.3.0](https://github.com/vectorize-io/hindsight/tree/integrations/claude-code/v0.3.0)
|
## [0.3.0](https://github.com/vectorize-io/hindsight/tree/integrations/claude-code/v0.3.0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="CrewAI Changelog" subtitle="hindsight-crewai — persistent memory for CrewAI agents." />
|
<PageHero title="CrewAI Changelog" subtitle="hindsight-crewai — persistent memory for CrewAI agents." />
|
||||||
|
|
||||||
[← CrewAI integration](../../sdks/integrations/crewai.md)
|
← CrewAI integration
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="LangGraph Changelog" subtitle="hindsight-langgraph — LangGraph and LangChain memory integration." />
|
<PageHero title="LangGraph Changelog" subtitle="hindsight-langgraph — LangGraph and LangChain memory integration." />
|
||||||
|
|
||||||
[← LangGraph integration](../../sdks/integrations/langgraph.md)
|
← LangGraph integration
|
||||||
|
|
||||||
## [0.1.1](https://github.com/vectorize-io/hindsight/tree/integrations/langgraph/v0.1.1)
|
## [0.1.1](https://github.com/vectorize-io/hindsight/tree/integrations/langgraph/v0.1.1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="LiteLLM Changelog" subtitle="hindsight-litellm — universal LLM memory integration via LiteLLM." />
|
<PageHero title="LiteLLM Changelog" subtitle="hindsight-litellm — universal LLM memory integration via LiteLLM." />
|
||||||
|
|
||||||
[← LiteLLM integration](../../sdks/integrations/litellm.md)
|
← LiteLLM integration
|
||||||
|
|
||||||
## [0.5.0](https://github.com/vectorize-io/hindsight/tree/integrations/litellm/v0.5.0)
|
## [0.5.0](https://github.com/vectorize-io/hindsight/tree/integrations/litellm/v0.5.0)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="NemoClaw Changelog" subtitle="@vectorize-io/hindsight-nemoclaw — persistent memory for NemoClaw sandboxed agents." />
|
<PageHero title="NemoClaw Changelog" subtitle="@vectorize-io/hindsight-nemoclaw — persistent memory for NemoClaw sandboxed agents." />
|
||||||
|
|
||||||
[← NemoClaw integration](../../sdks/integrations/nemoclaw.md)
|
← NemoClaw integration
|
||||||
|
|
||||||
## [0.1.1](https://github.com/vectorize-io/hindsight/tree/integrations/nemoclaw/v0.1.1)
|
## [0.1.1](https://github.com/vectorize-io/hindsight/tree/integrations/nemoclaw/v0.1.1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="OpenClaw Changelog" subtitle="@vectorize-io/hindsight-openclaw — Hindsight memory plugin for OpenClaw." />
|
<PageHero title="OpenClaw Changelog" subtitle="@vectorize-io/hindsight-openclaw — Hindsight memory plugin for OpenClaw." />
|
||||||
|
|
||||||
[← OpenClaw integration](../../sdks/integrations/openclaw.md)
|
← OpenClaw integration
|
||||||
|
|
||||||
## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1)
|
## [0.5.1](https://github.com/vectorize-io/hindsight/tree/integrations/openclaw/v0.5.1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,4 @@ import PageHero from '@site/src/components/PageHero';
|
||||||
|
|
||||||
<PageHero title="Pydantic AI Changelog" subtitle="hindsight-pydantic-ai — persistent memory tools for Pydantic AI agents." />
|
<PageHero title="Pydantic AI Changelog" subtitle="hindsight-pydantic-ai — persistent memory tools for Pydantic AI agents." />
|
||||||
|
|
||||||
[← Pydantic AI integration](../../sdks/integrations/pydantic-ai.md)
|
← Pydantic AI integration
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ console.log('Would apply config:', dryRunResult.config_applied);
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "$HINDSIGHT_URL/v1/default/banks/my-bank/import?dry_run=true" \
|
curl -X POST "$HINDSIGHT_URL/v1/default/banks/my-bank/import?dry_run=true" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d @template.json
|
-d '{"version": "1", "bank": {"retain_mission": "Dry run test."}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Go
|
### Go
|
||||||
|
|
|
||||||
|
|
@ -293,19 +293,50 @@ console.log(`Last refreshed: ${mentalModel.last_refreshed_at}`);
|
||||||
# Section 'get-mental-model' not found in api/mental-models.go
|
# Section 'get-mental-model' not found in api/mental-models.go
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Detail Levels
|
||||||
|
|
||||||
|
Both **List** and **Get** endpoints accept an optional `detail` query parameter that controls how much data is returned. This is useful for reducing response size, especially in agent boot flows or MCP clients where context budget is limited.
|
||||||
|
|
||||||
|
| Level | Fields Returned | Use Case |
|
||||||
|
|-------|----------------|----------|
|
||||||
|
| `metadata` | `id`, `bank_id`, `name`, `tags`, `last_refreshed_at`, `created_at` | Inventory — "what models exist?" |
|
||||||
|
| `content` | All metadata fields + `source_query`, `content`, `max_tokens`, `trigger` | Agent boot — "what do the models say?" |
|
||||||
|
| `full` (default) | All fields including `reflect_response` | Deep inspection — "what evidence backs this model?" |
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List only names and tags (smallest response)
|
||||||
|
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models?detail=metadata"
|
||||||
|
|
||||||
|
# List with content but without provenance chains
|
||||||
|
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models?detail=content"
|
||||||
|
|
||||||
|
# Get full detail (default behavior)
|
||||||
|
curl "$BASE_URL/v1/default/banks/$BANK_ID/mental-models/$MODEL_ID?detail=full"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `detail` parameter is also available in the MCP tools:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"bank_id": "my-bank", "detail": "metadata"}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Use `detail=content` for agent orientation flows. It includes everything the agent needs to understand the models without the heavyweight `reflect_response` provenance chains, which can exceed 200KB for banks with many models.
|
||||||
### Response Fields
|
### Response Fields
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Detail Level | Description |
|
||||||
|-------|------|-------------|
|
|-------|------|-------------|-------------|
|
||||||
| `id` | string | Unique mental model ID |
|
| `id` | string | metadata | Unique mental model ID |
|
||||||
| `bank_id` | string | Memory bank ID |
|
| `bank_id` | string | metadata | Memory bank ID |
|
||||||
| `name` | string | Human-readable name |
|
| `name` | string | metadata | Human-readable name |
|
||||||
| `source_query` | string | The query used to generate content |
|
| `tags` | list | metadata | Tags for filtering |
|
||||||
| `content` | string | The generated mental model text |
|
| `last_refreshed_at` | string | metadata | When the mental model was last updated |
|
||||||
| `tags` | list | Tags for filtering |
|
| `created_at` | string | metadata | When the mental model was created |
|
||||||
| `last_refreshed_at` | string | When the mental model was last updated |
|
| `source_query` | string | content | The query used to generate content |
|
||||||
| `created_at` | string | When the mental model was created |
|
| `content` | string | content | The generated mental model text |
|
||||||
| `reflect_response` | object | Full reflect response including `based_on` facts |
|
| `max_tokens` | int | content | Maximum tokens for the mental model content |
|
||||||
|
| `trigger` | object | content | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
|
||||||
|
| `reflect_response` | object | full | Full reflect response including `based_on` provenance facts |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -971,6 +971,23 @@
|
||||||
},
|
},
|
||||||
"description": "How to match tags"
|
"description": "How to match tags"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "detail",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"enum": [
|
||||||
|
"metadata",
|
||||||
|
"content",
|
||||||
|
"full"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"description": "Detail level: 'metadata' (names/tags only), 'content' (adds content/config), 'full' (includes reflect_response)",
|
||||||
|
"default": "full",
|
||||||
|
"title": "Detail"
|
||||||
|
},
|
||||||
|
"description": "Detail level: 'metadata' (names/tags only), 'content' (adds content/config), 'full' (includes reflect_response)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "limit",
|
"name": "limit",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
|
|
@ -1129,6 +1146,23 @@
|
||||||
"title": "Mental Model Id"
|
"title": "Mental Model Id"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "detail",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"enum": [
|
||||||
|
"metadata",
|
||||||
|
"content",
|
||||||
|
"full"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"description": "Detail level: 'metadata' (names/tags only), 'content' (adds content/config), 'full' (includes reflect_response)",
|
||||||
|
"default": "full",
|
||||||
|
"title": "Detail"
|
||||||
|
},
|
||||||
|
"description": "Detail level: 'metadata' (names/tags only), 'content' (adds content/config), 'full' (includes reflect_response)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "authorization",
|
"name": "authorization",
|
||||||
"in": "header",
|
"in": "header",
|
||||||
|
|
@ -7261,11 +7295,25 @@
|
||||||
"title": "Name"
|
"title": "Name"
|
||||||
},
|
},
|
||||||
"source_query": {
|
"source_query": {
|
||||||
"type": "string",
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
"title": "Source Query"
|
"title": "Source Query"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"type": "string",
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
"title": "Content",
|
"title": "Content",
|
||||||
"description": "The mental model content as well-formatted markdown (auto-generated from reflect endpoint)"
|
"description": "The mental model content as well-formatted markdown (auto-generated from reflect endpoint)"
|
||||||
},
|
},
|
||||||
|
|
@ -7278,13 +7326,25 @@
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
"max_tokens": {
|
"max_tokens": {
|
||||||
"type": "integer",
|
"anyOf": [
|
||||||
"title": "Max Tokens",
|
{
|
||||||
"default": 2048
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Max Tokens"
|
||||||
},
|
},
|
||||||
"trigger": {
|
"trigger": {
|
||||||
"$ref": "#/components/schemas/MentalModelTrigger-Output",
|
"anyOf": [
|
||||||
"default": {}
|
{
|
||||||
|
"$ref": "#/components/schemas/MentalModelTrigger-Output"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"last_refreshed_at": {
|
"last_refreshed_at": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
|
@ -7326,9 +7386,7 @@
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"bank_id",
|
"bank_id",
|
||||||
"name",
|
"name"
|
||||||
"source_query",
|
|
||||||
"content"
|
|
||||||
],
|
],
|
||||||
"title": "MentalModelResponse",
|
"title": "MentalModelResponse",
|
||||||
"description": "Response model for a mental model (stored reflect response)."
|
"description": "Response model for a mental model (stored reflect response)."
|
||||||
|
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 8
|
|
||||||
title: "AG2 (AutoGen) Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add long-term persistent memory to your AG2 (AutoGen) agents with Hindsight. Automatic fact extraction, entity tracking, and recall tools that persist across conversations."
|
|
||||||
---
|
|
||||||
|
|
||||||
# AG2
|
|
||||||
|
|
||||||
Persistent long-term memory for [AG2](https://ag2.ai) agents (community AutoGen fork). Give your agents retain/recall/reflect tools that persist across conversations.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/ag2.md)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Drop-in Tools** — `register_hindsight_tools()` registers retain, recall, and reflect in one line
|
|
||||||
- **AG2-native** — Uses `Annotated` type hints compatible with AG2's `@register_for_llm` / `@register_for_execution` pattern
|
|
||||||
- **GroupChat Support** — Multiple agents can share a single memory bank
|
|
||||||
- **Selective Tools** — Include only the tools you need (`include_retain`, `include_recall`, `include_reflect`)
|
|
||||||
- **Simple Configuration** — Configure once globally or override per tool set
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-ag2
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from autogen import AssistantAgent, UserProxyAgent, LLMConfig
|
|
||||||
from hindsight_ag2 import register_hindsight_tools
|
|
||||||
|
|
||||||
llm_config = LLMConfig(api_type="openai", model="gpt-4o-mini")
|
|
||||||
|
|
||||||
with llm_config:
|
|
||||||
assistant = AssistantAgent(
|
|
||||||
name="assistant",
|
|
||||||
system_message="You are a helpful assistant with long-term memory.",
|
|
||||||
)
|
|
||||||
user_proxy = UserProxyAgent(
|
|
||||||
name="user",
|
|
||||||
human_input_mode="NEVER",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Register Hindsight memory tools on both agents
|
|
||||||
register_hindsight_tools(
|
|
||||||
assistant, user_proxy,
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
# The assistant can now use hindsight_retain, hindsight_recall, hindsight_reflect
|
|
||||||
result = user_proxy.initiate_chat(
|
|
||||||
assistant,
|
|
||||||
message="Remember that I prefer Python over JavaScript.",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it. The assistant can now store and retrieve memories across conversations.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The integration provides three AG2-compatible tool functions backed by Hindsight's API:
|
|
||||||
|
|
||||||
| Tool | Hindsight | What happens |
|
|
||||||
|------|-----------|--------------|
|
|
||||||
| `hindsight_retain(content)` | `retain(bank_id, content, ...)` | Content is stored. Hindsight extracts facts, entities, and relationships from the raw text. |
|
|
||||||
| `hindsight_recall(query)` | `recall(bank_id, query, ...)` | Hindsight runs semantic search, BM25, graph traversal, and reranking. Returns a numbered list of matching memories. |
|
|
||||||
| `hindsight_reflect(query)` | `reflect(bank_id, query, ...)` | Hindsight synthesizes a reasoned answer from all relevant memories, using the bank's disposition traits. |
|
|
||||||
|
|
||||||
Tools are plain Python functions with `Annotated` type hints. AG2 uses these hints to generate the tool schema that the LLM sees.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Global Configuration
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-key", # or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # low / mid / high
|
|
||||||
max_tokens=4096,
|
|
||||||
tags=["source:ag2"], # default tags for retain
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Per-Tool Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
budget="high",
|
|
||||||
max_tokens=8192,
|
|
||||||
tags=["team:alpha"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## GroupChat with Shared Memory
|
|
||||||
|
|
||||||
Multiple agents can share a single memory bank in a GroupChat:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager, LLMConfig
|
|
||||||
from hindsight_ag2 import register_hindsight_tools
|
|
||||||
|
|
||||||
llm_config = LLMConfig(api_type="openai", model="gpt-4o-mini")
|
|
||||||
|
|
||||||
with llm_config:
|
|
||||||
researcher = AssistantAgent(name="researcher", system_message="You research topics.")
|
|
||||||
writer = AssistantAgent(name="writer", system_message="You write content.")
|
|
||||||
executor = UserProxyAgent(name="executor", human_input_mode="NEVER")
|
|
||||||
|
|
||||||
# All agents share the same memory bank
|
|
||||||
for agent in [researcher, writer]:
|
|
||||||
register_hindsight_tools(agent, executor, bank_id="team-memory")
|
|
||||||
|
|
||||||
group_chat = GroupChat(agents=[researcher, writer, executor], messages=[])
|
|
||||||
manager = GroupChatManager(groupchat=group_chat)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Manual Registration
|
|
||||||
|
|
||||||
For full control over how tools are registered:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_ag2 import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="my-bank",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
for tool_fn in tools:
|
|
||||||
assistant.register_for_llm(description=tool_fn.__doc__)(tool_fn)
|
|
||||||
user_proxy.register_for_execution()(tool_fn)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Set global connection and default settings |
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `reset_config()` | Reset configuration to None |
|
|
||||||
|
|
||||||
### create_hindsight_tools
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured `Hindsight` client |
|
|
||||||
| `hindsight_api_url` | from config | Hindsight API URL |
|
|
||||||
| `api_key` | from config | API key |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode (any/all/any_strict/all_strict) |
|
|
||||||
| `retain_metadata` | `None` | Metadata dict for retain operations |
|
|
||||||
| `retain_document_id` | `None` | Document ID for retain (groups/upserts memories) |
|
|
||||||
| `recall_types` | `None` | Fact types to filter (world, experience, opinion, observation) |
|
|
||||||
| `recall_include_entities` | `False` | Include entity information in recall results |
|
|
||||||
| `reflect_context` | `None` | Additional context for reflect operations |
|
|
||||||
| `reflect_max_tokens` | `max_tokens` | Max tokens for reflect results |
|
|
||||||
| `reflect_response_schema` | `None` | JSON schema to constrain reflect output format |
|
|
||||||
| `reflect_tags` | `recall_tags` | Tags to filter memories used in reflect |
|
|
||||||
| `reflect_tags_match` | `recall_tags_match` | Tag matching for reflect |
|
|
||||||
| `include_retain` | `True` | Include the retain tool |
|
|
||||||
| `include_recall` | `True` | Include the recall tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect tool |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- ag2 >= 0.9.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 9
|
|
||||||
title: "Agno Agent Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to Agno agents using Hindsight's retain, recall, and reflect tools. Plug into Agno's native Toolkit pattern for long-term memory across sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Agno
|
|
||||||
|
|
||||||
Persistent memory tools for [Agno](https://github.com/agno-agi/agno) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — using Agno's native Toolkit pattern.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Native Toolkit** - Extends Agno's `Toolkit` base class, just like `Mem0Tools`
|
|
||||||
- **Memory Instructions** - Pre-recall memories for injection into `Agent(instructions=[...])`
|
|
||||||
- **Three Memory Tools** - Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Flexible Bank Resolution** - Static bank ID, `RunContext.user_id`, or custom resolver
|
|
||||||
- **Simple Configuration** - Configure once globally, or pass a client directly
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-agno
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from agno.agent import Agent
|
|
||||||
from agno.models.openai import OpenAIChat
|
|
||||||
from hindsight_agno import HindsightTools
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
|
|
||||||
agent.print_response("Remember that I prefer dark mode")
|
|
||||||
agent.print_response("What are my preferences?")
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`retain_memory`** — Store information to long-term memory
|
|
||||||
- **`recall_memory`** — Search long-term memory for relevant facts
|
|
||||||
- **`reflect_on_memory`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## With Memory Instructions
|
|
||||||
|
|
||||||
Pre-recall relevant memories and inject them into the system prompt:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_agno import HindsightTools, memory_instructions
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
instructions=[memory_instructions(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = [HindsightTools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
enable_retain=True,
|
|
||||||
enable_recall=True,
|
|
||||||
enable_reflect=False, # Omit reflect
|
|
||||||
)]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bank Resolution
|
|
||||||
|
|
||||||
The bank ID is resolved in order:
|
|
||||||
|
|
||||||
1. **`bank_resolver`** — Custom callable `(RunContext) -> str`
|
|
||||||
2. **`bank_id`** — Static bank ID passed to constructor
|
|
||||||
3. **`run_context.user_id`** — Automatic per-user banks
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Per-user banks from RunContext
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(hindsight_api_url="http://localhost:8888")],
|
|
||||||
user_id="user-123", # Used as bank_id
|
|
||||||
)
|
|
||||||
|
|
||||||
# Custom resolver
|
|
||||||
def resolve_bank(ctx):
|
|
||||||
return f"team-{ctx.user_id}"
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
model=OpenAIChat(id="gpt-4o-mini"),
|
|
||||||
tools=[HindsightTools(
|
|
||||||
bank_resolver=resolve_bank,
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing connection details to every toolkit, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_agno import configure, HindsightTools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create toolkit without passing connection details
|
|
||||||
tools = [HindsightTools(bank_id="user-123")]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### `HindsightTools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static Hindsight memory bank ID |
|
|
||||||
| `bank_resolver` | `None` | Callable `(RunContext) -> str` for dynamic bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `enable_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `enable_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `enable_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- agno
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,196 +0,0 @@
|
||||||
|
|
||||||
# Vercel AI SDK
|
|
||||||
|
|
||||||
The `@vectorize-io/hindsight-ai-sdk` package integrates [Hindsight](https://hindsight.vectorize.io) memory with the [Vercel AI SDK](https://ai-sdk.dev). It provides five ready-to-use tools for retaining, recalling, and reflecting on long-term memories.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/ai-sdk.md)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @vectorize-io/hindsight-ai-sdk @vectorize-io/hindsight-client ai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Create a Hindsight client and pass it to `createHindsightTools` along with a `bankId`. The `bankId` identifies the memory store for this session—typically a user ID.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
||||||
import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
|
|
||||||
|
|
||||||
const client = new HindsightClient({ baseUrl: 'http://localhost:8888' });
|
|
||||||
|
|
||||||
const tools = createHindsightTools({
|
|
||||||
client,
|
|
||||||
bankId: 'user-123',
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
> **💡 Per-request bank IDs**
|
|
||||||
>
|
|
||||||
In multi-user applications, create `tools` inside your request handler so each request closes over the correct `bankId`. See the [Next.js example](#in-a-nextjs-route-handler) below.
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### With `generateText`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { generateText } from 'ai';
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
|
|
||||||
const { text } = await generateText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
tools,
|
|
||||||
maxSteps: 5,
|
|
||||||
system: 'You are a helpful assistant with long-term memory.',
|
|
||||||
prompt: 'Remember that I prefer dark mode and large fonts.',
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### With `streamText`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
|
|
||||||
const result = streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
tools,
|
|
||||||
maxSteps: 5,
|
|
||||||
system: 'You are a helpful assistant with long-term memory.',
|
|
||||||
prompt: 'What are my display preferences?',
|
|
||||||
});
|
|
||||||
|
|
||||||
for await (const chunk of result.textStream) {
|
|
||||||
process.stdout.write(chunk);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### With `ToolLoopAgent`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { generateText, ToolLoopAgent, stepCountIs } from 'ai';
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
||||||
import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
|
|
||||||
|
|
||||||
const client = new HindsightClient({ baseUrl: process.env.HINDSIGHT_API_URL! });
|
|
||||||
|
|
||||||
const agent = new ToolLoopAgent({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
tools: createHindsightTools({ client, bankId: 'user-123' }),
|
|
||||||
stopWhen: stepCountIs(10),
|
|
||||||
system: 'You are a helpful assistant with long-term memory.',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await agent.generate({
|
|
||||||
prompt: 'Remember that my favorite editor is Neovim',
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### In a Next.js Route Handler
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// app/api/chat/route.ts
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
||||||
import { createHindsightTools } from '@vectorize-io/hindsight-ai-sdk';
|
|
||||||
|
|
||||||
const hindsightClient = new HindsightClient({
|
|
||||||
baseUrl: process.env.HINDSIGHT_API_URL!,
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
|
||||||
const { messages, userId } = await req.json();
|
|
||||||
|
|
||||||
// Tools are created per-request, closing over the current user's bankId
|
|
||||||
const tools = createHindsightTools({
|
|
||||||
client: hindsightClient,
|
|
||||||
bankId: userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
return streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
tools,
|
|
||||||
maxSteps: 5,
|
|
||||||
system: 'You are a helpful assistant with long-term memory.',
|
|
||||||
messages,
|
|
||||||
}).toDataStreamResponse();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tools Reference
|
|
||||||
|
|
||||||
Five tools are registered. The `bankId` is fixed at creation time—the agent cannot change it.
|
|
||||||
|
|
||||||
| Tool | What the agent provides | What the constructor controls |
|
|
||||||
|------|------------------------|-------------------------------|
|
|
||||||
| `retain` | `content`, `documentId`, `timestamp`, `context` | `async`, `tags`, `metadata` |
|
|
||||||
| `recall` | `query`, `queryTimestamp` | `budget`, `types`, `maxTokens`, `includeEntities`, `includeChunks` |
|
|
||||||
| `reflect` | `query`, `context` | `budget` |
|
|
||||||
| `getMentalModel` | `mentalModelId` | — |
|
|
||||||
| `getDocument` | `documentId` | — |
|
|
||||||
|
|
||||||
**Why this split?** Semantic inputs (what to remember, what to search for) belong to the agent. Infrastructure concerns (cost budget, tagging strategy, async mode) belong to the application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Constructor Options
|
|
||||||
|
|
||||||
All options except `client` and `bankId` are optional. Each tool's options are grouped under the tool name.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const tools = createHindsightTools({
|
|
||||||
client,
|
|
||||||
bankId: userId,
|
|
||||||
|
|
||||||
retain: {
|
|
||||||
async: true, // fire-and-forget (default: false)
|
|
||||||
tags: ['env:prod', 'app:support'], // always attached to every retained memory
|
|
||||||
metadata: { version: '2.0' }, // always attached to every retained memory
|
|
||||||
},
|
|
||||||
|
|
||||||
recall: {
|
|
||||||
budget: 'high', // processing depth: low | mid | high (default: 'mid')
|
|
||||||
types: ['experience', 'world'], // restrict to these fact types (default: all)
|
|
||||||
maxTokens: 2048, // cap token budget (default: API default)
|
|
||||||
includeEntities: true, // include entity observations (default: false)
|
|
||||||
includeChunks: true, // include raw source chunks (default: false)
|
|
||||||
},
|
|
||||||
|
|
||||||
reflect: {
|
|
||||||
budget: 'mid', // processing depth (default: 'mid')
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### `retain`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `async` | `boolean` | `false` | Fire-and-forget — do not wait for ingestion to complete |
|
|
||||||
| `tags` | `string[]` | — | Tags attached to every retained memory |
|
|
||||||
| `metadata` | `Record<string, string>` | — | Metadata attached to every retained memory |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
||||||
### `recall`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls retrieval depth and latency |
|
|
||||||
| `types` | `('world' \| 'experience' \| 'observation')[]` | all | Restrict results to these fact types |
|
|
||||||
| `maxTokens` | `number` | API default | Cap the total tokens returned |
|
|
||||||
| `includeEntities` | `boolean` | `false` | Include entity observations in results |
|
|
||||||
| `includeChunks` | `boolean` | `false` | Include raw source chunks in results |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
||||||
### `reflect`
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Controls synthesis depth and latency |
|
|
||||||
| `maxTokens` | `number` | API default | Maximum tokens for the response |
|
|
||||||
| `description` | `string` | built-in | Override the tool description shown to the model |
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 12
|
|
||||||
---
|
|
||||||
|
|
||||||
# AutoGen
|
|
||||||
|
|
||||||
Persistent long-term memory for [AutoGen](https://microsoft.github.io/autogen/) agents via Hindsight. Provides `FunctionTool` instances that plug directly into AutoGen's `AssistantAgent`.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Memory Tools** — retain, recall, and reflect as AutoGen `FunctionTool` instances compatible with `AssistantAgent(tools=[...])`
|
|
||||||
- **Async-Native** — Uses `aretain`, `arecall`, `areflect` directly — works seamlessly in AutoGen's async runtime
|
|
||||||
- **Selective Tools** — Include only the tools you need with `include_retain/recall/reflect` flags
|
|
||||||
- **Tag-Based Scoping** — Partition memories by topic, session, or user with tags
|
|
||||||
- **Global Configuration** — Configure once with `configure()`, create tools anywhere
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-autogen autogen-agentchat "autogen-ext[openai]"
|
|
||||||
```
|
|
||||||
|
|
||||||
`hindsight-autogen` pulls in `autogen-core` and `hindsight-client`. You also need `autogen-agentchat` for `AssistantAgent` and `autogen-ext[openai]` for the OpenAI model client.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
from autogen_agentchat.agents import AssistantAgent
|
|
||||||
from autogen_ext.models.openai import OpenAIChatCompletionClient
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_autogen import create_hindsight_tools
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
await client.acreate_bank(bank_id="user-123")
|
|
||||||
|
|
||||||
model_client = OpenAIChatCompletionClient(model="gpt-4o")
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
agent = AssistantAgent(
|
|
||||||
name="assistant",
|
|
||||||
model_client=model_client,
|
|
||||||
tools=tools,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Store a memory
|
|
||||||
result = await agent.run(task="Remember that I prefer dark mode")
|
|
||||||
print(result.messages[-1].content)
|
|
||||||
|
|
||||||
# Hindsight processes retained content asynchronously (fact extraction,
|
|
||||||
# entity resolution, embeddings). A brief pause ensures memories are
|
|
||||||
# searchable before the next recall. In production, this delay is only
|
|
||||||
# needed when retain and recall happen back-to-back in the same script.
|
|
||||||
await asyncio.sleep(3)
|
|
||||||
|
|
||||||
# Recall it later
|
|
||||||
result = await agent.run(task="What are my UI preferences?")
|
|
||||||
print(result.messages[-1].content)
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
await client.aclose()
|
|
||||||
await model_client.close()
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
:::tip Jupyter Notebooks
|
|
||||||
If you're running in a Jupyter notebook, you don't need `asyncio.run()` — just use `await` directly in cells since the notebook already has an active event loop.
|
|
||||||
:::
|
|
||||||
|
|
||||||
The agent gets three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing a client to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_autogen import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client — uses global config
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Memory Scoping with Tags
|
|
||||||
|
|
||||||
Use tags to partition memories by topic, session, or user:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Store memories tagged by source
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
tags=["source:chat", "session:abc"],
|
|
||||||
recall_tags=["source:chat"],
|
|
||||||
recall_tags_match="any",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Production Patterns
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
Tools raise `HindsightError` on failure, which AutoGen surfaces to the agent as a tool error. Wrap agent calls for graceful degradation:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_autogen.errors import HindsightError
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = await agent.run(task="What do you remember about me?")
|
|
||||||
except HindsightError as e:
|
|
||||||
print(f"Memory operation failed: {e}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bank Lifecycle
|
|
||||||
|
|
||||||
Create banks before first use and clean up when done:
|
|
||||||
|
|
||||||
```python
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
# Create bank (idempotent)
|
|
||||||
await client.acreate_bank(bank_id="user-123")
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
# ... use tools ...
|
|
||||||
|
|
||||||
# Optional: delete bank when no longer needed
|
|
||||||
await client.adelete_bank(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Agent Teams
|
|
||||||
|
|
||||||
Give each agent its own memory bank, or share a bank across a team:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Per-agent memory
|
|
||||||
researcher_tools = create_hindsight_tools(client=client, bank_id="researcher-memory")
|
|
||||||
writer_tools = create_hindsight_tools(client=client, bank_id="writer-memory")
|
|
||||||
|
|
||||||
# Shared team memory
|
|
||||||
shared_tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="team-shared",
|
|
||||||
tags=["team:content"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode (any/all/any\_strict/all\_strict) |
|
|
||||||
| `retain_metadata` | `None` | Default metadata dict for retain operations |
|
|
||||||
| `retain_document_id` | `None` | Default document\_id for retain (groups/upserts memories) |
|
|
||||||
| `recall_types` | `None` | Fact types to filter (world, experience, opinion, observation) |
|
|
||||||
| `recall_include_entities` | `False` | Include entity information in recall results |
|
|
||||||
| `reflect_context` | `None` | Additional context for reflect operations |
|
|
||||||
| `reflect_max_tokens` | `None` | Max tokens for reflect results (defaults to `max_tokens`) |
|
|
||||||
| `reflect_response_schema` | `None` | JSON schema to constrain reflect output format |
|
|
||||||
| `reflect_tags` | `None` | Tags to filter memories used in reflect (defaults to `recall_tags`) |
|
|
||||||
| `reflect_tags_match` | `None` | Tag matching for reflect (defaults to `recall_tags_match`) |
|
|
||||||
| `include_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `include_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- autogen-core >= 0.4.0
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "Vercel Chat SDK Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Give your Vercel Chat SDK bot persistent, per-user memory across Slack, Discord, Teams, and more. Single handler wrapper, no custom plumbing required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Vercel Chat SDK
|
|
||||||
|
|
||||||
We built `@vectorize-io/hindsight-chat` to give [Vercel Chat SDK](https://github.com/vercel/chat) bots persistent, per-user memory with a single handler wrapper. The integration works across Slack, Discord, Teams, Google Chat, GitHub, and Linear — no custom plumbing required.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/chat.md)
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @vectorize-io/hindsight-chat
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { Chat } from 'chat';
|
|
||||||
import { HindsightClient } from '@vectorize-io/hindsight-client';
|
|
||||||
import { withHindsightChat } from '@vectorize-io/hindsight-chat';
|
|
||||||
import { streamText } from 'ai';
|
|
||||||
import { openai } from '@ai-sdk/openai';
|
|
||||||
|
|
||||||
const chat = new Chat({ connectors: [/* your connectors */] });
|
|
||||||
const hindsight = new HindsightClient({ apiKey: process.env.HINDSIGHT_API_KEY });
|
|
||||||
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId, // per-user memory
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
await thread.subscribe();
|
|
||||||
|
|
||||||
const result = await streamText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stream the response
|
|
||||||
const chunks: string[] = [];
|
|
||||||
for await (const chunk of result.textStream) {
|
|
||||||
chunks.push(chunk);
|
|
||||||
}
|
|
||||||
const fullResponse = chunks.join('');
|
|
||||||
await thread.post(fullResponse);
|
|
||||||
|
|
||||||
// Store the conversation in memory
|
|
||||||
await ctx.retain(
|
|
||||||
`User: ${message.text}\nAssistant: ${fullResponse}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### `withHindsightChat(options, handler)`
|
|
||||||
|
|
||||||
`withHindsightChat` wraps your existing Chat SDK handler and injects memory context automatically. It returns a standard handler `(thread, message) => Promise<void>` so it drops in without changing your handler signature.
|
|
||||||
|
|
||||||
#### Options
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|--------|------|---------|-------------|
|
|
||||||
| `client` | `HindsightClient` | *required* | Hindsight client instance |
|
|
||||||
| `bankId` | `string \| (msg) => string` | *required* | Memory bank ID or resolver function |
|
|
||||||
| `recall.enabled` | `boolean` | `true` | Auto-recall memories before handler |
|
|
||||||
| `recall.budget` | `'low' \| 'mid' \| 'high'` | `'mid'` | Processing budget for recall |
|
|
||||||
| `recall.maxTokens` | `number` | API default | Max tokens for recall results |
|
|
||||||
| `recall.types` | `FactType[]` | all | Filter to specific fact types |
|
|
||||||
| `recall.includeEntities` | `boolean` | `true` | Include entity observations |
|
|
||||||
| `retain.enabled` | `boolean` | `false` | Auto-retain inbound messages |
|
|
||||||
| `retain.async` | `boolean` | `true` | Fire-and-forget retain |
|
|
||||||
| `retain.tags` | `string[]` | – | Tags for retained memories |
|
|
||||||
| `retain.metadata` | `Record<string, string>` | – | Metadata for retained memories |
|
|
||||||
|
|
||||||
### Context (`ctx`)
|
|
||||||
|
|
||||||
We inject a third `ctx` argument into your handler that exposes the full Hindsight memory API scoped to the current user's bank:
|
|
||||||
|
|
||||||
| Property/Method | Description |
|
|
||||||
|----------------|-------------|
|
|
||||||
| `ctx.bankId` | Resolved bank ID |
|
|
||||||
| `ctx.memories` | Array of recalled memories |
|
|
||||||
| `ctx.entities` | Entity observations (or null) |
|
|
||||||
| `ctx.memoriesAsSystemPrompt(options?)` | Format memories for LLM system prompt |
|
|
||||||
| `ctx.retain(content, options?)` | Store content in memory |
|
|
||||||
| `ctx.recall(query, options?)` | Search memories |
|
|
||||||
| `ctx.reflect(query, options?)` | Reason over memories |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Subscribed Message Handler
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
chat.onSubscribedMessage(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId,
|
|
||||||
recall: { budget: 'high', maxTokens: 1000 },
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
const result = await generateText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
await thread.post(result.text);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Auto-Retain Inbound Messages
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{
|
|
||||||
client: hindsight,
|
|
||||||
bankId: (msg) => msg.author.userId,
|
|
||||||
retain: { enabled: true, tags: ['slack', 'inbound'] },
|
|
||||||
},
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
// Inbound message is already being retained automatically
|
|
||||||
const result = await generateText({
|
|
||||||
model: openai('gpt-4o'),
|
|
||||||
system: ctx.memoriesAsSystemPrompt(),
|
|
||||||
messages: [{ role: 'user', content: message.text }],
|
|
||||||
});
|
|
||||||
await thread.post(result.text);
|
|
||||||
|
|
||||||
// Retain the assistant response separately
|
|
||||||
await ctx.retain(`Assistant: ${result.text}`, {
|
|
||||||
tags: ['slack', 'outbound'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Static Bank ID (Shared Memory)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// All users share the same memory bank
|
|
||||||
chat.onNewMention(
|
|
||||||
withHindsightChat(
|
|
||||||
{ client: hindsight, bankId: 'shared-team-memory' },
|
|
||||||
async (thread, message, ctx) => {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
We designed the integration so that memory failures never break your bot. Auto-recall and auto-retain errors are caught internally, logged as warnings, and the handler continues with empty memories. Manual `ctx.retain()`, `ctx.recall()`, and `ctx.reflect()` calls propagate errors normally so you can handle them as needed.
|
|
||||||
|
|
@ -1,216 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "Claude Code Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Claude Code with Hindsight. Automatically captures conversations and recalls relevant context across sessions using Claude Code's hook-based architecture."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Claude Code
|
|
||||||
|
|
||||||
Biomimetic long-term memory for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) using [Hindsight](https://vectorize.io/hindsight). Automatically captures conversations and intelligently recalls relevant context — a complete port of [`hindsight-openclaw`](./openclaw) adapted to Claude Code's hook-based plugin architecture.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/claude-code.md)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Add the Hindsight marketplace and install the plugin
|
|
||||||
claude plugin marketplace add vectorize-io/hindsight
|
|
||||||
claude plugin install hindsight-memory
|
|
||||||
|
|
||||||
# 2. Configure your LLM provider for memory extraction
|
|
||||||
# Option A: OpenAI (auto-detected)
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option B: Anthropic (auto-detected)
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option C: No API key needed (uses Claude Code's own model — personal/local use only)
|
|
||||||
export HINDSIGHT_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option D: Connect to an external Hindsight server instead of running locally
|
|
||||||
mkdir -p ~/.hindsight
|
|
||||||
echo '{"hindsightApiUrl": "https://your-hindsight-server.com"}' > ~/.hindsight/claude-code.json
|
|
||||||
|
|
||||||
# 3. Start Claude Code — the plugin activates automatically
|
|
||||||
claude
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it! The plugin will automatically start capturing and recalling memories.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every user prompt, queries Hindsight for relevant memories and injects them as context (invisible to the chat transcript, visible to Claude)
|
|
||||||
- **Auto-retain** — after every response (or every N turns), extracts and retains conversation content to Hindsight for long-term storage
|
|
||||||
- **Daemon management** — can auto-start/stop `hindsight-embed` locally or connect to an external Hindsight server
|
|
||||||
- **Dynamic bank IDs** — supports per-agent, per-project, or per-session memory isolation
|
|
||||||
- **Channel-agnostic** — works with Claude Code Channels (Telegram, Discord, Slack) or interactive sessions
|
|
||||||
- **Zero dependencies** — pure Python stdlib, no pip install required
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin uses all four Claude Code hook events:
|
|
||||||
|
|
||||||
| Hook | Event | Purpose |
|
|
||||||
|------|-------|---------|
|
|
||||||
| `session_start.py` | `SessionStart` | Health check — verify Hindsight is reachable |
|
|
||||||
| `recall.py` | `UserPromptSubmit` | **Auto-recall** — query memories, inject as `additionalContext` |
|
|
||||||
| `retain.py` | `Stop` | **Auto-retain** — extract transcript, POST to Hindsight (async) |
|
|
||||||
| `session_end.py` | `SessionEnd` | Cleanup — stop auto-managed daemon if started |
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended for production)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted). No local LLM needed — the server handles fact extraction.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-token"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon (auto-managed)
|
|
||||||
|
|
||||||
The plugin automatically starts and stops `hindsight-embed` via `uvx`. Requires an LLM provider API key for local fact extraction.
|
|
||||||
|
|
||||||
Set an LLM provider:
|
|
||||||
```bash
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
# or
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
# or
|
|
||||||
export HINDSIGHT_LLM_PROVIDER=claude-code # No API key needed
|
|
||||||
```
|
|
||||||
|
|
||||||
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_LLM_MODEL`.
|
|
||||||
|
|
||||||
### 3. Existing Local Server
|
|
||||||
|
|
||||||
If you already have `hindsight-embed` running, leave `hindsightApiUrl` empty and set `apiPort` to match your server's port. The plugin will detect it automatically.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
All settings live in `~/.hindsight/claude-code.json`. Every setting can also be overridden via environment variables. The plugin ships with sensible defaults — you only need to configure what you want to change.
|
|
||||||
|
|
||||||
**Loading order** (later entries win):
|
|
||||||
1. Built-in defaults (hardcoded in the plugin)
|
|
||||||
2. Plugin `settings.json` (ships with the plugin, at `CLAUDE_PLUGIN_ROOT/settings.json`)
|
|
||||||
3. User config (`~/.hindsight/claude-code.json` — recommended for your overrides)
|
|
||||||
4. Environment variables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Connection & Daemon
|
|
||||||
|
|
||||||
These settings control how the plugin connects to the Hindsight API.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | `HINDSIGHT_API_URL` | `""` (empty) | URL of an external Hindsight API server. When empty, the plugin uses a local daemon instead. |
|
|
||||||
| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | `null` | Authentication token for the external API. Only needed when `hindsightApiUrl` is set. |
|
|
||||||
| `apiPort` | `HINDSIGHT_API_PORT` | `9077` | Port used by the local `hindsight-embed` daemon. Change this if you run multiple instances or have a port conflict. |
|
|
||||||
| `daemonIdleTimeout` | `HINDSIGHT_DAEMON_IDLE_TIMEOUT` | `0` | Seconds of inactivity before the local daemon shuts itself down. `0` means the daemon stays running until the session ends. |
|
|
||||||
| `embedVersion` | `HINDSIGHT_EMBED_VERSION` | `"latest"` | Which version of `hindsight-embed` to install via `uvx`. Pin to a specific version (e.g. `"0.5.2"`) for reproducibility. |
|
|
||||||
| `embedPackagePath` | `HINDSIGHT_EMBED_PACKAGE_PATH` | `null` | Local filesystem path to a `hindsight-embed` checkout. When set, the plugin runs from this path instead of installing via `uvx`. Useful for development. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### LLM Provider (local daemon only)
|
|
||||||
|
|
||||||
These settings configure which LLM the local daemon uses for fact extraction. They are **ignored** when connecting to an external API (the server uses its own LLM configuration).
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `llmProvider` | `HINDSIGHT_LLM_PROVIDER` | auto-detect | Which LLM provider to use. Supported values: `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `openai-codex`, `claude-code`. When omitted, the plugin auto-detects by checking for API key env vars in order: `OPENAI_API_KEY` → `ANTHROPIC_API_KEY` → `GEMINI_API_KEY` → `GROQ_API_KEY`. |
|
|
||||||
| `llmModel` | `HINDSIGHT_LLM_MODEL` | provider default | Override the default model for the chosen provider (e.g. `"gpt-4o"`, `"claude-sonnet-4-20250514"`). When omitted, the Hindsight API picks a sensible default for each provider. |
|
|
||||||
| `llmApiKeyEnv` | — | provider standard | Name of the environment variable that holds the API key. Normally auto-detected (e.g. `OPENAI_API_KEY` for the `openai` provider). Set this only if your key is in a non-standard env var. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
A **bank** is an isolated memory store — like a separate "brain." These settings control which bank the plugin reads from and writes to.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | `HINDSIGHT_BANK_ID` | `"claude_code"` | The bank ID to use when `dynamicBankId` is `false`. All sessions share this single bank. |
|
|
||||||
| `bankMission` | `HINDSIGHT_BANK_MISSION` | generic assistant prompt | A short description of the agent's identity and purpose. Sent to Hindsight when creating or updating the bank, and used during recall to contextualize results. |
|
|
||||||
| `retainMission` | — | extraction prompt | Instructions for the fact extraction LLM — tells it *what* to extract from conversations (e.g. "Extract technical decisions and user preferences"). |
|
|
||||||
| `dynamicBankId` | `HINDSIGHT_DYNAMIC_BANK_ID` | `false` | When `true`, the plugin derives a unique bank ID from context fields (see `dynamicBankGranularity`), giving each combination its own isolated memory. |
|
|
||||||
| `dynamicBankGranularity` | — | `["agent", "project"]` | Which context fields to combine when building a dynamic bank ID. Available fields: `agent` (agent name), `project` (working directory), `session` (session ID), `channel` (channel ID), `user` (user ID). |
|
|
||||||
| `bankIdPrefix` | — | `""` | A string prepended to all bank IDs — both static and dynamic. Useful for namespacing (e.g. `"prod"` or `"staging"`). |
|
|
||||||
| `agentName` | `HINDSIGHT_AGENT_NAME` | `"claude-code"` | Name used for the `agent` field in dynamic bank ID derivation. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
Auto-recall runs on every user prompt. It queries Hindsight for relevant memories and injects them into Claude's context as invisible `additionalContext` (the user doesn't see them in the chat transcript).
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `HINDSIGHT_AUTO_RECALL` | `true` | Master switch for auto-recall. Set to `false` to disable memory retrieval entirely. |
|
|
||||||
| `recallBudget` | `HINDSIGHT_RECALL_BUDGET` | `"mid"` | Controls how hard Hindsight searches for memories. `"low"` = fast, fewer strategies; `"mid"` = balanced; `"high"` = thorough, slower. Affects latency directly. |
|
|
||||||
| `recallMaxTokens` | `HINDSIGHT_RECALL_MAX_TOKENS` | `1024` | Maximum number of tokens in the recalled memory block. Lower values reduce context usage but may truncate relevant memories. |
|
|
||||||
| `recallTypes` | — | `["world", "experience"]` | Which memory types to retrieve. `"world"` = general facts; `"experience"` = personal experiences; `"observation"` = raw observations. |
|
|
||||||
| `recallContextTurns` | `HINDSIGHT_RECALL_CONTEXT_TURNS` | `1` | How many prior conversation turns to include when composing the recall query. `1` = only the latest user message; higher values give more context but may dilute the query. |
|
|
||||||
| `recallMaxQueryChars` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | `800` | Maximum character length of the query sent to Hindsight. Longer queries are truncated. |
|
|
||||||
| `recallRoles` | — | `["user", "assistant"]` | Which message roles to include when building the recall query from prior turns. |
|
|
||||||
| `recallPromptPreamble` | — | built-in string | Text placed above the recalled memories in the injected context block. Customize this to change how Claude interprets the memories. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
Auto-retain runs after Claude responds. It extracts the conversation transcript and sends it to Hindsight for long-term storage and fact extraction.
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `HINDSIGHT_AUTO_RETAIN` | `true` | Master switch for auto-retain. Set to `false` to disable memory storage entirely. |
|
|
||||||
| `retainMode` | `HINDSIGHT_RETAIN_MODE` | `"full-session"` | Retention strategy. `"full-session"` sends the full conversation transcript (with chunking). |
|
|
||||||
| `retainEveryNTurns` | — | `10` | How often to retain. `1` = every turn; `10` = every 10th turn. Higher values reduce API calls but delay memory capture. Values > 1 enable **chunked retention** with a sliding window. |
|
|
||||||
| `retainOverlapTurns` | — | `2` | When chunked retention fires, this many extra turns from the previous chunk are included for continuity. Total window size = `retainEveryNTurns + retainOverlapTurns`. |
|
|
||||||
| `retainRoles` | — | `["user", "assistant"]` | Which message roles to include in the retained transcript. |
|
|
||||||
| `retainToolCalls` | — | `true` | Whether to include tool calls (function invocations and results) in the retained transcript. Captures structured actions like file reads, searches, and code edits. |
|
|
||||||
| `retainTags` | — | `["{session_id}"]` | Tags attached to the retained document. Supports `{session_id}` placeholder which is replaced with the current session ID at runtime. |
|
|
||||||
| `retainMetadata` | — | `{}` | Arbitrary key-value metadata attached to the retained document. |
|
|
||||||
| `retainContext` | — | `"claude-code"` | A label attached to retained memories identifying their source. Useful when multiple integrations write to the same bank. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Debug
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `HINDSIGHT_DEBUG` | `false` | Enable verbose logging to stderr. All log lines are prefixed with `[Hindsight]`. Useful for diagnosing connection issues, recall/retain behavior, and bank ID derivation. |
|
|
||||||
|
|
||||||
## Claude Code Channels
|
|
||||||
|
|
||||||
With [Claude Code Channels](https://docs.anthropic.com/en/docs/claude-code), Claude Code can operate as a persistent background agent connected to Telegram, Discord, Slack, and other messaging platforms. This plugin gives Channel-based agents the same long-term memory that `hindsight-openclaw` provides for Openclaw agents.
|
|
||||||
|
|
||||||
For Channel agents, enable dynamic bank IDs for per-channel/per-user memory isolation:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dynamicBankId": true,
|
|
||||||
"dynamicBankGranularity": ["agent", "channel", "user"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And set channel context via environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_CHANNEL_ID="telegram-group-12345"
|
|
||||||
export HINDSIGHT_USER_ID="user-67890"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Plugin not activating**: Check Claude Code logs for `[Hindsight]` messages. Enable `"debug": true` in `~/.hindsight/claude-code.json`.
|
|
||||||
|
|
||||||
**Recall returning no memories**: Verify the Hindsight server is reachable (`curl http://localhost:9077/health`). Memories need at least one retain cycle before they're available.
|
|
||||||
|
|
||||||
**Daemon not starting**: Ensure an LLM API key is set (or use `HINDSIGHT_LLM_PROVIDER=claude-code`). Review daemon logs at `~/.hindsight/profiles/claude-code.log`.
|
|
||||||
|
|
||||||
**High latency on recall**: The recall hook has a 12-second timeout. Use `recallBudget: "low"` or reduce `recallMaxTokens` for faster responses.
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 6
|
|
||||||
title: "Codex CLI Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to OpenAI Codex CLI with Hindsight. Three Python hook scripts automatically recall context before each prompt and retain conversations — no workflow changes required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Codex
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/codex.md)
|
|
||||||
|
|
||||||
Persistent memory for [Codex CLI](https://github.com/openai/codex) using [Hindsight](https://vectorize.io/hindsight). Three Python hook scripts automatically recall relevant context before each prompt and retain conversations after each turn — no changes to your Codex workflow required.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-codex | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer will guide you through choosing local or cloud mode and configuring your connection. Once installed, start a new Codex session — memory is live.
|
|
||||||
|
|
||||||
To uninstall:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-codex | bash -s -- --uninstall
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every user prompt, queries Hindsight for relevant memories and injects them as `additionalContext` (invisible to the transcript, visible to Codex)
|
|
||||||
- **Auto-retain** — after each Codex response, stores the conversation transcript to Hindsight for future recall
|
|
||||||
- **Dynamic bank IDs** — supports per-project memory isolation based on the working directory
|
|
||||||
- **Session-level upsert** — uses the session ID as the document ID so re-running the same session updates rather than duplicates stored content
|
|
||||||
- **Zero dependencies** — pure Python stdlib, no pip install required
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin uses three Codex hook events:
|
|
||||||
|
|
||||||
| Hook | Event | Purpose |
|
|
||||||
|------|-------|---------|
|
|
||||||
| `session_start.py` | `SessionStart` | Warm up — verify Hindsight is reachable |
|
|
||||||
| `recall.py` | `UserPromptSubmit` | **Auto-recall** — query memories, inject as `additionalContext` |
|
|
||||||
| `retain.py` | `Stop` | **Auto-retain** — extract transcript, POST to Hindsight (async) |
|
|
||||||
|
|
||||||
On `UserPromptSubmit`, the hook reads the prompt, queries Hindsight for the most relevant memories, and outputs a `hookSpecificOutput.additionalContext` block. Codex prepends this to the conversation before sending it to the model:
|
|
||||||
|
|
||||||
```
|
|
||||||
<hindsight_memories>
|
|
||||||
Relevant memories from past conversations...
|
|
||||||
Current time - 2026-03-27 09:14
|
|
||||||
|
|
||||||
- Project uses FastAPI with asyncpg — not SQLAlchemy [world] (2026-03-26)
|
|
||||||
- Preferred testing framework: pytest with pytest-asyncio [experience] (2026-03-26)
|
|
||||||
</hindsight_memories>
|
|
||||||
```
|
|
||||||
|
|
||||||
On `Stop`, the hook reads the session transcript, strips previously injected memory tags (to prevent feedback loops), and POSTs the conversation to Hindsight asynchronously.
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://api.hindsight.vectorize.io",
|
|
||||||
"hindsightApiToken": "hsk_your_token"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon
|
|
||||||
|
|
||||||
Run `hindsight-embed` locally. The `session_start.py` hook will detect it on `apiPort` (default `9077`). The daemon is not auto-started by the Codex plugin — start it separately:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed
|
|
||||||
```
|
|
||||||
|
|
||||||
Then leave `hindsightApiUrl` empty in your config and the plugin will connect to `http://localhost:9077`.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Settings are loaded from `~/.hindsight/codex.json`. Every setting can also be overridden via environment variable.
|
|
||||||
|
|
||||||
**Loading order** (later entries win):
|
|
||||||
|
|
||||||
1. Built-in defaults
|
|
||||||
2. Plugin `settings.json` (at `~/.hindsight/codex/settings.json`)
|
|
||||||
3. User config (`~/.hindsight/codex.json`)
|
|
||||||
4. Environment variables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Connection
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | `HINDSIGHT_API_URL` | `""` | URL of the Hindsight API server. Required. |
|
|
||||||
| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | `null` | API token for authentication. Required for Hindsight Cloud. |
|
|
||||||
| `apiPort` | `HINDSIGHT_API_PORT` | `9077` | Port for the local `hindsight-embed` daemon. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | `HINDSIGHT_BANK_ID` | `"codex"` | The bank to read from and write to. All sessions share this bank unless `dynamicBankId` is enabled. |
|
|
||||||
| `bankMission` | `HINDSIGHT_BANK_MISSION` | coding assistant prompt | Describes the agent's purpose. Sent when creating or updating the bank. |
|
|
||||||
| `retainMission` | — | extraction prompt | Instructions for Hindsight's fact extraction — what to extract from coding conversations. |
|
|
||||||
| `dynamicBankId` | `HINDSIGHT_DYNAMIC_BANK_ID` | `false` | When `true`, derives a unique bank ID from `dynamicBankGranularity` fields — useful for per-project isolation. |
|
|
||||||
| `dynamicBankGranularity` | — | `["agent", "project"]` | Which fields to combine for dynamic bank IDs. `"project"` = working directory, `"agent"` = agent name. |
|
|
||||||
| `bankIdPrefix` | — | `""` | Prefix prepended to all bank IDs. |
|
|
||||||
| `agentName` | `HINDSIGHT_AGENT_NAME` | `"codex"` | Agent name used in dynamic bank ID derivation. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `HINDSIGHT_AUTO_RECALL` | `true` | Master switch for auto-recall. |
|
|
||||||
| `recallBudget` | `HINDSIGHT_RECALL_BUDGET` | `"mid"` | Search depth: `"low"` (fast), `"mid"` (balanced), `"high"` (thorough). |
|
|
||||||
| `recallMaxTokens` | `HINDSIGHT_RECALL_MAX_TOKENS` | `1024` | Max tokens in the recalled memory block. |
|
|
||||||
| `recallTypes` | — | `["world", "experience"]` | Memory types to retrieve. |
|
|
||||||
| `recallContextTurns` | `HINDSIGHT_RECALL_CONTEXT_TURNS` | `1` | Prior turns to include when building the recall query. `1` = latest prompt only. |
|
|
||||||
| `recallMaxQueryChars` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | `800` | Max characters in the query sent to Hindsight. |
|
|
||||||
| `recallRoles` | — | `["user", "assistant"]` | Which roles to include when building a multi-turn query. |
|
|
||||||
| `recallPromptPreamble` | — | built-in | Text placed above the recalled memories in the injected context block. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `HINDSIGHT_AUTO_RETAIN` | `true` | Master switch for auto-retain. |
|
|
||||||
| `retainMode` | `HINDSIGHT_RETAIN_MODE` | `"full-session"` | `"full-session"` sends the full transcript per session (upserted by session ID). `"chunked"` sends sliding windows every N turns. |
|
|
||||||
| `retainEveryNTurns` | — | `10` | Retain fires every N turns. `1` = every turn. Higher values reduce API calls. |
|
|
||||||
| `retainOverlapTurns` | — | `2` | Extra turns included from the previous chunk (chunked mode only). |
|
|
||||||
| `retainRoles` | — | `["user", "assistant"]` | Which roles to include in the retained transcript. |
|
|
||||||
| `retainTags` | — | `["{session_id}"]` | Tags attached to the stored document. `{session_id}` is replaced at runtime. |
|
|
||||||
| `retainMetadata` | — | `{}` | Arbitrary key-value metadata attached to the stored document. |
|
|
||||||
| `retainContext` | — | `"codex"` | Label identifying the source integration. Useful when multiple integrations write to the same bank. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Debug
|
|
||||||
|
|
||||||
| Setting | Env Var | Default | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `HINDSIGHT_DEBUG` | `false` | Enable verbose logging to stderr. All log lines are prefixed with `[Hindsight]`. |
|
|
||||||
|
|
||||||
## Per-Project Memory
|
|
||||||
|
|
||||||
To give each project its own isolated memory bank, enable dynamic bank IDs:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dynamicBankId": true,
|
|
||||||
"dynamicBankGranularity": ["agent", "project"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
With this config, running Codex in `~/projects/api` and `~/projects/frontend` stores and recalls memories separately. Bank IDs are derived from the working directory path.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Hooks not firing**: Check that `~/.codex/config.toml` contains `codex_hooks = true` under `[features]`. Re-run the installer to fix this automatically.
|
|
||||||
|
|
||||||
**No memories recalled**: Recall returns results only after something has been retained. Either complete one Codex session first, or seed your bank manually using the [cookbook example](https://github.com/vectorize-io/hindsight-cookbook/tree/main/applications/codex-memory).
|
|
||||||
|
|
||||||
**Memory not being stored**: `retainEveryNTurns` defaults to `10` — retain only fires every 10 turns. While testing, add `"retainEveryNTurns": 1` to `~/.hindsight/codex.json`.
|
|
||||||
|
|
||||||
**Debug mode**: Add `"debug": true` to `~/.hindsight/codex.json` to see what Hindsight is doing on each turn:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] Recalling from bank 'codex', query length: 42
|
|
||||||
[Hindsight] Injecting 3 memories
|
|
||||||
[Hindsight] Retaining to bank 'codex', doc 'sess-abc123', 2 messages, 847 chars
|
|
||||||
```
|
|
||||||
|
|
||||||
**High latency on recall**: Use `"recallBudget": "low"` or reduce `recallMaxTokens` to speed up recall queries.
|
|
||||||
|
|
@ -1,245 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "CrewAI Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add long-term memory to your CrewAI agent crews. Hindsight provides fact extraction, entity tracking, and temporal awareness — persisted automatically across all crew runs."
|
|
||||||
---
|
|
||||||
|
|
||||||
# CrewAI
|
|
||||||
|
|
||||||
Persistent memory for AI agent crews via [CrewAI](https://github.com/crewAIInc/crewAI). Give your crews long-term memory with fact extraction, entity tracking, and temporal awareness.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/crewai.md)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Drop-in Storage Backend** - Implements CrewAI's `Storage` interface for `ExternalMemory`
|
|
||||||
- **Automatic Memory Flow** - CrewAI automatically stores task outputs and retrieves relevant memories
|
|
||||||
- **Per-Agent Banks** - Optionally give each agent its own isolated memory bank
|
|
||||||
- **Reflect Tool** - Agents can explicitly reason over memories with disposition-aware synthesis
|
|
||||||
- **Simple Configuration** - Configure once, use everywhere
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-crewai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure, HindsightStorage
|
|
||||||
from crewai.memory.external.external_memory import ExternalMemory
|
|
||||||
from crewai import Agent, Crew, Task
|
|
||||||
|
|
||||||
configure(hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
crew = Crew(
|
|
||||||
agents=[Agent(role="Researcher", goal="Find information", backstory="...")],
|
|
||||||
tasks=[Task(description="Research AI trends", expected_output="Report")],
|
|
||||||
external_memory=ExternalMemory(
|
|
||||||
storage=HindsightStorage(bank_id="my-crew")
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
crew.kickoff()
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it. CrewAI will automatically:
|
|
||||||
- **Query memories** at the start of each task
|
|
||||||
- **Store task outputs** to Hindsight after each task completes
|
|
||||||
|
|
||||||
Memories persist across crew runs, so your crew learns over time.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The integration maps CrewAI's 3-method `Storage` interface to Hindsight's API:
|
|
||||||
|
|
||||||
| CrewAI | Hindsight | What happens |
|
|
||||||
|--------|-----------|--------------|
|
|
||||||
| `save(value, metadata, agent)` | `retain(bank_id, content, ...)` | Task output is stored. Hindsight extracts facts, entities, and relationships from the raw text. |
|
|
||||||
| `search(query, limit)` | `recall(bank_id, query, ...)` | CrewAI constructs a query from the task description. Hindsight runs semantic search, BM25, graph traversal, and reranking. |
|
|
||||||
| `reset()` | `delete_bank(bank_id)` | Wipes the bank and optionally recreates it with its original mission. |
|
|
||||||
|
|
||||||
CrewAI calls `search()` automatically at the start of each task and `save()` after each task completes.
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888", # Hindsight API URL
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: "low", "mid", "high"
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match: any/all/any_strict/all_strict
|
|
||||||
verbose=True, # Enable logging
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Per-Storage Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
budget="high",
|
|
||||||
max_tokens=8192,
|
|
||||||
tags=["team:alpha"],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bank Missions
|
|
||||||
|
|
||||||
Set a mission to guide how Hindsight processes and organizes memories:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
mission="Track software architecture decisions, technical debt, and team preferences.",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Per-Agent Memory Banks
|
|
||||||
|
|
||||||
Give each agent its own isolated memory bank:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
per_agent_banks=True,
|
|
||||||
# Researcher -> "my-crew-researcher"
|
|
||||||
# Writer -> "my-crew-writer"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use a custom bank resolver for full control:
|
|
||||||
|
|
||||||
```python
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="my-crew",
|
|
||||||
bank_resolver=lambda base, agent: f"{base}-{agent.lower()}" if agent else base,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
:::info
|
|
||||||
When `per_agent_banks=True`, the automatic `search()` at task start queries the base bank (shared context), since CrewAI's `search()` method does not receive the agent parameter. For per-agent search isolation, create separate `HindsightStorage` instances per agent.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Reflect Tool
|
|
||||||
|
|
||||||
CrewAI's storage interface only supports save/search/reset. To give agents access to Hindsight's `reflect` (disposition-aware memory synthesis), add it as a tool:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import HindsightReflectTool
|
|
||||||
|
|
||||||
reflect_tool = HindsightReflectTool(
|
|
||||||
bank_id="my-crew",
|
|
||||||
budget="mid",
|
|
||||||
reflect_context="You are helping a software team track decisions.",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
role="Analyst",
|
|
||||||
goal="Analyze project history",
|
|
||||||
backstory="...",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
When the agent calls this tool, it gets a synthesized, contextual answer based on all relevant memories rather than raw fact snippets.
|
|
||||||
|
|
||||||
## Full Example
|
|
||||||
|
|
||||||
A research crew that remembers findings across runs:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_crewai import configure, HindsightStorage, HindsightReflectTool
|
|
||||||
from crewai.memory.external.external_memory import ExternalMemory
|
|
||||||
from crewai import Agent, Crew, Task
|
|
||||||
|
|
||||||
configure(hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
storage = HindsightStorage(
|
|
||||||
bank_id="research-crew",
|
|
||||||
mission="Track technology research findings and comparisons.",
|
|
||||||
)
|
|
||||||
|
|
||||||
reflect_tool = HindsightReflectTool(bank_id="research-crew", budget="mid")
|
|
||||||
|
|
||||||
researcher = Agent(
|
|
||||||
role="Researcher",
|
|
||||||
goal="Research topics, building on prior knowledge.",
|
|
||||||
backstory="Before starting, use hindsight_reflect to check what you already know.",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
|
|
||||||
writer = Agent(
|
|
||||||
role="Writer",
|
|
||||||
goal="Write summaries incorporating prior findings.",
|
|
||||||
backstory="Use hindsight_reflect to recall prior research.",
|
|
||||||
tools=[reflect_tool],
|
|
||||||
)
|
|
||||||
|
|
||||||
crew = Crew(
|
|
||||||
agents=[researcher, writer],
|
|
||||||
tasks=[
|
|
||||||
Task(description="Research the benefits of Rust", expected_output="Analysis", agent=researcher),
|
|
||||||
Task(description="Write an executive summary", expected_output="Summary", agent=writer),
|
|
||||||
],
|
|
||||||
external_memory=ExternalMemory(storage=storage),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Run 1: researches Rust, stores findings
|
|
||||||
crew.kickoff()
|
|
||||||
|
|
||||||
# Run 2: recalls Rust research when comparing with Go
|
|
||||||
crew.tasks[0].description = "Compare Rust with Go"
|
|
||||||
crew.kickoff()
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Set global connection and default settings |
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `reset_config()` | Reset configuration to None |
|
|
||||||
|
|
||||||
### Storage
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `hindsight_api_url` | from config | Override API URL |
|
|
||||||
| `api_key` | from config | Override API key |
|
|
||||||
| `budget` | `"mid"` | Recall budget (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `per_agent_banks` | `False` | Give each agent its own bank |
|
|
||||||
| `bank_resolver` | `None` | Custom `(bank_id, agent) -> bank_id` |
|
|
||||||
| `mission` | `None` | Bank mission for memory organization |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
### Reflect Tool
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|-----------|---------|-------------|
|
|
||||||
| `bank_id` | required | Hindsight memory bank ID |
|
|
||||||
| `budget` | `"mid"` | Reflect budget (low/mid/high) |
|
|
||||||
| `reflect_context` | `None` | Additional context for reasoning |
|
|
||||||
| `hindsight_api_url` | from config | Override API URL |
|
|
||||||
| `api_key` | from config | Override API key |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- crewai >= 0.86.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 10
|
|
||||||
title: "Hermes Agent Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Hermes Agent with Hindsight. Automatically recalls context before every LLM call and retains conversations for future sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Hermes Agent
|
|
||||||
|
|
||||||
Persistent long-term memory for [Hermes Agent](https://github.com/NousResearch/hermes-agent) using [Hindsight](https://vectorize.io/hindsight). Automatically recalls relevant context before every LLM call and retains conversations for future sessions — plus explicit retain/recall/reflect tools.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Install the plugin into Hermes's Python environment
|
|
||||||
uv pip install hindsight-hermes --python $HOME/.hermes/hermes-agent/venv/bin/python
|
|
||||||
|
|
||||||
# 2. Configure (choose one)
|
|
||||||
# Option A: Config file (recommended)
|
|
||||||
mkdir -p ~/.hindsight
|
|
||||||
cat > ~/.hindsight/hermes.json << 'EOF'
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "http://localhost:9077",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Option B: Environment variables
|
|
||||||
export HINDSIGHT_API_URL=http://localhost:9077
|
|
||||||
export HINDSIGHT_BANK_ID=hermes
|
|
||||||
|
|
||||||
# 3. Start Hermes — the plugin activates automatically
|
|
||||||
hermes
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Auto-recall** — on every turn, queries Hindsight for relevant memories and injects them into the system prompt (via `pre_llm_call` hook)
|
|
||||||
- **Auto-retain** — after every response, retains the user/assistant exchange to Hindsight (via `post_llm_call` hook)
|
|
||||||
- **Explicit tools** — `hindsight_retain`, `hindsight_recall`, `hindsight_reflect` for direct model control
|
|
||||||
- **Config file** — `~/.hindsight/hermes.json` with the same field names as openclaw and claude-code integrations
|
|
||||||
- **Zero config overhead** — env vars still work as overrides for CI/automation
|
|
||||||
|
|
||||||
:::note
|
|
||||||
The lifecycle hooks (`pre_llm_call`/`post_llm_call`) require hermes-agent with [PR #2823](https://github.com/NousResearch/hermes-agent/pull/2823) or later. On older versions, only the three tools are registered — hooks are silently skipped.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The plugin registers via Hermes's `hermes_agent.plugins` entry point system:
|
|
||||||
|
|
||||||
| Component | Purpose |
|
|
||||||
|-----------|---------|
|
|
||||||
| `pre_llm_call` hook | **Auto-recall** — query memories, inject as ephemeral system prompt context |
|
|
||||||
| `post_llm_call` hook | **Auto-retain** — store user/assistant exchange to Hindsight |
|
|
||||||
| `hindsight_retain` tool | Explicit memory storage (model-initiated) |
|
|
||||||
| `hindsight_recall` tool | Explicit memory search (model-initiated) |
|
|
||||||
| `hindsight_reflect` tool | LLM-synthesized answer from stored memories |
|
|
||||||
|
|
||||||
## Connection Modes
|
|
||||||
|
|
||||||
### 1. External API (recommended for production)
|
|
||||||
|
|
||||||
Connect to a running Hindsight server (cloud or self-hosted). No local LLM needed — the server handles fact extraction.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-token",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Local Daemon
|
|
||||||
|
|
||||||
If you're running `hindsight-embed` locally, point to it:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"hindsightApiUrl": "http://localhost:9077",
|
|
||||||
"bankId": "hermes"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Follow the [Quick Start](../../developer/api/quickstart.md) guide to get the Hindsight API running.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
All settings are in `~/.hindsight/hermes.json`. Every setting can also be overridden via environment variables (env vars take priority).
|
|
||||||
|
|
||||||
### Connection & Daemon
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `hindsightApiUrl` | — | `HINDSIGHT_API_URL` | Hindsight API URL |
|
|
||||||
| `hindsightApiToken` | `null` | `HINDSIGHT_API_TOKEN` / `HINDSIGHT_API_KEY` | Auth token for API |
|
|
||||||
| `apiPort` | `9077` | `HINDSIGHT_API_PORT` | Port for local Hindsight daemon |
|
|
||||||
| `daemonIdleTimeout` | `0` | `HINDSIGHT_DAEMON_IDLE_TIMEOUT` | Seconds before idle daemon shuts down (0 = never) |
|
|
||||||
| `embedVersion` | `"latest"` | `HINDSIGHT_EMBED_VERSION` | `hindsight-embed` version for `uvx` |
|
|
||||||
|
|
||||||
### LLM Provider (daemon mode only)
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `llmProvider` | auto-detect | `HINDSIGHT_LLM_PROVIDER` | LLM provider: `openai`, `anthropic`, `gemini`, `groq`, `ollama` |
|
|
||||||
| `llmModel` | provider default | `HINDSIGHT_LLM_MODEL` | Model override |
|
|
||||||
|
|
||||||
### Memory Bank
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `bankId` | — | `HINDSIGHT_BANK_ID` | Memory bank ID |
|
|
||||||
| `bankMission` | `""` | `HINDSIGHT_BANK_MISSION` | Agent identity/purpose for the memory bank |
|
|
||||||
| `retainMission` | `null` | — | Custom retain mission (what to extract from conversations) |
|
|
||||||
| `bankIdPrefix` | `""` | — | Prefix for all bank IDs |
|
|
||||||
|
|
||||||
### Auto-Recall
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRecall` | `true` | `HINDSIGHT_AUTO_RECALL` | Enable automatic memory recall via `pre_llm_call` hook |
|
|
||||||
| `recallBudget` | `"mid"` | `HINDSIGHT_RECALL_BUDGET` | Recall effort: `low`, `mid`, `high` |
|
|
||||||
| `recallMaxTokens` | `4096` | `HINDSIGHT_RECALL_MAX_TOKENS` | Max tokens in recall response |
|
|
||||||
| `recallMaxQueryChars` | `800` | `HINDSIGHT_RECALL_MAX_QUERY_CHARS` | Max chars of user message used as query |
|
|
||||||
| `recallPromptPreamble` | see below | — | Header text injected before recalled memories |
|
|
||||||
|
|
||||||
Default preamble:
|
|
||||||
> Relevant memories from past conversations (prioritize recent when conflicting). Only use memories that are directly useful to continue this conversation; ignore the rest:
|
|
||||||
|
|
||||||
### Auto-Retain
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `autoRetain` | `true` | `HINDSIGHT_AUTO_RETAIN` | Enable automatic retention via `post_llm_call` hook |
|
|
||||||
| `retainEveryNTurns` | `1` | — | Retain every Nth turn |
|
|
||||||
| `retainOverlapTurns` | `2` | — | Extra overlap turns for continuity |
|
|
||||||
| `retainRoles` | `["user", "assistant"]` | — | Which message roles to retain |
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
|
|
||||||
| Setting | Default | Env Var | Description |
|
|
||||||
|---------|---------|---------|-------------|
|
|
||||||
| `debug` | `false` | `HINDSIGHT_DEBUG` | Enable debug logging to stderr |
|
|
||||||
|
|
||||||
## Hermes Gateway (Telegram, Discord, Slack)
|
|
||||||
|
|
||||||
When using Hermes in gateway mode (multi-platform messaging), the plugin works across all platforms. Hermes creates a fresh `AIAgent` per message, and the plugin's `pre_llm_call` hook ensures relevant memories are recalled for each turn regardless of platform.
|
|
||||||
|
|
||||||
## Disabling Hermes's Built-in Memory
|
|
||||||
|
|
||||||
Hermes has a built-in `memory` tool that saves to local markdown files. If both are active, the LLM may prefer the built-in one. Disable it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hermes tools disable memory
|
|
||||||
```
|
|
||||||
|
|
||||||
Re-enable later with `hermes tools enable memory`.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Plugin not loading**: Verify the entry point is registered:
|
|
||||||
```bash
|
|
||||||
python -c "
|
|
||||||
import importlib.metadata
|
|
||||||
eps = importlib.metadata.entry_points(group='hermes_agent.plugins')
|
|
||||||
print(list(eps))
|
|
||||||
"
|
|
||||||
```
|
|
||||||
You should see `EntryPoint(name='hindsight', value='hindsight_hermes', ...)`.
|
|
||||||
|
|
||||||
**Tools don't appear in `/tools`**: Check that `hindsightApiUrl` (or `HINDSIGHT_API_URL`) is set. The plugin silently skips registration when unconfigured.
|
|
||||||
|
|
||||||
**Connection refused**: Verify the Hindsight API is running:
|
|
||||||
```bash
|
|
||||||
curl http://localhost:9077/health
|
|
||||||
```
|
|
||||||
|
|
||||||
**Recall returning no memories**: Memories need at least one retain cycle. Try storing a fact first, then asking about it in a new session.
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 7
|
|
||||||
title: "LangGraph & LangChain Persistent Memory with Hindsight"
|
|
||||||
description: "Add long-term memory to LangGraph and LangChain agents with Hindsight. Three integration patterns — tools, nodes, and BaseStore adapter — for persistent memory across agent runs."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LangGraph / LangChain
|
|
||||||
|
|
||||||
Persistent long-term memory for [LangGraph](https://langchain-ai.github.io/langgraph/) and [LangChain](https://python.langchain.com/) agents via Hindsight. Three integration patterns at different abstraction levels — the tools pattern works with both LangChain and LangGraph, while nodes and the BaseStore adapter are LangGraph-specific.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/langgraph.md)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Memory Tools** — retain, recall, and reflect as LangChain `@tool` functions compatible with `bind_tools()` and `ToolNode`. Works with **both LangChain and LangGraph** — no LangGraph dependency required for this pattern.
|
|
||||||
- **Graph Nodes** *(LangGraph)* — Pre-built nodes that auto-inject memories before LLM calls and auto-store after responses
|
|
||||||
- **BaseStore Adapter** *(LangGraph)* — Drop-in `BaseStore` implementation backed by Hindsight, for LangGraph's native memory patterns
|
|
||||||
- **Dynamic Banks** — Resolve bank IDs per-request from `RunnableConfig` for per-user memory
|
|
||||||
- **Async-Native** — Uses `aretain`, `arecall`, `areflect` directly — no thread-pool workarounds
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-langgraph
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start: Tools (LangChain & LangGraph)
|
|
||||||
|
|
||||||
The tools pattern creates standard LangChain `@tool` functions that work with any LangChain-compatible model via `bind_tools()`. You can use them with a LangGraph agent or with plain LangChain — no LangGraph required.
|
|
||||||
|
|
||||||
**With LangGraph (recommended):**
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_hindsight_tools
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
from langgraph.prebuilt import create_react_agent
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
agent = create_react_agent(ChatOpenAI(model="gpt-4o"), tools=tools)
|
|
||||||
|
|
||||||
result = await agent.ainvoke(
|
|
||||||
{"messages": [{"role": "user", "content": "Remember that I prefer dark mode"}]}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**With plain LangChain:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_hindsight_tools
|
|
||||||
from langchain_openai import ChatOpenAI
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
tools = create_hindsight_tools(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
model = ChatOpenAI(model="gpt-4o").bind_tools(tools)
|
|
||||||
response = await model.ainvoke("Remember that I prefer dark mode")
|
|
||||||
```
|
|
||||||
|
|
||||||
When using plain LangChain, you handle the tool execution loop yourself — call the model, check for `tool_calls`, execute them, and feed results back. LangGraph automates this loop for you.
|
|
||||||
|
|
||||||
The agent gets three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## Quick Start: Memory Nodes (LangGraph)
|
|
||||||
|
|
||||||
Add recall and retain nodes to your graph for automatic memory injection and storage.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import create_recall_node, create_retain_node
|
|
||||||
from langgraph.graph import StateGraph, MessagesState, START, END
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
recall = create_recall_node(client=client, bank_id="user-123")
|
|
||||||
retain = create_retain_node(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
builder = StateGraph(MessagesState)
|
|
||||||
builder.add_node("recall", recall)
|
|
||||||
builder.add_node("agent", agent_node) # your LLM node
|
|
||||||
builder.add_node("retain", retain)
|
|
||||||
|
|
||||||
builder.add_edge(START, "recall")
|
|
||||||
builder.add_edge("recall", "agent")
|
|
||||||
builder.add_edge("agent", "retain")
|
|
||||||
builder.add_edge("retain", END)
|
|
||||||
|
|
||||||
graph = builder.compile()
|
|
||||||
```
|
|
||||||
|
|
||||||
The recall node extracts the latest user message, searches Hindsight, and injects matching memories as a `SystemMessage`. The retain node stores human messages (optionally AI messages too) after the response.
|
|
||||||
|
|
||||||
## Quick Start: BaseStore (LangGraph)
|
|
||||||
|
|
||||||
Use Hindsight as a LangGraph `BaseStore` for cross-thread persistent memory with semantic search.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_langgraph import HindsightStore
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
store = HindsightStore(client=client)
|
|
||||||
|
|
||||||
graph = builder.compile(checkpointer=checkpointer, store=store)
|
|
||||||
|
|
||||||
# Store and search via the store API
|
|
||||||
await store.aput(("user", "123", "prefs"), "theme", {"value": "dark mode"})
|
|
||||||
results = await store.asearch(("user", "123", "prefs"), query="theme preference")
|
|
||||||
```
|
|
||||||
|
|
||||||
Namespace tuples are mapped to Hindsight bank IDs with `.` as separator (e.g., `("user", "123")` becomes bank `user.123`). Banks are auto-created on first access.
|
|
||||||
|
|
||||||
## Dynamic Bank IDs
|
|
||||||
|
|
||||||
Both nodes and the store support per-user bank resolution from `RunnableConfig`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
recall = create_recall_node(client=client, bank_id_from_config="user_id")
|
|
||||||
retain = create_retain_node(client=client, bank_id_from_config="user_id")
|
|
||||||
|
|
||||||
# Bank ID resolved at runtime from config
|
|
||||||
result = await graph.ainvoke(
|
|
||||||
{"messages": [{"role": "user", "content": "hello"}]},
|
|
||||||
config={"configurable": {"user_id": "user-456"}},
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing a client to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_langgraph import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client — uses global config
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Retain Node Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
retain = create_retain_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
retain_human=True, # Store human messages (default: True)
|
|
||||||
retain_ai=False, # Store AI responses (default: False)
|
|
||||||
tags=["source:chat"], # Tags applied to stored memories
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recall Node Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
recall = create_recall_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
budget="low", # Recall budget: low/mid/high
|
|
||||||
max_results=10, # Max memories injected
|
|
||||||
max_tokens=4096, # Max tokens for recall
|
|
||||||
tags=["scope:user"], # Filter by tags
|
|
||||||
tags_match="all", # Tag match mode
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using `output_key` for Prompt Control
|
|
||||||
|
|
||||||
By default, the recall node appends a `SystemMessage` to `messages`. Use `output_key` to write memory text to a custom state field instead, giving you full control over prompt ordering:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import Optional
|
|
||||||
from langgraph.graph import MessagesState
|
|
||||||
|
|
||||||
class AgentState(MessagesState):
|
|
||||||
memory_context: Optional[str] = None
|
|
||||||
|
|
||||||
recall = create_recall_node(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
output_key="memory_context",
|
|
||||||
)
|
|
||||||
|
|
||||||
# In your agent node, read state["memory_context"] and prepend it
|
|
||||||
# to the system prompt before calling the model.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Limitations and Notes
|
|
||||||
|
|
||||||
### HindsightStore
|
|
||||||
|
|
||||||
- **Async-only.** All sync methods (`batch`, `get`, `put`, `delete`, `search`, `list_namespaces`) raise `NotImplementedError`. Use the async variants (`abatch`, `aget`, `aput`, `adelete`, `asearch`, `alist_namespaces`) instead.
|
|
||||||
- **`get()` relies on recall.** There is no direct key lookup — the key is used as a recall query and only exact `document_id` matches are returned. Items that do not rank in the top recall results may appear missing.
|
|
||||||
- **`list_namespaces` is session-scoped.** It only tracks namespaces that have been written to via `aput()` during the current process. After a restart, `list_namespaces` returns empty even though data still exists in Hindsight.
|
|
||||||
- **`delete` is a no-op.** Calling `adelete()` logs a debug message but does not remove data from Hindsight. Hindsight's memory model is append-oriented; fact superseding is handled automatically during retain.
|
|
||||||
|
|
||||||
### Memory Nodes
|
|
||||||
|
|
||||||
- **SystemMessage ordering.** The recall node adds a `SystemMessage` with recalled memories. Because `MessagesState` uses `add_messages` (which appends), this message appears after existing messages rather than at position 0. The message has a stable ID (`hindsight_memory_context`) so it is updated rather than duplicated across invocations. If your LLM provider requires system messages first, sort or filter messages in your agent node before passing them to the model.
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
- **Tools** raise `HindsightError` on failure, which surfaces to the agent as a tool error.
|
|
||||||
- **Nodes** silently log errors and return empty messages, so a Hindsight outage does not crash your graph.
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode (any/all/any\_strict/all\_strict) |
|
|
||||||
| `retain_metadata` | `None` | Default metadata dict for retain operations |
|
|
||||||
| `retain_document_id` | `None` | Default document\_id for retain (groups/upserts memories) |
|
|
||||||
| `recall_types` | `None` | Fact types to filter (world, experience, opinion, observation) |
|
|
||||||
| `recall_include_entities` | `False` | Include entity information in recall results |
|
|
||||||
| `reflect_context` | `None` | Additional context for reflect operations |
|
|
||||||
| `reflect_max_tokens` | `None` | Max tokens for reflect results (defaults to `max_tokens`) |
|
|
||||||
| `reflect_response_schema` | `None` | JSON schema to constrain reflect output format |
|
|
||||||
| `reflect_tags` | `None` | Tags to filter memories used in reflect (defaults to `recall_tags`) |
|
|
||||||
| `reflect_tags_match` | `None` | Tag matching for reflect (defaults to `recall_tags_match`) |
|
|
||||||
| `include_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `include_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `create_recall_node()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static bank ID (or use `bank_id_from_config`) |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Max tokens for recall results |
|
|
||||||
| `max_results` | `10` | Max memories to inject |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `bank_id_from_config` | `"user_id"` | Config key to resolve bank ID at runtime |
|
|
||||||
| `output_key` | `None` | If set, write memory text to this state key instead of appending a SystemMessage to `messages` |
|
|
||||||
|
|
||||||
### `create_retain_node()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | `None` | Static bank ID (or use `bank_id_from_config`) |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `tags` | `None` | Tags applied to stored memories |
|
|
||||||
| `bank_id_from_config` | `"user_id"` | Config key to resolve bank ID at runtime |
|
|
||||||
| `retain_human` | `True` | Store human messages |
|
|
||||||
| `retain_ai` | `False` | Store AI responses |
|
|
||||||
|
|
||||||
### `HindsightStore()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `tags` | `None` | Tags applied to all retain operations |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- langchain-core >= 0.3.0
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- langgraph >= 0.3.0 *(only for nodes and store patterns — install with `pip install hindsight-langgraph[langgraph]`)*
|
|
||||||
|
|
@ -1,349 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 1
|
|
||||||
title: "LiteLLM Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to any LLM application via LiteLLM and Hindsight. Universal integration — works with any model or provider with just a few lines of code."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LiteLLM
|
|
||||||
|
|
||||||
Universal LLM memory integration via [LiteLLM](https://github.com/BerriAI/litellm). Add persistent memory to any LLM application with just a few lines of code.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/litellm.md)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Universal LLM Support** - Works with 100+ LLM providers via LiteLLM (OpenAI, Anthropic, Groq, Azure, AWS Bedrock, Google Vertex AI, and more)
|
|
||||||
- **Simple Integration** - Just configure, enable, and use `hindsight_litellm.completion()`
|
|
||||||
- **Automatic Memory Injection** - Relevant memories are injected into prompts before LLM calls
|
|
||||||
- **Automatic Conversation Storage** - Conversations are stored to Hindsight for future recall
|
|
||||||
- **Two Memory Modes** - Choose between `reflect` (synthesized context) or `recall` (raw memory retrieval)
|
|
||||||
- **Direct Memory APIs** - Query, synthesize, and store memories manually
|
|
||||||
- **Native Client Wrappers** - Alternative wrappers for OpenAI and Anthropic SDKs
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-litellm
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
# Configure and enable memory integration
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# Use the convenience wrapper - memory is automatically injected and stored
|
|
||||||
response = hindsight_litellm.completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What did we discuss about AI?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
When you call `completion()`, the following happens automatically:
|
|
||||||
|
|
||||||
1. **Memory Retrieval** - Hindsight is queried for relevant memories based on the conversation
|
|
||||||
2. **Prompt Injection** - Memories are injected into the system message
|
|
||||||
3. **LLM Call** - The enriched prompt is sent to the LLM
|
|
||||||
4. **Conversation Storage** - The conversation is stored to Hindsight for future recall
|
|
||||||
5. **Response Returned** - You receive the response as normal
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
# Required
|
|
||||||
hindsight_api_url="http://localhost:8888", # Hindsight API server URL
|
|
||||||
bank_id="my-agent", # Memory bank ID
|
|
||||||
|
|
||||||
api_key="your-api-key", # Optional API key for authentication
|
|
||||||
|
|
||||||
# Optional - Memory behavior
|
|
||||||
store_conversations=True, # Store conversations after LLM calls
|
|
||||||
inject_memories=True, # Inject relevant memories into prompts
|
|
||||||
use_reflect=False, # Use reflect API (synthesized) vs recall (raw memories)
|
|
||||||
reflect_include_facts=False, # Include source facts with reflect responses
|
|
||||||
max_memories=None, # Maximum memories to inject (None = unlimited)
|
|
||||||
max_memory_tokens=4096, # Maximum tokens for memory context
|
|
||||||
recall_budget="mid", # Recall budget: "low", "mid", "high"
|
|
||||||
fact_types=["world", "agent"], # Filter fact types to inject
|
|
||||||
|
|
||||||
# Optional - Bank Configuration
|
|
||||||
bank_name="My Agent", # Human-readable display name for the memory bank
|
|
||||||
mission="This agent...", # Instructions guiding what Hindsight should remember
|
|
||||||
|
|
||||||
# Optional - Advanced
|
|
||||||
injection_mode="system_message", # or "prepend_user"
|
|
||||||
excluded_models=["gpt-3.5*"], # Exclude certain models
|
|
||||||
verbose=True, # Enable verbose logging and debug info
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bank Configuration
|
|
||||||
|
|
||||||
The `mission` and `bank_name` parameters configure the memory bank itself. When provided, `configure()` will automatically create or update the bank with these settings.
|
|
||||||
|
|
||||||
```python
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="support-router",
|
|
||||||
bank_name="Customer Support Router",
|
|
||||||
mission="""You're a customer support router - keep track of which types of issues
|
|
||||||
should go to which teams (billing, technical, sales), customer preferences for
|
|
||||||
communication channels, and past issue resolutions.""",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Memory Modes: Reflect vs Recall
|
|
||||||
|
|
||||||
- **Recall mode** (`use_reflect=False`, default): Retrieves raw memory facts and injects them as a numbered list. Best when you need precise, individual memories.
|
|
||||||
- **Reflect mode** (`use_reflect=True`): Synthesizes memories into a coherent context paragraph. Best for natural, conversational memory context.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Recall mode - raw memories
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=False, # Default
|
|
||||||
)
|
|
||||||
# Injects: "1. [WORLD] User prefers Python\n2. [MENTAL MODEL] User prefers simple code..."
|
|
||||||
|
|
||||||
# Reflect mode - synthesized context
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
use_reflect=True,
|
|
||||||
)
|
|
||||||
# Injects: "Based on previous conversations, the user is a Python developer who..."
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Provider Support
|
|
||||||
|
|
||||||
Works with any LiteLLM-supported provider:
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hindsight_litellm
|
|
||||||
|
|
||||||
hindsight_litellm.configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
bank_id="my-agent",
|
|
||||||
)
|
|
||||||
hindsight_litellm.enable()
|
|
||||||
|
|
||||||
# OpenAI
|
|
||||||
hindsight_litellm.completion(model="gpt-4o", messages=[...])
|
|
||||||
|
|
||||||
# Anthropic
|
|
||||||
hindsight_litellm.completion(model="claude-3-5-sonnet-20241022", messages=[...])
|
|
||||||
|
|
||||||
# Groq
|
|
||||||
hindsight_litellm.completion(model="groq/llama-3.1-70b-versatile", messages=[...])
|
|
||||||
|
|
||||||
# Azure OpenAI
|
|
||||||
hindsight_litellm.completion(model="azure/gpt-4", messages=[...])
|
|
||||||
|
|
||||||
# AWS Bedrock
|
|
||||||
hindsight_litellm.completion(model="bedrock/anthropic.claude-3", messages=[...])
|
|
||||||
|
|
||||||
# Google Vertex AI
|
|
||||||
hindsight_litellm.completion(model="vertex_ai/gemini-pro", messages=[...])
|
|
||||||
```
|
|
||||||
|
|
||||||
## Direct Memory APIs
|
|
||||||
|
|
||||||
### Recall - Query raw memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, recall
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
memories = recall("what projects am I working on?", budget="mid")
|
|
||||||
for m in memories:
|
|
||||||
print(f"- [{m.fact_type}] {m.text}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reflect - Get synthesized context
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, reflect
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = reflect("what do you know about the user's preferences?")
|
|
||||||
print(result.text)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Retain - Store memories
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, retain
|
|
||||||
|
|
||||||
configure(bank_id="my-agent", hindsight_api_url="http://localhost:8888")
|
|
||||||
|
|
||||||
result = retain(
|
|
||||||
content="User mentioned they're working on a machine learning project",
|
|
||||||
context="Discussion about current projects",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Async APIs
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import arecall, areflect, aretain
|
|
||||||
|
|
||||||
# Async versions of all memory APIs
|
|
||||||
memories = await arecall("what do you know about me?")
|
|
||||||
context = await areflect("summarize user preferences")
|
|
||||||
result = await aretain(content="New information to remember")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Native Client Wrappers
|
|
||||||
|
|
||||||
Alternative to LiteLLM callbacks for direct SDK integration.
|
|
||||||
|
|
||||||
### OpenAI Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from openai import OpenAI
|
|
||||||
from hindsight_litellm import wrap_openai
|
|
||||||
|
|
||||||
client = OpenAI()
|
|
||||||
wrapped = wrap_openai(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.chat.completions.create(
|
|
||||||
model="gpt-4",
|
|
||||||
messages=[{"role": "user", "content": "What do you know about me?"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Anthropic Wrapper
|
|
||||||
|
|
||||||
```python
|
|
||||||
from anthropic import Anthropic
|
|
||||||
from hindsight_litellm import wrap_anthropic
|
|
||||||
|
|
||||||
client = Anthropic()
|
|
||||||
wrapped = wrap_anthropic(
|
|
||||||
client,
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
response = wrapped.messages.create(
|
|
||||||
model="claude-3-5-sonnet-20241022",
|
|
||||||
max_tokens=1024,
|
|
||||||
messages=[{"role": "user", "content": "Hello!"}]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debug Mode
|
|
||||||
|
|
||||||
When `verbose=True`, you can inspect exactly what memories are being injected:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import configure, enable, completion, get_last_injection_debug
|
|
||||||
|
|
||||||
configure(
|
|
||||||
bank_id="my-agent",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
verbose=True,
|
|
||||||
)
|
|
||||||
enable()
|
|
||||||
|
|
||||||
response = completion(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[{"role": "user", "content": "What's my favorite color?"}]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Inspect what was injected
|
|
||||||
debug = get_last_injection_debug()
|
|
||||||
if debug:
|
|
||||||
print(f"Mode: {debug.mode}") # "reflect" or "recall"
|
|
||||||
print(f"Injected: {debug.injected}") # True/False
|
|
||||||
print(f"Results: {debug.results_count}")
|
|
||||||
print(f"Memory context:\n{debug.memory_context}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context Manager
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import hindsight_memory
|
|
||||||
import litellm
|
|
||||||
|
|
||||||
with hindsight_memory(bank_id="user-123"):
|
|
||||||
response = litellm.completion(model="gpt-4", messages=[...])
|
|
||||||
# Memory integration automatically disabled after context
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disabling and Cleanup
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_litellm import disable, cleanup
|
|
||||||
|
|
||||||
# Temporarily disable memory integration
|
|
||||||
disable()
|
|
||||||
|
|
||||||
# Clean up all resources (call when shutting down)
|
|
||||||
cleanup()
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### Main Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `configure(...)` | Configure global Hindsight settings |
|
|
||||||
| `enable()` | Enable memory integration with LiteLLM |
|
|
||||||
| `disable()` | Disable memory integration |
|
|
||||||
| `is_enabled()` | Check if memory integration is enabled |
|
|
||||||
| `cleanup()` | Clean up all resources |
|
|
||||||
|
|
||||||
### Configuration Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_config()` | Get current configuration |
|
|
||||||
| `is_configured()` | Check if Hindsight is configured |
|
|
||||||
| `reset_config()` | Reset configuration to defaults |
|
|
||||||
|
|
||||||
### Memory Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `recall(query, ...)` | Synchronously query raw memories |
|
|
||||||
| `arecall(query, ...)` | Asynchronously query raw memories |
|
|
||||||
| `reflect(query, ...)` | Synchronously get synthesized memory context |
|
|
||||||
| `areflect(query, ...)` | Asynchronously get synthesized memory context |
|
|
||||||
| `retain(content, ...)` | Synchronously store a memory |
|
|
||||||
| `aretain(content, ...)` | Asynchronously store a memory |
|
|
||||||
|
|
||||||
### Debug Functions
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `get_last_injection_debug()` | Get debug info from last memory injection |
|
|
||||||
| `clear_injection_debug()` | Clear stored debug info |
|
|
||||||
|
|
||||||
### Client Wrappers
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
|----------|-------------|
|
|
||||||
| `wrap_openai(client, ...)` | Wrap OpenAI client with memory |
|
|
||||||
| `wrap_anthropic(client, ...)` | Wrap Anthropic client with memory |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- litellm >= 1.40.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
|
|
@ -1,251 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 8
|
|
||||||
title: "LlamaIndex Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to LlamaIndex agents with Hindsight. Supports agent-driven tools (HindsightToolSpec) and automatic memory via the BaseMemory interface."
|
|
||||||
---
|
|
||||||
|
|
||||||
# LlamaIndex
|
|
||||||
|
|
||||||
Persistent long-term memory for [LlamaIndex](https://docs.llamaindex.ai/) agents via Hindsight. The `hindsight-llamaindex` package provides two complementary patterns:
|
|
||||||
|
|
||||||
- **`HindsightToolSpec`** — Agent-driven memory tools (retain/recall/reflect)
|
|
||||||
- **`HindsightMemory`** — Automatic memory via LlamaIndex's `BaseMemory` interface
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-llamaindex
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Automatic Memory (BaseMemory)
|
|
||||||
|
|
||||||
The simplest way to add Hindsight memory to a LlamaIndex agent. Messages are automatically stored on each turn, and relevant memories are recalled and injected as context.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_llamaindex import HindsightMemory
|
|
||||||
from llama_index.core.agent import ReActAgent
|
|
||||||
from llama_index.llms.openai import OpenAI
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
memory = HindsightMemory.from_client(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences and project context",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=[], llm=OpenAI(model="gpt-4o"))
|
|
||||||
response = await agent.run("Remember that I prefer dark mode", memory=memory)
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
### How It Works
|
|
||||||
|
|
||||||
| Event | What Happens |
|
|
||||||
|-------|-------------|
|
|
||||||
| Agent receives input | `aget(input)` recalls relevant memories from Hindsight, prepends as system message |
|
|
||||||
| Agent produces output | `aput(message)` retains the message to Hindsight for future recall |
|
|
||||||
| New session starts | Previous memories are available via recall; local chat buffer starts empty |
|
|
||||||
|
|
||||||
### `HindsightMemory.from_client()`
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| `client` | `Hindsight` | *required* | Hindsight client instance |
|
|
||||||
| `bank_id` | `str` | *required* | Memory bank ID |
|
|
||||||
| `mission` | `str` | `None` | Bank mission — auto-creates bank on first use |
|
|
||||||
| `context` | `str` | `"llamaindex"` | Source label for retain operations |
|
|
||||||
| `budget` | `str` | `"mid"` | Recall budget level |
|
|
||||||
| `max_tokens` | `int` | `4096` | Max recall tokens |
|
|
||||||
| `tags` | `list[str]` | `None` | Tags for retain operations |
|
|
||||||
| `recall_tags` | `list[str]` | `None` | Tags to filter recall |
|
|
||||||
| `recall_tags_match` | `str` | `"any"` | Tag matching mode |
|
|
||||||
| `system_prompt` | `str` | *(built-in)* | Template for memory system message. Must contain `{memories}` |
|
|
||||||
| `chat_history_limit` | `int` | `100` | Max messages in local buffer |
|
|
||||||
|
|
||||||
Also available: `HindsightMemory.from_url(hindsight_api_url, bank_id, ...)` for creating without a pre-built client.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Agent-Driven Tools (BaseToolSpec)
|
|
||||||
|
|
||||||
For explicit control, expose retain/recall/reflect as tools the agent can choose to call.
|
|
||||||
|
|
||||||
### Quick Start: Tool Spec
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_llamaindex import HindsightToolSpec
|
|
||||||
from llama_index.llms.openai import OpenAI
|
|
||||||
from llama_index.core.agent import ReActAgent
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
tools = spec.to_tool_list()
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=tools, llm=OpenAI(model="gpt-4o"))
|
|
||||||
response = await agent.run("Remember that I prefer dark mode")
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
asyncio.run(main())
|
|
||||||
```
|
|
||||||
|
|
||||||
### Quick Start: Factory Function
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Selecting Tools
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Via to_tool_list()
|
|
||||||
tools = spec.to_tool_list(spec_functions=["recall_memory", "reflect_on_memory"])
|
|
||||||
|
|
||||||
# Via factory flags
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False,
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
Set defaults via `configure()`, override per-call:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import configure
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid",
|
|
||||||
tags=["source:llamaindex"],
|
|
||||||
context="my-app",
|
|
||||||
mission="Track user preferences",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client/url
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
### `HindsightToolSpec()`
|
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
|
||||||
|-----------|------|---------|-------------|
|
|
||||||
| `bank_id` | `str` | *required* | Hindsight memory bank to operate on |
|
|
||||||
| `client` | `Hindsight` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `str` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `str` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `str` | `None` → `"mid"` | Recall/reflect budget: `low`, `mid`, `high` |
|
|
||||||
| `max_tokens` | `int` | `None` → `4096` | Max tokens for recall results |
|
|
||||||
| `tags` | `list[str]` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `list[str]` | `None` | Tags to filter recall results |
|
|
||||||
| `recall_tags_match` | `str` | `None` → `"any"` | Tag matching: `any`, `all`, `any_strict`, `all_strict` |
|
|
||||||
| `retain_metadata` | `dict[str, str]` | `None` | Default metadata for retain operations |
|
|
||||||
| `retain_document_id` | `str` | `None` | Document ID for retain. Auto-generates `{session}-{timestamp}` if not set |
|
|
||||||
| `retain_context` | `str` | `"llamaindex"` | Source label for retain operations |
|
|
||||||
| `recall_types` | `list[str]` | `None` | Fact types: `world`, `experience`, `opinion`, `observation` |
|
|
||||||
| `recall_include_entities` | `bool` | `False` | Include entity info in recall results |
|
|
||||||
| `reflect_context` | `str` | `None` | Additional context for reflect |
|
|
||||||
| `reflect_max_tokens` | `int` | `None` | Max tokens for reflect (defaults to `max_tokens`) |
|
|
||||||
| `reflect_response_schema` | `dict` | `None` | JSON schema to constrain reflect output |
|
|
||||||
| `reflect_tags` | `list[str]` | `None` | Tags for reflect (defaults to `recall_tags`) |
|
|
||||||
| `reflect_tags_match` | `str` | `None` | Tag matching for reflect (defaults to `recall_tags_match`) |
|
|
||||||
| `mission` | `str` | `None` | Bank mission — auto-creates bank on first use |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Production Patterns
|
|
||||||
|
|
||||||
### Bank Mission
|
|
||||||
|
|
||||||
Set a mission to give the memory engine context for fact extraction:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Tools
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user coding preferences, project context, and technical decisions",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Memory
|
|
||||||
memory = HindsightMemory.from_client(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
mission="Track user coding preferences, project context, and technical decisions",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
The bank is created automatically on first use. If it already exists, creation is silently skipped.
|
|
||||||
|
|
||||||
### Memory Scoping with Tags
|
|
||||||
|
|
||||||
```python
|
|
||||||
spec = HindsightToolSpec(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
tags=["source:chat", "session:abc"], # applied to all retains
|
|
||||||
recall_tags=["source:chat"], # filter recalls to chat memories
|
|
||||||
recall_tags_match="any",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
Both patterns handle errors gracefully — operations are logged and return friendly messages instead of raising exceptions. Agents continue functioning even if memory is unavailable.
|
|
||||||
|
|
||||||
### Combining Tools + Memory
|
|
||||||
|
|
||||||
Use both patterns together for maximum flexibility:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_llamaindex import create_hindsight_tools, HindsightMemory
|
|
||||||
|
|
||||||
# Automatic memory for context enrichment
|
|
||||||
memory = HindsightMemory.from_client(client=client, bank_id="user-123")
|
|
||||||
|
|
||||||
# Explicit tools for agent-driven reflect
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=False, # memory handles retain automatically
|
|
||||||
include_recall=False, # memory handles recall automatically
|
|
||||||
include_reflect=True, # agent can still explicitly reflect
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = ReActAgent(tools=tools, llm=llm)
|
|
||||||
|
|
||||||
# Pass memory to run()
|
|
||||||
response = await agent.run("What should I prioritize?", memory=memory)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python 3.10+
|
|
||||||
- `llama-index-core >= 0.11.0`
|
|
||||||
- `hindsight-client >= 0.4.0`
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 2
|
|
||||||
title: "Hindsight Local MCP Server | Persistent Memory for Claude"
|
|
||||||
description: "Run Hindsight as a local MCP server with embedded PostgreSQL — no external setup required. Ideal for Claude Code and Claude Desktop for long-term memory across conversations."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Local MCP Server
|
|
||||||
|
|
||||||
Hindsight provides a local MCP server that runs entirely on your machine with an embedded PostgreSQL database. No external server or database setup required.
|
|
||||||
|
|
||||||
This is ideal for:
|
|
||||||
- **Personal use with Claude Code / Claude Desktop** — Give Claude long-term memory across conversations
|
|
||||||
- **Development and testing** — Quick setup without infrastructure
|
|
||||||
- **Privacy-focused setups** — All data stays on your machine
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
Running `hindsight-local-mcp` starts the full Hindsight API on `localhost:8888` with an embedded PostgreSQL database (pg0). You then connect your MCP client to it over HTTP.
|
|
||||||
|
|
||||||
- Starts an embedded PostgreSQL (pg0) automatically
|
|
||||||
- Runs database migrations on startup
|
|
||||||
- Exposes the full MCP endpoint at `http://localhost:8888/mcp/`
|
|
||||||
- Data persists in `~/.pg0/hindsight-mcp/` across restarts
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
### 1. Start the server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Or with Ollama (no API key needed):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_PROVIDER=ollama HINDSIGHT_API_LLM_MODEL=llama3.2 uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure your MCP client
|
|
||||||
|
|
||||||
**Claude Code:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/
|
|
||||||
```
|
|
||||||
|
|
||||||
**Other MCP clients** — add an HTTP transport entry pointing to `http://localhost:8888/mcp/`.
|
|
||||||
|
|
||||||
## Bank Modes
|
|
||||||
|
|
||||||
The local server supports the same two modes as the hosted API:
|
|
||||||
|
|
||||||
### Multi-bank mode (default)
|
|
||||||
|
|
||||||
Use `http://localhost:8888/mcp/` — exposes all tools including bank management. Bank is selected per-request via the `bank_id` tool parameter or the `X-Bank-Id` header.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Single-bank mode
|
|
||||||
|
|
||||||
Use `http://localhost:8888/mcp/<bank-id>/` — pins all tools to one bank, no `bank_id` parameter needed. This replaces the old `HINDSIGHT_API_MCP_LOCAL_BANK_ID` env var.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
claude mcp add --transport http hindsight http://localhost:8888/mcp/my-bank/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Available Tools
|
|
||||||
|
|
||||||
The local server exposes the full tool set (29 tools in multi-bank mode, 26 in single-bank mode):
|
|
||||||
|
|
||||||
**Core Memory**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `retain` | Store information to long-term memory with optional tags, metadata, and document association |
|
|
||||||
| `recall` | Search memories with natural language, configurable budget, type filters, and tag filters |
|
|
||||||
| `reflect` | Synthesize memories into a reasoned answer with optional structured output |
|
|
||||||
|
|
||||||
**Mental Models**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_mental_models` | List pinned reflections for a bank |
|
|
||||||
| `get_mental_model` | Get a specific mental model |
|
|
||||||
| `create_mental_model` | Create a new mental model with optional auto-refresh trigger |
|
|
||||||
| `update_mental_model` | Update a mental model's metadata |
|
|
||||||
| `delete_mental_model` | Delete a mental model |
|
|
||||||
| `refresh_mental_model` | Regenerate a mental model's content |
|
|
||||||
|
|
||||||
**Directives**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_directives` | List directives that guide memory processing |
|
|
||||||
| `create_directive` | Create a new directive |
|
|
||||||
| `delete_directive` | Delete a directive |
|
|
||||||
|
|
||||||
**Memory Browsing**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_memories` | Browse memories with filtering and pagination |
|
|
||||||
| `get_memory` | Get a specific memory by ID |
|
|
||||||
| `delete_memory` | Delete a specific memory |
|
|
||||||
|
|
||||||
**Documents**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_documents` | List ingested documents |
|
|
||||||
| `get_document` | Get a specific document |
|
|
||||||
| `delete_document` | Delete a document and its linked memories |
|
|
||||||
|
|
||||||
**Operations**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_operations` | List async operations with status filtering |
|
|
||||||
| `get_operation` | Check operation status and progress |
|
|
||||||
| `cancel_operation` | Cancel a pending or running operation |
|
|
||||||
|
|
||||||
**Tags & Bank Management**
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `list_tags` | List unique tags used in a bank |
|
|
||||||
| `get_bank` | Get bank profile (name, mission, disposition) |
|
|
||||||
| `get_bank_stats` | Get bank statistics (multi-bank only) |
|
|
||||||
| `update_bank` | Update bank name or mission |
|
|
||||||
| `delete_bank` | Delete an entire bank and all its data |
|
|
||||||
| `clear_memories` | Clear memories without deleting the bank |
|
|
||||||
| `list_banks` | List all memory banks (multi-bank only) |
|
|
||||||
| `create_bank` | Create or configure a memory bank (multi-bank only) |
|
|
||||||
|
|
||||||
For detailed parameter documentation, see the [MCP Server reference](../../developer/mcp-server.md#available-tools).
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
All standard [Hindsight configuration variables](../../developer/configuration.md) are supported. Key ones for local use:
|
|
||||||
|
|
||||||
| Variable | Required | Default | Description |
|
|
||||||
|----------|----------|---------|-------------|
|
|
||||||
| `HINDSIGHT_API_LLM_API_KEY` | Yes* | — | API key for your LLM provider |
|
|
||||||
| `HINDSIGHT_API_LLM_PROVIDER` | No | `openai` | LLM provider (`openai`, `anthropic`, `ollama`, etc.) |
|
|
||||||
| `HINDSIGHT_API_LLM_MODEL` | No | `gpt-4o-mini` | Model name |
|
|
||||||
| `HINDSIGHT_API_DATABASE_URL` | No | `pg0://hindsight-mcp` | Override the database URL |
|
|
||||||
| `HINDSIGHT_API_PORT` | No | `8888` | Port to listen on |
|
|
||||||
| `HINDSIGHT_API_LOG_LEVEL` | No | `info` | Log level |
|
|
||||||
|
|
||||||
*Not required when using a local provider like Ollama.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Slow first startup
|
|
||||||
|
|
||||||
The first startup downloads the local embedding model (~100MB) and initializes the database. Subsequent starts are faster.
|
|
||||||
|
|
||||||
### Port already in use
|
|
||||||
|
|
||||||
Set a different port:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... HINDSIGHT_API_PORT=9000 uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
||||||
Then update your MCP client URL to `http://localhost:9000/mcp/`.
|
|
||||||
|
|
||||||
### Checking logs
|
|
||||||
|
|
||||||
Set `HINDSIGHT_API_LOG_LEVEL=debug` for verbose output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HINDSIGHT_API_LLM_API_KEY=sk-... HINDSIGHT_API_LOG_LEVEL=debug uvx --from hindsight-api hindsight-local-mcp
|
|
||||||
```
|
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 5
|
|
||||||
title: "NemoClaw Persistent Memory with Hindsight | Integration Guide"
|
|
||||||
description: "Add persistent memory to NemoClaw sandboxed agents with Hindsight. One command adds automated memory extraction and auto-recall to any NemoClaw sandbox — no code changes required."
|
|
||||||
---
|
|
||||||
|
|
||||||
# NemoClaw
|
|
||||||
|
|
||||||
Persistent memory for [NemoClaw](https://nemoclaw.ai) sandboxed agents using [Hindsight](https://hindsight.vectorize.io).
|
|
||||||
|
|
||||||
NemoClaw runs [OpenClaw](https://openclaw.ai) inside an OpenShell sandbox with controlled filesystem, process, and network egress policies. The `hindsight-nemoclaw` package automates adding Hindsight memory to a sandbox in one command — no code changes required.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/nemoclaw.md)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx @vectorize-io/hindsight-nemoclaw setup \
|
|
||||||
--sandbox my-assistant \
|
|
||||||
--api-url https://api.hindsight.vectorize.io \
|
|
||||||
--api-token <your-api-key> \
|
|
||||||
--bank-prefix my-sandbox
|
|
||||||
```
|
|
||||||
|
|
||||||
Get an API key at [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup).
|
|
||||||
|
|
||||||
You'll see output like:
|
|
||||||
|
|
||||||
```
|
|
||||||
[0] Preflight checks...
|
|
||||||
✓ openshell found
|
|
||||||
✓ openclaw found
|
|
||||||
|
|
||||||
[1] Installing @vectorize-io/hindsight-openclaw plugin...
|
|
||||||
✓ Plugin installed
|
|
||||||
|
|
||||||
[2] Configuring plugin in ~/.openclaw/openclaw.json...
|
|
||||||
✓ Plugin config written (bank: my-sandbox-openclaw)
|
|
||||||
|
|
||||||
[3] Applying Hindsight network policy to sandbox "my-assistant"...
|
|
||||||
✓ Policy version 2 submitted
|
|
||||||
✓ Policy version 2 loaded (active version: 2)
|
|
||||||
|
|
||||||
[4] Restarting OpenClaw gateway...
|
|
||||||
✓ Gateway restarted
|
|
||||||
|
|
||||||
✓ Setup complete!
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### The sandbox problem
|
|
||||||
|
|
||||||
OpenShell enforces strict network egress — every outbound endpoint must be explicitly permitted in the sandbox policy. By default, the Hindsight API (`api.hindsight.vectorize.io`) is not in that list.
|
|
||||||
|
|
||||||
The `hindsight-openclaw` plugin supports **external API mode**, where it skips the local daemon entirely and makes direct HTTPS calls to Hindsight Cloud. This is the natural fit for sandboxed environments: the plugin becomes a thin HTTP client, and the only sandbox change needed is one egress rule.
|
|
||||||
|
|
||||||
### What the setup command does
|
|
||||||
|
|
||||||
1. **Preflight** — verifies `openshell` and `openclaw` are installed
|
|
||||||
2. **Install plugin** — runs `openclaw plugins install @vectorize-io/hindsight-openclaw`
|
|
||||||
3. **Configure plugin** — writes external API mode config to `~/.openclaw/openclaw.json`
|
|
||||||
4. **Apply policy** — reads the current sandbox policy, merges the Hindsight egress block, and re-applies via `openshell policy set`
|
|
||||||
5. **Restart gateway** — runs `openclaw gateway restart`
|
|
||||||
|
|
||||||
### Memory flow
|
|
||||||
|
|
||||||
Once set up, the `hindsight-openclaw` plugin hooks into the OpenClaw gateway lifecycle:
|
|
||||||
|
|
||||||
- **`before_agent_start`** — recalls relevant memories from past sessions and injects them into context
|
|
||||||
- **`agent_end`** — retains the conversation to the Hindsight memory bank
|
|
||||||
|
|
||||||
The sandbox doesn't interfere with either step — it sees the Hindsight calls as normal HTTPS egress to a permitted endpoint.
|
|
||||||
|
|
||||||
## CLI Reference
|
|
||||||
|
|
||||||
```
|
|
||||||
hindsight-nemoclaw setup [options]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--sandbox <name> NemoClaw sandbox name (required)
|
|
||||||
--api-url <url> Hindsight API URL (required)
|
|
||||||
--api-token <token> Hindsight API token (required)
|
|
||||||
--bank-prefix <prefix> Memory bank prefix (default: "nemoclaw")
|
|
||||||
--skip-policy Skip sandbox network policy update
|
|
||||||
--skip-plugin-install Skip openclaw plugin installation
|
|
||||||
--dry-run Preview changes without applying
|
|
||||||
--help Show help
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `--dry-run` to preview all changes before applying anything. Use `--skip-policy` if you manage sandbox policies manually.
|
|
||||||
|
|
||||||
## Manual Setup
|
|
||||||
|
|
||||||
If you prefer to apply the steps yourself instead of using the CLI:
|
|
||||||
|
|
||||||
### 1. Install the plugin
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure `~/.openclaw/openclaw.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"hindsightApiUrl": "https://api.hindsight.vectorize.io",
|
|
||||||
"hindsightApiToken": "<your-api-key>",
|
|
||||||
"llmProvider": "claude-code",
|
|
||||||
"dynamicBankId": false,
|
|
||||||
"bankIdPrefix": "my-sandbox"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`llmProvider: "claude-code"` uses the Claude Code process already present in the sandbox — no additional API key needed.
|
|
||||||
|
|
||||||
### 3. Add the Hindsight network policy
|
|
||||||
|
|
||||||
`openshell policy set` replaces the entire policy document. Export your current policy first, add the Hindsight block, then re-apply:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
network_policies:
|
|
||||||
hindsight:
|
|
||||||
name: hindsight
|
|
||||||
endpoints:
|
|
||||||
- host: api.hindsight.vectorize.io
|
|
||||||
port: 443
|
|
||||||
protocol: rest
|
|
||||||
tls: terminate
|
|
||||||
enforcement: enforce
|
|
||||||
rules:
|
|
||||||
- allow:
|
|
||||||
method: GET
|
|
||||||
path: /**
|
|
||||||
- allow:
|
|
||||||
method: POST
|
|
||||||
path: /**
|
|
||||||
- allow:
|
|
||||||
method: PUT
|
|
||||||
path: /**
|
|
||||||
binaries:
|
|
||||||
- path: /usr/local/bin/openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openshell policy set my-sandbox --policy /path/to/full-policy.yaml --wait
|
|
||||||
openclaw gateway restart
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `hindsightApiUrl` | string | — | Hindsight API base URL |
|
|
||||||
| `hindsightApiToken` | string | — | API token for authentication |
|
|
||||||
| `llmProvider` | string | auto-detect | LLM provider for memory extraction |
|
|
||||||
| `dynamicBankId` | boolean | `false` | Isolate memory per user (`true`) or share across sessions (`false`) |
|
|
||||||
| `bankIdPrefix` | string | `"nemoclaw"` | Prefix for the memory bank name |
|
|
||||||
|
|
||||||
### Bank naming
|
|
||||||
|
|
||||||
When `dynamicBankId: false`, all sessions write to a single bank named `{bankIdPrefix}-openclaw`. When `dynamicBankId: true`, each user gets an isolated bank — useful for multi-tenant deployments.
|
|
||||||
|
|
||||||
## Verifying It Works
|
|
||||||
|
|
||||||
After setup, check the gateway logs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
```
|
|
||||||
|
|
||||||
On startup you should see:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] Plugin loaded successfully
|
|
||||||
[Hindsight] ✓ Using external API: https://api.hindsight.vectorize.io
|
|
||||||
[Hindsight] External API health: {"status":"healthy","database":"connected"}
|
|
||||||
[Hindsight] Default bank: my-sandbox-openclaw
|
|
||||||
[Hindsight] ✓ Ready (external API mode)
|
|
||||||
```
|
|
||||||
|
|
||||||
After a conversation:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hindsight] before_agent_start - bank: my-sandbox-openclaw, channel: undefined/webchat
|
|
||||||
[Hindsight Hook] agent_end triggered - bank: my-sandbox-openclaw
|
|
||||||
[Hindsight] Retained 6 messages to bank my-sandbox-openclaw for session agent:main:...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Pitfalls
|
|
||||||
|
|
||||||
### Policy replacement is full-document
|
|
||||||
|
|
||||||
`openshell policy set` replaces the entire policy document. The `hindsight-nemoclaw setup` command handles this automatically. If you're applying manually, export the current policy first so existing rules aren't lost.
|
|
||||||
|
|
||||||
### LaunchAgent can't follow symlinks on macOS
|
|
||||||
|
|
||||||
On macOS, the OpenClaw gateway runs as a LaunchAgent under a restricted security context. `openclaw plugins install --link` creates a symlink the LaunchAgent can't follow — the setup command installs as a copy instead. If you see `EPERM: operation not permitted, scandir` in gateway logs, this is the cause.
|
|
||||||
|
|
||||||
### Memory retention is asynchronous
|
|
||||||
|
|
||||||
Fact extraction and entity resolution happen in the background after `retain`. If you open a new session immediately after closing one, the most recent memories may not be indexed yet — typically a few seconds.
|
|
||||||
|
|
||||||
### Binary-scoped egress
|
|
||||||
|
|
||||||
The `binaries` field in the network policy restricts the egress rule to a specific executable path. If OpenClaw updates and the binary path changes, the rule silently stops working. Check your binary path after upgrades.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin not loading
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins list | grep hindsight
|
|
||||||
# Should show: ✓ enabled │ Hindsight Memory │ ...
|
|
||||||
|
|
||||||
# Reinstall
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Egress blocked
|
|
||||||
|
|
||||||
If calls to `api.hindsight.vectorize.io` are being blocked, check the active sandbox policy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openshell sandbox get my-assistant
|
|
||||||
```
|
|
||||||
|
|
||||||
Verify the `hindsight` block is present and the `binaries` path matches your OpenClaw binary:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
which openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### External API not connecting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# If you see daemon startup messages instead of "Using external API",
|
|
||||||
# the plugin config isn't being read — check ~/.openclaw/openclaw.json
|
|
||||||
```
|
|
||||||
|
|
@ -1,374 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 4
|
|
||||||
title: "OpenClaw Persistent Memory with Hindsight | Plugin Integration"
|
|
||||||
description: "Add persistent, automated memory to your OpenClaw agent with Hindsight. Local-first, open source — one plugin install replaces built-in memory with structured knowledge extraction and auto-recall."
|
|
||||||
---
|
|
||||||
|
|
||||||
# OpenClaw
|
|
||||||
|
|
||||||
Local, long term memory for [OpenClaw](https://openclaw.ai) agents using [Hindsight](https://vectorize.io/hindsight).
|
|
||||||
|
|
||||||
This plugin integrates [hindsight-embed](https://vectorize.io/hindsight/cli), a standalone daemon that bundles Hindsight's memory engine (API + PostgreSQL) into a single command. Everything runs locally on your machine, reuses the LLM you're already paying for, and costs nothing extra.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/openclaw.md)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
**Step 1: Set up LLM for memory extraction**
|
|
||||||
|
|
||||||
Choose one provider and set its API key:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option A: OpenAI
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option B: Anthropic
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option C: Gemini
|
|
||||||
export GEMINI_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option D: Groq
|
|
||||||
export GROQ_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option E: Claude Code (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option F: OpenAI Codex (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 2: Install the plugin**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
**Step 3: Start OpenClaw**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw gateway
|
|
||||||
```
|
|
||||||
|
|
||||||
The plugin will automatically:
|
|
||||||
- Start a local Hindsight daemon (port 9077)
|
|
||||||
- Capture conversations after each turn
|
|
||||||
- Inject relevant memories before agent responses
|
|
||||||
|
|
||||||
**Important:** The LLM you configure above is **only for memory extraction** (background processing). Your main OpenClaw agent can use any model you configure separately.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
**Auto-Capture:** Every conversation is automatically stored after each turn. Facts, entities, and relationships are extracted in the background.
|
|
||||||
|
|
||||||
**Auto-Recall:** Before each agent response, relevant memories are automatically injected into the context (up to 1024 tokens). The agent uses past context without needing to call tools.
|
|
||||||
|
|
||||||
**Feedback Loop Prevention:** The plugin automatically strips injected memory tags (`<hindsight_memories>`) before storing conversations. This prevents recalled memories from being re-extracted as new facts, which would cause exponential memory growth and duplicate entries.
|
|
||||||
|
|
||||||
Traditional memory systems give agents a `search_memory` tool - but models don't use it consistently. Auto-recall solves this by injecting memories automatically before every turn.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Plugin Settings
|
|
||||||
|
|
||||||
Optional settings in `~/.openclaw/openclaw.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"apiPort": 9077,
|
|
||||||
"daemonIdleTimeout": 0,
|
|
||||||
"embedVersion": "latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Options:**
|
|
||||||
- `apiPort` - Port for the openclaw profile daemon (default: `9077`)
|
|
||||||
- `daemonIdleTimeout` - Seconds before daemon shuts down from inactivity (default: `0` = never)
|
|
||||||
- `embedVersion` - hindsight-embed version (default: `"latest"`)
|
|
||||||
- `bankMission` - Agent identity/purpose stored on the memory bank. Helps the memory engine understand context for better fact extraction during retain. Set once per bank on first use — not a recall prompt.
|
|
||||||
- `dynamicBankId` - Enable per-context memory banks (default: `true`)
|
|
||||||
- `bankIdPrefix` - Optional prefix for bank IDs (e.g. `"prod"` → `"prod-slack-C123"`)
|
|
||||||
- `dynamicBankGranularity` - Fields used to derive bank ID: `agent`, `channel`, `user`, `provider` (default: `["agent", "channel", "user"]`)
|
|
||||||
- `excludeProviders` - Message providers to skip for recall/retain (e.g. `["slack"]`, `["telegram"]`, `["discord"]`)
|
|
||||||
- `autoRecall` - Auto-inject memories before each turn (default: `true`). Set to `false` when the agent has its own recall tool.
|
|
||||||
- `autoRetain` - Auto-retain conversations after each turn (default: `true`)
|
|
||||||
- `retainRoles` - Which message roles to retain (default: `["user", "assistant"]`). Options: `user`, `assistant`, `system`, `tool`
|
|
||||||
- `recallBudget` - Recall effort: `"low"`, `"mid"`, or `"high"` (default: `"mid"`). Higher budgets use more retrieval strategies for better results.
|
|
||||||
- `recallMaxTokens` - Max tokens for recall response (default: `1024`). Controls how much memory context is injected per turn.
|
|
||||||
- `recallTopK` - Max number of memories to inject per turn (default: unlimited).
|
|
||||||
- `recallTypes` - Memory types to recall (default: `["world", "experience"]`). Options: `world`, `experience`, `observation`.
|
|
||||||
- `recallContextTurns` - Number of prior user turns to include in the recall query (default: `1`).
|
|
||||||
- `recallMaxQueryChars` - Max characters for the composed recall query (default: `800`).
|
|
||||||
- `recallPromptPreamble` - Custom preamble text placed above recalled memories. Overrides the built-in guidance text.
|
|
||||||
- `recallInjectionPosition` - Where to inject recalled memories: `"prepend"` (default), `"append"`, or `"user"`. Use `"append"` to preserve prompt caching with large static system prompts. Use `"user"` to inject before the user message instead of in the system prompt.
|
|
||||||
- `recallRoles` - Which message roles to include when composing the contextual recall query (default: `["user", "assistant"]`).
|
|
||||||
- `retainEveryNTurns` - Retain every Nth turn (default: `1` = every turn). Values > 1 enable chunked retention.
|
|
||||||
- `retainOverlapTurns` - Extra prior turns included when chunked retention fires (default: `0`).
|
|
||||||
- `debug` - Enable debug logging (default: `false`).
|
|
||||||
|
|
||||||
### Memory Isolation
|
|
||||||
|
|
||||||
The plugin creates separate memory banks based on conversation context. By default, banks are derived from the `agent`, `channel`, and `user` fields — so each unique combination gets its own isolated memory store.
|
|
||||||
|
|
||||||
You can customize which fields are used for bank segmentation with `dynamicBankGranularity`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"dynamicBankGranularity": ["provider", "user"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, memories are isolated per provider + user, meaning the same user shares memories across all channels within a provider.
|
|
||||||
|
|
||||||
Available isolation fields:
|
|
||||||
- `agent` - The agent/bot identity
|
|
||||||
- `channel` - The channel or conversation ID
|
|
||||||
- `user` - The user interacting with the agent
|
|
||||||
- `provider` - The message provider (e.g. Slack, Discord)
|
|
||||||
|
|
||||||
Use `bankIdPrefix` to namespace bank IDs across environments (e.g. `"prod"`, `"staging"`). Set `dynamicBankId` to `false` to use a single shared bank for all conversations.
|
|
||||||
|
|
||||||
### Retention Controls
|
|
||||||
|
|
||||||
By default, the plugin retains `user` and `assistant` messages after each turn. You can customize this behavior:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"autoRetain": true,
|
|
||||||
"retainRoles": ["user", "assistant", "system"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `autoRetain` - Set to `false` to disable automatic retention entirely (useful if you handle retention yourself)
|
|
||||||
- `retainRoles` - Controls which message roles are included in the retained transcript. Only messages from the last user message onward are retained each turn, preventing duplicate storage.
|
|
||||||
|
|
||||||
### LLM Configuration
|
|
||||||
|
|
||||||
The plugin auto-detects your LLM provider from these environment variables:
|
|
||||||
|
|
||||||
| Provider | Env Var | Notes |
|
|
||||||
|----------|---------|-------|
|
|
||||||
| OpenAI | `OPENAI_API_KEY` | |
|
|
||||||
| Anthropic | `ANTHROPIC_API_KEY` | |
|
|
||||||
| Gemini | `GEMINI_API_KEY` | |
|
|
||||||
| Groq | `GROQ_API_KEY` | |
|
|
||||||
| Claude Code | `HINDSIGHT_API_LLM_PROVIDER=claude-code` | No API key needed |
|
|
||||||
| OpenAI Codex | `HINDSIGHT_API_LLM_PROVIDER=openai-codex` | No API key needed |
|
|
||||||
|
|
||||||
The model is selected automatically by the Hindsight API. To override, set `HINDSIGHT_API_LLM_MODEL`.
|
|
||||||
|
|
||||||
**Override with explicit config:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai
|
|
||||||
export HINDSIGHT_API_LLM_API_KEY=sk-your-key
|
|
||||||
|
|
||||||
# Optional: custom base URL (OpenRouter, Azure, vLLM, etc.)
|
|
||||||
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example: Free OpenRouter model**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai
|
|
||||||
export HINDSIGHT_API_LLM_MODEL=xiaomi/mimo-v2-flash # FREE!
|
|
||||||
export HINDSIGHT_API_LLM_API_KEY=sk-or-v1-your-openrouter-key
|
|
||||||
export HINDSIGHT_API_LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
||||||
```
|
|
||||||
|
|
||||||
### External API (Advanced)
|
|
||||||
|
|
||||||
Connect to a remote Hindsight API server instead of running a local daemon. This is useful for:
|
|
||||||
|
|
||||||
- **Shared memory** across multiple OpenClaw instances
|
|
||||||
- **Production deployments** with centralized memory storage
|
|
||||||
- **Team environments** where agents share knowledge
|
|
||||||
|
|
||||||
#### Plugin Configuration
|
|
||||||
|
|
||||||
Configure in `~/.openclaw/openclaw.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"plugins": {
|
|
||||||
"entries": {
|
|
||||||
"hindsight-openclaw": {
|
|
||||||
"enabled": true,
|
|
||||||
"config": {
|
|
||||||
"hindsightApiUrl": "https://your-hindsight-server.com",
|
|
||||||
"hindsightApiToken": "your-api-token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Options:**
|
|
||||||
- `hindsightApiUrl` - Full URL to external Hindsight API (e.g., `https://mcp.hindsight.example.com`)
|
|
||||||
- `hindsightApiToken` - API token for authentication (optional, only if API requires auth)
|
|
||||||
|
|
||||||
#### Environment Variables (Alternative)
|
|
||||||
|
|
||||||
You can also configure via environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export HINDSIGHT_EMBED_API_URL=https://your-hindsight-server.com
|
|
||||||
export HINDSIGHT_EMBED_API_TOKEN=your-api-token # Optional
|
|
||||||
|
|
||||||
openclaw gateway
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** Plugin config takes precedence over environment variables.
|
|
||||||
|
|
||||||
#### Behavior
|
|
||||||
|
|
||||||
When external API mode is enabled:
|
|
||||||
- **No local daemon** is started (no hindsight-embed process)
|
|
||||||
- **Health check** runs on startup to verify API connectivity
|
|
||||||
- **All memory operations** (retain, recall, reflect) go to the external API
|
|
||||||
- **Faster startup** since no local PostgreSQL or embedding models are needed
|
|
||||||
|
|
||||||
#### Verification
|
|
||||||
|
|
||||||
Check OpenClaw logs for external API mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# Should see on startup:
|
|
||||||
# [Hindsight] External API mode enabled: https://your-hindsight-server.com
|
|
||||||
# [Hindsight] External API health check passed
|
|
||||||
```
|
|
||||||
|
|
||||||
If you see daemon startup messages instead, verify your configuration is correct.
|
|
||||||
|
|
||||||
## Inspecting Memories
|
|
||||||
|
|
||||||
### Check Configuration
|
|
||||||
|
|
||||||
View the daemon config that was written by the plugin:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat ~/.hindsight/profiles/openclaw.env
|
|
||||||
```
|
|
||||||
|
|
||||||
This shows the LLM provider, model, port, and other settings the daemon is using.
|
|
||||||
|
|
||||||
### Check Daemon Status
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if daemon is running
|
|
||||||
uvx hindsight-embed@latest -p openclaw daemon status
|
|
||||||
|
|
||||||
# View daemon logs
|
|
||||||
tail -f ~/.hindsight/profiles/openclaw.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Query Memories
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Search memories
|
|
||||||
uvx hindsight-embed@latest -p openclaw memory recall openclaw "user preferences"
|
|
||||||
|
|
||||||
# View recent memories
|
|
||||||
uvx hindsight-embed@latest -p openclaw memory list openclaw --limit 10
|
|
||||||
|
|
||||||
# Open web UI (uses openclaw profile's daemon)
|
|
||||||
uvx hindsight-embed@latest -p openclaw ui
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Plugin not loading
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins list | grep hindsight
|
|
||||||
# Should show: ✓ enabled │ Hindsight Memory │ ...
|
|
||||||
|
|
||||||
# Reinstall if needed
|
|
||||||
openclaw plugins install @vectorize-io/hindsight-openclaw
|
|
||||||
```
|
|
||||||
|
|
||||||
### Daemon not starting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check daemon status (note: -p openclaw uses the openclaw profile)
|
|
||||||
uvx hindsight-embed@latest -p openclaw daemon status
|
|
||||||
|
|
||||||
# View logs for errors
|
|
||||||
tail -f ~/.hindsight/profiles/openclaw.log
|
|
||||||
|
|
||||||
# Check configuration
|
|
||||||
cat ~/.hindsight/profiles/openclaw.env
|
|
||||||
|
|
||||||
# List all profiles
|
|
||||||
uvx hindsight-embed@latest profile list
|
|
||||||
```
|
|
||||||
|
|
||||||
### No API key error
|
|
||||||
|
|
||||||
Make sure you've set one of the provider API keys (or use a provider that doesn't require one):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Option 1: OpenAI
|
|
||||||
export OPENAI_API_KEY="sk-your-key"
|
|
||||||
|
|
||||||
# Option 2: Anthropic
|
|
||||||
export ANTHROPIC_API_KEY="your-key"
|
|
||||||
|
|
||||||
# Option 3: Claude Code (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=claude-code
|
|
||||||
|
|
||||||
# Option 4: OpenAI Codex (no API key needed)
|
|
||||||
export HINDSIGHT_API_LLM_PROVIDER=openai-codex
|
|
||||||
|
|
||||||
# Verify it's set
|
|
||||||
echo $OPENAI_API_KEY
|
|
||||||
# or
|
|
||||||
echo $HINDSIGHT_API_LLM_PROVIDER
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify it's working
|
|
||||||
|
|
||||||
Check gateway logs for memory operations:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tail -f /tmp/openclaw/openclaw-*.log | grep Hindsight
|
|
||||||
|
|
||||||
# Should see on startup:
|
|
||||||
# [Hindsight] ✓ Using provider: openai, model: gpt-4o-mini
|
|
||||||
# or
|
|
||||||
# [Hindsight] ✓ Using provider: claude-code, model: claude-sonnet-4-20250514
|
|
||||||
|
|
||||||
# Should see after conversations:
|
|
||||||
# [Hindsight] Retained X messages for session ...
|
|
||||||
# [Hindsight] Auto-recall: Injecting X memories
|
|
||||||
```
|
|
||||||
|
|
@ -1,188 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 6
|
|
||||||
title: "Pydantic AI Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Pydantic AI agents with Hindsight. Async-native retain, recall, and reflect tools — persistent memory across all agent runs with no thread-pool hacks."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Pydantic AI
|
|
||||||
|
|
||||||
Persistent memory tools for [Pydantic AI](https://ai.pydantic.dev/) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — all async-native with no thread-pool hacks.
|
|
||||||
|
|
||||||
[View Changelog →](../../changelog/integrations/pydantic-ai.md)
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Async-Native Tools** — Uses Pydantic AI's async tool interface directly (`aretain`, `arecall`, `areflect`)
|
|
||||||
- **Memory Instructions** — Auto-inject relevant memories into every agent run via `instructions=[...]`
|
|
||||||
- **Three Memory Tools** — Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Simple Configuration** — Configure once globally, or pass a client directly
|
|
||||||
- **Lightweight** — Depends on `pydantic-ai-slim` to avoid pulling in all model providers
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-pydantic-ai
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_client import Hindsight
|
|
||||||
from hindsight_pydantic_ai import create_hindsight_tools, memory_instructions
|
|
||||||
from pydantic_ai import Agent
|
|
||||||
|
|
||||||
client = Hindsight(base_url="http://localhost:8888")
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
tools=create_hindsight_tools(client=client, bank_id="user-123"),
|
|
||||||
instructions=[memory_instructions(client=client, bank_id="user-123")],
|
|
||||||
)
|
|
||||||
|
|
||||||
result = await agent.run("What do you remember about my preferences?")
|
|
||||||
print(result.output)
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
The `memory_instructions` callable automatically recalls relevant memories and injects them into the system prompt on every run.
|
|
||||||
|
|
||||||
## Tools Only (No Auto-Injection)
|
|
||||||
|
|
||||||
If you want the agent to decide when to use memory rather than always injecting context:
|
|
||||||
|
|
||||||
```python
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
tools=create_hindsight_tools(client=client, bank_id="user-123"),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Instructions Only (No Tools)
|
|
||||||
|
|
||||||
If you just want memories auto-injected without giving the agent explicit memory tools:
|
|
||||||
|
|
||||||
```python
|
|
||||||
agent = Agent(
|
|
||||||
"openai:gpt-4o",
|
|
||||||
instructions=[memory_instructions(client=client, bank_id="user-123")],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
include_retain=True,
|
|
||||||
include_recall=True,
|
|
||||||
include_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing a client to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_pydantic_ai import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing client — uses global config
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Per-Tool Overrides
|
|
||||||
|
|
||||||
Constructor arguments override global configuration:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
budget="high", # Override global budget
|
|
||||||
max_tokens=8192, # Override global max_tokens
|
|
||||||
tags=["session:abc"], # Override global tags
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Memory Instructions Options
|
|
||||||
|
|
||||||
Customize what memories get injected and how:
|
|
||||||
|
|
||||||
```python
|
|
||||||
instructions_fn = memory_instructions(
|
|
||||||
client=client,
|
|
||||||
bank_id="user-123",
|
|
||||||
query="relevant context about the user", # What to search for
|
|
||||||
budget="low", # Keep it fast
|
|
||||||
max_results=5, # Limit injected memories
|
|
||||||
max_tokens=4096, # Max recall tokens
|
|
||||||
prefix="Relevant memories:\n", # Text before the memory list
|
|
||||||
tags=["scope:global"], # Filter by tags
|
|
||||||
tags_match="any", # Tag match mode
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `include_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `include_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `include_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
@ -1,325 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 3
|
|
||||||
title: "Hindsight Agent Memory Skill | AI Coding Assistant Integration"
|
|
||||||
description: "Give AI coding assistants like Claude Code and Codex persistent memory across sessions with Hindsight's Agent Skill — a reusable prompt template for long-term context retention."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Skills
|
|
||||||
|
|
||||||
Hindsight provides an Agent Skill that gives AI coding assistants persistent memory across sessions. Skills are reusable prompt templates that agents can load when needed to gain specialized capabilities.
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
| Platform | Skills Directory |
|
|
||||||
|----------|-----------------|
|
|
||||||
| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `~/.claude/skills/` |
|
|
||||||
| [OpenCode](https://github.com/opencode-ai/opencode) | `~/.opencode/skills/` |
|
|
||||||
| [Codex CLI](https://github.com/openai/codex) | `~/.codex/skills/` |
|
|
||||||
|
|
||||||
## Deployment Modes
|
|
||||||
|
|
||||||
The skill supports two deployment modes:
|
|
||||||
|
|
||||||
| Mode | Best For | Data Location |
|
|
||||||
|------|----------|---------------|
|
|
||||||
| **Local** | Individual developers | Your machine (`~/.pg0/`) |
|
|
||||||
| **Cloud** | Teams sharing knowledge | Hindsight Cloud |
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
|
|
||||||
### Option 1: Interactive Installer (Recommended)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer will:
|
|
||||||
1. Prompt you to select your AI coding assistant
|
|
||||||
2. Select deployment mode (local or cloud)
|
|
||||||
3. Configure the appropriate settings
|
|
||||||
4. Install the skill to the appropriate directory
|
|
||||||
|
|
||||||
### Install for a Specific Platform
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Claude Code (interactive mode selection)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude
|
|
||||||
|
|
||||||
# OpenCode
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app opencode
|
|
||||||
|
|
||||||
# Codex CLI
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app codex
|
|
||||||
```
|
|
||||||
|
|
||||||
### Install with Cloud Mode
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Direct cloud setup (skips interactive prompts for mode)
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --app claude --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Using add-skill
|
|
||||||
|
|
||||||
If you use [add-skill](https://add-skill.org/) to manage your agent skills:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# For local mode (individual developers)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-local
|
|
||||||
|
|
||||||
# For Hindsight Cloud (teams)
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-cloud
|
|
||||||
|
|
||||||
# For self-hosted Hindsight servers
|
|
||||||
npx add-skill vectorize-io/hindsight --skill hindsight-self-hosted
|
|
||||||
```
|
|
||||||
|
|
||||||
On first use, the AI will guide you through the remaining setup:
|
|
||||||
- **Local**: Run `uvx hindsight-embed configure` to set up your LLM provider
|
|
||||||
- **Cloud**: Provide your API key and bank ID
|
|
||||||
- **Self-hosted**: Provide your server URL, API key, and bank ID
|
|
||||||
|
|
||||||
## What the Skill Provides
|
|
||||||
|
|
||||||
Once installed, your AI assistant gains the ability to:
|
|
||||||
|
|
||||||
- **Retain** - Store user preferences, learnings, and procedure outcomes
|
|
||||||
- **Recall** - Search for relevant context before starting tasks
|
|
||||||
- **Reflect** - Synthesize memories into contextual answers
|
|
||||||
|
|
||||||
The skill uses the `hindsight-embed` CLI which runs a lightweight local daemon with an embedded database.
|
|
||||||
|
|
||||||
## How Skills Work
|
|
||||||
|
|
||||||
Skills are **model-invoked**, meaning the AI assistant automatically decides when to use them based on the context of your conversation. You don't need to explicitly trigger the skill.
|
|
||||||
|
|
||||||
The assistant will:
|
|
||||||
- **Store** when you share preferences, when tasks succeed/fail, or when learnings emerge
|
|
||||||
- **Recall** before starting non-trivial tasks to get relevant context
|
|
||||||
|
|
||||||
### What Gets Stored
|
|
||||||
|
|
||||||
The skill is optimized to store:
|
|
||||||
|
|
||||||
| Category | Examples |
|
|
||||||
|----------|----------|
|
|
||||||
| **User Preferences** | Coding style, tool preferences, language choices |
|
|
||||||
| **Procedure Outcomes** | Commands that worked, configurations that resolved issues |
|
|
||||||
| **Learnings** | Bug solutions, workarounds, architecture decisions |
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-embed CLI
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Local Daemon (auto-started)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Embedded PostgreSQL (~/.pg0/hindsight-embed/)
|
|
||||||
```
|
|
||||||
|
|
||||||
All data stays on your machine. The daemon auto-starts when needed and shuts down after inactivity.
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
|
|
||||||
```
|
|
||||||
AI Coding Assistant
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Skill (SKILL.md)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
hindsight-cli
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Hindsight Cloud API (https://api.hindsight.vectorize.io)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Shared Memory Bank (team-accessible)
|
|
||||||
```
|
|
||||||
|
|
||||||
Data is stored in Hindsight Cloud and shared across your team. All team members with the same bank ID can access shared memories.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Local Mode Setup
|
|
||||||
|
|
||||||
The skill uses configuration stored in `~/.hindsight/config.env`. Reconfigure anytime:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Cloud Mode Setup
|
|
||||||
|
|
||||||
Cloud mode connects to [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup), allowing teams to share memories about a codebase. When one team member learns something, everyone benefits.
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
1. A Hindsight Cloud account ([sign up](https://ui.hindsight.vectorize.io/signup))
|
|
||||||
2. An API key from your team admin
|
|
||||||
3. A bank ID for your project (e.g., `team-acme-frontend`)
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
Run the installer with cloud mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll be prompted for:
|
|
||||||
|
|
||||||
| Setting | Description | Example |
|
|
||||||
|---------|-------------|---------|
|
|
||||||
| **Cloud API URL** | Hindsight Cloud endpoint | `https://api.hindsight.vectorize.io` |
|
|
||||||
| **API Key** | Your authentication key | `hs_xxx...` |
|
|
||||||
| **Bank ID** | Shared memory bank for your team | `team-acme-frontend` |
|
|
||||||
|
|
||||||
### Configuration Files
|
|
||||||
|
|
||||||
Cloud mode creates two files:
|
|
||||||
|
|
||||||
**`~/.hindsight/config`** — API connection settings (TOML format):
|
|
||||||
```toml
|
|
||||||
api_url = "https://api.hindsight.vectorize.io"
|
|
||||||
api_key = "hs_xxx..."
|
|
||||||
```
|
|
||||||
|
|
||||||
**`~/.claude/skills/hindsight/SKILL.md`** — Skill definition with your bank ID baked in.
|
|
||||||
|
|
||||||
### Team Setup
|
|
||||||
|
|
||||||
To set up cloud mode for your team:
|
|
||||||
|
|
||||||
1. **Team admin** creates a bank in Hindsight Cloud (e.g., `team-acme-frontend`)
|
|
||||||
2. **Team admin** generates API keys for each team member
|
|
||||||
3. **Each developer** runs the installer with their API key and the shared bank ID
|
|
||||||
4. All team members now share the same memory bank
|
|
||||||
|
|
||||||
### What to Store in Team Banks
|
|
||||||
|
|
||||||
Cloud mode uses a **shared team bank**. Be thoughtful about what goes in:
|
|
||||||
|
|
||||||
| Type | Examples | How to Store |
|
|
||||||
|------|----------|--------------|
|
|
||||||
| **Project conventions** | Linting rules, testing requirements, Node version | `"Project uses ESLint with Airbnb config"` |
|
|
||||||
| **Team knowledge** | Architecture decisions, common pitfalls, domain logic | `"Auth module requires Redis 7+"` |
|
|
||||||
| **Individual preferences** | Personal coding style, communication preferences | `"Alice prefers verbose commit messages"` |
|
|
||||||
|
|
||||||
**Key distinction**: Project conventions apply to everyone. Individual preferences should include the person's name so the AI knows when to apply them.
|
|
||||||
|
|
||||||
### Example Workflow
|
|
||||||
|
|
||||||
```
|
|
||||||
Day 1: Alice discovers a requirement
|
|
||||||
─────────────────────────────────────
|
|
||||||
Alice's AI assistant stores:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
"Alice prefers explicit error handling over silent failures"
|
|
||||||
|
|
||||||
Day 2: Bob starts working on auth
|
|
||||||
─────────────────────────────────
|
|
||||||
Bob's AI assistant recalls:
|
|
||||||
"The auth module requires Redis 7+ due to HEXPIRE command usage"
|
|
||||||
|
|
||||||
Bob avoids the same issue Alice hit!
|
|
||||||
(Alice's personal preference is stored but won't be applied to Bob)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Cloud Connection
|
|
||||||
|
|
||||||
After installation, verify the connection:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Store a test memory
|
|
||||||
hindsight memory retain team-acme-frontend "Alice works at Google as a software engineer"
|
|
||||||
|
|
||||||
# Recall it
|
|
||||||
hindsight memory recall team-acme-frontend "Alice"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Switching Between Banks
|
|
||||||
|
|
||||||
If you work on multiple projects, you can have different skills installed for each AI assistant, or manually switch banks:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Environment variable override (temporary)
|
|
||||||
HINDSIGHT_API_URL=https://api.hindsight.vectorize.io \
|
|
||||||
HINDSIGHT_API_KEY=hs_xxx \
|
|
||||||
hindsight memory recall different-bank "query"
|
|
||||||
```
|
|
||||||
|
|
||||||
For permanent multi-bank setups, reinstall the skill with a different bank ID.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Skill not activating
|
|
||||||
|
|
||||||
The skill activates based on its description matching your request. Try being explicit:
|
|
||||||
- "Remember that..." triggers storage
|
|
||||||
- "What do you know about..." triggers recall
|
|
||||||
|
|
||||||
### Local Mode Issues
|
|
||||||
|
|
||||||
**Daemon not starting:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed daemon status
|
|
||||||
uvx hindsight-embed daemon logs
|
|
||||||
```
|
|
||||||
|
|
||||||
**Reconfigure LLM provider:**
|
|
||||||
```bash
|
|
||||||
uvx hindsight-embed configure
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cloud Mode Issues
|
|
||||||
|
|
||||||
**Authentication errors:**
|
|
||||||
```bash
|
|
||||||
# Verify your config
|
|
||||||
cat ~/.hindsight/config
|
|
||||||
|
|
||||||
# Test connection manually
|
|
||||||
hindsight bank list
|
|
||||||
```
|
|
||||||
|
|
||||||
**Wrong bank ID:**
|
|
||||||
|
|
||||||
Check your SKILL.md file to see which bank ID is configured:
|
|
||||||
```bash
|
|
||||||
cat ~/.claude/skills/hindsight/SKILL.md | grep "memory retain"
|
|
||||||
```
|
|
||||||
|
|
||||||
To change the bank ID, reinstall the skill:
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://hindsight.vectorize.io/get-skill | bash -s -- --mode cloud
|
|
||||||
```
|
|
||||||
|
|
||||||
**Network/firewall issues:**
|
|
||||||
```bash
|
|
||||||
# Test connectivity to cloud API
|
|
||||||
curl -I https://api.hindsight.vectorize.io/health
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
### Local Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- An LLM API key (OpenAI, Anthropic, Groq, etc.)
|
|
||||||
|
|
||||||
### Cloud Mode
|
|
||||||
- Python 3.10+ (for `uvx`)
|
|
||||||
- Hindsight Cloud API key
|
|
||||||
- Network access to `https://api.hindsight.vectorize.io`
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
---
|
|
||||||
sidebar_position: 13
|
|
||||||
title: "Strands Agents Persistent Memory with Hindsight | Integration"
|
|
||||||
description: "Add long-term memory to Strands Agents SDK agents with Hindsight. Retain, recall, and reflect tools using Strands' native @tool pattern for persistent memory across sessions."
|
|
||||||
---
|
|
||||||
|
|
||||||
# Strands Agents
|
|
||||||
|
|
||||||
Persistent memory tools for [Strands Agents SDK](https://github.com/strands-agents/sdk-python) agents via Hindsight. Give your agents long-term memory with retain, recall, and reflect — using Strands' native `@tool` pattern.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Native `@tool` Functions** - Tools are plain Python functions, compatible with `Agent(tools=[...])`
|
|
||||||
- **Memory Instructions** - Pre-recall memories for injection into agent system prompt
|
|
||||||
- **Three Memory Tools** - Retain (store), Recall (search), Reflect (synthesize) — include any combination
|
|
||||||
- **Simple Configuration** - Configure once globally, or pass a client directly
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install hindsight-strands
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```python
|
|
||||||
from strands import Agent
|
|
||||||
from hindsight_strands import create_hindsight_tools
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(tools=tools)
|
|
||||||
agent("Remember that I prefer dark mode")
|
|
||||||
agent("What are my preferences?")
|
|
||||||
```
|
|
||||||
|
|
||||||
The agent now has three tools it can call:
|
|
||||||
|
|
||||||
- **`hindsight_retain`** — Store information to long-term memory
|
|
||||||
- **`hindsight_recall`** — Search long-term memory for relevant facts
|
|
||||||
- **`hindsight_reflect`** — Synthesize a reasoned answer from memories
|
|
||||||
|
|
||||||
## With Memory Instructions
|
|
||||||
|
|
||||||
Pre-recall relevant memories and inject them into the system prompt:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_strands import create_hindsight_tools, memory_instructions
|
|
||||||
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
memories = memory_instructions(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
)
|
|
||||||
|
|
||||||
agent = Agent(
|
|
||||||
tools=tools,
|
|
||||||
system_prompt=f"You are a helpful assistant.\n\n{memories}",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Selecting Tools
|
|
||||||
|
|
||||||
Include only the tools you need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
tools = create_hindsight_tools(
|
|
||||||
bank_id="user-123",
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
enable_retain=True,
|
|
||||||
enable_recall=True,
|
|
||||||
enable_reflect=False, # Omit reflect
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Global Configuration
|
|
||||||
|
|
||||||
Instead of passing connection details to every call, configure once:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from hindsight_strands import configure, create_hindsight_tools
|
|
||||||
|
|
||||||
configure(
|
|
||||||
hindsight_api_url="http://localhost:8888",
|
|
||||||
api_key="your-api-key", # Or set HINDSIGHT_API_KEY env var
|
|
||||||
budget="mid", # Recall budget: low/mid/high
|
|
||||||
max_tokens=4096, # Max tokens for recall results
|
|
||||||
tags=["env:prod"], # Tags for stored memories
|
|
||||||
recall_tags=["scope:global"], # Tags to filter recall
|
|
||||||
recall_tags_match="any", # Tag match mode: any/all/any_strict/all_strict
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now create tools without passing connection details
|
|
||||||
tools = create_hindsight_tools(bank_id="user-123")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### `create_hindsight_tools()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `budget` | `"mid"` | Recall/reflect budget level (low/mid/high) |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `tags` | `None` | Tags applied when storing memories |
|
|
||||||
| `recall_tags` | `None` | Tags to filter when searching |
|
|
||||||
| `recall_tags_match` | `"any"` | Tag matching mode |
|
|
||||||
| `enable_retain` | `True` | Include the retain (store) tool |
|
|
||||||
| `enable_recall` | `True` | Include the recall (search) tool |
|
|
||||||
| `enable_reflect` | `True` | Include the reflect (synthesize) tool |
|
|
||||||
|
|
||||||
### `memory_instructions()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `bank_id` | *required* | Hindsight memory bank ID |
|
|
||||||
| `client` | `None` | Pre-configured Hindsight client |
|
|
||||||
| `hindsight_api_url` | `None` | API URL (used if no client provided) |
|
|
||||||
| `api_key` | `None` | API key (used if no client provided) |
|
|
||||||
| `query` | `"relevant context about the user"` | Recall query for memory injection |
|
|
||||||
| `budget` | `"low"` | Recall budget level |
|
|
||||||
| `max_results` | `5` | Maximum memories to inject |
|
|
||||||
| `max_tokens` | `4096` | Maximum tokens for recall results |
|
|
||||||
| `prefix` | `"Relevant memories:\n"` | Text prepended before memory list |
|
|
||||||
| `tags` | `None` | Tags to filter recall results |
|
|
||||||
| `tags_match` | `"any"` | Tag matching mode |
|
|
||||||
|
|
||||||
### `configure()`
|
|
||||||
|
|
||||||
| Parameter | Default | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `hindsight_api_url` | Production API | Hindsight API URL |
|
|
||||||
| `api_key` | `HINDSIGHT_API_KEY` env | API key for authentication |
|
|
||||||
| `budget` | `"mid"` | Default recall budget level |
|
|
||||||
| `max_tokens` | `4096` | Default max tokens for recall |
|
|
||||||
| `tags` | `None` | Default tags for retain operations |
|
|
||||||
| `recall_tags` | `None` | Default tags to filter recall |
|
|
||||||
| `recall_tags_match` | `"any"` | Default tag matching mode |
|
|
||||||
| `verbose` | `False` | Enable verbose logging |
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
- Python >= 3.10
|
|
||||||
- strands-agents
|
|
||||||
- hindsight-client >= 0.4.0
|
|
||||||
- A running Hindsight API server
|
|
||||||
Loading…
Reference in a new issue