Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17accfc961 | ||
|
|
c1377c5e1e | ||
|
|
04187ddbc4 | ||
|
|
61e81c81d1 | ||
|
|
3c6850ca92 |
@@ -74,7 +74,7 @@ from hindsight_api.config import get_config
|
||||
from hindsight_api.engine.db_utils import acquire_with_retry
|
||||
from hindsight_api.engine.memory_engine import Budget, _get_tiktoken_encoding, fq_table
|
||||
from hindsight_api.engine.reflect.observations import Observation
|
||||
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES, TokenUsage
|
||||
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES, MemoryFact, TokenUsage
|
||||
from hindsight_api.engine.search.tags import TagsMatch
|
||||
from hindsight_api.extensions import HttpExtension, OperationValidationError, load_extension
|
||||
from hindsight_api.metrics import create_metrics_collector, get_metrics_collector, initialize_metrics
|
||||
@@ -97,6 +97,12 @@ class ChunkIncludeOptions(BaseModel):
|
||||
max_tokens: int = Field(default=8192, description="Maximum tokens for chunks (chunks may be truncated)")
|
||||
|
||||
|
||||
class SourceFactsIncludeOptions(BaseModel):
|
||||
"""Options for including source facts for observation-type results."""
|
||||
|
||||
max_tokens: int = Field(default=4096, description="Maximum tokens for source facts")
|
||||
|
||||
|
||||
class IncludeOptions(BaseModel):
|
||||
"""Options for including additional data in recall results."""
|
||||
|
||||
@@ -107,6 +113,10 @@ class IncludeOptions(BaseModel):
|
||||
chunks: ChunkIncludeOptions | None = Field(
|
||||
default=None, description="Include raw chunks. Set to {} to enable, null to disable (default: disabled)."
|
||||
)
|
||||
source_facts: SourceFactsIncludeOptions | None = Field(
|
||||
default=None,
|
||||
description="Include source facts for observation-type results. Set to {} to enable, null to disable (default: disabled).",
|
||||
)
|
||||
|
||||
|
||||
class RecallRequest(BaseModel):
|
||||
@@ -189,6 +199,9 @@ class RecallResult(BaseModel):
|
||||
metadata: dict[str, str] | None = None # User-defined metadata
|
||||
chunk_id: str | None = None # Chunk this fact was extracted from
|
||||
tags: list[str] | None = None # Visibility scope tags
|
||||
source_fact_ids: list[str] | None = (
|
||||
None # IDs of source facts (observation type only, when source_facts is enabled)
|
||||
)
|
||||
|
||||
|
||||
class EntityObservationResponse(BaseModel):
|
||||
@@ -340,6 +353,9 @@ class RecallResponse(BaseModel):
|
||||
default=None, description="Entity states for entities mentioned in results"
|
||||
)
|
||||
chunks: dict[str, ChunkData] | None = Field(default=None, description="Chunks for facts, keyed by chunk_id")
|
||||
source_facts: dict[str, RecallResult] | None = Field(
|
||||
default=None, description="Source facts for observation-type results, keyed by fact ID"
|
||||
)
|
||||
|
||||
|
||||
class EntityInput(BaseModel):
|
||||
@@ -1959,6 +1975,10 @@ def _register_routes(app: FastAPI):
|
||||
include_chunks = request.include.chunks is not None
|
||||
max_chunk_tokens = request.include.chunks.max_tokens if include_chunks else 8192
|
||||
|
||||
# Determine source facts inclusion settings
|
||||
include_source_facts = request.include.source_facts is not None
|
||||
max_source_facts_tokens = request.include.source_facts.max_tokens if include_source_facts else 4096
|
||||
|
||||
pre_recall = time.time() - handler_start
|
||||
# Run recall with tracing (record metrics)
|
||||
with metrics.record_operation(
|
||||
@@ -1977,14 +1997,16 @@ def _register_routes(app: FastAPI):
|
||||
max_entity_tokens=max_entity_tokens,
|
||||
include_chunks=include_chunks,
|
||||
max_chunk_tokens=max_chunk_tokens,
|
||||
include_source_facts=include_source_facts,
|
||||
max_source_facts_tokens=max_source_facts_tokens,
|
||||
request_context=request_context,
|
||||
tags=request.tags,
|
||||
tags_match=request.tags_match,
|
||||
)
|
||||
|
||||
# Convert core MemoryFact objects to API RecallResult objects (excluding internal metrics)
|
||||
recall_results = [
|
||||
RecallResult(
|
||||
def _fact_to_result(fact: "MemoryFact") -> RecallResult:
|
||||
return RecallResult(
|
||||
id=fact.id,
|
||||
text=fact.text,
|
||||
type=fact.fact_type,
|
||||
@@ -1996,9 +2018,10 @@ def _register_routes(app: FastAPI):
|
||||
document_id=fact.document_id,
|
||||
chunk_id=fact.chunk_id,
|
||||
tags=fact.tags,
|
||||
source_fact_ids=fact.source_fact_ids,
|
||||
)
|
||||
for fact in core_result.results
|
||||
]
|
||||
|
||||
recall_results = [_fact_to_result(fact) for fact in core_result.results]
|
||||
|
||||
# Convert chunks from engine to HTTP API format
|
||||
chunks_response = None
|
||||
@@ -2026,11 +2049,19 @@ def _register_routes(app: FastAPI):
|
||||
],
|
||||
)
|
||||
|
||||
# Convert source facts dict to API format
|
||||
source_facts_response = None
|
||||
if core_result.source_facts:
|
||||
source_facts_response = {
|
||||
fact_id: _fact_to_result(fact) for fact_id, fact in core_result.source_facts.items()
|
||||
}
|
||||
|
||||
response = RecallResponse(
|
||||
results=recall_results,
|
||||
trace=core_result.trace,
|
||||
entities=entities_response,
|
||||
chunks=chunks_response,
|
||||
source_facts=source_facts_response,
|
||||
)
|
||||
|
||||
handler_duration = time.time() - handler_start
|
||||
|
||||
@@ -2020,6 +2020,8 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
max_entity_tokens: int = 500,
|
||||
include_chunks: bool = False,
|
||||
max_chunk_tokens: int = 8192,
|
||||
include_source_facts: bool = False,
|
||||
max_source_facts_tokens: int = 4096,
|
||||
request_context: "RequestContext",
|
||||
tags: list[str] | None = None,
|
||||
tags_match: TagsMatch = "any",
|
||||
@@ -2159,6 +2161,8 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
tags_match=tags_match,
|
||||
connection_budget=_connection_budget,
|
||||
quiet=_quiet,
|
||||
include_source_facts=include_source_facts,
|
||||
max_source_facts_tokens=max_source_facts_tokens,
|
||||
)
|
||||
break # Success - exit retry loop
|
||||
except Exception as e:
|
||||
@@ -2283,6 +2287,8 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
tags_match: TagsMatch = "any",
|
||||
connection_budget: int | None = None,
|
||||
quiet: bool = False,
|
||||
include_source_facts: bool = False,
|
||||
max_source_facts_tokens: int = 4096,
|
||||
) -> RecallResultModel:
|
||||
"""
|
||||
Search implementation with modular retrieval and reranking.
|
||||
@@ -2879,6 +2885,74 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
)
|
||||
top_results_dicts.append(result_dict)
|
||||
|
||||
# Fetch source facts for observation-type results (mirrors chunks pattern)
|
||||
source_fact_ids_by_obs: dict[str, list[str]] = {} # obs_id -> [source_id, ...]
|
||||
source_facts_dict: dict[str, MemoryFact] | None = None
|
||||
if include_source_facts:
|
||||
observation_ids = [uuid.UUID(sr.id) for sr in top_scored if sr.retrieval.fact_type == "observation"]
|
||||
if observation_ids:
|
||||
async with acquire_with_retry(pool) as sf_conn:
|
||||
# Fetch source_memory_ids for all observation results
|
||||
obs_rows = await sf_conn.fetch(
|
||||
f"""
|
||||
SELECT id, source_memory_ids
|
||||
FROM {fq_table("memory_units")}
|
||||
WHERE id = ANY($1::uuid[]) AND fact_type = 'observation'
|
||||
""",
|
||||
observation_ids,
|
||||
)
|
||||
|
||||
# Collect unique source IDs in order of first appearance
|
||||
seen_source_ids: set[str] = set()
|
||||
source_ids_ordered: list[str] = []
|
||||
for obs_row in obs_rows:
|
||||
obs_id = str(obs_row["id"])
|
||||
sids = [str(s) for s in (obs_row["source_memory_ids"] or [])]
|
||||
source_fact_ids_by_obs[obs_id] = sids
|
||||
for sid in sids:
|
||||
if sid not in seen_source_ids:
|
||||
source_ids_ordered.append(sid)
|
||||
seen_source_ids.add(sid)
|
||||
|
||||
# Fetch source fact content up to token budget
|
||||
if source_ids_ordered:
|
||||
import uuid as uuid_module
|
||||
|
||||
source_rows = await sf_conn.fetch(
|
||||
f"""
|
||||
SELECT id, text, fact_type, context, occurred_start, occurred_end,
|
||||
mentioned_at, document_id, chunk_id, tags
|
||||
FROM {fq_table("memory_units")}
|
||||
WHERE id = ANY($1::uuid[])
|
||||
""",
|
||||
[uuid_module.UUID(sid) for sid in source_ids_ordered],
|
||||
)
|
||||
source_row_by_id = {str(r["id"]): r for r in source_rows}
|
||||
|
||||
encoding = _get_tiktoken_encoding()
|
||||
source_facts_dict = {}
|
||||
total_source_tokens = 0
|
||||
for sid in source_ids_ordered:
|
||||
if sid not in source_row_by_id:
|
||||
continue
|
||||
r = source_row_by_id[sid]
|
||||
fact_tokens = len(encoding.encode(r["text"]))
|
||||
if total_source_tokens + fact_tokens > max_source_facts_tokens:
|
||||
break
|
||||
source_facts_dict[sid] = MemoryFact(
|
||||
id=sid,
|
||||
text=r["text"],
|
||||
fact_type=r["fact_type"],
|
||||
context=r["context"],
|
||||
occurred_start=r["occurred_start"].isoformat() if r["occurred_start"] else None,
|
||||
occurred_end=r["occurred_end"].isoformat() if r["occurred_end"] else None,
|
||||
mentioned_at=r["mentioned_at"].isoformat() if r["mentioned_at"] else None,
|
||||
document_id=r["document_id"],
|
||||
chunk_id=str(r["chunk_id"]) if r["chunk_id"] else None,
|
||||
tags=r["tags"] or None,
|
||||
)
|
||||
total_source_tokens += fact_tokens
|
||||
|
||||
# Get entities for each fact if include_entities is requested
|
||||
fact_entity_map = {} # unit_id -> list of (entity_id, entity_name)
|
||||
if include_entities and top_scored:
|
||||
@@ -2924,6 +2998,7 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
document_id=result_dict.get("document_id"),
|
||||
chunk_id=result_dict.get("chunk_id"),
|
||||
tags=result_dict.get("tags"),
|
||||
source_fact_ids=source_fact_ids_by_obs.get(result_id) if include_source_facts else None,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -2977,7 +3052,13 @@ class MemoryEngine(MemoryEngineInterface):
|
||||
if not quiet:
|
||||
logger.info("\n" + "\n".join(log_buffer))
|
||||
|
||||
return RecallResultModel(results=memory_facts, trace=trace_dict, entities=entities_dict, chunks=chunks_dict)
|
||||
return RecallResultModel(
|
||||
results=memory_facts,
|
||||
trace=trace_dict,
|
||||
entities=entities_dict,
|
||||
chunks=chunks_dict,
|
||||
source_facts=source_facts_dict,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
log_buffer.append(f"[RECALL {recall_id}] ERROR after {time.time() - recall_start:.3f}s: {str(e)}")
|
||||
|
||||
@@ -159,6 +159,10 @@ class MemoryFact(BaseModel):
|
||||
None, description="ID of the chunk this fact was extracted from (format: bank_id_document_id_chunk_index)"
|
||||
)
|
||||
tags: list[str] | None = Field(None, description="Visibility scope tags associated with this fact")
|
||||
source_fact_ids: list[str] | None = Field(
|
||||
None,
|
||||
description="IDs of source facts this observation was derived from (observation type only, when source_facts is enabled)",
|
||||
)
|
||||
|
||||
|
||||
class ChunkInfo(BaseModel):
|
||||
@@ -226,6 +230,9 @@ class RecallResult(BaseModel):
|
||||
chunks: dict[str, ChunkInfo] | None = Field(
|
||||
None, description="Chunks for facts, keyed by '{document_id}_{chunk_index}'"
|
||||
)
|
||||
source_facts: dict[str, MemoryFact] | None = Field(
|
||||
None, description="Source facts for observation-type results, keyed by fact ID"
|
||||
)
|
||||
|
||||
|
||||
class ReflectResult(BaseModel):
|
||||
|
||||
@@ -266,6 +266,7 @@ pub fn recall(
|
||||
max_tokens: chunk_max_tokens,
|
||||
}),
|
||||
entities: None,
|
||||
source_facts: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -3226,6 +3226,8 @@ components:
|
||||
$ref: '#/components/schemas/EntityIncludeOptions'
|
||||
chunks:
|
||||
$ref: '#/components/schemas/ChunkIncludeOptions'
|
||||
source_facts:
|
||||
$ref: '#/components/schemas/SourceFactsIncludeOptions'
|
||||
title: IncludeOptions
|
||||
ListDocumentsResponse:
|
||||
description: Response model for list documents endpoint.
|
||||
@@ -3724,6 +3726,10 @@ components:
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/ChunkData'
|
||||
nullable: true
|
||||
source_facts:
|
||||
additionalProperties:
|
||||
$ref: '#/components/schemas/RecallResult'
|
||||
nullable: true
|
||||
required:
|
||||
- results
|
||||
title: RecallResponse
|
||||
@@ -3789,6 +3795,11 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
source_fact_ids:
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
required:
|
||||
- id
|
||||
- text
|
||||
@@ -4148,6 +4159,15 @@ components:
|
||||
- items_count
|
||||
- success
|
||||
title: RetainResponse
|
||||
SourceFactsIncludeOptions:
|
||||
description: Options for including source facts for observation-type results.
|
||||
properties:
|
||||
max_tokens:
|
||||
default: 4096
|
||||
description: Maximum tokens for source facts
|
||||
title: Max Tokens
|
||||
type: integer
|
||||
title: SourceFactsIncludeOptions
|
||||
TagItem:
|
||||
description: Single tag with usage count.
|
||||
properties:
|
||||
|
||||
@@ -21,6 +21,7 @@ var _ MappedNullable = &IncludeOptions{}
|
||||
type IncludeOptions struct {
|
||||
Entities NullableEntityIncludeOptions `json:"entities,omitempty"`
|
||||
Chunks NullableChunkIncludeOptions `json:"chunks,omitempty"`
|
||||
SourceFacts NullableSourceFactsIncludeOptions `json:"source_facts,omitempty"`
|
||||
}
|
||||
|
||||
// NewIncludeOptions instantiates a new IncludeOptions object
|
||||
@@ -124,6 +125,48 @@ func (o *IncludeOptions) UnsetChunks() {
|
||||
o.Chunks.Unset()
|
||||
}
|
||||
|
||||
// GetSourceFacts returns the SourceFacts field value if set, zero value otherwise (both if not set or set to explicit null).
|
||||
func (o *IncludeOptions) GetSourceFacts() SourceFactsIncludeOptions {
|
||||
if o == nil || IsNil(o.SourceFacts.Get()) {
|
||||
var ret SourceFactsIncludeOptions
|
||||
return ret
|
||||
}
|
||||
return *o.SourceFacts.Get()
|
||||
}
|
||||
|
||||
// GetSourceFactsOk returns a tuple with the SourceFacts field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
// NOTE: If the value is an explicit nil, `nil, true` will be returned
|
||||
func (o *IncludeOptions) GetSourceFactsOk() (*SourceFactsIncludeOptions, bool) {
|
||||
if o == nil {
|
||||
return nil, false
|
||||
}
|
||||
return o.SourceFacts.Get(), o.SourceFacts.IsSet()
|
||||
}
|
||||
|
||||
// HasSourceFacts returns a boolean if a field has been set.
|
||||
func (o *IncludeOptions) HasSourceFacts() bool {
|
||||
if o != nil && o.SourceFacts.IsSet() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetSourceFacts gets a reference to the given NullableSourceFactsIncludeOptions and assigns it to the SourceFacts field.
|
||||
func (o *IncludeOptions) SetSourceFacts(v SourceFactsIncludeOptions) {
|
||||
o.SourceFacts.Set(&v)
|
||||
}
|
||||
// SetSourceFactsNil sets the value for SourceFacts to be an explicit nil
|
||||
func (o *IncludeOptions) SetSourceFactsNil() {
|
||||
o.SourceFacts.Set(nil)
|
||||
}
|
||||
|
||||
// UnsetSourceFacts ensures that no value is present for SourceFacts, not even an explicit nil
|
||||
func (o *IncludeOptions) UnsetSourceFacts() {
|
||||
o.SourceFacts.Unset()
|
||||
}
|
||||
|
||||
func (o IncludeOptions) MarshalJSON() ([]byte, error) {
|
||||
toSerialize,err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -140,6 +183,9 @@ func (o IncludeOptions) ToMap() (map[string]interface{}, error) {
|
||||
if o.Chunks.IsSet() {
|
||||
toSerialize["chunks"] = o.Chunks.Get()
|
||||
}
|
||||
if o.SourceFacts.IsSet() {
|
||||
toSerialize["source_facts"] = o.SourceFacts.Get()
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ type RecallResponse struct {
|
||||
Trace map[string]interface{} `json:"trace,omitempty"`
|
||||
Entities map[string]EntityStateResponse `json:"entities,omitempty"`
|
||||
Chunks map[string]ChunkData `json:"chunks,omitempty"`
|
||||
SourceFacts map[string]RecallResult `json:"source_facts,omitempty"`
|
||||
}
|
||||
|
||||
type _RecallResponse RecallResponse
|
||||
@@ -170,6 +171,39 @@ func (o *RecallResponse) SetChunks(v map[string]ChunkData) {
|
||||
o.Chunks = v
|
||||
}
|
||||
|
||||
// GetSourceFacts returns the SourceFacts field value if set, zero value otherwise (both if not set or set to explicit null).
|
||||
func (o *RecallResponse) GetSourceFacts() map[string]RecallResult {
|
||||
if o == nil {
|
||||
var ret map[string]RecallResult
|
||||
return ret
|
||||
}
|
||||
return o.SourceFacts
|
||||
}
|
||||
|
||||
// GetSourceFactsOk returns a tuple with the SourceFacts field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
// NOTE: If the value is an explicit nil, `nil, true` will be returned
|
||||
func (o *RecallResponse) GetSourceFactsOk() (map[string]RecallResult, bool) {
|
||||
if o == nil || IsNil(o.SourceFacts) {
|
||||
return map[string]RecallResult{}, false
|
||||
}
|
||||
return o.SourceFacts, true
|
||||
}
|
||||
|
||||
// HasSourceFacts returns a boolean if a field has been set.
|
||||
func (o *RecallResponse) HasSourceFacts() bool {
|
||||
if o != nil && !IsNil(o.SourceFacts) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetSourceFacts gets a reference to the given map[string]RecallResult and assigns it to the SourceFacts field.
|
||||
func (o *RecallResponse) SetSourceFacts(v map[string]RecallResult) {
|
||||
o.SourceFacts = v
|
||||
}
|
||||
|
||||
func (o RecallResponse) MarshalJSON() ([]byte, error) {
|
||||
toSerialize,err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -190,6 +224,9 @@ func (o RecallResponse) ToMap() (map[string]interface{}, error) {
|
||||
if o.Chunks != nil {
|
||||
toSerialize["chunks"] = o.Chunks
|
||||
}
|
||||
if o.SourceFacts != nil {
|
||||
toSerialize["source_facts"] = o.SourceFacts
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ type RecallResult struct {
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
ChunkId NullableString `json:"chunk_id,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
SourceFactIds []string `json:"source_fact_ids,omitempty"`
|
||||
}
|
||||
|
||||
type _RecallResult RecallResult
|
||||
@@ -497,6 +498,39 @@ func (o *RecallResult) SetTags(v []string) {
|
||||
o.Tags = v
|
||||
}
|
||||
|
||||
// GetSourceFactIds returns the SourceFactIds field value if set, zero value otherwise (both if not set or set to explicit null).
|
||||
func (o *RecallResult) GetSourceFactIds() []string {
|
||||
if o == nil {
|
||||
var ret []string
|
||||
return ret
|
||||
}
|
||||
return o.SourceFactIds
|
||||
}
|
||||
|
||||
// GetSourceFactIdsOk returns a tuple with the SourceFactIds field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
// NOTE: If the value is an explicit nil, `nil, true` will be returned
|
||||
func (o *RecallResult) GetSourceFactIdsOk() ([]string, bool) {
|
||||
if o == nil || IsNil(o.SourceFactIds) {
|
||||
return nil, false
|
||||
}
|
||||
return o.SourceFactIds, true
|
||||
}
|
||||
|
||||
// HasSourceFactIds returns a boolean if a field has been set.
|
||||
func (o *RecallResult) HasSourceFactIds() bool {
|
||||
if o != nil && !IsNil(o.SourceFactIds) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetSourceFactIds gets a reference to the given []string and assigns it to the SourceFactIds field.
|
||||
func (o *RecallResult) SetSourceFactIds(v []string) {
|
||||
o.SourceFactIds = v
|
||||
}
|
||||
|
||||
func (o RecallResult) MarshalJSON() ([]byte, error) {
|
||||
toSerialize,err := o.ToMap()
|
||||
if err != nil {
|
||||
@@ -539,6 +573,9 @@ func (o RecallResult) ToMap() (map[string]interface{}, error) {
|
||||
if o.Tags != nil {
|
||||
toSerialize["tags"] = o.Tags
|
||||
}
|
||||
if o.SourceFactIds != nil {
|
||||
toSerialize["source_fact_ids"] = o.SourceFactIds
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
API version: 0.4.12
|
||||
*/
|
||||
|
||||
// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
|
||||
|
||||
package hindsight
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// checks if the SourceFactsIncludeOptions type satisfies the MappedNullable interface at compile time
|
||||
var _ MappedNullable = &SourceFactsIncludeOptions{}
|
||||
|
||||
// SourceFactsIncludeOptions Options for including source facts for observation-type results.
|
||||
type SourceFactsIncludeOptions struct {
|
||||
// Maximum tokens for source facts
|
||||
MaxTokens *int32 `json:"max_tokens,omitempty"`
|
||||
}
|
||||
|
||||
// NewSourceFactsIncludeOptions instantiates a new SourceFactsIncludeOptions object
|
||||
// This constructor will assign default values to properties that have it defined,
|
||||
// and makes sure properties required by API are set, but the set of arguments
|
||||
// will change when the set of required properties is changed
|
||||
func NewSourceFactsIncludeOptions() *SourceFactsIncludeOptions {
|
||||
this := SourceFactsIncludeOptions{}
|
||||
var maxTokens int32 = 4096
|
||||
this.MaxTokens = &maxTokens
|
||||
return &this
|
||||
}
|
||||
|
||||
// NewSourceFactsIncludeOptionsWithDefaults instantiates a new SourceFactsIncludeOptions object
|
||||
// This constructor will only assign default values to properties that have it defined,
|
||||
// but it doesn't guarantee that properties required by API are set
|
||||
func NewSourceFactsIncludeOptionsWithDefaults() *SourceFactsIncludeOptions {
|
||||
this := SourceFactsIncludeOptions{}
|
||||
var maxTokens int32 = 4096
|
||||
this.MaxTokens = &maxTokens
|
||||
return &this
|
||||
}
|
||||
|
||||
// GetMaxTokens returns the MaxTokens field value if set, zero value otherwise.
|
||||
func (o *SourceFactsIncludeOptions) GetMaxTokens() int32 {
|
||||
if o == nil || IsNil(o.MaxTokens) {
|
||||
var ret int32
|
||||
return ret
|
||||
}
|
||||
return *o.MaxTokens
|
||||
}
|
||||
|
||||
// GetMaxTokensOk returns a tuple with the MaxTokens field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *SourceFactsIncludeOptions) GetMaxTokensOk() (*int32, bool) {
|
||||
if o == nil || IsNil(o.MaxTokens) {
|
||||
return nil, false
|
||||
}
|
||||
return o.MaxTokens, true
|
||||
}
|
||||
|
||||
// HasMaxTokens returns a boolean if a field has been set.
|
||||
func (o *SourceFactsIncludeOptions) HasMaxTokens() bool {
|
||||
if o != nil && !IsNil(o.MaxTokens) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetMaxTokens gets a reference to the given int32 and assigns it to the MaxTokens field.
|
||||
func (o *SourceFactsIncludeOptions) SetMaxTokens(v int32) {
|
||||
o.MaxTokens = &v
|
||||
}
|
||||
|
||||
func (o SourceFactsIncludeOptions) MarshalJSON() ([]byte, error) {
|
||||
toSerialize,err := o.ToMap()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return json.Marshal(toSerialize)
|
||||
}
|
||||
|
||||
func (o SourceFactsIncludeOptions) ToMap() (map[string]interface{}, error) {
|
||||
toSerialize := map[string]interface{}{}
|
||||
if !IsNil(o.MaxTokens) {
|
||||
toSerialize["max_tokens"] = o.MaxTokens
|
||||
}
|
||||
return toSerialize, nil
|
||||
}
|
||||
|
||||
type NullableSourceFactsIncludeOptions struct {
|
||||
value *SourceFactsIncludeOptions
|
||||
isSet bool
|
||||
}
|
||||
|
||||
func (v NullableSourceFactsIncludeOptions) Get() *SourceFactsIncludeOptions {
|
||||
return v.value
|
||||
}
|
||||
|
||||
func (v *NullableSourceFactsIncludeOptions) Set(val *SourceFactsIncludeOptions) {
|
||||
v.value = val
|
||||
v.isSet = true
|
||||
}
|
||||
|
||||
func (v NullableSourceFactsIncludeOptions) IsSet() bool {
|
||||
return v.isSet
|
||||
}
|
||||
|
||||
func (v *NullableSourceFactsIncludeOptions) Unset() {
|
||||
v.value = nil
|
||||
v.isSet = false
|
||||
}
|
||||
|
||||
func NewNullableSourceFactsIncludeOptions(val *SourceFactsIncludeOptions) *NullableSourceFactsIncludeOptions {
|
||||
return &NullableSourceFactsIncludeOptions{value: val, isSet: true}
|
||||
}
|
||||
|
||||
func (v NullableSourceFactsIncludeOptions) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.value)
|
||||
}
|
||||
|
||||
func (v *NullableSourceFactsIncludeOptions) UnmarshalJSON(src []byte) error {
|
||||
v.isSet = true
|
||||
return json.Unmarshal(src, &v.value)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ hindsight_client_api/models/reflect_tool_call.py
|
||||
hindsight_client_api/models/reflect_trace.py
|
||||
hindsight_client_api/models/retain_request.py
|
||||
hindsight_client_api/models/retain_response.py
|
||||
hindsight_client_api/models/source_facts_include_options.py
|
||||
hindsight_client_api/models/tag_item.py
|
||||
hindsight_client_api/models/token_usage.py
|
||||
hindsight_client_api/models/tool_calls_include_options.py
|
||||
|
||||
@@ -252,6 +252,8 @@ class Hindsight:
|
||||
max_entity_tokens: int = 500,
|
||||
include_chunks: bool = False,
|
||||
max_chunk_tokens: int = 8192,
|
||||
include_source_facts: bool = False,
|
||||
max_source_facts_tokens: int = 4096,
|
||||
tags: list[str] | None = None,
|
||||
tags_match: Literal["any", "all", "any_strict", "all_strict"] = "any",
|
||||
) -> RecallResponse:
|
||||
@@ -270,20 +272,30 @@ class Hindsight:
|
||||
max_entity_tokens: Maximum tokens for entity observations (default: 500)
|
||||
include_chunks: Include raw text chunks in results (default: False)
|
||||
max_chunk_tokens: Maximum tokens for chunks (default: 8192)
|
||||
include_source_facts: Include source facts for observation-type results (default: False)
|
||||
max_source_facts_tokens: Maximum tokens for source facts (default: 4096)
|
||||
tags: Optional list of tags to filter memories by
|
||||
tags_match: How to match tags - "any" (OR, includes untagged), "all" (AND, includes untagged),
|
||||
"any_strict" (OR, excludes untagged), "all_strict" (AND, excludes untagged). Default: "any"
|
||||
|
||||
Returns:
|
||||
RecallResponse with results, optional entities, optional chunks, and optional trace
|
||||
RecallResponse with results, optional entities, optional chunks, optional source_facts, and optional trace
|
||||
"""
|
||||
from hindsight_client_api.models import chunk_include_options, entity_include_options, include_options
|
||||
from hindsight_client_api.models import (
|
||||
chunk_include_options,
|
||||
entity_include_options,
|
||||
include_options,
|
||||
source_facts_include_options,
|
||||
)
|
||||
|
||||
include_opts = include_options.IncludeOptions(
|
||||
entities=entity_include_options.EntityIncludeOptions(max_tokens=max_entity_tokens)
|
||||
if include_entities
|
||||
else None,
|
||||
chunks=chunk_include_options.ChunkIncludeOptions(max_tokens=max_chunk_tokens) if include_chunks else None,
|
||||
source_facts=source_facts_include_options.SourceFactsIncludeOptions(max_tokens=max_source_facts_tokens)
|
||||
if include_source_facts
|
||||
else None,
|
||||
)
|
||||
|
||||
request_obj = recall_request.RecallRequest(
|
||||
@@ -567,6 +579,8 @@ class Hindsight:
|
||||
max_entity_tokens: int = 500,
|
||||
include_chunks: bool = False,
|
||||
max_chunk_tokens: int = 8192,
|
||||
include_source_facts: bool = False,
|
||||
max_source_facts_tokens: int = 4096,
|
||||
tags: list[str] | None = None,
|
||||
tags_match: Literal["any", "all", "any_strict", "all_strict"] = "any",
|
||||
) -> RecallResponse:
|
||||
@@ -585,20 +599,30 @@ class Hindsight:
|
||||
max_entity_tokens: Maximum tokens for entity observations (default: 500)
|
||||
include_chunks: Include raw text chunks in results (default: False)
|
||||
max_chunk_tokens: Maximum tokens for chunks (default: 8192)
|
||||
include_source_facts: Include source facts for observation-type results (default: False)
|
||||
max_source_facts_tokens: Maximum tokens for source facts (default: 4096)
|
||||
tags: Optional list of tags to filter memories by
|
||||
tags_match: How to match tags - "any" (OR, includes untagged), "all" (AND, includes untagged),
|
||||
"any_strict" (OR, excludes untagged), "all_strict" (AND, excludes untagged). Default: "any"
|
||||
|
||||
Returns:
|
||||
RecallResponse with results, optional entities, optional chunks, and optional trace
|
||||
RecallResponse with results, optional entities, optional chunks, optional source_facts, and optional trace
|
||||
"""
|
||||
from hindsight_client_api.models import chunk_include_options, entity_include_options, include_options
|
||||
from hindsight_client_api.models import (
|
||||
chunk_include_options,
|
||||
entity_include_options,
|
||||
include_options,
|
||||
source_facts_include_options,
|
||||
)
|
||||
|
||||
include_opts = include_options.IncludeOptions(
|
||||
entities=entity_include_options.EntityIncludeOptions(max_tokens=max_entity_tokens)
|
||||
if include_entities
|
||||
else None,
|
||||
chunks=chunk_include_options.ChunkIncludeOptions(max_tokens=max_chunk_tokens) if include_chunks else None,
|
||||
source_facts=source_facts_include_options.SourceFactsIncludeOptions(max_tokens=max_source_facts_tokens)
|
||||
if include_source_facts
|
||||
else None,
|
||||
)
|
||||
|
||||
request_obj = recall_request.RecallRequest(
|
||||
|
||||
@@ -102,6 +102,7 @@ from hindsight_client_api.models.reflect_tool_call import ReflectToolCall
|
||||
from hindsight_client_api.models.reflect_trace import ReflectTrace
|
||||
from hindsight_client_api.models.retain_request import RetainRequest
|
||||
from hindsight_client_api.models.retain_response import RetainResponse
|
||||
from hindsight_client_api.models.source_facts_include_options import SourceFactsIncludeOptions
|
||||
from hindsight_client_api.models.tag_item import TagItem
|
||||
from hindsight_client_api.models.token_usage import TokenUsage
|
||||
from hindsight_client_api.models.tool_calls_include_options import ToolCallsIncludeOptions
|
||||
|
||||
@@ -77,6 +77,7 @@ from hindsight_client_api.models.reflect_tool_call import ReflectToolCall
|
||||
from hindsight_client_api.models.reflect_trace import ReflectTrace
|
||||
from hindsight_client_api.models.retain_request import RetainRequest
|
||||
from hindsight_client_api.models.retain_response import RetainResponse
|
||||
from hindsight_client_api.models.source_facts_include_options import SourceFactsIncludeOptions
|
||||
from hindsight_client_api.models.tag_item import TagItem
|
||||
from hindsight_client_api.models.token_usage import TokenUsage
|
||||
from hindsight_client_api.models.tool_calls_include_options import ToolCallsIncludeOptions
|
||||
|
||||
@@ -21,6 +21,7 @@ from pydantic import BaseModel, ConfigDict
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
|
||||
from hindsight_client_api.models.entity_include_options import EntityIncludeOptions
|
||||
from hindsight_client_api.models.source_facts_include_options import SourceFactsIncludeOptions
|
||||
from typing import Optional, Set
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -30,7 +31,8 @@ class IncludeOptions(BaseModel):
|
||||
""" # noqa: E501
|
||||
entities: Optional[EntityIncludeOptions] = None
|
||||
chunks: Optional[ChunkIncludeOptions] = None
|
||||
__properties: ClassVar[List[str]] = ["entities", "chunks"]
|
||||
source_facts: Optional[SourceFactsIncludeOptions] = None
|
||||
__properties: ClassVar[List[str]] = ["entities", "chunks", "source_facts"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
@@ -77,6 +79,9 @@ class IncludeOptions(BaseModel):
|
||||
# override the default output from pydantic by calling `to_dict()` of chunks
|
||||
if self.chunks:
|
||||
_dict['chunks'] = self.chunks.to_dict()
|
||||
# override the default output from pydantic by calling `to_dict()` of source_facts
|
||||
if self.source_facts:
|
||||
_dict['source_facts'] = self.source_facts.to_dict()
|
||||
# set to None if entities (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.entities is None and "entities" in self.model_fields_set:
|
||||
@@ -87,6 +92,11 @@ class IncludeOptions(BaseModel):
|
||||
if self.chunks is None and "chunks" in self.model_fields_set:
|
||||
_dict['chunks'] = None
|
||||
|
||||
# set to None if source_facts (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.source_facts is None and "source_facts" in self.model_fields_set:
|
||||
_dict['source_facts'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
@@ -100,7 +110,8 @@ class IncludeOptions(BaseModel):
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"entities": EntityIncludeOptions.from_dict(obj["entities"]) if obj.get("entities") is not None else None,
|
||||
"chunks": ChunkIncludeOptions.from_dict(obj["chunks"]) if obj.get("chunks") is not None else None
|
||||
"chunks": ChunkIncludeOptions.from_dict(obj["chunks"]) if obj.get("chunks") is not None else None,
|
||||
"source_facts": SourceFactsIncludeOptions.from_dict(obj["source_facts"]) if obj.get("source_facts") is not None else None
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ class RecallResponse(BaseModel):
|
||||
trace: Optional[Dict[str, Any]] = None
|
||||
entities: Optional[Dict[str, EntityStateResponse]] = None
|
||||
chunks: Optional[Dict[str, ChunkData]] = None
|
||||
__properties: ClassVar[List[str]] = ["results", "trace", "entities", "chunks"]
|
||||
source_facts: Optional[Dict[str, RecallResult]] = None
|
||||
__properties: ClassVar[List[str]] = ["results", "trace", "entities", "chunks", "source_facts"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
@@ -95,6 +96,13 @@ class RecallResponse(BaseModel):
|
||||
if self.chunks[_key_chunks]:
|
||||
_field_dict[_key_chunks] = self.chunks[_key_chunks].to_dict()
|
||||
_dict['chunks'] = _field_dict
|
||||
# override the default output from pydantic by calling `to_dict()` of each value in source_facts (dict)
|
||||
_field_dict = {}
|
||||
if self.source_facts:
|
||||
for _key_source_facts in self.source_facts:
|
||||
if self.source_facts[_key_source_facts]:
|
||||
_field_dict[_key_source_facts] = self.source_facts[_key_source_facts].to_dict()
|
||||
_dict['source_facts'] = _field_dict
|
||||
# set to None if trace (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.trace is None and "trace" in self.model_fields_set:
|
||||
@@ -110,6 +118,11 @@ class RecallResponse(BaseModel):
|
||||
if self.chunks is None and "chunks" in self.model_fields_set:
|
||||
_dict['chunks'] = None
|
||||
|
||||
# set to None if source_facts (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.source_facts is None and "source_facts" in self.model_fields_set:
|
||||
_dict['source_facts'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
@@ -135,6 +148,12 @@ class RecallResponse(BaseModel):
|
||||
for _k, _v in obj["chunks"].items()
|
||||
)
|
||||
if obj.get("chunks") is not None
|
||||
else None,
|
||||
"source_facts": dict(
|
||||
(_k, RecallResult.from_dict(_v))
|
||||
for _k, _v in obj["source_facts"].items()
|
||||
)
|
||||
if obj.get("source_facts") is not None
|
||||
else None
|
||||
})
|
||||
return _obj
|
||||
|
||||
@@ -38,7 +38,8 @@ class RecallResult(BaseModel):
|
||||
metadata: Optional[Dict[str, StrictStr]] = None
|
||||
chunk_id: Optional[StrictStr] = None
|
||||
tags: Optional[List[StrictStr]] = None
|
||||
__properties: ClassVar[List[str]] = ["id", "text", "type", "entities", "context", "occurred_start", "occurred_end", "mentioned_at", "document_id", "metadata", "chunk_id", "tags"]
|
||||
source_fact_ids: Optional[List[StrictStr]] = None
|
||||
__properties: ClassVar[List[str]] = ["id", "text", "type", "entities", "context", "occurred_start", "occurred_end", "mentioned_at", "document_id", "metadata", "chunk_id", "tags", "source_fact_ids"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
@@ -129,6 +130,11 @@ class RecallResult(BaseModel):
|
||||
if self.tags is None and "tags" in self.model_fields_set:
|
||||
_dict['tags'] = None
|
||||
|
||||
# set to None if source_fact_ids (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.source_fact_ids is None and "source_fact_ids" in self.model_fields_set:
|
||||
_dict['source_fact_ids'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
@@ -152,7 +158,8 @@ class RecallResult(BaseModel):
|
||||
"document_id": obj.get("document_id"),
|
||||
"metadata": obj.get("metadata"),
|
||||
"chunk_id": obj.get("chunk_id"),
|
||||
"tags": obj.get("tags")
|
||||
"tags": obj.get("tags"),
|
||||
"source_fact_ids": obj.get("source_fact_ids")
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 0.4.12
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
import pprint
|
||||
import re # noqa: F401
|
||||
import json
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, StrictInt
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from typing import Optional, Set
|
||||
from typing_extensions import Self
|
||||
|
||||
class SourceFactsIncludeOptions(BaseModel):
|
||||
"""
|
||||
Options for including source facts for observation-type results.
|
||||
""" # noqa: E501
|
||||
max_tokens: Optional[StrictInt] = Field(default=4096, description="Maximum tokens for source facts")
|
||||
__properties: ClassVar[List[str]] = ["max_tokens"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
validate_assignment=True,
|
||||
protected_namespaces=(),
|
||||
)
|
||||
|
||||
|
||||
def to_str(self) -> str:
|
||||
"""Returns the string representation of the model using alias"""
|
||||
return pprint.pformat(self.model_dump(by_alias=True))
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Returns the JSON representation of the model using alias"""
|
||||
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> Optional[Self]:
|
||||
"""Create an instance of SourceFactsIncludeOptions from a JSON string"""
|
||||
return cls.from_dict(json.loads(json_str))
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Return the dictionary representation of the model using alias.
|
||||
|
||||
This has the following differences from calling pydantic's
|
||||
`self.model_dump(by_alias=True)`:
|
||||
|
||||
* `None` is only added to the output dict for nullable fields that
|
||||
were set at model initialization. Other fields with value `None`
|
||||
are ignored.
|
||||
"""
|
||||
excluded_fields: Set[str] = set([
|
||||
])
|
||||
|
||||
_dict = self.model_dump(
|
||||
by_alias=True,
|
||||
exclude=excluded_fields,
|
||||
exclude_none=True,
|
||||
)
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
||||
"""Create an instance of SourceFactsIncludeOptions from a dict"""
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if not isinstance(obj, dict):
|
||||
return cls.model_validate(obj)
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"max_tokens": obj.get("max_tokens") if obj.get("max_tokens") is not None else 4096
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
||||
@@ -994,6 +994,10 @@ export type IncludeOptions = {
|
||||
* Include raw chunks. Set to {} to enable, null to disable (default: disabled).
|
||||
*/
|
||||
chunks?: ChunkIncludeOptions | null;
|
||||
/**
|
||||
* Include source facts for observation-type results. Set to {} to enable, null to disable (default: disabled).
|
||||
*/
|
||||
source_facts?: SourceFactsIncludeOptions | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1392,6 +1396,14 @@ export type RecallResponse = {
|
||||
chunks?: {
|
||||
[key: string]: ChunkData;
|
||||
} | null;
|
||||
/**
|
||||
* Source Facts
|
||||
*
|
||||
* Source facts for observation-type results, keyed by fact ID
|
||||
*/
|
||||
source_facts?: {
|
||||
[key: string]: RecallResult;
|
||||
} | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1450,6 +1462,10 @@ export type RecallResult = {
|
||||
* Tags
|
||||
*/
|
||||
tags?: Array<string> | null;
|
||||
/**
|
||||
* Source Fact Ids
|
||||
*/
|
||||
source_fact_ids?: Array<string> | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1807,6 +1823,20 @@ export type RetainResponse = {
|
||||
usage?: TokenUsage | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* SourceFactsIncludeOptions
|
||||
*
|
||||
* Options for including source facts for observation-type results.
|
||||
*/
|
||||
export type SourceFactsIncludeOptions = {
|
||||
/**
|
||||
* Max Tokens
|
||||
*
|
||||
* Maximum tokens for source facts
|
||||
*/
|
||||
max_tokens?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* TagItem
|
||||
*
|
||||
|
||||
@@ -262,6 +262,10 @@ export class HindsightClient {
|
||||
maxEntityTokens?: number;
|
||||
includeChunks?: boolean;
|
||||
maxChunkTokens?: number;
|
||||
/** Include source facts for observation-type results */
|
||||
includeSourceFacts?: boolean;
|
||||
/** Maximum tokens for source facts (default: 4096) */
|
||||
maxSourceFactsTokens?: number;
|
||||
/** Optional list of tags to filter memories by */
|
||||
tags?: string[];
|
||||
/** How to match tags: 'any' (OR, includes untagged), 'all' (AND, includes untagged), 'any_strict' (OR, excludes untagged), 'all_strict' (AND, excludes untagged). Default: 'any' */
|
||||
@@ -281,6 +285,7 @@ export class HindsightClient {
|
||||
include: {
|
||||
entities: options?.includeEntities ? { max_tokens: options?.maxEntityTokens ?? 500 } : undefined,
|
||||
chunks: options?.includeChunks ? { max_tokens: options?.maxChunkTokens ?? 8192 } : undefined,
|
||||
source_facts: options?.includeSourceFacts ? { max_tokens: options?.maxSourceFactsTokens ?? 4096 } : undefined,
|
||||
},
|
||||
tags: options?.tags,
|
||||
tags_match: options?.tagsMatch,
|
||||
|
||||
@@ -125,7 +125,7 @@ export class ControlPlaneClient {
|
||||
include?: {
|
||||
entities?: { max_tokens: number } | null;
|
||||
chunks?: { max_tokens: number } | null;
|
||||
observations?: { max_results?: number } | null;
|
||||
source_facts?: { max_tokens?: number } | null;
|
||||
};
|
||||
query_timestamp?: string;
|
||||
tags?: string[];
|
||||
|
||||
@@ -48,6 +48,8 @@ Make sure you've completed the [Quick Start](./quickstart) to install the client
|
||||
| `trace` | bool | false | Enable trace output for debugging |
|
||||
| `include_chunks` | bool | false | Include raw text chunks that generated the memories |
|
||||
| `max_chunk_tokens` | int | 500 | Token budget for chunks (independent of `max_tokens`) |
|
||||
| `include_source_facts` | bool | false | Include source facts for observation-type results (see [Source Facts](#source-facts)) |
|
||||
| `max_source_facts_tokens` | int | 4096 | Token budget for source facts |
|
||||
| `tags` | list | None | Filter memories by tags (see [Tag Filtering](#filter-by-tags)) |
|
||||
| `tags_match` | string | "any" | How to match tags: `any`, `all`, `any_strict`, `all_strict` |
|
||||
|
||||
@@ -79,6 +81,25 @@ Recall specific memory types:
|
||||
Observations are consolidated knowledge synthesized from multiple facts. They capture patterns, preferences, and learnings that the memory bank has built up over time. Observations are automatically created in the background after retain operations.
|
||||
:::
|
||||
|
||||
## Source Facts
|
||||
|
||||
When recalling `observation`-type memories, you can fetch the underlying facts they were derived from. This is useful when you need to understand or verify the evidence behind a synthesized observation.
|
||||
|
||||
Source facts are returned as a top-level `source_facts` dict keyed by fact ID. Each observation result includes a `source_fact_ids` list for cross-referencing. Facts are deduplicated — if two observations share a source fact, it only appears once.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="python" label="Python">
|
||||
<CodeSnippet code={recallPy} section="recall-source-facts" language="python" />
|
||||
</TabItem>
|
||||
<TabItem value="node" label="Node.js">
|
||||
<CodeSnippet code={recallMjs} section="recall-source-facts" language="javascript" />
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note Source Facts Token Budget
|
||||
Source facts are fetched independently of the main `max_tokens` budget, up to `max_source_facts_tokens`. Facts are included in order of first appearance across all observations — once the budget is reached, remaining source facts are omitted.
|
||||
:::
|
||||
|
||||
## Token Budget Management
|
||||
|
||||
Hindsight is built for AI agents, not humans. Traditional retrieval systems return "top-k" results, but agents don't think in terms of result counts—they think in tokens. An agent's context window is measured in tokens, and that's exactly how Hindsight measures results.
|
||||
|
||||
@@ -44,6 +44,27 @@ for (const r of detailedResponse.results) {
|
||||
// [/docs:recall-with-options]
|
||||
|
||||
|
||||
// [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?', {
|
||||
types: ['observation'],
|
||||
includeSourceFacts: true,
|
||||
maxSourceFactsTokens: 4096,
|
||||
});
|
||||
|
||||
for (const obs of obsResponse.results) {
|
||||
console.log(`Observation: ${obs.text}`);
|
||||
if (obs.source_fact_ids && obsResponse.source_facts) {
|
||||
console.log(' Derived from:');
|
||||
for (const factId of obs.source_fact_ids) {
|
||||
const fact = obsResponse.source_facts[factId];
|
||||
if (fact) console.log(` - [${fact.type}] ${fact.text}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// [/docs:recall-source-facts]
|
||||
|
||||
|
||||
// [docs:recall-budget-levels]
|
||||
// Quick lookup
|
||||
const quickResults = await client.recall('my-bank', "Alice's email", { budget: 'low' });
|
||||
|
||||
@@ -94,6 +94,27 @@ observations = client.recall(
|
||||
# [/docs:recall-with-observations]
|
||||
|
||||
|
||||
# [docs:recall-source-facts]
|
||||
# Recall observations and include their source facts
|
||||
response = client.recall(
|
||||
bank_id="my-bank",
|
||||
query="What patterns have I learned about Alice?",
|
||||
types=["observation"],
|
||||
include_source_facts=True,
|
||||
max_source_facts_tokens=4096,
|
||||
)
|
||||
|
||||
for obs in response.results:
|
||||
print(f"Observation: {obs.text}")
|
||||
if obs.source_fact_ids and response.source_facts:
|
||||
print(" Derived from:")
|
||||
for fact_id in obs.source_fact_ids:
|
||||
fact = response.source_facts.get(fact_id)
|
||||
if fact:
|
||||
print(f" - [{fact.type}] {fact.text}")
|
||||
# [/docs:recall-source-facts]
|
||||
|
||||
|
||||
# [docs:recall-token-budget]
|
||||
# Fill up to 4K tokens of context with relevant memories
|
||||
results = client.recall(bank_id="my-bank", query="What do I know about Alice?", max_tokens=4096)
|
||||
|
||||
@@ -4801,6 +4801,17 @@
|
||||
}
|
||||
],
|
||||
"description": "Include raw chunks. Set to {} to enable, null to disable (default: disabled)."
|
||||
},
|
||||
"source_facts": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/SourceFactsIncludeOptions"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Include source facts for observation-type results. Set to {} to enable, null to disable (default: disabled)."
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -5580,6 +5591,21 @@
|
||||
],
|
||||
"title": "Chunks",
|
||||
"description": "Chunks for facts, keyed by chunk_id"
|
||||
},
|
||||
"source_facts": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/RecallResult"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Source Facts",
|
||||
"description": "Source facts for observation-type results, keyed by fact ID"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -5758,6 +5784,20 @@
|
||||
}
|
||||
],
|
||||
"title": "Tags"
|
||||
},
|
||||
"source_fact_ids": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Source Fact Ids"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -6431,6 +6471,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SourceFactsIncludeOptions": {
|
||||
"properties": {
|
||||
"max_tokens": {
|
||||
"type": "integer",
|
||||
"title": "Max Tokens",
|
||||
"description": "Maximum tokens for source facts",
|
||||
"default": 4096
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "SourceFactsIncludeOptions",
|
||||
"description": "Options for including source facts for observation-type results."
|
||||
},
|
||||
"TagItem": {
|
||||
"properties": {
|
||||
"tag": {
|
||||
|
||||
Reference in New Issue
Block a user