* feat: add @vectorize-io/hindsight-embed daemon lifecycle package
Create a new top-level `hindsight-embed-npm/` package that owns the daemon
lifecycle for the Python `hindsight-embed` CLI: spawning via `uvx`, writing
the profile, waiting for `/health`, and shutting down. Nothing more.
Deliberately does not ship an HTTP client — `@vectorize-io/hindsight-client`
already covers retain / recall / reflect / createBank against the Hindsight
API, and the two packages compose: once `manager.start()` returns, consumers
talk to the daemon via `new HindsightClient({ baseUrl: manager.getBaseUrl() })`.
`HindsightEmbedManagerOptions.env` forwards an arbitrary `Record<string,
string>` to both the daemon process and the profile config via `--env K=V`,
and `extraProfileCreateArgs` / `extraDaemonStartArgs` escape hatches cover
any new CLI flag without waiting for a wrapper release.
Refactor `hindsight-integrations/openclaw` to consume both packages:
`HindsightEmbedManager` for daemon lifecycle in local mode, `HindsightClient`
for all HTTP memory operations. Drop the bespoke subprocess/HTTP client that
used to live in openclaw. The retain queue stays local to openclaw (it's a
client-side reliability workaround with a single consumer today — will move
to the client package or server-side when a second consumer needs it).
Wire the new package into the main release pipeline (versioned alongside
the other core packages, published from `v*` tags) and add a CI build job.
* docs: add Embedded Node.js SDK page for @vectorize-io/hindsight-embed
* refactor: rename hindsight-embed-npm to hindsight-all, restructure docs sidebar
The Node package previously named @vectorize-io/hindsight-embed was
semantically misnamed: hindsight-embed (Python) is a CLI tool, while what
this Node package actually provides is the Node equivalent of hindsight-all
— a programmatic lifecycle manager for a local Hindsight daemon. Rename to
match.
Package rename
- hindsight-embed-npm/ → hindsight-all-npm/ (git mv, history preserved)
- @vectorize-io/hindsight-embed → @vectorize-io/hindsight-all
- class HindsightEmbedManager → HindsightServer (matches Python hindsight-all)
- HindsightEmbedManagerOptions → HindsightServerOptions
- src/manager.ts → src/server.ts, src/manager.test.ts → src/server.test.ts
- openclaw (index.ts, backfill.ts, tests) and the claude-code Python port
updated to reference the new names
Docs restructure
- Split sdks/python.md: now client-only content. New sdks/hindsight-all.md
covers the programmatic hindsight-all Python package (HindsightServer and
HindsightEmbedded).
- Rename sdks/embed-npm.md → sdks/hindsight-all-npm.md with HindsightServer
examples.
- New "Installation" sidebar section, placed after Hosting, containing
Docker / Kubernetes / Bare Metal (anchor links into developer/installation)
plus Programmatic API (Python), Programmatic API (Node.js), and Daemon CLI.
- Add si-docker, si-kubernetes, si-nodedotjs, lu-hard-drive to the sidebar
ICON_MAP.
Docs dev-server fix
- docusaurus.config.ts: drop the flaky NODE_ENV sniff for including the
"Next" version. Use INCLUDE_CURRENT_VERSION exclusively. NODE_ENV was
unreliable across hot-reload paths and caused the Next version to
disappear intermittently when editing files.
- scripts/dev/start-docs.sh: export INCLUDE_CURRENT_VERSION=true so local
dev always shows Next; production builds leave it unset.
Lockfile cleanup
- package-lock.json and hindsight-integrations/openclaw/package-lock.json
had extraneous hindsight-embed-npm blocks left over from the rename.
Removed manually and verified with npm install.
* ci: fix openclaw jobs by pre-building workspace deps; regenerate docs-skill
The build-openclaw-integration and test-openclaw-integration jobs failed
with "Failed to resolve entry for package @vectorize-io/hindsight-all"
because openclaw depends on two monorepo workspaces via `file:` deps
(@vectorize-io/hindsight-client and @vectorize-io/hindsight-all) whose
`dist/` directories are gitignored and never built before openclaw's npm ci.
Both jobs now install the root workspace and build the two deps first,
mirroring the release-control-plane pattern.
Also regenerate skills/hindsight-docs/references/* via
./scripts/generate-docs-skill.sh:
- new skill pages for sdks/hindsight-all{.md,-npm.md}
- updated skill pages for sdks/embed.md and sdks/python.md to match
the new H1s and split content
- incidental refreshes to changelog/index.md, developer/models.md,
openapi.json, and uv.lock that verify-generated-files picked up
* ci: build openclaw before running tests so symlink test can realpath dist
219 lines
4.9 KiB
Markdown
219 lines
4.9 KiB
Markdown
---
|
|
sidebar_position: 1
|
|
---
|
|
|
|
# Python Client
|
|
|
|
Official HTTP client for the Hindsight API. Use this when you have a Hindsight server already running — locally, in Docker, or as a managed service — and you want a typed Python client to talk to it.
|
|
|
|
If you want to **embed and run a Hindsight server in your Python process** (no external server required), see [Embedded Python (hindsight-all)](./hindsight-all.md) instead.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
pip install hindsight-client
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```python
|
|
from hindsight_client import Hindsight
|
|
|
|
client = Hindsight(base_url="http://localhost:8888")
|
|
|
|
# Retain a memory
|
|
client.retain(bank_id="my-bank", content="Alice works at Google")
|
|
|
|
# Recall memories
|
|
results = client.recall(bank_id="my-bank", query="What does Alice do?")
|
|
for r in results.results:
|
|
print(r.text)
|
|
|
|
# Reflect - generate a contextual answer
|
|
answer = client.reflect(bank_id="my-bank", query="Tell me about Alice")
|
|
print(answer.text)
|
|
```
|
|
|
|
## Client Initialization
|
|
|
|
```python
|
|
from hindsight_client import Hindsight
|
|
|
|
client = Hindsight(
|
|
base_url="http://localhost:8888", # Hindsight API URL
|
|
timeout=30.0, # Request timeout in seconds
|
|
# api_key="your-api-key", # Optional bearer token
|
|
)
|
|
|
|
# Core operations
|
|
client.retain(bank_id="test", content="Hello world")
|
|
results = client.recall(bank_id="test", query="Hello")
|
|
|
|
# Organized API namespaces
|
|
client.banks.create(bank_id="test", name="Test Bank")
|
|
models = client.mental_models.list(bank_id="test")
|
|
directives = client.directives.list(bank_id="test")
|
|
memories = client.memories.list(bank_id="test")
|
|
```
|
|
|
|
## Core Operations
|
|
|
|
### Retain (Store Memory)
|
|
|
|
```python
|
|
# Simple
|
|
client.retain(
|
|
bank_id="my-bank",
|
|
content="Alice works at Google as a software engineer",
|
|
)
|
|
|
|
# With options
|
|
from datetime import datetime
|
|
|
|
client.retain(
|
|
bank_id="my-bank",
|
|
content="Alice got promoted",
|
|
context="career update",
|
|
timestamp=datetime(2024, 1, 15),
|
|
document_id="conversation_001",
|
|
metadata={"source": "slack"},
|
|
)
|
|
```
|
|
|
|
### Retain Batch
|
|
|
|
```python
|
|
client.retain_batch(
|
|
bank_id="my-bank",
|
|
items=[
|
|
{"content": "Alice works at Google", "context": "career"},
|
|
{"content": "Bob is a data scientist", "context": "career"},
|
|
],
|
|
document_id="conversation_001",
|
|
retain_async=False, # Set True for background processing
|
|
)
|
|
```
|
|
|
|
### Recall (Search)
|
|
|
|
```python
|
|
# Simple - returns list of RecallResult
|
|
results = client.recall(
|
|
bank_id="my-bank",
|
|
query="What does Alice do?",
|
|
)
|
|
|
|
for r in results.results:
|
|
print(f"{r.text} (type: {r.type})")
|
|
|
|
# With options
|
|
results = client.recall(
|
|
bank_id="my-bank",
|
|
query="What does Alice do?",
|
|
types=["world", "observation"], # Filter by fact type
|
|
max_tokens=4096,
|
|
budget="high", # low, mid, or high
|
|
)
|
|
```
|
|
|
|
### Recall with Chunks
|
|
|
|
```python
|
|
# Returns RecallResponse with source chunks
|
|
response = client.recall(
|
|
bank_id="my-bank",
|
|
query="What does Alice do?",
|
|
types=["world", "experience"],
|
|
budget="mid",
|
|
max_tokens=4096,
|
|
include_chunks=True,
|
|
max_chunk_tokens=500
|
|
)
|
|
|
|
print(f"Found {len(response.results)} memories")
|
|
for r in response.results:
|
|
print(f" - {r.text}")
|
|
if r.chunks:
|
|
print(f" Source: {r.chunks[0].text[:100]}...")
|
|
```
|
|
|
|
### Reflect (Generate Response)
|
|
|
|
```python
|
|
answer = client.reflect(
|
|
bank_id="my-bank",
|
|
query="What should I know about Alice?",
|
|
budget="low", # low, mid, or high
|
|
context="preparing for a meeting",
|
|
)
|
|
|
|
print(answer.text) # Generated response
|
|
```
|
|
|
|
## Bank Management
|
|
|
|
### Create Bank
|
|
|
|
```python
|
|
client.create_bank(
|
|
bank_id="my-bank",
|
|
name="Assistant",
|
|
mission="You're a helpful AI assistant - keep track of user preferences and conversation history.",
|
|
disposition={
|
|
"skepticism": 3, # 1-5: trusting to skeptical
|
|
"literalism": 3, # 1-5: flexible to literal
|
|
"empathy": 3, # 1-5: detached to empathetic
|
|
},
|
|
)
|
|
```
|
|
|
|
### List Memories
|
|
|
|
```python
|
|
client.list_memories(
|
|
bank_id="my-bank",
|
|
type="world", # Optional: filter by type
|
|
search_query="Alice", # Optional: text search
|
|
limit=100,
|
|
offset=0,
|
|
)
|
|
```
|
|
|
|
## Async Support
|
|
|
|
All methods have async versions prefixed with `a`:
|
|
|
|
```python
|
|
import asyncio
|
|
from hindsight_client import Hindsight
|
|
|
|
async def main():
|
|
client = Hindsight(base_url="http://localhost:8888")
|
|
|
|
# Async retain
|
|
await client.aretain(bank_id="my-bank", content="Hello world")
|
|
|
|
# Async recall
|
|
results = await client.arecall(bank_id="my-bank", query="Hello")
|
|
for r in results:
|
|
print(r.text)
|
|
|
|
# Async reflect
|
|
answer = await client.areflect(bank_id="my-bank", query="What did I say?")
|
|
print(answer.text)
|
|
|
|
client.close()
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
## Context Manager
|
|
|
|
```python
|
|
from hindsight_client import Hindsight
|
|
|
|
with Hindsight(base_url="http://localhost:8888") as client:
|
|
client.retain(bank_id="my-bank", content="Hello")
|
|
results = client.recall(bank_id="my-bank", query="Hello")
|
|
# Client automatically closed
|
|
```
|