feat: 4-tab code parity across all documentation examples (#613)

* feat: independent versioning for integrations

- Add per-integration changelog pages at /changelog/integrations/<name>
- Move main changelog to changelog/index.md (URL unchanged)
- Add --integration flag to generate-changelog for LLM-based per-integration changelog generation
- Add scripts/release-integration.sh <name> <version> for cutting integration releases
- Add .github/workflows/release-integration.yml to publish on integrations/** tags
- Remove integrations from main release.sh and release.yml cycle

* fix: add agno and hermes integration docs to version-0.4 for production build

* chore: apply ruff formatting to generate_changelog.py

* feat: add 4-tab code parity across all documentation examples

Every code snippet Tabs block now has Python, Node.js, CLI, and Go variants.
Raw HTTP/curl tabs replaced with proper SDK calls.

New example files:
- Go: retain.go, recall.go, reflect.go, memory-banks.go, directives.go,
  mental-models.go, documents.go, main-methods.go
- Shell: memory-banks.sh, directives.sh, mental-models.sh
- Node.js: mental-models.mjs

Extended example files with missing sections:
- recall.mjs/sh: world/experience/observation types, token-budget, all tag modes
- reflect.sh: reflect-with-params, reflect-disposition, reflect-sources, reflect-with-tags
- reflect.mjs: reflect-with-tags, fixed reflect-sources API usage
- retain.mjs/sh: retain-conversation, retain-batch, retain-files-batch

SDK/CLI additions:
- TypeScript: getMentalModelHistory method
- CLI recall: --tags, --tags-match flags
- CLI reflect: --tags, --tags-match, --include-facts flags
- CLI directive update: --is-active flag
- CLI bank set-config: --retain-mission, --retain-extraction-mode,
  --observations-mission, --reflect-mission, --disposition-* flags

Build validation:
- scripts/check-code-parity.mjs validates 4-tab parity across all MDX files
- Integrated into npm run build — fails if any Tabs block is missing a variant

* fix: fix doc examples for Go, Node.js, CLI + add mental model with-id examples

- Fix Go Budget constants: BUDGET_HIGH/LOW/MID → HIGH/LOW/MID
- Fix Go documents.go: ListDocuments returns []map[string]interface{}, use map access
- Fix Go retain.go: use correct relative path for sample.pdf
- Fix Node.js createMentalModel: use positional args (name, sourceQuery) not object
- Add CLI 'history' subcommand for mental models (api.rs, main.rs, mental_model.rs)
- Rebuild TypeScript/Python clients to support id param in createMentalModel
- Add create-mental-model-with-id examples across all 4 languages and docs

* fix: move id param to end of create_mental_model signature for backwards compat
This commit is contained in:
Nicolò Boschi 2026-03-19 11:31:51 +01:00 committed by GitHub
parent 438ce98b40
commit a56cd044e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 2306 additions and 104 deletions

View file

@ -601,6 +601,13 @@ impl ApiClient {
})
}
pub fn get_mental_model_history(&self, bank_id: &str, mental_model_id: &str, _verbose: bool) -> Result<serde_json::Value> {
self.runtime.block_on(async {
let response = self.client.get_mental_model_history(bank_id, mental_model_id, None).await?;
Ok(response.into_inner())
})
}
// --- Directive Methods ---
pub fn list_directives(&self, bank_id: &str, _verbose: bool) -> Result<types::DirectiveListResponse> {

View file

@ -717,6 +717,13 @@ pub fn set_config(
llm_model: Option<String>,
llm_api_key: Option<String>,
llm_base_url: Option<String>,
retain_mission: Option<String>,
retain_extraction_mode: Option<String>,
observations_mission: Option<String>,
reflect_mission: Option<String>,
disposition_skepticism: Option<i64>,
disposition_literalism: Option<i64>,
disposition_empathy: Option<i64>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
@ -736,9 +743,30 @@ pub fn set_config(
if let Some(base_url) = llm_base_url {
updates.insert("llm_base_url".to_string(), serde_json::Value::String(base_url));
}
if let Some(mission) = retain_mission {
updates.insert("retain_mission".to_string(), serde_json::Value::String(mission));
}
if let Some(mode) = retain_extraction_mode {
updates.insert("retain_extraction_mode".to_string(), serde_json::Value::String(mode));
}
if let Some(mission) = observations_mission {
updates.insert("observations_mission".to_string(), serde_json::Value::String(mission));
}
if let Some(mission) = reflect_mission {
updates.insert("reflect_mission".to_string(), serde_json::Value::String(mission));
}
if let Some(skepticism) = disposition_skepticism {
updates.insert("disposition_skepticism".to_string(), serde_json::Value::Number(skepticism.into()));
}
if let Some(literalism) = disposition_literalism {
updates.insert("disposition_literalism".to_string(), serde_json::Value::Number(literalism.into()));
}
if let Some(empathy) = disposition_empathy {
updates.insert("disposition_empathy".to_string(), serde_json::Value::Number(empathy.into()));
}
if updates.is_empty() {
return Err(anyhow!("No config updates provided. Use --llm-provider, --llm-model, --llm-api-key, or --llm-base-url".to_string()));
return Err(anyhow!("No config updates provided. Use --llm-provider, --llm-model, --retain-mission, --observations-mission, or other flags".to_string()));
}
let spinner = if output_format == OutputFormat::Pretty {

View file

@ -149,11 +149,12 @@ pub fn update(
directive_id: &str,
name: Option<String>,
content: Option<String>,
is_active: Option<bool>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
if name.is_none() && content.is_none() {
anyhow::bail!("At least one of --name or --content must be provided");
if name.is_none() && content.is_none() && is_active.is_none() {
anyhow::bail!("At least one of --name, --content, or --is-active must be provided");
}
let spinner = if output_format == OutputFormat::Pretty {
@ -165,7 +166,7 @@ pub fn update(
let request = types::UpdateDirectiveRequest {
name,
content,
is_active: None,
is_active,
priority: None,
tags: None,
};

View file

@ -9,7 +9,7 @@ use crate::output::{self, OutputFormat};
use crate::ui;
// Import types from generated client
use hindsight_client::types::{Budget, ChunkIncludeOptions, IncludeOptions, TagsMatch};
use hindsight_client::types::{Budget, ChunkIncludeOptions, FactsIncludeOptions, IncludeOptions, ReflectIncludeOptions, TagsMatch};
use serde::Deserialize;
use serde_json;
@ -43,6 +43,16 @@ fn parse_budget(budget: &str) -> Budget {
}
}
// Helper function to parse tags_match string to TagsMatch enum
fn parse_tags_match(tags_match: &Option<String>) -> TagsMatch {
match tags_match.as_deref().unwrap_or("any").to_lowercase().as_str() {
"all" => TagsMatch::All,
"any_strict" => TagsMatch::AnyStrict,
"all_strict" => TagsMatch::AllStrict,
_ => TagsMatch::Any,
}
}
/// List memory units with pagination and optional filters
pub fn list(
client: &ApiClient,
@ -250,6 +260,8 @@ pub fn recall(
trace: bool,
include_chunks: bool,
chunk_max_tokens: i64,
tags: Vec<String>,
tags_match: Option<String>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
@ -280,8 +292,8 @@ pub fn recall(
trace,
query_timestamp: None,
include,
tags: None,
tags_match: TagsMatch::Any,
tags: if tags.is_empty() { None } else { Some(tags) },
tags_match: parse_tags_match(&tags_match),
tag_groups: None,
};
@ -312,6 +324,9 @@ pub fn reflect(
context: Option<String>,
max_tokens: Option<i64>,
schema_path: Option<PathBuf>,
tags: Vec<String>,
tags_match: Option<String>,
include_facts: bool,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
@ -332,15 +347,24 @@ pub fn reflect(
None
};
let include = if include_facts {
Some(ReflectIncludeOptions {
facts: Some(FactsIncludeOptions(serde_json::Map::new())),
tool_calls: None,
})
} else {
None
};
let request = ReflectRequest {
query,
budget: Some(parse_budget(&budget)),
context,
max_tokens: max_tokens.unwrap_or(4096),
include: None,
include,
response_schema,
tags: None,
tags_match: TagsMatch::Any,
tags: if tags.is_empty() { None } else { Some(tags) },
tags_match: parse_tags_match(&tags_match),
tag_groups: None,
};

View file

@ -272,6 +272,55 @@ pub fn refresh(
}
}
/// Get the change history of a mental model
pub fn history(
client: &ApiClient,
bank_id: &str,
mental_model_id: &str,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
let spinner = if output_format == OutputFormat::Pretty {
Some(ui::create_spinner("Fetching mental model history..."))
} else {
None
};
let response = client.get_mental_model_history(bank_id, mental_model_id, verbose);
if let Some(mut sp) = spinner {
sp.finish();
}
match response {
Ok(history) => {
if output_format == OutputFormat::Pretty {
ui::print_section_header(&format!("History: {}", mental_model_id));
if let Some(entries) = history.as_array() {
if entries.is_empty() {
println!(" {}", ui::dim("No history entries found."));
} else {
for entry in entries {
let changed_at = entry.get("changed_at").and_then(|v| v.as_str()).unwrap_or("unknown");
let previous = entry.get("previous_content").and_then(|v| v.as_str()).unwrap_or("(none)");
println!(" {} {}", ui::dim("Changed at:"), changed_at);
let preview: String = previous.chars().take(80).collect();
let ellipsis = if previous.len() > 80 { "..." } else { "" };
println!(" {} {}{}", ui::dim("Previous:"), ui::dim(&preview), ellipsis);
println!();
}
}
}
} else {
output::print_output(&history, output_format)?;
}
Ok(())
}
Err(e) => Err(e),
}
}
// Helper function to print mental model details
fn print_mental_model_detail(mental_model: &types::MentalModelResponse) {
ui::print_section_header(&mental_model.name);

View file

@ -310,6 +310,34 @@ enum BankCommands {
/// LLM base URL override
#[arg(long)]
llm_base_url: Option<String>,
/// Retain mission: what to focus on during fact extraction
#[arg(long)]
retain_mission: Option<String>,
/// Retain extraction mode (concise, verbose, custom)
#[arg(long)]
retain_extraction_mode: Option<String>,
/// Observations mission: what to synthesize into durable observations
#[arg(long)]
observations_mission: Option<String>,
/// Reflect mission: first-person identity for reflect operations
#[arg(long)]
reflect_mission: Option<String>,
/// Disposition skepticism trait (1-5)
#[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
disposition_skepticism: Option<i64>,
/// Disposition literalism trait (1-5)
#[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
disposition_literalism: Option<i64>,
/// Disposition empathy trait (1-5)
#[arg(long, value_parser = clap::value_parser!(i64).range(1..=5))]
disposition_empathy: Option<i64>,
},
/// Reset bank configuration to defaults (remove all overrides)
@ -387,6 +415,14 @@ enum MemoryCommands {
/// Maximum tokens for chunks (only used with --include-chunks)
#[arg(long, default_value = "8192")]
chunk_max_tokens: i64,
/// Filter by tags (comma-separated, e.g. user:alice,team)
#[arg(long, value_delimiter = ',')]
tags: Vec<String>,
/// Tag matching mode: any, all, any_strict, all_strict (default: any)
#[arg(long)]
tags_match: Option<String>,
},
/// Generate answers using bank identity (reflect/reasoning)
@ -412,6 +448,18 @@ enum MemoryCommands {
/// Path to JSON schema file for structured output
#[arg(short = 's', long)]
schema: Option<PathBuf>,
/// Filter by tags (comma-separated, e.g. user:alice,team)
#[arg(long, value_delimiter = ',')]
tags: Vec<String>,
/// Tag matching mode: any, all, any_strict, all_strict (default: any)
#[arg(long)]
tags_match: Option<String>,
/// Include source facts (based_on) in the response
#[arg(long)]
include_facts: bool,
},
/// Store (retain) a single memory
@ -678,6 +726,15 @@ enum MentalModelCommands {
/// Mental model ID
mental_model_id: String,
},
/// Get the change history of a mental model
History {
/// Bank ID
bank_id: String,
/// Mental model ID
mental_model_id: String,
},
}
#[derive(Subcommand)]
@ -724,6 +781,10 @@ enum DirectiveCommands {
/// New content
#[arg(long)]
content: Option<String>,
/// Enable or disable the directive
#[arg(long)]
is_active: Option<bool>,
},
/// Delete a directive
@ -821,8 +882,8 @@ fn run() -> Result<()> {
BankCommands::Config { bank_id, overrides_only } => {
commands::bank::config(&client, &bank_id, overrides_only, verbose, output_format)
}
BankCommands::SetConfig { bank_id, llm_provider, llm_model, llm_api_key, llm_base_url } => {
commands::bank::set_config(&client, &bank_id, llm_provider, llm_model, llm_api_key, llm_base_url, verbose, output_format)
BankCommands::SetConfig { bank_id, llm_provider, llm_model, llm_api_key, llm_base_url, retain_mission, retain_extraction_mode, observations_mission, reflect_mission, disposition_skepticism, disposition_literalism, disposition_empathy } => {
commands::bank::set_config(&client, &bank_id, llm_provider, llm_model, llm_api_key, llm_base_url, retain_mission, retain_extraction_mode, observations_mission, reflect_mission, disposition_skepticism, disposition_literalism, disposition_empathy, verbose, output_format)
}
BankCommands::ResetConfig { bank_id, yes } => {
commands::bank::reset_config(&client, &bank_id, yes, verbose, output_format)
@ -837,11 +898,11 @@ fn run() -> Result<()> {
MemoryCommands::Get { bank_id, memory_id } => {
commands::memory::get(&client, &bank_id, &memory_id, verbose, output_format)
}
MemoryCommands::Recall { bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens } => {
commands::memory::recall(&client, &bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens, verbose, output_format)
MemoryCommands::Recall { bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens, tags, tags_match } => {
commands::memory::recall(&client, &bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens, tags, tags_match, verbose, output_format)
}
MemoryCommands::Reflect { bank_id, query, budget, context, max_tokens, schema } => {
commands::memory::reflect(&client, &bank_id, query, budget, context, max_tokens, schema, verbose, output_format)
MemoryCommands::Reflect { bank_id, query, budget, context, max_tokens, schema, tags, tags_match, include_facts } => {
commands::memory::reflect(&client, &bank_id, query, budget, context, max_tokens, schema, tags, tags_match, include_facts, verbose, output_format)
}
MemoryCommands::Retain { bank_id, content, doc_id, context, r#async } => {
commands::memory::retain(&client, &bank_id, content, doc_id, context, r#async, verbose, output_format)
@ -930,6 +991,9 @@ fn run() -> Result<()> {
MentalModelCommands::Refresh { bank_id, mental_model_id } => {
commands::mental_model::refresh(&client, &bank_id, &mental_model_id, verbose, output_format)
}
MentalModelCommands::History { bank_id, mental_model_id } => {
commands::mental_model::history(&client, &bank_id, &mental_model_id, verbose, output_format)
}
},
// Directive commands
@ -943,8 +1007,8 @@ fn run() -> Result<()> {
DirectiveCommands::Create { bank_id, name, content } => {
commands::directive::create(&client, &bank_id, &name, &content, verbose, output_format)
}
DirectiveCommands::Update { bank_id, directive_id, name, content } => {
commands::directive::update(&client, &bank_id, &directive_id, name, content, verbose, output_format)
DirectiveCommands::Update { bank_id, directive_id, name, content, is_active } => {
commands::directive::update(&client, &bank_id, &directive_id, name, content, is_active, verbose, output_format)
}
DirectiveCommands::Delete { bank_id, directive_id, yes } => {
commands::directive::delete(&client, &bank_id, &directive_id, yes, verbose, output_format)

View file

@ -792,6 +792,7 @@ class Hindsight:
tags: list[str] | None = None,
max_tokens: int | None = None,
trigger: dict[str, Any] | None = None,
id: str | None = None,
):
"""
Create a mental model (runs reflect in background).
@ -803,6 +804,7 @@ class Hindsight:
tags: Optional tags for filtering during retrieval
max_tokens: Optional maximum tokens for the mental model content
trigger: Optional trigger settings (e.g., {"refresh_after_consolidation": True})
id: Optional custom ID for the mental model (alphanumeric lowercase with hyphens)
Returns:
CreateMentalModelResponse with operation_id
@ -814,6 +816,7 @@ class Hindsight:
trigger_obj = mental_model_trigger.MentalModelTrigger(**trigger)
request_obj = create_mental_model_request.CreateMentalModelRequest(
id=id,
name=name,
source_query=source_query,
tags=tags,

View file

@ -628,6 +628,7 @@ export class HindsightClient {
name: string,
sourceQuery: string,
options?: {
id?: string;
tags?: string[];
maxTokens?: number;
trigger?: { refreshAfterConsolidation?: boolean };
@ -637,6 +638,7 @@ export class HindsightClient {
client: this.client,
path: { bank_id: bankId },
body: {
id: options?.id,
name,
source_query: sourceQuery,
tags: options?.tags,
@ -726,6 +728,18 @@ export class HindsightClient {
throw new Error(`deleteMentalModel failed: ${JSON.stringify(response.error)}`);
}
}
/**
* Get the change history of a mental model.
*/
async getMentalModelHistory(bankId: string, mentalModelId: string): Promise<any> {
const response = await sdk.getMentalModelHistory({
client: this.client,
path: { bank_id: bankId, mental_model_id: mentalModelId },
});
return this.validateResponse(response, 'getMentalModelHistory');
}
}
// Re-export types for convenience

View file

@ -13,6 +13,7 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import documentsPy from '!!raw-loader!@site/examples/api/documents.py';
import documentsMjs from '!!raw-loader!@site/examples/api/documents.mjs';
import documentsGo from '!!raw-loader!@site/examples/api/documents.go';
:::tip Prerequisites
Make sure you've completed the [Quick Start](./quickstart) and understand [how retain works](./retain).
@ -60,6 +61,9 @@ hindsight memory retain my-bank "Meeting notes content..." --doc-id notes-2024-0
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-retain" language="go" />
</TabItem>
</Tabs>
@ -84,6 +88,9 @@ hindsight memory retain my-bank "Project deadline: March 31" --doc-id project-pl
hindsight memory retain my-bank "Project deadline: April 15 (extended)" --doc-id project-plan
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
@ -104,6 +111,9 @@ Retrieve a document's original text and metadata. This is useful for expanding d
hindsight document get my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-get" language="go" />
</TabItem>
</Tabs>
@ -128,6 +138,9 @@ hindsight document update-tags my-bank meeting-2024-03-15 --tags team-a --tags t
hindsight document update-tags my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-update" language="go" />
</TabItem>
</Tabs>
@ -152,6 +165,9 @@ Remove a document and all its associated memories:
hindsight document delete my-bank meeting-2024-03-15
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-delete" language="go" />
</TabItem>
</Tabs>
@ -183,6 +199,9 @@ hindsight document list my-bank --q report
hindsight document list my-bank --tags team-a --tags team-b
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={documentsGo} section="document-list" language="go" />
</TabItem>
</Tabs>

View file

@ -13,6 +13,7 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mainMethodsPy from '!!raw-loader!@site/examples/api/main-methods.py';
import mainMethodsMjs from '!!raw-loader!@site/examples/api/main-methods.mjs';
import mainMethodsGo from '!!raw-loader!@site/examples/api/main-methods.go';
:::tip Prerequisites
Make sure you've [installed Hindsight](../installation) and completed the [Quick Start](./quickstart).
@ -33,15 +34,18 @@ Store conversations, documents, and facts into a memory bank.
```bash
# Store a single fact
hindsight retain my-bank "Alice joined Google in March 2024 as a Senior ML Engineer"
hindsight memory retain my-bank "Alice joined Google in March 2024 as a Senior ML Engineer"
# Store from a file
hindsight retain my-bank --file conversation.txt --context "Daily standup"
hindsight memory retain-files my-bank conversation.txt --context "Daily standup"
# Store multiple files
hindsight retain my-bank --files docs/*.md
hindsight memory retain-files my-bank docs/
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-retain" language="go" />
</TabItem>
</Tabs>
@ -66,18 +70,21 @@ Search for relevant memories using multi-strategy retrieval.
```bash
# Basic search
hindsight recall my-bank "What does Alice do at Google?"
hindsight memory recall my-bank "What does Alice do at Google?"
# Search with options
hindsight recall my-bank "What happened last spring?" \
hindsight memory recall my-bank "What happened last spring?" \
--budget high \
--max-tokens 8192 \
--fact-type world
--fact-type world,experience
# Verbose output (shows weights and sources)
hindsight recall my-bank "Tell me about Alice" -v
# Verbose output
hindsight memory recall my-bank "Tell me about Alice" -v
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-recall" language="go" />
</TabItem>
</Tabs>
@ -102,15 +109,15 @@ Generate disposition-aware responses using memories and observations.
```bash
# Basic reflect
hindsight reflect my-bank "Should we adopt TypeScript for our backend?"
# Verbose output (shows sources and observations)
hindsight reflect my-bank "What are Alice's strengths for the team lead role?" -v
hindsight memory reflect my-bank "Should we adopt TypeScript for our backend?"
# With higher reasoning budget
hindsight reflect my-bank "Analyze our tech stack" --budget high
hindsight memory reflect my-bank "Analyze our tech stack" --budget high
```
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mainMethodsGo} section="main-reflect" language="go" />
</TabItem>
</Tabs>

View file

@ -13,8 +13,12 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import memoryBanksPy from '!!raw-loader!@site/examples/api/memory-banks.py';
import memoryBanksMjs from '!!raw-loader!@site/examples/api/memory-banks.mjs';
import memoryBanksSh from '!!raw-loader!@site/examples/api/memory-banks.sh';
import memoryBanksGo from '!!raw-loader!@site/examples/api/memory-banks.go';
import directivesPy from '!!raw-loader!@site/examples/api/directives.py';
import directivesMjs from '!!raw-loader!@site/examples/api/directives.mjs';
import directivesSh from '!!raw-loader!@site/examples/api/directives.sh';
import directivesGo from '!!raw-loader!@site/examples/api/directives.go';
## What is a Memory Bank?
@ -44,11 +48,10 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
<CodeSnippet code={memoryBanksMjs} section="create-bank" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
hindsight bank create my-bank
```
<CodeSnippet code={memoryBanksSh} section="create-bank" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="create-bank" language="go" />
</TabItem>
</Tabs>
@ -205,6 +208,12 @@ How skeptical vs trusting the bank is when evaluating claims during `reflect`. S
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="bank-with-disposition" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="bank-with-disposition" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="bank-with-disposition" language="go" />
</TabItem>
</Tabs>
| Value | Behaviour |
@ -275,6 +284,12 @@ Bank configuration fields (retain mission, extraction mode, observations mission
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="update-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="update-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="update-bank-config" language="go" />
</TabItem>
</Tabs>
You can update any subset of fields — only the keys you provide are changed.
@ -288,6 +303,12 @@ You can update any subset of fields — only the keys you provide are changed.
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="get-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="get-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="get-bank-config" language="go" />
</TabItem>
</Tabs>
The response distinguishes:
@ -303,6 +324,12 @@ The response distinguishes:
<TabItem value="node" label="Node.js">
<CodeSnippet code={memoryBanksMjs} section="reset-bank-config" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={memoryBanksSh} section="reset-bank-config" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={memoryBanksGo} section="reset-bank-config" language="go" />
</TabItem>
</Tabs>
This removes all bank-level overrides. The bank reverts to server-wide defaults (set via environment variables).
@ -337,6 +364,12 @@ Use directives for rules that must never be violated:
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="create-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="create-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="create-directive" language="go" />
</TabItem>
</Tabs>
### Listing Directives
@ -348,6 +381,12 @@ Use directives for rules that must never be violated:
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="list-directives" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="list-directives" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="list-directives" language="go" />
</TabItem>
</Tabs>
### Updating Directives
@ -359,6 +398,12 @@ Use directives for rules that must never be violated:
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="update-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="update-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="update-directive" language="go" />
</TabItem>
</Tabs>
### Deleting Directives
@ -370,6 +415,12 @@ Use directives for rules that must never be violated:
<TabItem value="node" label="Node.js">
<CodeSnippet code={directivesMjs} section="delete-directive" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={directivesSh} section="delete-directive" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={directivesGo} section="delete-directive" language="go" />
</TabItem>
</Tabs>
### Directives vs Disposition

View file

@ -12,6 +12,9 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
{/* Import raw source files */}
import mentalModelsPy from '!!raw-loader!@site/examples/api/mental-models.py';
import mentalModelsMjs from '!!raw-loader!@site/examples/api/mental-models.mjs';
import mentalModelsSh from '!!raw-loader!@site/examples/api/mental-models.sh';
import mentalModelsGo from '!!raw-loader!@site/examples/api/mental-models.go';
## What Are Mental Models?
@ -56,22 +59,14 @@ Creating a mental model runs a reflect operation in the background and saves the
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Create a mental model (async operation)
curl -X POST "http://localhost:8888/v1/default/banks/my-bank/mental-models" \
-H "Content-Type: application/json" \
-d '{
"name": "Team Communication Preferences",
"source_query": "How does the team prefer to communicate?",
"tags": ["team"]
}'
# Response: {"operation_id": "op-123"}
# Use the operations endpoint to check completion
```
<CodeSnippet code={mentalModelsSh} section="create-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model" language="go" />
</TabItem>
</Tabs>
@ -81,12 +76,38 @@ curl -X POST "http://localhost:8888/v1/default/banks/my-bank/mental-models" \
|-----------|------|----------|-------------|
| `name` | string | Yes | Human-readable name for the mental model |
| `source_query` | string | Yes | The query to run to generate content |
| `id` | string | No | Custom ID for the mental model (alphanumeric lowercase with hyphens). Auto-generated if omitted. |
| `tags` | list | No | Tags for filtering during retrieval |
| `max_tokens` | int | No | Maximum tokens for the mental model content |
| `trigger` | object | No | Trigger settings (see [Automatic Refresh](#automatic-refresh)) |
---
## Create with Custom ID
Assign a stable, human-readable ID to a mental model so you can retrieve or update it by name instead of relying on the auto-generated UUID:
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-id" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-id" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-id" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-id" language="go" />
</TabItem>
</Tabs>
:::tip
Custom IDs must be lowercase alphanumeric and may contain hyphens (e.g. `team-policies`, `q4-status`). If a mental model with that ID already exists, the request is rejected.
:::
---
## Automatic Refresh
Mental models can be configured to **automatically refresh** when observations are updated. This keeps them in sync with the latest knowledge without manual intervention.
@ -103,19 +124,14 @@ When `refresh_after_consolidation` is enabled, the mental model will be re-gener
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="create-mental-model-with-trigger" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="create-mental-model-with-trigger" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
# Create a mental model with automatic refresh enabled
curl -X POST "http://localhost:8888/v1/default/banks/my-bank/mental-models" \
-H "Content-Type: application/json" \
-d '{
"name": "Project Status",
"source_query": "What is the current project status?",
"trigger": {"refresh_after_consolidation": true}
}'
```
<CodeSnippet code={mentalModelsSh} section="create-mental-model-with-trigger" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="create-mental-model-with-trigger" language="go" />
</TabItem>
</Tabs>
@ -140,12 +156,14 @@ Enable automatic refresh for mental models that need to stay current. Disable it
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="list-mental-models" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="list-mental-models" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl "http://localhost:8888/v1/default/banks/my-bank/mental-models"
```
<CodeSnippet code={mentalModelsSh} section="list-mental-models" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="list-mental-models" language="go" />
</TabItem>
</Tabs>
@ -157,12 +175,14 @@ curl "http://localhost:8888/v1/default/banks/my-bank/mental-models"
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl "http://localhost:8888/v1/default/banks/my-bank/mental-models/{mental_model_id}"
```
<CodeSnippet code={mentalModelsSh} section="get-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model" language="go" />
</TabItem>
</Tabs>
@ -190,12 +210,14 @@ Re-run the source query to update the mental model with current knowledge:
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="refresh-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="refresh-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X POST "http://localhost:8888/v1/default/banks/my-bank/mental-models/{mental_model_id}/refresh"
```
<CodeSnippet code={mentalModelsSh} section="refresh-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="refresh-mental-model" language="go" />
</TabItem>
</Tabs>
@ -214,14 +236,14 @@ Update the mental model's name:
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="update-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="update-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X PATCH "http://localhost:8888/v1/default/banks/my-bank/mental-models/{mental_model_id}" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Team Communication Preferences"}'
```
<CodeSnippet code={mentalModelsSh} section="update-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="update-mental-model" language="go" />
</TabItem>
</Tabs>
@ -233,12 +255,14 @@ curl -X PATCH "http://localhost:8888/v1/default/banks/my-bank/mental-models/{men
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="delete-mental-model" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="delete-mental-model" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
```bash
curl -X DELETE "http://localhost:8888/v1/default/banks/my-bank/mental-models/{mental_model_id}"
```
<CodeSnippet code={mentalModelsSh} section="delete-mental-model" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="delete-mental-model" language="go" />
</TabItem>
</Tabs>
@ -280,6 +304,15 @@ Every time a mental model's content changes (via refresh or manual update), the
<TabItem value="python" label="Python">
<CodeSnippet code={mentalModelsPy} section="get-mental-model-history" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={mentalModelsMjs} section="get-mental-model-history" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={mentalModelsSh} section="get-mental-model-history" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={mentalModelsGo} section="get-mental-model-history" language="go" />
</TabItem>
</Tabs>
### Response

View file

@ -15,6 +15,7 @@ import {ClientsGrid, IntegrationsGrid} from '@site/src/components/SupportedGrids
import quickstartPy from '!!raw-loader!@site/examples/api/quickstart.py';
import quickstartMjs from '!!raw-loader!@site/examples/api/quickstart.mjs';
import quickstartSh from '!!raw-loader!@site/examples/api/quickstart.sh';
import quickstartGo from '!!raw-loader!@site/examples/api/quickstart.go';
## Clients
@ -90,6 +91,15 @@ curl -fsSL https://hindsight.vectorize.io/get-cli | bash
<CodeSnippet code={quickstartSh} section="quickstart-full" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
```bash
go get github.com/vectorize-io/hindsight/hindsight-clients/go
```
<CodeSnippet code={quickstartGo} section="quickstart-full" language="go" />
</TabItem>
</Tabs>

View file

@ -16,6 +16,7 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
import recallPy from '!!raw-loader!@site/examples/api/recall.py';
import recallMjs from '!!raw-loader!@site/examples/api/recall.mjs';
import recallSh from '!!raw-loader!@site/examples/api/recall.sh';
import recallGo from '!!raw-loader!@site/examples/api/recall.go';
:::info How Recall Works
Learn about the four retrieval strategies (semantic, keyword, graph, temporal) and RRF fusion in the [Recall Architecture](/developer/retrieval) guide.
@ -37,6 +38,9 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-basic" language="go" />
</TabItem>
</Tabs>
---
@ -59,9 +63,19 @@ Each type runs the full four-strategy retrieval pipeline independently, so narro
<CodeSnippet code={recallPy} section="recall-experience-only" language="python" />
<CodeSnippet code={recallPy} section="recall-observations-only" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-world-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-experience-only" language="javascript" />
<CodeSnippet code={recallMjs} section="recall-observations-only" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-fact-type" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-world-only" language="go" />
<CodeSnippet code={recallGo} section="recall-experience-only" language="go" />
<CodeSnippet code={recallGo} section="recall-observations-only" language="go" />
</TabItem>
</Tabs>
:::tip About Observations
@ -79,6 +93,12 @@ Controls retrieval depth and breadth. Accepted values are `low`, `mid` (default)
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-budget-levels" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-budget-levels" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-budget-levels" language="go" />
</TabItem>
</Tabs>
### max_tokens
@ -89,6 +109,15 @@ The maximum number of tokens the returned facts can collectively occupy. Default
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-token-budget" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-token-budget" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-token-budget" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-token-budget" language="go" />
</TabItem>
</Tabs>
### query_timestamp
@ -118,6 +147,12 @@ When enabled and `types` includes `observation`, each observation result is acco
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-source-facts" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-source-facts" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-source-facts" language="go" />
</TabItem>
</Tabs>
#### entities
@ -152,7 +187,20 @@ Consider a bank with these four memories:
Returns memories that have **at least one** matching tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-with-tags" language="go" />
</TabItem>
</Tabs>
Use this for **shared global knowledge + user-specific** patterns, where untagged memories represent information everyone should see.
@ -160,7 +208,20 @@ Use this for **shared global knowledge + user-specific** patterns, where untagge
Same as `any` but untagged memories are excluded.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-any-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-any-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-any-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-strict" language="go" />
</TabItem>
</Tabs>
Use this when memories are **fully partitioned by tags** and untagged memories should never be visible.
@ -168,7 +229,20 @@ Use this when memories are **fully partitioned by tags** and untagged memories s
Returns memories that have **every** specified tag, plus untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-mode" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-mode" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-mode" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all-mode" language="go" />
</TabItem>
</Tabs>
Use this when memories must belong to a **specific intersection** of scopes (e.g., only memories relevant to both a user and a project), while still surfacing shared global knowledge.
@ -176,7 +250,20 @@ Use this when memories must belong to a **specific intersection** of scopes (e.g
Returns memories that have **every** specified tag, and excludes untagged memories.
<Tabs>
<TabItem value="python" label="Python">
<CodeSnippet code={recallPy} section="recall-tags-all-strict" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={recallMjs} section="recall-tags-all-strict" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={recallSh} section="recall-tags-all-strict" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={recallGo} section="recall-tags-all" language="go" />
</TabItem>
</Tabs>
Use this for strict scope enforcement where a memory must explicitly belong to **all** specified contexts.

View file

@ -16,6 +16,7 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
import reflectPy from '!!raw-loader!@site/examples/api/reflect.py';
import reflectMjs from '!!raw-loader!@site/examples/api/reflect.mjs';
import reflectSh from '!!raw-loader!@site/examples/api/reflect.sh';
import reflectGo from '!!raw-loader!@site/examples/api/reflect.go';
:::info How Reflect Works
Learn about disposition-driven reasoning in the [Reflect Architecture](/developer/reflect) guide.
@ -37,6 +38,9 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-basic" language="go" />
</TabItem>
</Tabs>
---
@ -58,6 +62,12 @@ Controls how thoroughly the agent explores the memory bank before answering. Acc
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-params" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-params" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-params" language="go" />
</TabItem>
</Tabs>
### max_tokens
@ -78,6 +88,9 @@ An optional JSON Schema object. When provided, the LLM generates a response that
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-structured-output" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-structured-output" language="go" />
</TabItem>
</Tabs>
### tags
@ -88,6 +101,15 @@ Filters which memories the agent can access during reflection. Works identically
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-with-tags" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-with-tags" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-with-tags" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-with-tags" language="go" />
</TabItem>
</Tabs>
### include
@ -102,6 +124,15 @@ When enabled, the response includes a `based_on` object listing the memories, me
<TabItem value="python" label="Python">
<CodeSnippet code={reflectPy} section="reflect-sources" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={reflectMjs} section="reflect-sources" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={reflectSh} section="reflect-sources" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={reflectGo} section="reflect-sources" language="go" />
</TabItem>
</Tabs>
#### include.tool_calls

View file

@ -16,6 +16,7 @@ import CodeSnippet from '@site/src/components/CodeSnippet';
import retainPy from '!!raw-loader!@site/examples/api/retain.py';
import retainMjs from '!!raw-loader!@site/examples/api/retain.mjs';
import retainSh from '!!raw-loader!@site/examples/api/retain.sh';
import retainGo from '!!raw-loader!@site/examples/api/retain.go';
:::info How Retain Works
Learn about fact extraction, entity resolution, and graph construction in the [Retain Architecture](/developer/retain) guide.
@ -39,6 +40,9 @@ A single retain call accepts one or more **items**. Each item is a piece of raw
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-basic" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-basic" language="go" />
</TabItem>
</Tabs>
### Retaining a Conversation
@ -52,6 +56,12 @@ A full conversation should be retained as a single item. The LLM can parse any f
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-conversation" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-conversation" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-conversation" language="go" />
</TabItem>
</Tabs>
When the conversation grows — a new message arrives — just retain again with the full updated content and the same `document_id`. Hindsight will delete the previous version and reprocess from scratch, so memories always reflect the latest state of the conversation.
@ -92,6 +102,9 @@ Providing context consistently is one of the highest-leverage things you can do
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-with-context" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-with-context" language="go" />
</TabItem>
</Tabs>
### metadata
@ -203,6 +216,12 @@ Multiple items can be submitted in a single request. Batch ingestion is the reco
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-batch" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-batch" language="go" />
</TabItem>
</Tabs>
@ -215,18 +234,18 @@ Upload files directly — Hindsight converts them to text and extracts memories
**Supported formats:** PDF, DOCX, DOC, PPTX, PPT, XLSX, XLS, images (JPG, PNG, GIF, etc. — OCR), audio (MP3, WAV, FLAC, etc. — transcription), HTML, and plain text formats (TXT, MD, CSV, JSON, YAML, etc.)
<Tabs>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="curl" label="HTTP">
<CodeSnippet code={retainSh} section="retain-files-curl" language="bash" />
</TabItem>
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
The file retain endpoint always returns asynchronously. The response contains `operation_ids` — one per uploaded file — which you can poll via `GET /v1/default/banks/{bank_id}/operations` to track progress.
@ -237,6 +256,15 @@ Upload up to 10 files per request (max 100 MB total). Each file becomes a separa
<TabItem value="python" label="Python">
<CodeSnippet code={retainPy} section="retain-files-batch" language="python" />
</TabItem>
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-files-batch" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-files" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-files" language="go" />
</TabItem>
</Tabs>
:::info File Storage
@ -256,6 +284,12 @@ For large batches, use async ingestion to avoid blocking your application:
<TabItem value="node" label="Node.js">
<CodeSnippet code={retainMjs} section="retain-async" language="javascript" />
</TabItem>
<TabItem value="cli" label="CLI">
<CodeSnippet code={retainSh} section="retain-async" language="bash" />
</TabItem>
<TabItem value="go" label="Go">
<CodeSnippet code={retainGo} section="retain-async" language="go" />
</TabItem>
</Tabs>
When `async: true`, the call returns immediately with an `operation_id`. Processing runs in the background via the worker service. No `usage` metrics are returned for async operations.

View file

@ -0,0 +1,86 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
const bankID = "directives-example-bank"
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
client.BanksAPI.CreateOrUpdateBank(ctx, bankID).
CreateBankRequest(hindsight.CreateBankRequest{
Name: *hindsight.NewNullableString(hindsight.PtrString("Test Bank")),
}).Execute()
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:create-directive]
// Create a directive (hard rule for reflect)
directive, _, _ := client.DirectivesAPI.CreateDirective(ctx, bankID).
CreateDirectiveRequest(hindsight.CreateDirectiveRequest{
Name: "Formal Language",
Content: "Always respond in formal English, avoiding slang and colloquialisms.",
}).Execute()
fmt.Printf("Created directive: %s\n", directive.GetId())
// [/docs:create-directive]
directiveID := directive.GetId()
// [docs:list-directives]
// List all directives in a bank
directives, _, _ := client.DirectivesAPI.ListDirectives(ctx, bankID).Execute()
for _, d := range directives.GetItems() {
content := d.GetContent()
if len(content) > 50 {
content = content[:50]
}
fmt.Printf("- %s: %s...\n", d.GetName(), content)
}
// [/docs:list-directives]
// [docs:update-directive]
// Update a directive (e.g., disable without deleting)
isActiveFalse := false
updated, _, _ := client.DirectivesAPI.UpdateDirective(ctx, bankID, directiveID).
UpdateDirectiveRequest(hindsight.UpdateDirectiveRequest{
IsActive: *hindsight.NewNullableBool(&isActiveFalse),
}).Execute()
fmt.Printf("Directive active: %v\n", updated.GetIsActive())
// [/docs:update-directive]
// [docs:delete-directive]
// Delete a directive
client.DirectivesAPI.DeleteDirective(ctx, bankID, directiveID).Execute()
// [/docs:delete-directive]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/%s", apiURL, bankID), nil)
http.DefaultClient.Do(req)
fmt.Println("directives.go: All examples passed")
}

View file

@ -0,0 +1,51 @@
#!/bin/bash
# Directives API examples for Hindsight CLI
# Run: bash examples/api/directives.sh
set -e
HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}"
BANK_ID="directives-example-bank"
# =============================================================================
# Setup (not shown in docs)
# =============================================================================
hindsight bank create "$BANK_ID" --name "Test Bank"
# =============================================================================
# Doc Examples
# =============================================================================
# [docs:create-directive]
# Create a directive (hard rule for reflect)
hindsight directive create "$BANK_ID" \
"Formal Language" \
"Always respond in formal English, avoiding slang and colloquialisms."
# [/docs:create-directive]
# Get the directive ID for subsequent operations
DIRECTIVE_ID=$(hindsight directive list "$BANK_ID" -o json | python3 -c "import sys,json; items=json.load(sys.stdin).get('items',[]); print(items[0]['id'] if items else '')" 2>/dev/null || echo "")
# [docs:list-directives]
# List all directives in a bank
hindsight directive list "$BANK_ID"
# [/docs:list-directives]
if [ -n "$DIRECTIVE_ID" ]; then
# [docs:update-directive]
# Update a directive (e.g., disable without deleting)
hindsight directive update "$BANK_ID" "$DIRECTIVE_ID" --is-active false
# [/docs:update-directive]
# [docs:delete-directive]
# Delete a directive
hindsight directive delete "$BANK_ID" "$DIRECTIVE_ID" -y
# [/docs:delete-directive]
fi
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
hindsight bank delete "$BANK_ID" -y
echo "directives.sh: All examples passed"

View file

@ -0,0 +1,94 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// [docs:document-retain]
// Retain with document ID
docID := "meeting-2024-03-15"
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{
Content: "Alice presented the Q4 roadmap...",
DocumentId: *hindsight.NewNullableString(&docID),
},
},
}).Execute()
// [/docs:document-retain]
// [docs:document-update]
// Original
planDoc := "project-plan"
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{
Content: "Project deadline: March 31",
DocumentId: *hindsight.NewNullableString(&planDoc),
},
},
}).Execute()
// Update (deletes old facts, creates new ones)
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{
Content: "Project deadline: April 15 (extended)",
DocumentId: *hindsight.NewNullableString(&planDoc),
},
},
}).Execute()
// [/docs:document-update]
// [docs:document-get]
doc, _, err := client.DocumentsAPI.GetDocument(ctx, "my-bank", "meeting-2024-03-15").Execute()
if err != nil {
log.Fatalf("Failed to get document: %v", err)
}
fmt.Printf("Document ID: %s\n", doc.GetId())
fmt.Printf("Memory units: %d\n", doc.GetMemoryUnitCount())
// [/docs:document-get]
// [docs:document-delete]
client.DocumentsAPI.DeleteDocument(ctx, "my-bank", "meeting-2024-03-15").Execute()
// [/docs:document-delete]
// [docs:document-list]
// List all documents
docs, _, err := client.DocumentsAPI.ListDocuments(ctx, "my-bank").Execute()
if err != nil {
log.Fatalf("Failed to list documents: %v", err)
}
for _, d := range docs.Items {
id, _ := d["id"].(string)
memCount, _ := d["memory_unit_count"].(float64)
fmt.Printf("%s: %d memories\n", id, int(memCount))
}
// [/docs:document-list]
// Cleanup (not shown in docs)
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/my-bank", apiURL), nil)
http.DefaultClient.Do(req)
fmt.Println("documents.go: All examples passed")
}

View file

@ -0,0 +1,60 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// [docs:main-retain]
// Store a fact or conversation into a memory bank
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{Content: "Alice joined Google in March 2024 as a Senior ML Engineer"},
},
}).Execute()
// [/docs:main-retain]
// [docs:main-recall]
// Search for memories using a natural language query
resp, _, _ := client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What does Alice do at Google?",
}).Execute()
for _, r := range resp.Results {
fmt.Println(r.Text)
}
// [/docs:main-recall]
// [docs:main-reflect]
// Generate a reasoned response using memories and bank disposition
answer, _, _ := client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "Should we adopt TypeScript for our backend?",
}).Execute()
fmt.Println(answer.GetText())
// [/docs:main-reflect]
// Cleanup (not shown in docs)
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/my-bank", apiURL), nil)
http.DefaultClient.Do(req)
fmt.Println("main-methods.go: All examples passed")
}

View file

@ -0,0 +1,114 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:create-bank]
client.BanksAPI.CreateOrUpdateBank(ctx, "my-bank").
CreateBankRequest(hindsight.CreateBankRequest{}).Execute()
// [/docs:create-bank]
// [docs:bank-with-disposition]
client.BanksAPI.CreateOrUpdateBank(ctx, "architect-bank").
CreateBankRequest(hindsight.CreateBankRequest{
ReflectMission: *hindsight.NewNullableString(hindsight.PtrString(
"You're a senior software architect - keep track of system designs, " +
"technology decisions, and architectural patterns. Prefer simplicity over cutting-edge.",
)),
DispositionSkepticism: *hindsight.NewNullableInt32(hindsight.PtrInt32(4)),
DispositionLiteralism: *hindsight.NewNullableInt32(hindsight.PtrInt32(4)),
DispositionEmpathy: *hindsight.NewNullableInt32(hindsight.PtrInt32(2)),
}).Execute()
// [/docs:bank-with-disposition]
// [docs:bank-background]
client.BanksAPI.CreateOrUpdateBank(ctx, "my-bank").
CreateBankRequest(hindsight.CreateBankRequest{
ReflectMission: *hindsight.NewNullableString(hindsight.PtrString(
"I am a research assistant specializing in machine learning.",
)),
}).Execute()
// [/docs:bank-background]
// [docs:bank-mission]
client.BanksAPI.CreateOrUpdateBank(ctx, "my-bank").
CreateBankRequest(hindsight.CreateBankRequest{
ReflectMission: *hindsight.NewNullableString(hindsight.PtrString(
"You're a senior software architect - keep track of system designs, " +
"technology decisions, and architectural patterns.",
)),
}).Execute()
// [/docs:bank-mission]
// [docs:bank-support-agent]
client.BanksAPI.CreateOrUpdateBank(ctx, "support-bank").
CreateBankRequest(hindsight.CreateBankRequest{}).Execute()
client.BanksAPI.UpdateBankConfig(ctx, "support-bank").
BankConfigUpdate(hindsight.BankConfigUpdate{
Updates: map[string]interface{}{
"observations_mission": "I am a customer support agent. Track customer preferences, " +
"recurring issues, and resolution history to provide consistent, personalized support.",
},
}).Execute()
// [/docs:bank-support-agent]
// [docs:update-bank-config]
client.BanksAPI.UpdateBankConfig(ctx, "my-bank").
BankConfigUpdate(hindsight.BankConfigUpdate{
Updates: map[string]interface{}{
"retain_mission": "Always include technical decisions, API design choices, and architectural trade-offs. " +
"Ignore meeting logistics and social exchanges.",
"retain_extraction_mode": "verbose",
"observations_mission": "Observations are stable facts about people and projects. " +
"Always include preferences, skills, and recurring patterns. Ignore one-off events.",
"disposition_skepticism": 4,
"disposition_literalism": 4,
"disposition_empathy": 2,
},
}).Execute()
// [/docs:update-bank-config]
// [docs:get-bank-config]
// Returns resolved config (server defaults merged with bank overrides) and the raw overrides
result, _, _ := client.BanksAPI.GetBankConfig(ctx, "my-bank").Execute()
// result.Config — full resolved configuration
// result.Overrides — only fields overridden at the bank level
fmt.Println("Config keys:", len(result.GetConfig()))
// [/docs:get-bank-config]
// [docs:reset-bank-config]
// Remove all bank-level overrides, reverting to server defaults
client.BanksAPI.ResetBankConfig(ctx, "my-bank").Execute()
// [/docs:reset-bank-config]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
for _, bankID := range []string{"my-bank", "architect-bank", "support-bank"} {
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/%s", apiURL, bankID), nil)
http.DefaultClient.Do(req)
}
fmt.Println("memory-banks.go: All examples passed")
}

View file

@ -0,0 +1,71 @@
#!/bin/bash
# Memory Banks API examples for Hindsight CLI
# Run: bash examples/api/memory-banks.sh
set -e
HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}"
# =============================================================================
# Doc Examples
# =============================================================================
# [docs:create-bank]
hindsight bank create my-bank
# [/docs:create-bank]
# [docs:bank-with-disposition]
hindsight bank create architect-bank \
--mission "You're a senior software architect - keep track of system designs, technology decisions, and architectural patterns. Prefer simplicity over cutting-edge." \
--skepticism 4 \
--literalism 4 \
--empathy 2
# [/docs:bank-with-disposition]
# [docs:bank-background]
hindsight bank create my-bank \
--mission "I am a research assistant specializing in machine learning."
# [/docs:bank-background]
# [docs:bank-mission]
hindsight bank create my-bank \
--mission "You're a senior software architect - keep track of system designs, technology decisions, and architectural patterns."
# [/docs:bank-mission]
# [docs:bank-support-agent]
hindsight bank create support-bank
hindsight bank set-config support-bank \
--observations-mission "I am a customer support agent. Track customer preferences, recurring issues, and resolution history."
# [/docs:bank-support-agent]
# [docs:update-bank-config]
hindsight bank set-config my-bank \
--retain-mission "Always include technical decisions, API design choices, and architectural trade-offs. Ignore meeting logistics and social exchanges." \
--retain-extraction-mode verbose \
--observations-mission "Observations are stable facts about people and projects. Always include preferences, skills, and recurring patterns. Ignore one-off events." \
--disposition-skepticism 4 \
--disposition-literalism 4 \
--disposition-empathy 2
# [/docs:update-bank-config]
# [docs:get-bank-config]
# Returns resolved config (server defaults merged with bank overrides)
hindsight bank config my-bank
# Show only bank-specific overrides
hindsight bank config my-bank --overrides-only
# [/docs:get-bank-config]
# [docs:reset-bank-config]
# Remove all bank-level overrides, reverting to server defaults
hindsight bank reset-config my-bank -y
# [/docs:reset-bank-config]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
for bank_id in my-bank architect-bank support-bank; do
curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/${bank_id}" > /dev/null
done
echo "memory-banks.sh: All examples passed"

View file

@ -0,0 +1,173 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
const mmBankID = "mental-models-demo-bank"
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
client.BanksAPI.CreateOrUpdateBank(ctx, mmBankID).
CreateBankRequest(hindsight.CreateBankRequest{
Name: *hindsight.NewNullableString(hindsight.PtrString("Mental Models Demo")),
}).Execute()
for _, content := range []string{
"The team prefers async communication via Slack",
"For urgent issues, use the #incidents channel",
"Weekly syncs happen every Monday at 10am",
} {
client.MemoryAPI.RetainMemories(ctx, mmBankID).
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{{Content: content}},
}).Execute()
}
time.Sleep(2 * time.Second)
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:create-mental-model]
// Create a mental model (runs reflect in background)
result, _, _ := client.MentalModelsAPI.CreateMentalModel(ctx, mmBankID).
CreateMentalModelRequest(hindsight.CreateMentalModelRequest{
Name: "Team Communication Preferences",
SourceQuery: "How does the team prefer to communicate?",
Tags: []string{"team", "communication"},
}).Execute()
// Returns an operation_id — check operations endpoint for completion
fmt.Printf("Operation ID: %s\n", result.GetOperationId())
// [/docs:create-mental-model]
// [docs:create-mental-model-with-id]
// Create a mental model with a specific custom ID
mmID := "communication-policy"
resultWithID, _, _ := client.MentalModelsAPI.CreateMentalModel(ctx, mmBankID).
CreateMentalModelRequest(hindsight.CreateMentalModelRequest{
Id: *hindsight.NewNullableString(&mmID),
Name: "Communication Policy",
SourceQuery: "What are the team's communication guidelines?",
}).Execute()
fmt.Printf("Created with custom ID: %s\n", resultWithID.GetOperationId())
// [/docs:create-mental-model-with-id]
time.Sleep(5 * time.Second)
// [docs:create-mental-model-with-trigger]
// Create a mental model with automatic refresh enabled
refreshTrue := true
result2, _, _ := client.MentalModelsAPI.CreateMentalModel(ctx, mmBankID).
CreateMentalModelRequest(hindsight.CreateMentalModelRequest{
Name: "Project Status",
SourceQuery: "What is the current project status?",
Trigger: &hindsight.MentalModelTrigger{
RefreshAfterConsolidation: &refreshTrue,
},
}).Execute()
// This mental model will automatically refresh when observations are updated
fmt.Printf("Operation ID: %s\n", result2.GetOperationId())
// [/docs:create-mental-model-with-trigger]
time.Sleep(5 * time.Second)
// [docs:list-mental-models]
// List all mental models in a bank
mentalModels, _, _ := client.MentalModelsAPI.ListMentalModels(ctx, mmBankID).Execute()
for _, mm := range mentalModels.GetItems() {
fmt.Printf("- %s: %s\n", mm.GetName(), mm.GetSourceQuery())
}
// [/docs:list-mental-models]
if len(mentalModels.GetItems()) == 0 {
fmt.Println("mental-models.go: All examples passed (no mental models created yet)")
cleanupMentalModels(client, ctx, apiURL)
return
}
mentalModelID := mentalModels.GetItems()[0].GetId()
// [docs:get-mental-model]
// Get a specific mental model
mentalModel, _, _ := client.MentalModelsAPI.GetMentalModel(ctx, mmBankID, mentalModelID).Execute()
fmt.Printf("Name: %s\n", mentalModel.GetName())
fmt.Printf("Content: %s\n", mentalModel.GetContent())
fmt.Printf("Last refreshed: %s\n", mentalModel.GetLastRefreshedAt())
// [/docs:get-mental-model]
// [docs:refresh-mental-model]
// Refresh a mental model to update with current knowledge
refreshResult, _, _ := client.MentalModelsAPI.RefreshMentalModel(ctx, mmBankID, mentalModelID).Execute()
fmt.Printf("Refresh operation ID: %s\n", refreshResult.GetOperationId())
// [/docs:refresh-mental-model]
// [docs:update-mental-model]
// Update a mental model's metadata
newName := "Updated Team Communication Preferences"
refreshAfter := true
updated, _, _ := client.MentalModelsAPI.UpdateMentalModel(ctx, mmBankID, mentalModelID).
UpdateMentalModelRequest(hindsight.UpdateMentalModelRequest{
Name: *hindsight.NewNullableString(&newName),
Trigger: *hindsight.NewNullableMentalModelTrigger(&hindsight.MentalModelTrigger{
RefreshAfterConsolidation: &refreshAfter,
}),
}).Execute()
fmt.Printf("Updated name: %s\n", updated.GetName())
// [/docs:update-mental-model]
// [docs:get-mental-model-history]
// Get the change history of a mental model
history, _, _ := client.MentalModelsAPI.GetMentalModelHistory(ctx, mmBankID, mentalModelID).Execute()
if entries, ok := history.([]interface{}); ok {
for _, entry := range entries {
if e, ok := entry.(map[string]interface{}); ok {
fmt.Printf("Changed at: %v\n", e["changed_at"])
fmt.Printf("Previous content: %v\n", e["previous_content"])
}
}
}
// [/docs:get-mental-model-history]
// [docs:delete-mental-model]
// Delete a mental model
client.MentalModelsAPI.DeleteMentalModel(ctx, mmBankID, mentalModelID).Execute()
// [/docs:delete-mental-model]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
cleanupMentalModels(client, ctx, apiURL)
fmt.Println("mental-models.go: All examples passed")
}
func cleanupMentalModels(client *hindsight.APIClient, ctx context.Context, apiURL string) {
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/%s", apiURL, mmBankID), nil)
http.DefaultClient.Do(req)
}

View file

@ -0,0 +1,129 @@
#!/usr/bin/env node
/**
* Mental Models API examples for Hindsight (Node.js)
* Run: node examples/api/mental-models.mjs
*/
import { HindsightClient } from '@vectorize-io/hindsight-client';
const HINDSIGHT_URL = process.env.HINDSIGHT_API_URL || 'http://localhost:8888';
const BANK_ID = 'mental-models-demo-bank';
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
const client = new HindsightClient({ baseUrl: HINDSIGHT_URL });
await client.createBank(BANK_ID, { name: 'Mental Models Demo' });
await client.retain(BANK_ID, 'The team prefers async communication via Slack');
await client.retain(BANK_ID, 'For urgent issues, use the #incidents channel');
await client.retain(BANK_ID, 'Weekly syncs happen every Monday at 10am');
await new Promise(r => setTimeout(r, 2000));
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:create-mental-model]
// Create a mental model (runs reflect in background)
const result = await client.createMentalModel(
BANK_ID,
'Team Communication Preferences',
'How does the team prefer to communicate?',
{ tags: ['team', 'communication'] },
);
// Returns an operation_id — check operations endpoint for completion
console.log(`Operation ID: ${result.operation_id}`);
// [/docs:create-mental-model]
// [docs:create-mental-model-with-id]
// Create a mental model with a specific custom ID
const resultWithId = await client.createMentalModel(
BANK_ID,
'Communication Policy',
"What are the team's communication guidelines?",
{ id: 'communication-policy' },
);
console.log(`Created with custom ID: ${resultWithId.operation_id}`);
// [/docs:create-mental-model-with-id]
await new Promise(r => setTimeout(r, 5000));
// [docs:create-mental-model-with-trigger]
// Create a mental model with automatic refresh enabled
const result2 = await client.createMentalModel(
BANK_ID,
'Project Status',
'What is the current project status?',
{ trigger: { refreshAfterConsolidation: true } },
);
// This mental model will automatically refresh when observations are updated
console.log(`Operation ID: ${result2.operation_id}`);
// [/docs:create-mental-model-with-trigger]
await new Promise(r => setTimeout(r, 5000));
// [docs:list-mental-models]
// List all mental models in a bank
const mentalModels = await client.listMentalModels(BANK_ID);
for (const mm of mentalModels.items) {
console.log(`- ${mm.name}: ${mm.source_query}`);
}
// [/docs:list-mental-models]
const mentalModelId = mentalModels.items[0]?.id;
if (!mentalModelId) {
console.log('mental-models.mjs: All examples passed (no mental models created yet)');
await fetch(`${HINDSIGHT_URL}/v1/default/banks/${BANK_ID}`, { method: 'DELETE' });
process.exit(0);
}
// [docs:get-mental-model]
// Get a specific mental model
const mentalModel = await client.getMentalModel(BANK_ID, mentalModelId);
console.log(`Name: ${mentalModel.name}`);
console.log(`Content: ${mentalModel.content}`);
console.log(`Last refreshed: ${mentalModel.last_refreshed_at}`);
// [/docs:get-mental-model]
// [docs:refresh-mental-model]
// Refresh a mental model to update with current knowledge
const refreshResult = await client.refreshMentalModel(BANK_ID, mentalModelId);
console.log(`Refresh operation ID: ${refreshResult.operation_id}`);
// [/docs:refresh-mental-model]
// [docs:update-mental-model]
// Update a mental model's metadata
const updated = await client.updateMentalModel(BANK_ID, mentalModelId, {
name: 'Updated Team Communication Preferences',
trigger: { refresh_after_consolidation: true },
});
console.log(`Updated name: ${updated.name}`);
// [/docs:update-mental-model]
// [docs:get-mental-model-history]
// Get the change history of a mental model
const history = await client.getMentalModelHistory(BANK_ID, mentalModelId);
for (const entry of history) {
console.log(`Changed at: ${entry.changed_at}`);
console.log(`Previous content: ${entry.previous_content}`);
}
// [/docs:get-mental-model-history]
// [docs:delete-mental-model]
// Delete a mental model
await client.deleteMentalModel(BANK_ID, mentalModelId);
// [/docs:delete-mental-model]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
await client.deleteBank(BANK_ID);
console.log('mental-models.mjs: All examples passed');

View file

@ -42,6 +42,18 @@ result = client.create_mental_model(
print(f"Operation ID: {result.operation_id}")
# [/docs:create-mental-model]
# [docs:create-mental-model-with-id]
# Create a mental model with a specific custom ID
result_with_id = client.create_mental_model(
bank_id=BANK_ID,
name="Communication Policy",
source_query="What are the team's communication guidelines?",
id="communication-policy"
)
print(f"Created with custom ID: {result_with_id.operation_id}")
# [/docs:create-mental-model-with-id]
# Wait for the mental model to be created
time.sleep(5)

View file

@ -0,0 +1,90 @@
#!/bin/bash
# Mental Models API examples for Hindsight CLI
# Run: bash examples/api/mental-models.sh
set -e
HINDSIGHT_URL="${HINDSIGHT_API_URL:-http://localhost:8888}"
BANK_ID="mental-models-demo-bank"
# =============================================================================
# Setup (not shown in docs)
# =============================================================================
hindsight bank create "$BANK_ID" --name "Mental Models Demo"
hindsight memory retain "$BANK_ID" "The team prefers async communication via Slack"
hindsight memory retain "$BANK_ID" "For urgent issues, use the #incidents channel"
hindsight memory retain "$BANK_ID" "Weekly syncs happen every Monday at 10am"
sleep 2
# =============================================================================
# Doc Examples
# =============================================================================
# [docs:create-mental-model]
# Create a mental model (runs reflect in background)
hindsight mental-model create "$BANK_ID" \
"Team Communication Preferences" \
"How does the team prefer to communicate?"
# [/docs:create-mental-model]
# [docs:create-mental-model-with-id]
# Create a mental model with a specific custom ID
hindsight mental-model create "$BANK_ID" \
"Communication Policy" \
"What are the team's communication guidelines?" \
--id communication-policy
# [/docs:create-mental-model-with-id]
sleep 5
# [docs:create-mental-model-with-trigger]
# Create a mental model and get its ID for subsequent operations
hindsight mental-model create "$BANK_ID" \
"Project Status" \
"What is the current project status?"
# [/docs:create-mental-model-with-trigger]
sleep 5
# [docs:list-mental-models]
# List all mental models in a bank
hindsight mental-model list "$BANK_ID"
# [/docs:list-mental-models]
# Get the first mental model ID for subsequent examples
MENTAL_MODEL_ID=$(hindsight mental-model list "$BANK_ID" -o json | python3 -c "import sys,json; items=json.load(sys.stdin).get('items',[]); print(items[0]['id'] if items else '')" 2>/dev/null || echo "")
if [ -n "$MENTAL_MODEL_ID" ]; then
# [docs:get-mental-model]
# Get a specific mental model
hindsight mental-model get "$BANK_ID" "$MENTAL_MODEL_ID"
# [/docs:get-mental-model]
# [docs:refresh-mental-model]
# Refresh a mental model to update with current knowledge
hindsight mental-model refresh "$BANK_ID" "$MENTAL_MODEL_ID"
# [/docs:refresh-mental-model]
# [docs:update-mental-model]
# Update a mental model's metadata
hindsight mental-model update "$BANK_ID" "$MENTAL_MODEL_ID" \
--name "Updated Team Communication Preferences"
# [/docs:update-mental-model]
# [docs:get-mental-model-history]
# Get the change history of a mental model
hindsight mental-model history "$BANK_ID" "$MENTAL_MODEL_ID"
# [/docs:get-mental-model-history]
# [docs:delete-mental-model]
# Delete a mental model
hindsight mental-model delete "$BANK_ID" "$MENTAL_MODEL_ID" -y
# [/docs:delete-mental-model]
fi
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================
curl -s -X DELETE "${HINDSIGHT_URL}/v1/default/banks/${BANK_ID}" > /dev/null
echo "mental-models.sh: All examples passed"

View file

@ -0,0 +1,217 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
for _, content := range []string{
"Alice works at Google as a software engineer",
"Alice loves hiking on weekends",
"Bob is a data scientist who works with Alice",
} {
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{{Content: content}},
}).Execute()
}
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:recall-basic]
response, _, _ := client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What does Alice do?",
}).Execute()
// response.Results is a slice of RecallResult, each with:
// - Id: fact ID
// - Text: the extracted fact
// - Type: "world", "experience", or "observation"
// - Context: context label set during retain
// - Tags: []string of tags
// - Entities: []string of entity names linked to this fact
// - OccurredStart: ISO datetime of when the event started
// - OccurredEnd: ISO datetime of when the event ended
// - MentionedAt: ISO datetime of when the fact was retained
// - DocumentId: document this fact belongs to
for _, r := range response.GetResults() {
fmt.Println(r.GetText())
}
// [/docs:recall-basic]
// [docs:recall-with-options]
budgetHigh := hindsight.HIGH
maxTokens := int32(8000)
traceTrue := true
detailedResponse, _, _ := client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What does Alice do?",
Types: []string{"world", "experience"},
Budget: &budgetHigh,
MaxTokens: &maxTokens,
Trace: &traceTrue,
}).Execute()
for _, r := range detailedResponse.GetResults() {
fmt.Println("-", r.GetText())
}
// [/docs:recall-with-options]
// [docs:recall-world-only]
// Only world facts (objective information)
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "Where does Alice work?",
Types: []string{"world"},
}).Execute()
// [/docs:recall-world-only]
// [docs:recall-experience-only]
// Only experience (conversations and events)
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What have I recommended?",
Types: []string{"experience"},
}).Execute()
// [/docs:recall-experience-only]
// [docs:recall-observations-only]
// Only observations (consolidated knowledge)
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What patterns have I learned?",
Types: []string{"observation"},
}).Execute()
// [/docs:recall-observations-only]
// [docs:recall-source-facts]
// Recall observations and include their source facts
maxSFTokens := int32(4096)
sfOpts := hindsight.SourceFactsIncludeOptions{MaxTokens: &maxSFTokens}
obsResponse, _, _ := client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What patterns have I learned about Alice?",
Types: []string{"observation"},
Include: &hindsight.IncludeOptions{
SourceFacts: *hindsight.NewNullableSourceFactsIncludeOptions(&sfOpts),
},
}).Execute()
for _, obs := range obsResponse.GetResults() {
fmt.Printf("Observation: %s\n", obs.GetText())
for _, factID := range obs.GetSourceFactIds() {
if fact, ok := obsResponse.GetSourceFacts()[factID]; ok {
fmt.Printf(" - [%s] %s\n", fact.GetType(), fact.GetText())
}
}
}
// [/docs:recall-source-facts]
// [docs:recall-budget-levels]
budgetLow := hindsight.LOW
// Quick lookup
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "Alice's email",
Budget: &budgetLow,
}).Execute()
// Deep exploration
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "How are Alice and Bob connected?",
Budget: &budgetHigh,
}).Execute()
// [/docs:recall-budget-levels]
// [docs:recall-token-budget]
// Fill up to 4K tokens of context with relevant memories
mt4k := int32(4096)
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What do I know about Alice?",
MaxTokens: &mt4k,
}).Execute()
// Smaller budget for quick lookups
mt500 := int32(500)
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "Alice's email",
MaxTokens: &mt500,
}).Execute()
// [/docs:recall-token-budget]
// [docs:recall-with-tags]
// Filter recall to only memories tagged for a specific user
tagsMatch := "any"
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What feedback did the user give?",
Tags: []string{"user:alice"},
TagsMatch: &tagsMatch,
}).Execute()
// [/docs:recall-with-tags]
// [docs:recall-tags-strict]
// Strict mode: only return memories that have matching tags (exclude untagged)
tagsMatchStrict := "any_strict"
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What did the user say?",
Tags: []string{"user:alice"},
TagsMatch: &tagsMatchStrict,
}).Execute()
// [/docs:recall-tags-strict]
// [docs:recall-tags-all]
// AND matching: require ALL specified tags to be present
tagsMatchAll := "all_strict"
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "What bugs were reported?",
Tags: []string{"user:alice", "bug-report"},
TagsMatch: &tagsMatchAll,
}).Execute()
// [/docs:recall-tags-all]
// [docs:recall-tags-all-mode]
// AND matching, includes untagged memories
tagsMatchAllMode := "all"
client.MemoryAPI.RecallMemories(ctx, "my-bank").
RecallRequest(hindsight.RecallRequest{
Query: "communication tools",
Tags: []string{"user:alice", "team"},
TagsMatch: &tagsMatchAllMode,
}).Execute()
// [/docs:recall-tags-all-mode]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/my-bank", apiURL), nil)
http.DefaultClient.Do(req)
fmt.Println("recall.go: All examples passed")
}

View file

@ -61,6 +61,88 @@ for (const r of detailedResponse.results) {
// [/docs:recall-with-options]
// [docs:recall-world-only]
await client.recall('my-bank', 'query', { types: ['world'] });
// [/docs:recall-world-only]
// [docs:recall-experience-only]
await client.recall('my-bank', 'query', { types: ['experience'] });
// [/docs:recall-experience-only]
// [docs:recall-observations-only]
await client.recall('my-bank', 'query', { types: ['observation'] });
// [/docs:recall-observations-only]
// [docs:recall-token-budget]
// Fill up to 4K tokens of context with relevant memories
await client.recall('my-bank', 'What do I know about Alice?', { maxTokens: 4096 });
// Smaller budget for quick lookups
await client.recall('my-bank', "Alice's email", { maxTokens: 500 });
// [/docs:recall-token-budget]
// [docs:recall-with-tags]
// Filter recall to only memories tagged for a specific user
await client.recall('my-bank', 'What feedback did the user give?', {
tags: ['user:alice']
});
// [/docs:recall-with-tags]
// [docs:recall-tags-strict]
// Strict: only memories that have matching tags (excludes untagged)
await client.recall('my-bank', 'What did the user say?', {
tags: ['user:alice'],
tagsMatch: 'any_strict'
});
// [/docs:recall-tags-strict]
// [docs:recall-tags-all]
// AND matching: require ALL specified tags to be present
await client.recall('my-bank', 'What bugs were reported?', {
tags: ['user:alice', 'bug-report'],
tagsMatch: 'all_strict'
});
// [/docs:recall-tags-all]
// [docs:recall-tags-any]
await client.recall('my-bank', 'communication preferences', {
tags: ['user:alice'],
tagsMatch: 'any'
});
// [/docs:recall-tags-any]
// [docs:recall-tags-any-strict]
await client.recall('my-bank', 'communication preferences', {
tags: ['user:alice'],
tagsMatch: 'any_strict'
});
// [/docs:recall-tags-any-strict]
// [docs:recall-tags-all-mode]
await client.recall('my-bank', 'communication tools', {
tags: ['user:alice', 'team'],
tagsMatch: 'all'
});
// [/docs:recall-tags-all-mode]
// [docs:recall-tags-all-strict]
await client.recall('my-bank', 'communication tools', {
tags: ['user:alice', 'team'],
tagsMatch: 'all_strict'
});
// [/docs:recall-tags-all-strict]
// [docs:recall-source-facts]
// Recall observations and include their source facts
const obsResponse = await client.recall('my-bank', 'What patterns have I learned about Alice?', {

View file

@ -38,6 +38,76 @@ hindsight memory recall my-bank "query" --trace
# [/docs:recall-trace]
# [docs:recall-budget-levels]
# Quick lookup
hindsight memory recall my-bank "Alice's email" --budget low
# Deep exploration
hindsight memory recall my-bank "How are Alice and Bob connected?" --budget high
# [/docs:recall-budget-levels]
# [docs:recall-token-budget]
# Fill up to 4K tokens of context with relevant memories
hindsight memory recall my-bank "What do I know about Alice?" --max-tokens 4096
# Smaller budget for quick lookups
hindsight memory recall my-bank "Alice's email" --max-tokens 500
# [/docs:recall-token-budget]
# [docs:recall-source-facts]
# Recall observations with source facts
hindsight memory recall my-bank "What patterns have I learned about Alice?" \
--fact-type observation
# [/docs:recall-source-facts]
# [docs:recall-with-tags]
# Filter recall to only memories tagged for a specific user
hindsight memory recall my-bank "What feedback did the user give?" \
--tags "user:alice"
# [/docs:recall-with-tags]
# [docs:recall-tags-strict]
# Strict: only memories that have matching tags (excludes untagged)
hindsight memory recall my-bank "What did the user say?" \
--tags "user:alice" --tags-match any_strict
# [/docs:recall-tags-strict]
# [docs:recall-tags-all]
# AND matching: require ALL specified tags to be present
hindsight memory recall my-bank "What bugs were reported?" \
--tags "user:alice,bug-report" --tags-match all_strict
# [/docs:recall-tags-all]
# [docs:recall-tags-any]
hindsight memory recall my-bank "communication preferences" \
--tags "user:alice" --tags-match any
# [/docs:recall-tags-any]
# [docs:recall-tags-any-strict]
hindsight memory recall my-bank "communication preferences" \
--tags "user:alice" --tags-match any_strict
# [/docs:recall-tags-any-strict]
# [docs:recall-tags-all-mode]
hindsight memory recall my-bank "communication tools" \
--tags "user:alice,team" --tags-match all
# [/docs:recall-tags-all-mode]
# [docs:recall-tags-all-strict]
hindsight memory recall my-bank "communication tools" \
--tags "user:alice,team" --tags-match all_strict
# [/docs:recall-tags-all-strict]
# =============================================================================
# Cleanup (not shown in docs)
# =============================================================================

View file

@ -0,0 +1,155 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Setup (not shown in docs)
// =============================================================================
for _, content := range []string{
"Alice works at Google as a software engineer",
"Alice has been working there for 5 years",
"Alice recently got promoted to senior engineer",
} {
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{{Content: content}},
}).Execute()
}
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:reflect-basic]
client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "What should I know about Alice?",
}).Execute()
// [/docs:reflect-basic]
// [docs:reflect-with-params]
budgetMid := hindsight.MID
client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "We're considering a hybrid work policy. What do you think about remote work?",
Budget: &budgetMid,
}).Execute()
// [/docs:reflect-with-params]
// [docs:reflect-with-context]
// Context is passed to the LLM to help it understand the situation
ctxText := "We're in a budget review meeting discussing Q4 spending"
client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "What do you think about the proposal?",
Context: *hindsight.NewNullableString(&ctxText),
}).Execute()
// [/docs:reflect-with-context]
// [docs:reflect-disposition]
// Create a bank with specific disposition
skepticism := int32(5)
literalism := int32(4)
empathy := int32(2)
mission := "I am a risk-aware financial advisor"
client.BanksAPI.CreateOrUpdateBank(ctx, "cautious-advisor").
CreateBankRequest(hindsight.CreateBankRequest{
Name: *hindsight.NewNullableString(hindsight.PtrString("Cautious Advisor")),
ReflectMission: *hindsight.NewNullableString(&mission),
DispositionSkepticism: *hindsight.NewNullableInt32(&skepticism),
DispositionLiteralism: *hindsight.NewNullableInt32(&literalism),
DispositionEmpathy: *hindsight.NewNullableInt32(&empathy),
}).Execute()
// Reflect responses will reflect this disposition
client.MemoryAPI.Reflect(ctx, "cautious-advisor").
ReflectRequest(hindsight.ReflectRequest{
Query: "Should I invest in crypto?",
}).Execute()
// Response will likely emphasize risks and caution
// [/docs:reflect-disposition]
// [docs:reflect-sources]
// include.facts enables the based_on field in the response
sourcesResponse, _, _ := client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "Tell me about Alice",
Include: &hindsight.ReflectIncludeOptions{
Facts: map[string]interface{}{}, // empty map enables fact inclusion
},
}).Execute()
fmt.Println("Response:", sourcesResponse.GetText())
fmt.Println("\nBased on:")
if basedOn := sourcesResponse.GetBasedOn(); basedOn.Memories != nil {
for _, fact := range basedOn.GetMemories() {
fmt.Printf(" - [%s] %s\n", fact.GetType(), fact.GetText())
}
}
// [/docs:reflect-sources]
// [docs:reflect-with-tags]
// Filter reflection to only consider memories for a specific user
tagsMatch := "any_strict"
client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "What does this user think about our product?",
Tags: []string{"user:alice"},
TagsMatch: &tagsMatch,
}).Execute()
// [/docs:reflect-with-tags]
// [docs:reflect-structured-output]
// Define JSON schema for structured output
responseSchema := map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"recommendation": map[string]interface{}{"type": "string"},
"confidence": map[string]interface{}{"type": "string", "enum": []string{"low", "medium", "high"}},
"key_factors": map[string]interface{}{"type": "array", "items": map[string]interface{}{"type": "string"}},
"risks": map[string]interface{}{"type": "array", "items": map[string]interface{}{"type": "string"}},
},
"required": []string{"recommendation", "confidence", "key_factors"},
}
structuredResponse, _, _ := client.MemoryAPI.Reflect(ctx, "my-bank").
ReflectRequest(hindsight.ReflectRequest{
Query: "Should we hire Alice for the ML team lead position?",
ResponseSchema: responseSchema,
}).Execute()
// Access structured output
if out := structuredResponse.GetStructuredOutput(); out != nil {
fmt.Println("Recommendation:", out["recommendation"])
fmt.Println("Key factors:", out["key_factors"])
}
// [/docs:reflect-structured-output]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
for _, bankID := range []string{"my-bank", "cautious-advisor"} {
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/%s", apiURL, bankID), nil)
http.DefaultClient.Do(req)
}
fmt.Println("reflect.go: All examples passed")
}

View file

@ -60,16 +60,27 @@ const advisorResponse = await client.reflect('cautious-advisor', 'Should I inves
// [docs:reflect-sources]
const sourcesResponse = await client.reflect('my-bank', 'Tell me about Alice');
const sourcesResponse = await client.reflect('my-bank', 'Tell me about Alice', {
includeFacts: true
});
console.log('Response:', sourcesResponse.text);
console.log('\nBased on:');
for (const fact of sourcesResponse.based_on || []) {
for (const fact of (sourcesResponse.based_on?.memories || [])) {
console.log(` - [${fact.type}] ${fact.text}`);
}
// [/docs:reflect-sources]
// [docs:reflect-with-tags]
// Filter reflect to only use memories tagged for a specific user
await client.reflect('my-bank', 'What feedback did the user give?', {
tags: ['user:alice'],
tagsMatch: 'any_strict'
});
// [/docs:reflect-with-tags]
// [docs:reflect-structured-output]
// Define JSON schema directly
const responseSchema = {

View file

@ -26,9 +26,29 @@ hindsight memory reflect my-bank "Should I learn Python?" --context "career advi
# [/docs:reflect-with-context]
# [docs:reflect-high-budget]
hindsight memory reflect my-bank "Summarize my week" --budget high
# [/docs:reflect-high-budget]
# [docs:reflect-with-params]
hindsight memory reflect my-bank "Summarize my week" --budget high --max-tokens 8192
# [/docs:reflect-with-params]
# [docs:reflect-disposition]
hindsight bank set-config my-bank \
--disposition-skepticism 5 \
--disposition-literalism 4 \
--disposition-empathy 2
hindsight memory reflect my-bank "Should I invest in crypto?"
# [/docs:reflect-disposition]
# [docs:reflect-sources]
hindsight memory reflect my-bank "Tell me about Alice" --include-facts
# [/docs:reflect-sources]
# [docs:reflect-with-tags]
hindsight memory reflect my-bank "What feedback did the user give?" \
--tags "user:alice" --tags-match any_strict
# [/docs:reflect-with-tags]
# [docs:reflect-structured-output]

View file

@ -0,0 +1,137 @@
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
hindsight "github.com/vectorize-io/hindsight/hindsight-clients/go"
)
func main() {
apiURL := os.Getenv("HINDSIGHT_API_URL")
if apiURL == "" {
apiURL = "http://localhost:8888"
}
cfg := hindsight.NewConfiguration()
cfg.Servers = hindsight.ServerConfigurations{{URL: apiURL}}
client := hindsight.NewAPIClient(cfg)
ctx := context.Background()
// =============================================================================
// Doc Examples
// =============================================================================
// [docs:retain-basic]
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{Content: "Alice works at Google as a software engineer"},
},
}).Execute()
// [/docs:retain-basic]
// [docs:retain-conversation]
// Retain an entire conversation as a single document.
conversation := "Alice (2024-03-15T09:00:00Z): Hi Bob! Did you end up going to the doctor last week?\n" +
"Bob (2024-03-15T09:01:00Z): Yes, finally. Turns out I have a mild peanut allergy.\n" +
"Alice (2024-03-15T09:02:00Z): Oh no! Are you okay?\n" +
"Bob (2024-03-15T09:03:00Z): Yeah, nothing serious. Just need to carry an antihistamine.\n" +
"Alice (2024-03-15T09:04:00Z): Good to know. We'll avoid peanuts at the team lunch."
docID := "chat-2024-03-15-alice-bob"
context_ := "team chat"
ts := "2024-03-15T09:04:00Z"
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{
Content: conversation,
Context: *hindsight.NewNullableString(&context_),
DocumentId: *hindsight.NewNullableString(&docID),
Timestamp: *hindsight.NewNullableTimestamp(&hindsight.Timestamp{
String: &ts,
}),
},
},
}).Execute()
// [/docs:retain-conversation]
// [docs:retain-with-context]
ctxLabel := "career update"
ts2 := "2024-03-15T10:00:00Z"
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{
Content: "Alice got promoted to senior engineer",
Context: *hindsight.NewNullableString(&ctxLabel),
Timestamp: *hindsight.NewNullableTimestamp(&hindsight.Timestamp{
String: &ts2,
}),
},
},
}).Execute()
// [/docs:retain-with-context]
// [docs:retain-batch]
doc1 := "conversation_001_msg_1"
doc2 := "conversation_001_msg_2"
doc3 := "conversation_001_msg_3"
ctx1 := "career"
ctx2 := "relationship"
client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{Content: "Alice works at Google", Context: *hindsight.NewNullableString(&ctx1), DocumentId: *hindsight.NewNullableString(&doc1)},
{Content: "Bob is a data scientist at Meta", Context: *hindsight.NewNullableString(&ctx1), DocumentId: *hindsight.NewNullableString(&doc2)},
{Content: "Alice and Bob are friends", Context: *hindsight.NewNullableString(&ctx2), DocumentId: *hindsight.NewNullableString(&doc3)},
},
}).Execute()
// [/docs:retain-batch]
// [docs:retain-async]
// Start async ingestion (returns immediately)
asyncTrue := true
largeDoc1 := "large-doc-1"
largeDoc2 := "large-doc-2"
retainResp, _, _ := client.MemoryAPI.RetainMemories(ctx, "my-bank").
RetainRequest(hindsight.RetainRequest{
Items: []hindsight.MemoryItem{
{Content: "Large batch item 1", DocumentId: *hindsight.NewNullableString(&largeDoc1)},
{Content: "Large batch item 2", DocumentId: *hindsight.NewNullableString(&largeDoc2)},
},
Async: &asyncTrue,
}).Execute()
// Check if it was processed asynchronously
fmt.Println("Async:", retainResp.GetAsync())
// [/docs:retain-async]
// [docs:retain-files]
// Open a file and upload it — Hindsight converts it to text and extracts memories.
// Supports: PDF, DOCX, PPTX, XLSX, images (OCR), audio (transcription), and text formats.
f, err := os.Open("../../hindsight-docs/examples/api/sample.pdf")
if err != nil {
log.Fatalf("Failed to open file: %v", err)
}
defer f.Close()
fileResp, _, _ := client.FilesAPI.FileRetain(ctx, "my-bank").
Files([]*os.File{f}).
Request(`{"files_metadata": [{"context": "quarterly report"}]}`).
Execute()
fmt.Println("Operation IDs:", fileResp.GetOperationIds()) // Track processing via the operations endpoint
// [/docs:retain-files]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================
req, _ := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/default/banks/my-bank", apiURL), nil)
http.DefaultClient.Do(req)
fmt.Println("retain.go: All examples passed")
}

View file

@ -85,6 +85,21 @@ console.log(result.operation_ids); // Track processing via the operations endpo
// [/docs:retain-files]
// [docs:retain-files-batch]
// Upload multiple files with per-file metadata (up to 10 files per request)
const batchResult = await client.retainFiles('my-bank', [
new File([pdfBytes], 'report.pdf'),
new File([pdfBytes], 'notes.pdf'),
], {
filesMetadata: [
{ context: 'quarterly report', document_id: 'q1-report', tags: ['project:alpha'] },
{ context: 'meeting notes', document_id: 'q1-notes', tags: ['project:alpha'] },
]
});
console.log(batchResult.operation_ids); // One operation ID per file
// [/docs:retain-files-batch]
// =============================================================================
// Cleanup (not shown in docs)
// =============================================================================

View file

@ -25,12 +25,37 @@ hindsight memory retain my-bank "Alice works at Google as a software engineer"
# [/docs:retain-basic]
# [docs:retain-conversation]
# Retain an entire conversation as a single document.
CONVERSATION="Alice (2024-03-15T09:00:00Z): Hi Bob! Did you end up going to the doctor last week?
Bob (2024-03-15T09:01:00Z): Yes, finally. Turns out I have a mild peanut allergy.
Alice (2024-03-15T09:02:00Z): Oh no! Are you okay?
Bob (2024-03-15T09:03:00Z): Yeah, nothing serious. Just need to carry an antihistamine.
Alice (2024-03-15T09:04:00Z): Good to know. We'll avoid peanuts at the team lunch."
hindsight memory retain my-bank "$CONVERSATION" \
--context "team chat" \
--doc-id "chat-2024-03-15-alice-bob"
# [/docs:retain-conversation]
# [docs:retain-with-context]
hindsight memory retain my-bank "Alice got promoted" \
--context "career update"
# [/docs:retain-with-context]
# [docs:retain-batch]
# Batch ingestion via individual retain calls (CLI processes items one at a time)
hindsight memory retain my-bank "Alice works at Google" \
--context "career" --doc-id "conversation_001_msg_1"
hindsight memory retain my-bank "Bob is a data scientist at Meta" \
--context "career" --doc-id "conversation_001_msg_2"
hindsight memory retain my-bank "Alice and Bob are friends" \
--context "relationship" --doc-id "conversation_001_msg_3"
# [/docs:retain-batch]
# [docs:retain-async]
hindsight memory retain my-bank "Meeting notes" --async
# [/docs:retain-async]

View file

@ -5,7 +5,7 @@
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"build": "node scripts/check-code-parity.mjs && docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",

View file

@ -0,0 +1,128 @@
#!/usr/bin/env node
/**
* Validates that every "language" Tabs block in MDX docs has all 4 required variants:
* Python, Node.js, CLI, Go.
*
* A Tabs block is considered a "language" block if it contains at least one TabItem
* with value "python", "node", "cli", or "go".
*
* Run: node scripts/check-code-parity.mjs
*/
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const docsRoot = join(__dirname, '..');
const REQUIRED_TABS = new Set(['python', 'node', 'cli', 'go']);
const IGNORED_PATHS = [
'node_modules',
'build',
'.docusaurus',
'versioned_docs', // skip versioned docs
];
/**
* Recursively find all .mdx files under a directory.
*/
function findMdxFiles(dir) {
const results = [];
for (const entry of readdirSync(dir)) {
if (IGNORED_PATHS.includes(entry)) continue;
const full = join(dir, entry);
const stat = statSync(full);
if (stat.isDirectory()) {
results.push(...findMdxFiles(full));
} else if (entry.endsWith('.mdx') || entry.endsWith('.md')) {
results.push(full);
}
}
return results;
}
/**
* Parse a single MDX file and return all violations.
* A violation is a Tabs block that has at least one language tab but is missing
* one or more of the 4 required language variants.
*/
function checkFile(filePath) {
const content = readFileSync(filePath, 'utf8');
const violations = [];
// Split content into Tabs blocks.
// Strategy: find <Tabs> ... </Tabs> sections and scan for TabItem values.
// We use a simple line-by-line state machine.
const lines = content.split('\n');
let inTabs = false;
let tabsStartLine = -1;
let currentTabValues = new Set();
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!inTabs) {
// Look for opening <Tabs> tag (not <TabItem>)
if (/^\s*<Tabs[\s>]/.test(line) && !/<\/Tabs/.test(line)) {
inTabs = true;
tabsStartLine = i + 1; // 1-indexed
currentTabValues = new Set();
}
} else {
// Inside a Tabs block — look for </Tabs> or nested TabItem values
if (/^\s*<\/Tabs\s*>/.test(line)) {
// End of Tabs block — check if it's a language block
const hasLanguageTab = [...currentTabValues].some(v => REQUIRED_TABS.has(v));
if (hasLanguageTab) {
const missing = [...REQUIRED_TABS].filter(t => !currentTabValues.has(t));
if (missing.length > 0) {
violations.push({
line: tabsStartLine,
found: [...currentTabValues].filter(v => REQUIRED_TABS.has(v)),
missing,
});
}
}
inTabs = false;
currentTabValues = new Set();
} else {
// Look for TabItem value attributes
// Matches: <TabItem value="python" or <TabItem value='cli'
const match = line.match(/TabItem[^>]*value=["']([^"']+)["']/);
if (match) {
currentTabValues.add(match[1]);
}
}
}
}
return violations;
}
// ─── Main ────────────────────────────────────────────────────────────────────
const mdxFiles = findMdxFiles(docsRoot);
let totalViolations = 0;
for (const filePath of mdxFiles) {
const violations = checkFile(filePath);
if (violations.length > 0) {
const rel = relative(docsRoot, filePath);
for (const v of violations) {
console.error(
`[code-parity] ${rel}:${v.line} — Tabs block missing language tabs: ${v.missing.join(', ')} (found: ${v.found.join(', ')})`
);
}
totalViolations += violations.length;
}
}
if (totalViolations > 0) {
console.error(`\n[code-parity] ❌ Found ${totalViolations} Tabs block(s) missing required language variants.`);
console.error('[code-parity] Every Tabs block with language tabs must include: python, node, cli, go');
process.exit(1);
} else {
console.log(`[code-parity] ✅ All ${mdxFiles.length} docs files pass 4-tab parity check.`);
}

4
package-lock.json generated
View file

@ -13,7 +13,7 @@
},
"hindsight-clients/typescript": {
"name": "@vectorize-io/hindsight-client",
"version": "0.4.18",
"version": "0.4.19",
"license": "MIT",
"devDependencies": {
"@hey-api/openapi-ts": "0.88.0",
@ -293,7 +293,7 @@
},
"hindsight-control-plane": {
"name": "@vectorize-io/hindsight-control-plane",
"version": "0.4.18",
"version": "0.4.19",
"license": "ISC",
"dependencies": {
"@radix-ui/react-alert-dialog": "^1.1.15",