* feat(retain): add verbatim extraction mode Adds retain_extraction_mode="verbatim" that stores each chunk as-is without LLM summarization. The LLM still runs to extract entities, temporal info, and location for full indexability — only the fact text is replaced with the original chunk content (one memory per chunk). Useful for RAG-style indexing and benchmarks where original text must be preserved in memory. - Add "verbatim" to RETAIN_EXTRACTION_MODES in config.py - Add VERBATIM_FACT_EXTRACTION_PROMPT with instructions to preserve text - Add _collapse_to_verbatim() post-processing to enforce 1 fact/chunk - Expose in bank config UI dropdown with updated description - Update configuration.md docs with verbatim mode description - Add unit test for _collapse_to_verbatim and integration test via LLM - Fix pre-existing main.py CLI override missing new reranker fields - Fix pre-existing cross_encoder.py ty type error via setattr * refactor(retain): verbatim mode skips 'what' field entirely Instead of asking the LLM to echo the chunk text back into 'what' and then discarding it, verbatim mode now uses a dedicated schema (VerbatimExtractedFact) that omits the 'what' field altogether. The LLM only returns metadata (entities, temporal info, location, who), saving output tokens and avoiding any risk of paraphrasing before the backfill. - Add VerbatimExtractedFact / VerbatimFactExtractionResponse models - Verbatim mode skips causal-relations section (nothing to relate causally) - _extract_facts_from_chunk: allow missing 'what' in verbatim mode, set combined_text="" (backfilled by _collapse_to_verbatim) - Update verbatim prompt to say DO NOT include 'what' * feat(retain): add index_only extraction mode Zero-LLM retain mode: chunks are stored as-is with no LLM call, no entity extraction, and no temporal indexing. Embeddings still run for semantic search. User-provided entities via RetainContent.entities are the sole source of entity data. Early return placed before the batch-API check so no LLM queue or concurrency locks are acquired. - Add "index_only" to RETAIN_EXTRACTION_MODES - Add _extract_facts_index_only() with pure Python chunking path - Add to UI dropdown and update description - Update configuration.md with index_only docs and table entry - Add unit test asserting zero token usage and exact text preservation * feat(retain): add named retain strategies Allows mixing extraction modes in a single bank via named strategies. Each strategy is a set of hierarchical config overrides (extraction_mode, chunk_size, entity_labels, entities_allow_free_form, etc.) applied on top of the resolved bank config at retain time. - retain_strategies: dict of strategy_name → config overrides (bank config) - retain_default_strategy: default strategy when none specified (bank config) - strategy field on /retain request: per-call override - apply_strategy() in config_resolver applies overrides via dataclasses.replace() - strategy propagates through retain_batch_async → _retain_batch_async_internal and through the async worker task payload - Any hierarchical field is overridable per strategy, including entity_labels and entities_allow_free_form - Docs updated with strategy configuration example and RRF fairness note - Unit test for apply_strategy covering overrides, unknown strategy, and non-hierarchical field filtering * feat(retain): add per-item strategy and strategy tests - Add `strategy` field to `MemoryItem` so individual items in a retain request can override the request-level strategy - Add `strategy` field to `FileRetainMetadata` for per-file strategy override in file retain requests - Group memory items by effective strategy in `api_retain`; each group is processed as a separate batch, results are aggregated - Thread strategy through `submit_async_file_retain` → `_handle_file_convert_retain` → retain task payload - Add `operation_ids` to `RetainResponse` for async requests with mixed per-item strategies - Add `test_strategy_overrides_extraction_mode_for_index_only`: unit test verifying a named strategy with index_only bypasses the LLM - Add `test_retain_request_per_item_strategy_field`: unit test for per-item strategy grouping logic * feat(ui): add retain strategies and default strategy to bank config UI - Add StrategiesEditor component: per-strategy cards with name input and JSON overrides textarea; supports add/remove; validates JSON inline - Add Default Strategy text input (retain_default_strategy) - Update RetainEdits type and retainSlice() to include both new fields - Regenerate OpenAPI spec (retain_strategies, retain_default_strategy, per-item strategy on MemoryItem/FileRetainMetadata, operation_ids on RetainResponse) * refactor(ui): move retain strategies into its own dedicated config section * feat(ui): improve retain strategies UX and add strategy to document dialog - Strategy form now includes entity section (free form toggle + entity labels editor) - Default strategy selector moved outside tab panel, above strategy chips - Strategy tabs redesigned with underline indicator style for clarity - Remove strategy confirms with AlertDialog - Fix tab re-render bug when typing strategy name (skipSyncRef) - Add strategy field to Add New Document dialog (text + per-file for uploads) - File upload collapsible uses same Document/Tags/Source tabbed layout - API: validate empty strategy names in config_resolver - api.ts: add strategy field to retain and uploadFiles types * fix: forward strategy through HTTP layer and SDK; add integration test - route.ts: extract and forward `strategy` from request body to retainBatch - TypeScript SDK: accept and forward `strategy` in retainBatch options and per-item - config_resolver.py: validate empty strategy name keys on update - bank-config-view.tsx: merge entity fields into RetainStrategyForm, redesign strategy tabs with underline style, add confirmation dialog for removal, fix tab-reset-on-typing with skipSyncRef, move default strategy selector outside panel - bank-selector.tsx: add strategy field to Add Document dialog (per-file in tabbed collapsible) - test_retain.py: add end-to-end integration test verifying named strategy application (index_only = 0 LLM tokens) * fix: regenerate TypeScript client with strategy field in RetainRequest/MemoryItem - Regenerate OpenAPI spec to include strategy field in RetainRequest and MemoryItem - Regenerate TypeScript client from updated spec - Add strategy to MemoryItemInput interface - Remove (item as any) cast now that strategy is properly typed * rename: index_only extraction mode → chunks * remove top-level strategy from RetainRequest; strategy is per-item only * fix(clients): update Go and Python generated clients with strategy/operation_ids fields * fix(ci): update hierarchical field count, add strategy to Rust MemoryItem initializers * fix(go-client): minimal targeted YAML updates for strategy/operation_ids fields |
||
|---|---|---|
| .. | ||
| src | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| INTEGRATION.md | ||
| README.md | ||
Hindsight Rust Client
Auto-generated Rust client library for the Hindsight semantic memory system API.
Features
- 🦀 Fully typed - Complete type safety with Rust's type system
- 🔄 Auto-generated - Stays in sync with the OpenAPI spec automatically
- ⚡ Async/await - Built on tokio and reqwest for modern async Rust
- 📦 Standalone - Can be published to crates.io independently
Installation
Add to your Cargo.toml:
[dependencies]
hindsight-client = "0.1.0"
tokio = { version = "1", features = ["full"] }
Quick Start
use hindsight_client::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a client
let client = Client::new("http://localhost:8888");
// List all agents
let agents = client.list_agents().await?;
for agent in agents {
println!("Agent: {} - {}", agent.agent_id, agent.name);
}
// Get agent profile
let profile = client.get_agent_profile("my-agent").await?;
println!("Background: {}", profile.background);
// Search memories
let search_request = hindsight_client::types::SearchRequest {
query: "What did I learn today?".to_string(),
fact_type: None,
thinking_budget: Some(100),
max_tokens: Some(4096),
trace: Some(false),
};
let results = client.search_memories("my-agent", &search_request).await?;
for result in results.results {
println!("- {}", result.text);
}
// Store a memory
let memory_request = hindsight_client::types::BatchMemoryRequest {
items: vec![
hindsight_client::types::MemoryItem {
content: "I learned about Rust today".to_string(),
context: Some("Daily learning".to_string()),
}
],
document_id: Some("my-doc".to_string()),
};
client.batch_put_memories("my-agent", &memory_request).await?;
Ok(())
}
How It Works
This library uses progenitor to generate the client code from the OpenAPI specification at build time.
The generation happens automatically when you run cargo build, so the client always stays in sync with the API schema.
Build Process
build.rsreads the OpenAPI spec from../../openapi.json- Converts OpenAPI 3.1 → 3.0 (for progenitor compatibility)
- Generates Rust client code using progenitor
- Code is included in the library via
include!()macro
API Methods
All API endpoints are available as async methods on the Client struct:
Agent Management
list_agents()- List all agentscreate_or_update_agent()- Create or update an agentget_agent_profile()- Get agent profile with personalityupdate_agent_personality()- Update agent personality traitsadd_agent_background()- Add/merge agent backgroundget_agent_stats()- Get memory statistics
Memory Operations
search_memories()- Semantic search across memoriesthink()- Generate contextual answers using agent identitybatch_put_memories()- Store multiple memoriesbatch_put_async()- Queue memories for background processinglist_memories()- List memory units with paginationdelete_memory_unit()- Delete a specific memoryclear_agent_memories()- Clear all or filtered memories
Document Management
list_documents()- List documents with optional searchget_document()- Get document details and contentdelete_document()- Delete document and its memories
Operations (Async Tasks)
list_operations()- List async operationscancel_operation()- Cancel a pending operation
Visualization
get_graph()- Get memory graph data for visualization
Error Handling
The client uses progenitor_client::Error for all errors:
match client.get_agent_profile("my-agent").await {
Ok(profile) => println!("Got profile: {}", profile.name),
Err(progenitor_client::Error::ErrorResponse(resp)) => {
println!("API error: {} - {}", resp.status, resp.body);
}
Err(e) => println!("Other error: {}", e),
}
Development
Building
cargo build
The OpenAPI spec is automatically converted and the client is generated during build.
Testing
cargo test
Releasing
This client can be published to crates.io independently of the CLI:
cargo publish
Architecture
hindsight-clients/rust/
├── Cargo.toml # Package definition
├── build.rs # Build script (generates client)
├── src/
│ └── lib.rs # Library entry point
└── target/
└── debug/build/
└── hindsight-client-.../out/
└── hindsight_client_generated.rs # Generated code
License
MIT