Compare commits

...
16 Commits
Author SHA1 Message Date
Chris Bartholomew 5261e5193a Load .env file automatically on startup
Add automatic .env file loading using python-dotenv. This searches
the current working directory and parent directories for a .env file
and loads environment variables from it.

Uses override=True so .env file values take precedence over existing
shell environment variables, which is the expected behavior when
running from a project directory.
2026-01-05 17:43:12 -05:00
Chris Bartholomew ce45d301ce Add operation validator extension support with proper HTTP error handling (#86)
* Load operation validator extension in main entry point

Enable the operation validator extension to be loaded from environment
configuration and passed to MemoryEngine, allowing pre/post operation
hooks for usage metering, rate limiting, and audit logging.

* Fix reflect background task authentication and add internal flag

- Pass API key to background opinion storage task for proper auth
- Add internal flag to RequestContext for tracking internal operations
- Background opinion storage now authenticates correctly with tenant

* Add api_key_id to RequestContext for usage tracking

- Add api_key_id field to RequestContext to track which API key was used
- Enables per-API-key usage analytics in the metering system

* Fix HTTP error handling for authentication and validation errors

- Add status_code parameter to ValidationResult and OperationValidationError
- Convert OperationValidationError to HTTPException with proper status codes
- Fix authentication errors to return 401 instead of raising internal errors
- Re-raise HTTPException in exception handlers to prevent swallowing errors

* Fix AuthenticationError handling in memory engine

- Raise AuthenticationError from memory_engine._authenticate_tenant instead
  of HTTPException so unit tests pass
- Add AuthenticationError handling in HTTP layer to convert to 401 responses
- Fixes failing TestMemoryEngineTenantAuth tests

* Add global exception handler for AuthenticationError

Returns proper 401 status code for all authentication failures
across all endpoints, not just the ones with explicit handlers.

* Simplify exception handling: use global AuthenticationError handler

- Remove redundant individual exception handlers
- Add 'except AuthenticationError: raise' before generic Exception handlers
  to let global handler process auth errors uniformly

* Refactor background tasks to use tenant_id instead of api_key

This makes the core more generic - it passes tenant_id (which is
extension-agnostic) rather than api_key (which is cloud-specific).

- Add tenant_id field to RequestContext
- Pass tenant_id instead of api_key to background tasks
- Extensions can check internal=True with tenant_id to bypass normal auth

* Fix exception propagation: include HTTPException in re-raise

After cleanup of redundant exception handlers, 404 errors were
returning 500 because HTTPException was caught by the generic
except Exception handler. Fixed by combining AuthenticationError
and HTTPException in the re-raise pattern.
2026-01-01 20:19:52 -05:00
Nicolò Boschi d49e8201b4 feat: add max_tokens and structured output to /reflect (#74)
* feat: add structured output to /reflect

* feat: add structured output to /reflect

* imrpove

* add max_toksn

* fix rust client

* fix rust client

* fix rust client

* try fix

* try fix

* no stricts
2026-01-01 17:09:39 +01:00
Nicolò Boschi c8c7603580 feat(doc): add new config options and supported providers (#84) 2026-01-01 17:09:05 +01:00
csfet9andClaude Opus 4.5 787ed60763 feat: Add Anthropic Claude and LM Studio provider support (#36)
* feat: Add Anthropic Claude and LM Studio provider support

- Add Anthropic as LLM provider with full async support
- Add LM Studio provider for local model inference
- Fix JSON response format compatibility for local models
- Update .env.example with configuration examples
- Update docstrings with all supported providers

Tested with:
- Claude Sonnet 4 (claude-sonnet-4-20250514)
- Claude Haiku 4.5 (claude-haiku-4-5-20251001)
- Qwen 30B via LM Studio

* feat: Add dynamic timeout for local LLM providers

Add configurable timeout support for LLM API calls:
- Environment variable override via HINDSIGHT_API_LLM_TIMEOUT
- Dynamic heuristic for lmstudio/ollama: 20 mins for large models
  (30b, 33b, 34b, 65b, 70b, 72b, 8x7b, 8x22b), 5 mins for others
- Pass timeout to Anthropic, OpenAI, and local model clients

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: Address PR review feedback

- Remove CLAUDE.md from .gitignore (should stay in repository)
- Pass max_completion_tokens to _call_anthropic instead of hardcoding 4096

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: Remove deleted AI assistant files from .gitignore

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* docs: Add CLAUDE.md for Claude Code integration

Provides project context and development commands for AI-assisted coding.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: Include local dev files and sync changes

- Add docker-compose.yml for local development
- Add test_internal.py for local testing
- Sync uv.lock and llm_wrapper.py changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: Address PR review feedback for LLM provider support

- Move LLM config to config.py with HINDSIGHT_API_ prefix
  - Add HINDSIGHT_API_LLM_MAX_CONCURRENT (default: 32)
  - Add HINDSIGHT_API_LLM_TIMEOUT (default: 120s)
- Remove fragile model-size timeout heuristic
- Apply markdown JSON extraction to all providers, not just local
- Fix Anthropic markdown extraction bug (missing split)
- Change LLM request/response logs from info to debug level

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: Remove local dev docker-compose.yml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: Add local dev docker-compose.yml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: Update LM Studio port to 2222 in docker-compose

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: Remove obsolete version attribute from docker-compose

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: Remove test file and docker-compose per PR review

- Remove test_internal.py (debug file)
- Remove docker-compose.yml (to be moved to hindsight-cookbook repo)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

---------

Co-authored-by: Claude Opus 4.5 <[email protected]>
2026-01-01 16:34:11 +01:00
Bjorn SchliebitzandClaude Opus 4.5 6b78f7d949 fix(mcp): Chain MCP lifespan with FastAPI app lifespan (#81)
The MCP server's lifespan was not being properly chained with the
FastAPI app's lifespan, causing the MCP server to not start/stop
correctly when mounted as a sub-application.

Changes:
- Create MCP app before FastAPI app to access its lifespan
- Chain MCP lifespan context with FastAPI's lifespan context
- Ensures MCP server lifecycle is properly managed

This fix is required for the MCP server to function correctly when
used with Claude Code and other MCP clients.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <[email protected]>
2026-01-01 16:33:58 +01:00
Bjorn SchliebitzandClaude Opus 4.5 54e2df0baf feat(config): Add configurable observation thresholds (#83)
Allows tuning of entity observation generation via environment variables.

## New Environment Variables
- `HINDSIGHT_API_OBSERVATION_MIN_FACTS` - Minimum facts required to
  generate entity observations (default: 5)
- `HINDSIGHT_API_OBSERVATION_TOP_ENTITIES` - Maximum entities to process
  per retain batch (default: 5)

## Changes
- Added threshold configuration to HindsightConfig
- Updated memory_engine.py to use config values
- Updated observation_regeneration.py to use config values

## Use Case
Lower thresholds generate more observations (better recall, higher cost).
Higher thresholds are more selective (lower cost, may miss patterns).

Example:
```bash
# Generate more observations
docker run -e HINDSIGHT_API_OBSERVATION_MIN_FACTS=3 \
           -e HINDSIGHT_API_OBSERVATION_TOP_ENTITIES=10 ...
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <[email protected]>
2026-01-01 16:22:25 +01:00
Chris Latimer 967e586e01 Add model providers on README 2025-12-24 10:46:53 -07:00
Chris Bartholomew dfa7cec05b Load operation validator extension in main entry point (#72)
Enable the operation validator extension to be loaded from environment
configuration and passed to MemoryEngine, allowing pre/post operation
hooks for usage metering, rate limiting, and audit logging.
2025-12-23 15:47:26 +01:00
Nicolò Boschi 36e48a7166 doc: add skills documentation (#73)
* doc: add skills documentation

* doc: add skills documentation
2025-12-23 15:42:27 +01:00
Nicolò Boschi 786b1ecbbd Release v0.1.16
- Update version to 0.1.16 in all components
- Python packages: hindsight-api, hindsight-dev, hindsight-all, hindsight-litellm, hindsight-embed
- Python client: hindsight-clients/python
- TypeScript client: hindsight-clients/typescript
- Rust CLI: hindsight-cli
- Control Plane: hindsight-control-plane
- Helm chart
2025-12-23 14:12:03 +01:00
Nicolò Boschi f14f277692 fix: hindsight-embed release version 2025-12-23 14:11:49 +01:00
Nicolò Boschi c9f3657de6 0.1.15 changelog 2025-12-23 13:54:41 +01:00
Nicolò Boschi 0ae0374dc8 Release v0.1.15
- Update version to 0.1.15 in all components
- Python packages: hindsight-api, hindsight-dev, hindsight-all, hindsight-litellm
- Python client: hindsight-clients/python
- TypeScript client: hindsight-clients/typescript
- Rust CLI: hindsight-cli
- Control Plane: hindsight-control-plane
- Helm chart
2025-12-23 13:54:14 +01:00
Nicolò Boschi f7ff32d49d feat: delete document from ui (#71)
* feat: delete document from ui

* feat: delete document from ui
2025-12-23 13:54:06 +01:00
Nicolò Boschi e06a6120a3 feat(misc): update clients types, test coverage, improve /health endpoint and add changelog (#70)
* doc: changelog and delete doc info

* others

* others

* fixes

* fixes
2025-12-23 12:49:31 +01:00
204 changed files with 17171 additions and 13071 deletions
+12
View File
@@ -2,11 +2,23 @@
# Copy this file to .env and fill in your values
# LLM Configuration (Required)
# Supported providers: openai, groq, ollama, gemini, anthropic, lmstudio
HINDSIGHT_API_LLM_PROVIDER=openai
HINDSIGHT_API_LLM_API_KEY=your-api-key-here
HINDSIGHT_API_LLM_MODEL=o3-mini
HINDSIGHT_API_LLM_BASE_URL=https://api.openai.com/v1
# Example: Anthropic Claude configuration
# HINDSIGHT_API_LLM_PROVIDER=anthropic
# HINDSIGHT_API_LLM_API_KEY=your-anthropic-api-key
# HINDSIGHT_API_LLM_MODEL=claude-sonnet-4-20250514
# Example: LM Studio local configuration (Qwen 2.5 32B recommended)
# HINDSIGHT_API_LLM_PROVIDER=lmstudio
# HINDSIGHT_API_LLM_API_KEY=lmstudio
# HINDSIGHT_API_LLM_BASE_URL=http://localhost:1234/v1
# HINDSIGHT_API_LLM_MODEL=qwen2.5-32b-instruct
# API Configuration (Optional)
HINDSIGHT_API_HOST=0.0.0.0
HINDSIGHT_API_PORT=8888
+77
View File
@@ -182,6 +182,83 @@ jobs:
path: hindsight-cli/target/release/hindsight
retention-days: 1
test-rust-cli:
runs-on: ubuntu-latest
needs: build-rust-cli
env:
HINDSIGHT_API_LLM_PROVIDER: groq
HINDSIGHT_API_LLM_API_KEY: ${{ secrets.GROQ_API_KEY }}
HINDSIGHT_API_LLM_MODEL: openai/gpt-oss-20b
HINDSIGHT_API_URL: http://localhost:8888
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
UV_INDEX: pytorch=https://download.pytorch.org/whl/cpu
steps:
- uses: actions/checkout@v4
- name: Download CLI artifact
uses: actions/download-artifact@v4
with:
name: hindsight-cli
path: /tmp/cli
- name: Make CLI executable
run: chmod +x /tmp/cli/hindsight
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
prune-cache: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: Build API
working-directory: ./hindsight-api
run: uv build
- name: Install API dependencies
working-directory: ./hindsight-api
run: uv sync --no-install-project --index-strategy unsafe-best-match
- name: Create .env file
run: |
cat > .env << EOF
HINDSIGHT_API_LLM_PROVIDER=${{ env.HINDSIGHT_API_LLM_PROVIDER }}
HINDSIGHT_API_LLM_API_KEY=${{ env.HINDSIGHT_API_LLM_API_KEY }}
HINDSIGHT_API_LLM_MODEL=${{ env.HINDSIGHT_API_LLM_MODEL }}
EOF
- name: Start API server
run: |
./scripts/dev/start-api.sh > /tmp/api-server.log 2>&1 &
echo "Waiting for API server to be ready..."
for i in {1..60}; do
if curl -sf http://localhost:8888/health > /dev/null 2>&1; then
echo "API server is ready after ${i}s"
break
fi
if [ $i -eq 60 ]; then
echo "API server failed to start after 60s"
cat /tmp/api-server.log
exit 1
fi
sleep 1
done
- name: Run CLI smoke test
run: |
HINDSIGHT_CLI=/tmp/cli/hindsight ./hindsight-cli/smoke-test.sh
- name: Show API server logs
if: always()
run: |
echo "=== API Server Logs ==="
cat /tmp/api-server.log || echo "No API server log found"
lint-helm-chart:
runs-on: ubuntu-latest
+125
View File
@@ -0,0 +1,125 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Hindsight is an agent memory system that provides long-term memory for AI agents using biomimetic data structures. It stores memories as World facts, Experiences, Opinions, and Observations across memory banks.
## Development Commands
### API Server (Python/FastAPI)
```bash
# Start API server (loads .env automatically)
./scripts/dev/start-api.sh
# Run tests
cd hindsight-api && uv run pytest tests/
# Run specific test file
cd hindsight-api && uv run pytest tests/test_http_api_integration.py -v
# Lint
cd hindsight-api && uv run ruff check .
```
### Control Plane (Next.js)
```bash
./scripts/dev/start-control-plane.sh
# Or manually:
cd hindsight-control-plane && npm run dev
```
### Documentation Site (Docusaurus)
```bash
./scripts/dev/start-docs.sh
```
### Generating Clients/OpenAPI
```bash
# Regenerate OpenAPI spec after API changes
./scripts/generate-openapi.sh
# Regenerate all client SDKs (Python, TypeScript, Rust)
./scripts/generate-clients.sh
```
### Benchmarks
```bash
./scripts/benchmarks/run-longmemeval.sh
./scripts/benchmarks/run-locomo.sh
./scripts/benchmarks/start-visualizer.sh # View results at localhost:8001
```
## Architecture
### Monorepo Structure
- **hindsight-api/**: Core FastAPI server with memory engine (Python, uv)
- **hindsight/**: Embedded Python bundle (hindsight-all package)
- **hindsight-control-plane/**: Admin UI (Next.js, npm)
- **hindsight-cli/**: CLI tool (Rust, cargo)
- **hindsight-clients/**: Generated SDK clients (Python, TypeScript, Rust)
- **hindsight-docs/**: Docusaurus documentation site
- **hindsight-integrations/**: Framework integrations (LiteLLM, OpenAI)
- **hindsight-dev/**: Development tools and benchmarks
### Core Engine (hindsight-api/hindsight_api/engine/)
- `memory_engine.py`: Main orchestrator for retain/recall/reflect operations
- `llm_wrapper.py`: LLM abstraction supporting OpenAI, Anthropic, Gemini, Groq, Ollama, LM Studio
- `embeddings.py`: Embedding generation (local or TEI)
- `cross_encoder.py`: Reranking (local or TEI)
- `entity_resolver.py`: Entity extraction and normalization
- `query_analyzer.py`: Query intent analysis
- `retain/`: Memory ingestion pipeline
- `search/`: Multi-strategy retrieval (semantic, BM25, graph, temporal)
### API Layer (hindsight-api/hindsight_api/api/)
FastAPI routers for all endpoints. Main operations:
- **Retain**: Store memories, extracts facts/entities/relationships
- **Recall**: Retrieve memories via parallel search strategies + reranking
- **Reflect**: Deep analysis forming new opinions/observations
### Database
PostgreSQL with pgvector. Schema managed via Alembic migrations in `hindsight-api/hindsight_api/alembic/`. Migrations run automatically on API startup.
Key tables: `banks`, `memory_units`, `documents`, `entities`, `entity_links`
## Key Conventions
### Memory Banks
- Each bank is isolated (no cross-bank data access)
- Banks have dispositions (skepticism, literalism, empathy traits 1-5) affecting reflect
- Banks can have background context
### API Design
- All endpoints operate on a single bank per request
- Multi-bank queries are client responsibility
- Disposition traits only affect reflect, not recall
### Python Style
- Python 3.11+, type hints required
- Async throughout (asyncpg, async FastAPI)
- Pydantic models for request/response
- Ruff for linting (line-length 120)
### TypeScript Style
- Next.js App Router for control plane
- Tailwind CSS with shadcn/ui components
## Environment Setup
```bash
cp .env.example .env
# Edit .env with LLM API key
# Python deps
uv sync --directory hindsight-api/
# Node deps (workspace)
npm install
```
Required env vars:
- `HINDSIGHT_API_LLM_PROVIDER`: openai, anthropic, gemini, groq, ollama, lmstudio
- `HINDSIGHT_API_LLM_API_KEY`: Your API key
- `HINDSIGHT_API_LLM_MODEL`: Model name (e.g., o3-mini, claude-sonnet-4-20250514)
+2
View File
@@ -81,6 +81,8 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \
ghcr.io/vectorize-io/hindsight:latest
```
You can modify the LLM provider by setting `HINDSIGHT_API_LLM_PROVIDER`. Valid options are `openai`, `anthropic`, `gemini`, `groq`, `ollama`, and `lmstudio`. The documentation provides more details on [supported models](https://hindsight.vectorize.io/developer/models).
API: http://localhost:8888
UI: http://localhost:9999
+2 -2
View File
@@ -2,8 +2,8 @@ apiVersion: v2
name: hindsight
description: Hindsight helm chart
type: application
version: 0.1.14
appVersion: "0.1.14"
version: 0.1.16
appVersion: "0.1.16"
keywords:
- ai
- memory
+1 -1
View File
@@ -80,7 +80,7 @@ Configure via environment variables:
| Variable | Description | Default |
|----------|-------------|---------|
| `HINDSIGHT_API_DATABASE_URL` | PostgreSQL connection string | `pg0` (embedded) |
| `HINDSIGHT_API_LLM_PROVIDER` | `openai`, `groq`, `gemini`, `ollama` | `openai` |
| `HINDSIGHT_API_LLM_PROVIDER` | `openai`, `anthropic`, `gemini`, `groq`, `ollama`, `lmstudio` | `openai` |
| `HINDSIGHT_API_LLM_API_KEY` | API key for LLM provider | - |
| `HINDSIGHT_API_LLM_MODEL` | Model name | `gpt-4o-mini` |
| `HINDSIGHT_API_HOST` | Server bind address | `0.0.0.0` |
+37 -13
View File
@@ -5,6 +5,7 @@ Provides both HTTP REST API and MCP (Model Context Protocol) server.
"""
import logging
from contextlib import asynccontextmanager
from typing import Optional
from fastapi import FastAPI
@@ -45,6 +46,18 @@ def create_app(
# Both HTTP and MCP
app = create_app(memory, mcp_api_enabled=True)
"""
mcp_app = None
# Create MCP app first if enabled (we need its lifespan for chaining)
if mcp_api_enabled:
try:
from .mcp import create_mcp_app
mcp_app = create_mcp_app(memory=memory)
except ImportError as e:
logger.error(f"MCP server requested but dependencies not available: {e}")
logger.error("Install with: pip install hindsight-api[mcp]")
raise
# Import and create HTTP API if enabled
if http_api_enabled:
@@ -57,20 +70,31 @@ def create_app(
app = FastAPI(title="Hindsight API", version="0.0.7")
logger.info("HTTP REST API disabled")
# Mount MCP server if enabled
if mcp_api_enabled:
try:
from .mcp import create_mcp_app
# Mount MCP server and chain its lifespan if enabled
if mcp_app is not None:
# Get the MCP app's underlying Starlette app for lifespan access
mcp_starlette_app = mcp_app.mcp_app
# Create MCP app with dynamic bank_id support
# Supports: /mcp/{bank_id}/sse (bank-specific SSE endpoint)
mcp_app = create_mcp_app(memory=memory)
app.mount(mcp_mount_path, mcp_app)
logger.info(f"MCP server enabled at {mcp_mount_path}/{{bank_id}}/sse")
except ImportError as e:
logger.error(f"MCP server requested but dependencies not available: {e}")
logger.error("Install with: pip install hindsight-api[mcp]")
raise
# Store the original lifespan
original_lifespan = app.router.lifespan_context
@asynccontextmanager
async def chained_lifespan(app_instance: FastAPI):
"""Chain the MCP lifespan with the main app lifespan."""
# Start MCP lifespan first
async with mcp_starlette_app.router.lifespan_context(mcp_starlette_app):
logger.info("MCP lifespan started")
# Then start the original app lifespan
async with original_lifespan(app_instance):
yield
logger.info("MCP lifespan stopped")
# Replace the app's lifespan with the chained version
app.router.lifespan_context = chained_lifespan
# Mount the MCP middleware
app.mount(mcp_mount_path, mcp_app)
logger.info(f"MCP server enabled at {mcp_mount_path}/{{bank_id}}/mcp")
return app
+232 -29
View File
@@ -14,6 +14,8 @@ from typing import Any
from fastapi import Depends, FastAPI, Header, HTTPException, Query
from hindsight_api.extensions import AuthenticationError
def _parse_metadata(metadata: Any) -> dict[str, Any]:
"""Parse metadata that may be a dict, JSON string, or None."""
@@ -35,7 +37,7 @@ from hindsight_api import MemoryEngine
from hindsight_api.engine.db_utils import acquire_with_retry
from hindsight_api.engine.memory_engine import Budget, fq_table
from hindsight_api.engine.response_models import VALID_RECALL_FACT_TYPES
from hindsight_api.extensions import HttpExtension, load_extension
from hindsight_api.extensions import HttpExtension, OperationValidationError, load_extension
from hindsight_api.metrics import create_metrics_collector, get_metrics_collector, initialize_metrics
from hindsight_api.models import RequestContext
@@ -385,7 +387,16 @@ class ReflectRequest(BaseModel):
"query": "What do you think about artificial intelligence?",
"budget": "low",
"context": "This is for a research paper on AI ethics",
"max_tokens": 4096,
"include": {"facts": {}},
"response_schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"key_points": {"type": "array", "items": {"type": "string"}},
},
"required": ["summary", "key_points"],
},
}
}
)
@@ -393,9 +404,14 @@ class ReflectRequest(BaseModel):
query: str
budget: Budget = Budget.LOW
context: str | None = None
max_tokens: int = Field(default=4096, description="Maximum tokens for the response")
include: ReflectIncludeOptions = Field(
default_factory=ReflectIncludeOptions, description="Options for including additional data (disabled by default)"
)
response_schema: dict | None = Field(
default=None,
description="Optional JSON Schema for structured output. When provided, the response will include a 'structured_output' field with the LLM response parsed according to this schema.",
)
class OpinionItem(BaseModel):
@@ -440,12 +456,20 @@ class ReflectResponse(BaseModel):
{"id": "123", "text": "AI is used in healthcare", "type": "world"},
{"id": "456", "text": "I discussed AI applications last week", "type": "experience"},
],
"structured_output": {
"summary": "AI is transformative",
"key_points": ["Used in healthcare", "Discussed recently"],
},
}
}
)
text: str
based_on: list[ReflectFact] = [] # Facts used to generate the response
structured_output: dict | None = Field(
default=None,
description="Structured output parsed according to the request's response_schema. Only present when response_schema was provided in the request.",
)
class BanksResponse(BaseModel):
@@ -689,6 +713,26 @@ class DocumentResponse(BaseModel):
memory_unit_count: int
class DeleteDocumentResponse(BaseModel):
"""Response model for delete document endpoint."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"success": True,
"message": "Document 'session_1' and 5 associated memory units deleted successfully",
"document_id": "session_1",
"memory_units_deleted": 5,
}
}
)
success: bool
message: str
document_id: str
memory_units_deleted: int
class ChunkResponse(BaseModel):
"""Response model for get chunk endpoint."""
@@ -725,6 +769,108 @@ class DeleteResponse(BaseModel):
deleted_count: int | None = None
class BankStatsResponse(BaseModel):
"""Response model for bank statistics endpoint."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"bank_id": "user123",
"total_nodes": 150,
"total_links": 300,
"total_documents": 10,
"nodes_by_fact_type": {"fact": 100, "preference": 30, "observation": 20},
"links_by_link_type": {"temporal": 150, "semantic": 100, "entity": 50},
"links_by_fact_type": {"fact": 200, "preference": 60, "observation": 40},
"links_breakdown": {"fact": {"temporal": 100, "semantic": 60, "entity": 40}},
"pending_operations": 2,
"failed_operations": 0,
}
}
)
bank_id: str
total_nodes: int
total_links: int
total_documents: int
nodes_by_fact_type: dict[str, int]
links_by_link_type: dict[str, int]
links_by_fact_type: dict[str, int]
links_breakdown: dict[str, dict[str, int]]
pending_operations: int
failed_operations: int
class OperationResponse(BaseModel):
"""Response model for a single async operation."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"task_type": "retain",
"items_count": 5,
"document_id": "meeting-notes-2024",
"created_at": "2024-01-15T10:30:00Z",
"status": "pending",
"error_message": None,
}
}
)
id: str
task_type: str
items_count: int
document_id: str | None
created_at: str
status: str
error_message: str | None
class OperationsListResponse(BaseModel):
"""Response model for list operations endpoint."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"bank_id": "user123",
"operations": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"task_type": "retain",
"items_count": 5,
"document_id": None,
"created_at": "2024-01-15T10:30:00Z",
"status": "pending",
"error_message": None,
}
],
}
}
)
bank_id: str
operations: list[OperationResponse]
class CancelOperationResponse(BaseModel):
"""Response model for cancel operation endpoint."""
model_config = ConfigDict(
json_schema_extra={
"example": {
"success": True,
"message": "Operation 550e8400-e29b-41d4-a716-446655440000 cancelled",
"operation_id": "550e8400-e29b-41d4-a716-446655440000",
}
}
)
success: bool
message: str
operation_id: str
def create_app(
memory: MemoryEngine,
initialize_memory: bool = True,
@@ -845,6 +991,16 @@ def _register_routes(app: FastAPI):
api_key = authorization.strip()
return RequestContext(api_key=api_key)
# Global exception handler for authentication errors
@app.exception_handler(AuthenticationError)
async def authentication_error_handler(request, exc: AuthenticationError):
from fastapi.responses import JSONResponse
return JSONResponse(
status_code=401,
content={"detail": str(exc)},
)
@app.get(
"/health",
summary="Health check endpoint",
@@ -892,6 +1048,8 @@ def _register_routes(app: FastAPI):
try:
data = await app.state.memory.get_graph_data(bank_id, type, request_context=request_context)
return data
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -938,6 +1096,8 @@ def _register_routes(app: FastAPI):
request_context=request_context,
)
return data
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1054,6 +1214,10 @@ def _register_routes(app: FastAPI):
)
except HTTPException:
raise
except OperationValidationError as e:
raise HTTPException(status_code=e.status_code, detail=e.reason)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1089,6 +1253,8 @@ def _register_routes(app: FastAPI):
query=request.query,
budget=request.budget,
context=request.context,
max_tokens=request.max_tokens,
response_schema=request.response_schema,
request_context=request_context,
)
@@ -1111,8 +1277,13 @@ def _register_routes(app: FastAPI):
return ReflectResponse(
text=core_result.text,
based_on=based_on_facts,
structured_output=core_result.structured_output,
)
except OperationValidationError as e:
raise HTTPException(status_code=e.status_code, detail=e.reason)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1133,6 +1304,8 @@ def _register_routes(app: FastAPI):
try:
banks = await app.state.memory.list_banks(request_context=request_context)
return BankListResponse(banks=banks)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1142,6 +1315,7 @@ def _register_routes(app: FastAPI):
@app.get(
"/v1/default/banks/{bank_id}/stats",
response_model=BankStatsResponse,
summary="Get statistics for memory bank",
description="Get statistics about nodes and links for a specific agent",
operation_id="get_agent_stats",
@@ -1242,19 +1416,21 @@ def _register_routes(app: FastAPI):
total_nodes = sum(nodes_by_type.values())
total_links = sum(links_by_type.values())
return {
"bank_id": bank_id,
"total_nodes": total_nodes,
"total_links": total_links,
"total_documents": total_documents,
"nodes_by_fact_type": nodes_by_type,
"links_by_link_type": links_by_type,
"links_by_fact_type": links_by_fact_type,
"links_breakdown": links_breakdown,
"pending_operations": pending_operations,
"failed_operations": failed_operations,
}
return BankStatsResponse(
bank_id=bank_id,
total_nodes=total_nodes,
total_links=total_links,
total_documents=total_documents,
nodes_by_fact_type=nodes_by_type,
links_by_link_type=links_by_type,
links_by_fact_type=links_by_fact_type,
links_breakdown=links_breakdown,
pending_operations=pending_operations,
failed_operations=failed_operations,
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1279,6 +1455,8 @@ def _register_routes(app: FastAPI):
try:
entities = await app.state.memory.list_entities(bank_id, limit=limit, request_context=request_context)
return EntityListResponse(items=[EntityListItem(**e) for e in entities])
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1316,7 +1494,7 @@ def _register_routes(app: FastAPI):
for obs in entity["observations"]
],
)
except HTTPException:
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1369,7 +1547,7 @@ def _register_routes(app: FastAPI):
for obs in entity["observations"]
],
)
except HTTPException:
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1407,6 +1585,8 @@ def _register_routes(app: FastAPI):
bank_id=bank_id, search_query=q, limit=limit, offset=offset, request_context=request_context
)
return data
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1437,7 +1617,7 @@ def _register_routes(app: FastAPI):
if not document:
raise HTTPException(status_code=404, detail="Document not found")
return document
except HTTPException:
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1466,7 +1646,7 @@ def _register_routes(app: FastAPI):
if not chunk:
raise HTTPException(status_code=404, detail="Chunk not found")
return chunk
except HTTPException:
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1477,6 +1657,7 @@ def _register_routes(app: FastAPI):
@app.delete(
"/v1/default/banks/{bank_id}/documents/{document_id}",
response_model=DeleteDocumentResponse,
summary="Delete a document",
description="Delete a document and all its associated memory units and links.\n\n"
"This will cascade delete:\n"
@@ -1503,13 +1684,13 @@ def _register_routes(app: FastAPI):
if result["document_deleted"] == 0:
raise HTTPException(status_code=404, detail="Document not found")
return {
"success": True,
"message": f"Document '{document_id}' and {result['memory_units_deleted']} associated memory units deleted successfully",
"document_id": document_id,
"memory_units_deleted": result["memory_units_deleted"],
}
except HTTPException:
return DeleteDocumentResponse(
success=True,
message=f"Document '{document_id}' and {result['memory_units_deleted']} associated memory units deleted successfully",
document_id=document_id,
memory_units_deleted=result["memory_units_deleted"],
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1520,6 +1701,7 @@ def _register_routes(app: FastAPI):
@app.get(
"/v1/default/banks/{bank_id}/operations",
response_model=OperationsListResponse,
summary="List async operations",
description="Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations",
operation_id="list_operations",
@@ -1529,10 +1711,12 @@ def _register_routes(app: FastAPI):
"""List all async operations (pending and failed) for a memory bank."""
try:
operations = await app.state.memory.list_operations(bank_id, request_context=request_context)
return {
"bank_id": bank_id,
"operations": operations,
}
return OperationsListResponse(
bank_id=bank_id,
operations=[OperationResponse(**op) for op in operations],
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1542,6 +1726,7 @@ def _register_routes(app: FastAPI):
@app.delete(
"/v1/default/banks/{bank_id}/operations/{operation_id}",
response_model=CancelOperationResponse,
summary="Cancel a pending async operation",
description="Cancel a pending async operation by removing it from the queue",
operation_id="cancel_operation",
@@ -1559,9 +1744,11 @@ def _register_routes(app: FastAPI):
raise HTTPException(status_code=400, detail=f"Invalid operation_id format: {operation_id}")
result = await app.state.memory.cancel_operation(bank_id, operation_id, request_context=request_context)
return result
return CancelOperationResponse(**result)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1593,6 +1780,8 @@ def _register_routes(app: FastAPI):
disposition=DispositionTraits(**disposition_dict),
background=profile["background"],
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1631,6 +1820,8 @@ def _register_routes(app: FastAPI):
disposition=DispositionTraits(**disposition_dict),
background=profile["background"],
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1660,6 +1851,8 @@ def _register_routes(app: FastAPI):
response.disposition = DispositionTraits(**result["disposition"])
return response
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1711,6 +1904,8 @@ def _register_routes(app: FastAPI):
disposition=DispositionTraits(**disposition_dict),
background=final_profile["background"],
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1738,6 +1933,8 @@ def _register_routes(app: FastAPI):
+ result.get("entities_deleted", 0)
+ result.get("documents_deleted", 0),
)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1812,6 +2009,10 @@ def _register_routes(app: FastAPI):
return RetainResponse.model_validate(
{"success": True, "bank_id": bank_id, "items_count": len(contents), "async": False}
)
except OperationValidationError as e:
raise HTTPException(status_code=e.status_code, detail=e.reason)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
@@ -1850,6 +2051,8 @@ def _register_routes(app: FastAPI):
await app.state.memory.delete_bank(bank_id, fact_type=type, request_context=request_context)
return DeleteResponse(success=True)
except (AuthenticationError, HTTPException):
raise
except Exception as e:
import traceback
+32
View File
@@ -8,6 +8,11 @@ import logging
import os
from dataclasses import dataclass
from dotenv import find_dotenv, load_dotenv
# Load .env file, searching current and parent directories (overrides existing env vars)
load_dotenv(find_dotenv(usecwd=True), override=True)
logger = logging.getLogger(__name__)
# Environment variable names
@@ -16,6 +21,8 @@ ENV_LLM_PROVIDER = "HINDSIGHT_API_LLM_PROVIDER"
ENV_LLM_API_KEY = "HINDSIGHT_API_LLM_API_KEY"
ENV_LLM_MODEL = "HINDSIGHT_API_LLM_MODEL"
ENV_LLM_BASE_URL = "HINDSIGHT_API_LLM_BASE_URL"
ENV_LLM_MAX_CONCURRENT = "HINDSIGHT_API_LLM_MAX_CONCURRENT"
ENV_LLM_TIMEOUT = "HINDSIGHT_API_LLM_TIMEOUT"
ENV_EMBEDDINGS_PROVIDER = "HINDSIGHT_API_EMBEDDINGS_PROVIDER"
ENV_EMBEDDINGS_LOCAL_MODEL = "HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL"
@@ -33,6 +40,10 @@ ENV_GRAPH_RETRIEVER = "HINDSIGHT_API_GRAPH_RETRIEVER"
ENV_MCP_LOCAL_BANK_ID = "HINDSIGHT_API_MCP_LOCAL_BANK_ID"
ENV_MCP_INSTRUCTIONS = "HINDSIGHT_API_MCP_INSTRUCTIONS"
# Observation thresholds
ENV_OBSERVATION_MIN_FACTS = "HINDSIGHT_API_OBSERVATION_MIN_FACTS"
ENV_OBSERVATION_TOP_ENTITIES = "HINDSIGHT_API_OBSERVATION_TOP_ENTITIES"
# Optimization flags
ENV_SKIP_LLM_VERIFICATION = "HINDSIGHT_API_SKIP_LLM_VERIFICATION"
ENV_LAZY_RERANKER = "HINDSIGHT_API_LAZY_RERANKER"
@@ -41,6 +52,8 @@ ENV_LAZY_RERANKER = "HINDSIGHT_API_LAZY_RERANKER"
DEFAULT_DATABASE_URL = "pg0"
DEFAULT_LLM_PROVIDER = "openai"
DEFAULT_LLM_MODEL = "gpt-5-mini"
DEFAULT_LLM_MAX_CONCURRENT = 32
DEFAULT_LLM_TIMEOUT = 120.0 # seconds
DEFAULT_EMBEDDINGS_PROVIDER = "local"
DEFAULT_EMBEDDINGS_LOCAL_MODEL = "BAAI/bge-small-en-v1.5"
@@ -55,6 +68,10 @@ DEFAULT_MCP_ENABLED = True
DEFAULT_GRAPH_RETRIEVER = "bfs" # Options: "bfs", "mpfp"
DEFAULT_MCP_LOCAL_BANK_ID = "mcp"
# Observation thresholds
DEFAULT_OBSERVATION_MIN_FACTS = 5 # Min facts required to generate entity observations
DEFAULT_OBSERVATION_TOP_ENTITIES = 5 # Max entities to process per retain batch
# Default MCP tool descriptions (can be customized via env vars)
DEFAULT_MCP_RETAIN_DESCRIPTION = """Store important information to long-term memory.
@@ -91,6 +108,8 @@ class HindsightConfig:
llm_api_key: str | None
llm_model: str
llm_base_url: str | None
llm_max_concurrent: int
llm_timeout: float
# Embeddings
embeddings_provider: str
@@ -111,6 +130,10 @@ class HindsightConfig:
# Recall
graph_retriever: str
# Observation thresholds
observation_min_facts: int
observation_top_entities: int
# Optimization flags
skip_llm_verification: bool
lazy_reranker: bool
@@ -126,6 +149,8 @@ class HindsightConfig:
llm_api_key=os.getenv(ENV_LLM_API_KEY),
llm_model=os.getenv(ENV_LLM_MODEL, DEFAULT_LLM_MODEL),
llm_base_url=os.getenv(ENV_LLM_BASE_URL) or None,
llm_max_concurrent=int(os.getenv(ENV_LLM_MAX_CONCURRENT, str(DEFAULT_LLM_MAX_CONCURRENT))),
llm_timeout=float(os.getenv(ENV_LLM_TIMEOUT, str(DEFAULT_LLM_TIMEOUT))),
# Embeddings
embeddings_provider=os.getenv(ENV_EMBEDDINGS_PROVIDER, DEFAULT_EMBEDDINGS_PROVIDER),
embeddings_local_model=os.getenv(ENV_EMBEDDINGS_LOCAL_MODEL, DEFAULT_EMBEDDINGS_LOCAL_MODEL),
@@ -144,6 +169,11 @@ class HindsightConfig:
# Optimization flags
skip_llm_verification=os.getenv(ENV_SKIP_LLM_VERIFICATION, "false").lower() == "true",
lazy_reranker=os.getenv(ENV_LAZY_RERANKER, "false").lower() == "true",
# Observation thresholds
observation_min_facts=int(os.getenv(ENV_OBSERVATION_MIN_FACTS, str(DEFAULT_OBSERVATION_MIN_FACTS))),
observation_top_entities=int(
os.getenv(ENV_OBSERVATION_TOP_ENTITIES, str(DEFAULT_OBSERVATION_TOP_ENTITIES))
),
)
def get_llm_base_url(self) -> str:
@@ -156,6 +186,8 @@ class HindsightConfig:
return "https://api.groq.com/openai/v1"
elif provider == "ollama":
return "http://localhost:11434/v1"
elif provider == "lmstudio":
return "http://localhost:1234/v1"
else:
return ""
@@ -110,6 +110,8 @@ class MemoryEngineInterface(ABC):
*,
budget: "Budget | None" = None,
context: str | None = None,
max_tokens: int = 4096,
response_schema: dict | None = None,
request_context: "RequestContext",
) -> "ReflectResult":
"""
@@ -120,6 +122,8 @@ class MemoryEngineInterface(ABC):
query: The question to reflect on.
budget: Search budget for retrieving context.
context: Additional context for the reflection.
max_tokens: Maximum tokens for the response.
response_schema: Optional JSON Schema for structured output.
request_context: Request context for authentication.
Returns:
+258 -44
View File
@@ -15,6 +15,13 @@ from google.genai import errors as genai_errors
from google.genai import types as genai_types
from openai import APIConnectionError, APIStatusError, AsyncOpenAI, LengthFinishReasonError
from ..config import (
DEFAULT_LLM_MAX_CONCURRENT,
DEFAULT_LLM_TIMEOUT,
ENV_LLM_MAX_CONCURRENT,
ENV_LLM_TIMEOUT,
)
# Seed applied to every Groq request for deterministic behavior.
DEFAULT_LLM_SEED = 4242
@@ -24,7 +31,9 @@ logger = logging.getLogger(__name__)
logging.getLogger("httpx").setLevel(logging.WARNING)
# Global semaphore to limit concurrent LLM requests across all instances
_global_llm_semaphore = asyncio.Semaphore(32)
# Set HINDSIGHT_API_LLM_MAX_CONCURRENT=1 for local LLMs (LM Studio, Ollama)
_llm_max_concurrent = int(os.getenv(ENV_LLM_MAX_CONCURRENT, str(DEFAULT_LLM_MAX_CONCURRENT)))
_global_llm_semaphore = asyncio.Semaphore(_llm_max_concurrent)
class OutputTooLongError(Exception):
@@ -58,7 +67,7 @@ class LLMProvider:
Initialize LLM provider.
Args:
provider: Provider name ("openai", "groq", "ollama", "gemini").
provider: Provider name ("openai", "groq", "ollama", "gemini", "anthropic", "lmstudio").
api_key: API key.
base_url: Base URL for the API.
model: Model name.
@@ -71,7 +80,7 @@ class LLMProvider:
self.reasoning_effort = reasoning_effort
# Validate provider
valid_providers = ["openai", "groq", "ollama", "gemini"]
valid_providers = ["openai", "groq", "ollama", "gemini", "anthropic", "lmstudio"]
if self.provider not in valid_providers:
raise ValueError(f"Invalid LLM provider: {self.provider}. Must be one of: {', '.join(valid_providers)}")
@@ -81,25 +90,48 @@ class LLMProvider:
self.base_url = "https://api.groq.com/openai/v1"
elif self.provider == "ollama":
self.base_url = "http://localhost:11434/v1"
elif self.provider == "lmstudio":
self.base_url = "http://localhost:1234/v1"
# Validate API key (not needed for ollama)
if self.provider != "ollama" and not self.api_key:
# Validate API key (not needed for ollama or lmstudio)
if self.provider not in ("ollama", "lmstudio") and not self.api_key:
raise ValueError(f"API key not found for {self.provider}")
# Get timeout config (set HINDSIGHT_API_LLM_TIMEOUT for local LLMs that need longer timeouts)
self.timeout = float(os.getenv(ENV_LLM_TIMEOUT, str(DEFAULT_LLM_TIMEOUT)))
# Create client based on provider
self._client = None
self._gemini_client = None
self._anthropic_client = None
if self.provider == "gemini":
self._gemini_client = genai.Client(api_key=self.api_key)
self._client = None
elif self.provider == "ollama":
self._client = AsyncOpenAI(api_key="ollama", base_url=self.base_url, max_retries=0)
self._gemini_client = None
elif self.provider == "anthropic":
from anthropic import AsyncAnthropic
# Only pass base_url if it's set (Anthropic uses default URL otherwise)
anthropic_kwargs = {"api_key": self.api_key}
if self.base_url:
anthropic_kwargs["base_url"] = self.base_url
if self.timeout:
anthropic_kwargs["timeout"] = self.timeout
self._anthropic_client = AsyncAnthropic(**anthropic_kwargs)
elif self.provider in ("ollama", "lmstudio"):
# Use dummy key if not provided for local
api_key = self.api_key or "local"
client_kwargs = {"api_key": api_key, "base_url": self.base_url, "max_retries": 0}
if self.timeout:
client_kwargs["timeout"] = self.timeout
self._client = AsyncOpenAI(**client_kwargs)
else:
# Only pass base_url if it's set (OpenAI uses default URL otherwise)
client_kwargs = {"api_key": self.api_key, "max_retries": 0}
if self.base_url:
client_kwargs["base_url"] = self.base_url
self._client = AsyncOpenAI(**client_kwargs) # type: ignore[invalid-argument-type] - dict kwargs
self._gemini_client = None
if self.timeout:
client_kwargs["timeout"] = self.timeout
self._client = AsyncOpenAI(**client_kwargs)
async def verify_connection(self) -> None:
"""
@@ -135,6 +167,7 @@ class LLMProvider:
initial_backoff: float = 1.0,
max_backoff: float = 60.0,
skip_validation: bool = False,
strict_schema: bool = False,
) -> Any:
"""
Make an LLM API call with retry logic.
@@ -149,6 +182,7 @@ class LLMProvider:
initial_backoff: Initial backoff time in seconds.
max_backoff: Maximum backoff time in seconds.
skip_validation: Return raw JSON without Pydantic validation.
strict_schema: Use strict JSON schema enforcement (OpenAI only). Guarantees all required fields.
Returns:
Parsed response if response_format is provided, otherwise text content.
@@ -166,6 +200,19 @@ class LLMProvider:
messages, response_format, max_retries, initial_backoff, max_backoff, skip_validation, start_time
)
# Handle Anthropic provider separately
if self.provider == "anthropic":
return await self._call_anthropic(
messages,
response_format,
max_completion_tokens,
max_retries,
initial_backoff,
max_backoff,
skip_validation,
start_time,
)
# Handle Ollama with native API for structured output (better schema enforcement)
if self.provider == "ollama" and response_format is not None:
return await self._call_ollama_native(
@@ -226,47 +273,78 @@ class LLMProvider:
for attempt in range(max_retries + 1):
try:
if response_format is not None:
# Add schema to system message for JSON mode
schema = None
if hasattr(response_format, "model_json_schema"):
schema = response_format.model_json_schema()
schema_msg = f"\n\nYou must respond with valid JSON matching this schema:\n{json.dumps(schema, indent=2)}"
if call_params["messages"] and call_params["messages"][0].get("role") == "system":
call_params["messages"][0]["content"] += schema_msg
elif call_params["messages"]:
call_params["messages"][0]["content"] = (
schema_msg + "\n\n" + call_params["messages"][0]["content"]
)
if strict_schema and schema is not None:
# Use OpenAI's strict JSON schema enforcement
# This guarantees all required fields are returned
call_params["response_format"] = {
"type": "json_schema",
"json_schema": {
"name": "response",
"strict": True,
"schema": schema,
},
}
else:
# Soft enforcement: add schema to prompt and use json_object mode
if schema is not None:
schema_msg = f"\n\nYou must respond with valid JSON matching this schema:\n{json.dumps(schema, indent=2)}"
call_params["response_format"] = {"type": "json_object"}
if call_params["messages"] and call_params["messages"][0].get("role") == "system":
call_params["messages"][0]["content"] += schema_msg
elif call_params["messages"]:
call_params["messages"][0]["content"] = (
schema_msg + "\n\n" + call_params["messages"][0]["content"]
)
if self.provider not in ("lmstudio", "ollama"):
call_params["response_format"] = {"type": "json_object"}
logger.debug(f"Sending request to {self.provider}/{self.model} (timeout={self.timeout})")
response = await self._client.chat.completions.create(**call_params)
logger.debug(f"Received response from {self.provider}/{self.model}")
content = response.choices[0].message.content
# Log raw LLM response for debugging JSON parse issues
try:
json_data = json.loads(content)
except json.JSONDecodeError as json_err:
# Truncate content for logging (first 500 and last 200 chars)
content_preview = content[:500] if content else "<empty>"
if content and len(content) > 700:
content_preview = f"{content[:500]}...TRUNCATED...{content[-200:]}"
logger.warning(
f"JSON parse error from LLM response (attempt {attempt + 1}/{max_retries + 1}): {json_err}\n"
f" Model: {self.provider}/{self.model}\n"
f" Content length: {len(content) if content else 0} chars\n"
f" Content preview: {content_preview!r}\n"
f" Finish reason: {response.choices[0].finish_reason if response.choices else 'unknown'}"
)
# Retry on JSON parse errors - LLM may return valid JSON on next attempt
if attempt < max_retries:
backoff = min(initial_backoff * (2**attempt), max_backoff)
await asyncio.sleep(backoff)
last_exception = json_err
continue
else:
logger.error(f"JSON parse error after {max_retries + 1} attempts, giving up")
raise
# For local models, they may wrap JSON in markdown code blocks
if self.provider in ("lmstudio", "ollama"):
clean_content = content
if "```json" in content:
clean_content = content.split("```json")[1].split("```")[0].strip()
elif "```" in content:
clean_content = content.split("```")[1].split("```")[0].strip()
try:
json_data = json.loads(clean_content)
except json.JSONDecodeError:
# Fallback to parsing raw content
json_data = json.loads(content)
else:
# Log raw LLM response for debugging JSON parse issues
try:
json_data = json.loads(content)
except json.JSONDecodeError as json_err:
# Truncate content for logging (first 500 and last 200 chars)
content_preview = content[:500] if content else "<empty>"
if content and len(content) > 700:
content_preview = f"{content[:500]}...TRUNCATED...{content[-200:]}"
logger.warning(
f"JSON parse error from LLM response (attempt {attempt + 1}/{max_retries + 1}): {json_err}\n"
f" Model: {self.provider}/{self.model}\n"
f" Content length: {len(content) if content else 0} chars\n"
f" Content preview: {content_preview!r}\n"
f" Finish reason: {response.choices[0].finish_reason if response.choices else 'unknown'}"
)
# Retry on JSON parse errors - LLM may return valid JSON on next attempt
if attempt < max_retries:
backoff = min(initial_backoff * (2**attempt), max_backoff)
await asyncio.sleep(backoff)
last_exception = json_err
continue
else:
logger.error(f"JSON parse error after {max_retries + 1} attempts, giving up")
raise
if skip_validation:
result = json_data
@@ -339,6 +417,142 @@ class LLMProvider:
raise last_exception
raise RuntimeError("LLM call failed after all retries with no exception captured")
async def _call_anthropic(
self,
messages: list[dict[str, str]],
response_format: Any | None,
max_completion_tokens: int | None,
max_retries: int,
initial_backoff: float,
max_backoff: float,
skip_validation: bool,
start_time: float,
) -> Any:
"""Handle Anthropic-specific API calls."""
from anthropic import APIConnectionError, APIStatusError, RateLimitError
# Convert OpenAI-style messages to Anthropic format
system_prompt = None
anthropic_messages = []
for msg in messages:
role = msg.get("role", "user")
content = msg.get("content", "")
if role == "system":
if system_prompt:
system_prompt += "\n\n" + content
else:
system_prompt = content
else:
anthropic_messages.append({"role": role, "content": content})
# Add JSON schema instruction if response_format is provided
if response_format is not None and hasattr(response_format, "model_json_schema"):
schema = response_format.model_json_schema()
schema_msg = f"\n\nYou must respond with valid JSON matching this schema:\n{json.dumps(schema, indent=2)}"
if system_prompt:
system_prompt += schema_msg
else:
system_prompt = schema_msg
# Prepare parameters
call_params = {
"model": self.model,
"messages": anthropic_messages,
"max_tokens": max_completion_tokens if max_completion_tokens is not None else 4096,
}
if system_prompt:
call_params["system"] = system_prompt
last_exception = None
for attempt in range(max_retries + 1):
try:
response = await self._anthropic_client.messages.create(**call_params)
# Anthropic response content is a list of blocks
content = ""
for block in response.content:
if block.type == "text":
content += block.text
if response_format is not None:
# Models may wrap JSON in markdown code blocks
clean_content = content
if "```json" in content:
clean_content = content.split("```json")[1].split("```")[0].strip()
elif "```" in content:
clean_content = content.split("```")[1].split("```")[0].strip()
try:
json_data = json.loads(clean_content)
except json.JSONDecodeError:
# Fallback to parsing raw content if markdown stripping failed
json_data = json.loads(content)
if skip_validation:
result = json_data
else:
result = response_format.model_validate(json_data)
else:
result = content
# Log slow calls
duration = time.time() - start_time
if duration > 10.0:
input_tokens = response.usage.input_tokens
output_tokens = response.usage.output_tokens
logger.info(
f"slow llm call: model={self.provider}/{self.model}, "
f"input_tokens={input_tokens}, output_tokens={output_tokens}, "
f"time={duration:.3f}s"
)
return result
except json.JSONDecodeError as e:
last_exception = e
if attempt < max_retries:
logger.warning("Anthropic returned invalid JSON, retrying...")
backoff = min(initial_backoff * (2**attempt), max_backoff)
await asyncio.sleep(backoff)
continue
else:
logger.error(f"Anthropic returned invalid JSON after {max_retries + 1} attempts")
raise
except (APIConnectionError, RateLimitError, APIStatusError) as e:
# Fast fail on 401/403
if isinstance(e, APIStatusError) and e.status_code in (401, 403):
logger.error(f"Anthropic auth error (HTTP {e.status_code}), not retrying: {str(e)}")
raise
last_exception = e
if attempt < max_retries:
# Check if it's a rate limit or server error
should_retry = isinstance(e, (APIConnectionError, RateLimitError)) or (
isinstance(e, APIStatusError) and e.status_code >= 500
)
if should_retry:
backoff = min(initial_backoff * (2**attempt), max_backoff)
jitter = backoff * 0.2 * (2 * (time.time() % 1) - 1)
await asyncio.sleep(backoff + jitter)
continue
logger.error(f"Anthropic API error after {max_retries + 1} attempts: {str(e)}")
raise
except Exception as e:
logger.error(f"Unexpected error during Anthropic call: {type(e).__name__}: {str(e)}")
raise
if last_exception:
raise last_exception
raise RuntimeError("Anthropic call failed after all retries")
async def _call_ollama_native(
self,
messages: list[dict[str, str]],
@@ -17,6 +17,8 @@ import uuid
from datetime import UTC, datetime, timedelta
from typing import TYPE_CHECKING, Any
from ..config import get_config
# Context variable for current schema (async-safe, per-task isolation)
_current_schema: contextvars.ContextVar[str] = contextvars.ContextVar("current_schema", default="public")
@@ -372,7 +374,7 @@ class MemoryEngine(MemoryEngineInterface):
result = await validation_coro
if not result.allowed:
raise OperationValidationError(result.reason or "Operation not allowed")
raise OperationValidationError(result.reason or "Operation not allowed", result.status_code)
async def _authenticate_tenant(self, request_context: "RequestContext | None") -> str:
"""
@@ -399,7 +401,9 @@ class MemoryEngine(MemoryEngineInterface):
if request_context is None:
raise AuthenticationError("RequestContext is required when tenant extension is configured")
# Let AuthenticationError propagate - HTTP layer will convert to 401
tenant_context = await self._tenant_extension.authenticate(request_context)
_current_schema.set(tenant_context.schema_name)
return tenant_context.schema_name
@@ -695,7 +699,14 @@ class MemoryEngine(MemoryEngineInterface):
Returns:
dict with status and optional error message
Note:
Returns unhealthy until initialize() has completed successfully.
"""
# Not healthy until fully initialized
if not self._initialized:
return {"status": "unhealthy", "reason": "not_initialized"}
try:
pool = await self._get_pool()
async with pool.acquire() as conn:
@@ -2818,13 +2829,16 @@ Guidelines:
Handler for form opinion tasks.
Args:
task_dict: Dict with keys: 'bank_id', 'answer_text', 'query'
task_dict: Dict with keys: 'bank_id', 'answer_text', 'query', 'tenant_id'
"""
bank_id = task_dict["bank_id"]
answer_text = task_dict["answer_text"]
query = task_dict["query"]
tenant_id = task_dict.get("tenant_id")
await self._extract_and_store_opinions_async(bank_id=bank_id, answer_text=answer_text, query=query)
await self._extract_and_store_opinions_async(
bank_id=bank_id, answer_text=answer_text, query=query, tenant_id=tenant_id
)
async def _handle_reinforce_opinion(self, task_dict: dict[str, Any]):
"""
@@ -3069,6 +3083,8 @@ Guidelines:
*,
budget: Budget | None = None,
context: str | None = None,
max_tokens: int = 4096,
response_schema: dict | None = None,
request_context: "RequestContext",
) -> ReflectResult:
"""
@@ -3080,19 +3096,22 @@ Guidelines:
3. Retrieves existing opinions (bank's formed perspectives)
4. Uses LLM to formulate an answer
5. Extracts and stores any new opinions formed during reflection
6. Returns plain text answer and the facts used
6. Optionally generates structured output based on response_schema
7. Returns plain text answer and the facts used
Args:
bank_id: bank identifier
query: Question to answer
budget: Budget level for memory exploration (low=100, mid=300, high=600 units)
context: Additional context string to include in LLM prompt (not used in recall)
response_schema: Optional JSON Schema for structured output
Returns:
ReflectResult containing:
- text: Plain text answer (no markdown)
- based_on: Dict with 'world', 'experience', and 'opinion' fact lists (MemoryFact objects)
- new_opinions: List of newly formed opinions
- structured_output: Optional dict if response_schema was provided
"""
# Use cached LLM config
if self._llm_config is None:
@@ -3170,21 +3189,53 @@ Guidelines:
log_buffer.append(f"[REFLECT {reflect_id}] Prompt: {len(prompt)} chars")
system_message = think_utils.get_system_message(disposition)
messages = [{"role": "system", "content": system_message}, {"role": "user", "content": prompt}]
# Prepare response_format if schema provided
response_format = None
if response_schema is not None:
# Wrapper class to provide Pydantic-like interface for raw JSON schemas
class JsonSchemaWrapper:
def __init__(self, schema: dict):
self._schema = schema
def model_json_schema(self):
return self._schema
response_format = JsonSchemaWrapper(response_schema)
llm_start = time.time()
answer_text = await self._llm_config.call(
messages=[{"role": "system", "content": system_message}, {"role": "user", "content": prompt}],
scope="memory_think",
temperature=0.9,
max_completion_tokens=1000,
result = await self._llm_config.call(
messages=messages,
scope="memory_reflect",
max_completion_tokens=max_tokens,
response_format=response_format,
skip_validation=True if response_format else False,
# Don't enforce strict_schema - not all providers support it and may retry forever
# Soft enforcement (schema in prompt + json_object mode) is sufficient
strict_schema=False,
)
llm_time = time.time() - llm_start
answer_text = answer_text.strip()
# Handle response based on whether structured output was requested
if response_schema is not None:
structured_output = result
answer_text = "" # Empty for backward compatibility
log_buffer.append(f"[REFLECT {reflect_id}] Structured output generated")
else:
structured_output = None
answer_text = result.strip()
# Submit form_opinion task for background processing
# Pass tenant_id from request context for internal authentication in background task
await self._task_backend.submit_task(
{"type": "form_opinion", "bank_id": bank_id, "answer_text": answer_text, "query": query}
{
"type": "form_opinion",
"bank_id": bank_id,
"answer_text": answer_text,
"query": query,
"tenant_id": getattr(request_context, "tenant_id", None) if request_context else None,
}
)
total_time = time.time() - reflect_start
@@ -3198,6 +3249,7 @@ Guidelines:
text=answer_text,
based_on={"world": world_results, "experience": agent_results, "opinion": opinion_results},
new_opinions=[], # Opinions are being extracted asynchronously
structured_output=structured_output,
)
# Call post-operation hook if validator is configured
@@ -3221,7 +3273,9 @@ Guidelines:
return result
async def _extract_and_store_opinions_async(self, bank_id: str, answer_text: str, query: str):
async def _extract_and_store_opinions_async(
self, bank_id: str, answer_text: str, query: str, tenant_id: str | None = None
):
"""
Background task to extract and store opinions from think response.
@@ -3231,6 +3285,7 @@ Guidelines:
bank_id: bank IDentifier
answer_text: The generated answer text
query: The original query
tenant_id: Tenant identifier for internal authentication
"""
try:
# Extract opinions from the answer
@@ -3241,10 +3296,11 @@ Guidelines:
from datetime import datetime
current_time = datetime.now(UTC)
# Use internal request context for background tasks
# Use internal context with tenant_id for background authentication
# Extension can check internal=True to bypass normal auth
from hindsight_api.models import RequestContext
internal_context = RequestContext()
internal_context = RequestContext(tenant_id=tenant_id, internal=True)
for opinion in new_opinions:
await self.retain_async(
bank_id=bank_id,
@@ -3565,7 +3621,7 @@ Guidelines:
self,
bank_id: str,
entity_ids: list[str],
min_facts: int = 5,
min_facts: int | None = None,
conn=None,
request_context: "RequestContext | None" = None,
) -> None:
@@ -3577,12 +3633,16 @@ Guidelines:
Args:
bank_id: Bank identifier
entity_ids: List of entity IDs to process
min_facts: Minimum facts required to regenerate observations
min_facts: Minimum facts required to regenerate observations (uses config default if None)
conn: Optional database connection (for transactional atomicity)
"""
if not bank_id or not entity_ids:
return
# Use config default if min_facts not specified
if min_facts is None:
min_facts = get_config().observation_min_facts
# Convert to UUIDs
entity_uuids = [uuid.UUID(eid) if isinstance(eid, str) else eid for eid in entity_ids]
@@ -123,7 +123,8 @@ class ReflectResult(BaseModel):
Result from a reflect operation.
Contains the formulated answer, the facts it was based on (organized by type),
and any new opinions that were formed during the reflection process.
any new opinions that were formed during the reflection process, and optionally
structured output if a response schema was provided.
"""
model_config = ConfigDict(
@@ -145,6 +146,7 @@ class ReflectResult(BaseModel):
"opinion": [],
},
"new_opinions": ["Machine learning has great potential in healthcare"],
"structured_output": {"summary": "ML in healthcare", "confidence": 0.9},
}
}
)
@@ -154,6 +156,10 @@ class ReflectResult(BaseModel):
description="Facts used to formulate the answer, organized by type (world, experience, opinion)"
)
new_opinions: list[str] = Field(default_factory=list, description="List of newly formed opinions during reflection")
structured_output: dict[str, Any] | None = Field(
default=None,
description="Structured output parsed according to the provided response schema. Only present when response_schema was provided.",
)
class Opinion(BaseModel):
@@ -9,6 +9,7 @@ import time
import uuid
from datetime import UTC, datetime
from ...config import get_config
from ..memory_engine import fq_table
from ..search import observation_utils
from . import embedding_utils
@@ -49,8 +50,9 @@ async def regenerate_observations_batch(
entity_links: Entity links from this batch
log_buffer: Optional log buffer for timing
"""
TOP_N_ENTITIES = 5
MIN_FACTS_THRESHOLD = 5
config = get_config()
TOP_N_ENTITIES = config.observation_top_entities
MIN_FACTS_THRESHOLD = config.observation_min_facts
if not entity_links:
return
@@ -106,9 +106,62 @@ async def retain_batch(
)
if not extracted_facts:
# Still need to create document if document_id was provided
async with acquire_with_retry(pool) as conn:
async with conn.transaction():
await fact_storage.ensure_bank_exists(conn, bank_id)
# Handle document tracking even with no facts
if document_id:
combined_content = "\n".join([c.get("content", "") for c in contents_dicts])
retain_params = {}
if contents_dicts:
first_item = contents_dicts[0]
if first_item.get("context"):
retain_params["context"] = first_item["context"]
if first_item.get("event_date"):
retain_params["event_date"] = (
first_item["event_date"].isoformat()
if hasattr(first_item["event_date"], "isoformat")
else str(first_item["event_date"])
)
if first_item.get("metadata"):
retain_params["metadata"] = first_item["metadata"]
await fact_storage.handle_document_tracking(
conn, bank_id, document_id, combined_content, is_first_batch, retain_params
)
else:
# Check for per-item document_ids
from collections import defaultdict
contents_by_doc = defaultdict(list)
for idx, content_dict in enumerate(contents_dicts):
doc_id = content_dict.get("document_id")
if doc_id:
contents_by_doc[doc_id].append((idx, content_dict))
for doc_id, doc_contents in contents_by_doc.items():
combined_content = "\n".join([c.get("content", "") for _, c in doc_contents])
retain_params = {}
if doc_contents:
first_item = doc_contents[0][1]
if first_item.get("context"):
retain_params["context"] = first_item["context"]
if first_item.get("event_date"):
retain_params["event_date"] = (
first_item["event_date"].isoformat()
if hasattr(first_item["event_date"], "isoformat")
else str(first_item["event_date"])
)
if first_item.get("metadata"):
retain_params["metadata"] = first_item["metadata"]
await fact_storage.handle_document_tracking(
conn, bank_id, doc_id, combined_content, is_first_batch, retain_params
)
total_time = time.time() - start_time
logger.info(
f"RETAIN_BATCH COMPLETE: 0 facts extracted from {len(contents)} contents in {total_time:.3f}s (nothing to store)"
f"RETAIN_BATCH COMPLETE: 0 facts extracted from {len(contents)} contents in {total_time:.3f}s (document tracked, no facts)"
)
return [[] for _ in contents]
@@ -98,7 +98,14 @@ class DefaultExtensionContext(ExtensionContext):
"""Run migrations for a specific schema."""
from hindsight_api.migrations import run_migrations
run_migrations(self._database_url, schema=schema)
# Prefer getting URL from memory engine (handles pg0 case where URL is set after init)
db_url = self._database_url
if self._memory_engine is not None:
engine_url = getattr(self._memory_engine, "db_url", None)
if engine_url:
db_url = engine_url
run_migrations(db_url, schema=schema)
def get_memory_engine(self) -> "MemoryEngineInterface":
"""Get the memory engine interface."""
@@ -17,8 +17,9 @@ if TYPE_CHECKING:
class OperationValidationError(Exception):
"""Raised when an operation fails validation."""
def __init__(self, reason: str):
def __init__(self, reason: str, status_code: int = 403):
self.reason = reason
self.status_code = status_code
super().__init__(f"Operation validation failed: {reason}")
@@ -28,6 +29,7 @@ class ValidationResult:
allowed: bool
reason: str | None = None
status_code: int = 403 # Default to Forbidden
@classmethod
def accept(cls) -> "ValidationResult":
@@ -35,9 +37,9 @@ class ValidationResult:
return cls(allowed=True)
@classmethod
def reject(cls, reason: str) -> "ValidationResult":
"""Create a rejected validation result with a reason."""
return cls(allowed=False, reason=reason)
def reject(cls, reason: str, status_code: int = 403) -> "ValidationResult":
"""Create a rejected validation result with a reason and HTTP status code."""
return cls(allowed=False, reason=reason, status_code=status_code)
# =============================================================================
+29 -1
View File
@@ -31,6 +31,7 @@ from .daemon import (
IdleTimeoutMiddleware,
daemonize,
)
from .extensions import DefaultExtensionContext, OperationValidatorExtension, TenantExtension, load_extension
# Filter deprecation warnings from third-party libraries
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated")
@@ -168,6 +169,8 @@ def main():
llm_api_key=config.llm_api_key,
llm_model=config.llm_model,
llm_base_url=config.llm_base_url,
llm_max_concurrent=config.llm_max_concurrent,
llm_timeout=config.llm_timeout,
embeddings_provider=config.embeddings_provider,
embeddings_local_model=config.embeddings_local_model,
embeddings_tei_url=config.embeddings_tei_url,
@@ -179,6 +182,8 @@ def main():
log_level=args.log_level,
mcp_enabled=config.mcp_enabled,
graph_retriever=config.graph_retriever,
observation_min_facts=config.observation_min_facts,
observation_top_entities=config.observation_top_entities,
skip_llm_verification=config.skip_llm_verification,
lazy_reranker=config.lazy_reranker,
)
@@ -191,8 +196,31 @@ def main():
signal.signal(signal.SIGINT, _signal_handler)
signal.signal(signal.SIGTERM, _signal_handler)
# Load operation validator extension if configured
operation_validator = load_extension("OPERATION_VALIDATOR", OperationValidatorExtension)
if operation_validator:
import logging
logging.info(f"Loaded operation validator: {operation_validator.__class__.__name__}")
# Load tenant extension if configured
tenant_extension = load_extension("TENANT", TenantExtension)
if tenant_extension:
import logging
logging.info(f"Loaded tenant extension: {tenant_extension.__class__.__name__}")
# Create MemoryEngine (reads configuration from environment)
_memory = MemoryEngine()
_memory = MemoryEngine(operation_validator=operation_validator, tenant_extension=tenant_extension)
# Set extension context on tenant extension (needed for schema provisioning)
if tenant_extension:
extension_context = DefaultExtensionContext(
database_url=config.database_url,
memory_engine=_memory,
)
tenant_extension.set_context(extension_context)
logging.info("Extension context set on tenant extension")
# Create FastAPI app
app = create_app(
+3
View File
@@ -18,6 +18,9 @@ class RequestContext:
"""
api_key: str | None = None
api_key_id: str | None = None # UUID of the API key used for authentication
tenant_id: str | None = None # Tenant identifier (set by extension after auth)
internal: bool = False # True for background/internal operations (not user-visible)
from pgvector.sqlalchemy import Vector
+2 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "hindsight-api"
version = "0.1.14"
version = "0.1.16"
description = "Hindsight: Agent Memory That Works Like Human Memory"
readme = "README.md"
requires-python = ">=3.11"
@@ -37,6 +37,7 @@ dependencies = [
"opentelemetry-exporter-prometheus>=0.41b0",
"dateparser>=1.2.2",
"google-genai>=1.0.0",
"anthropic>=0.40.0",
]
[project.optional-dependencies]
@@ -608,3 +608,167 @@ async def test_async_retain_parallel(api_client):
assert response.status_code == 200
results = response.json()["results"]
assert len(results) > 0, f"Should find memories for document {i}"
@pytest.mark.asyncio
async def test_reflect_structured_output(api_client):
"""Test reflect endpoint with structured output via response_schema.
When response_schema is provided, the reflect endpoint should return
both the natural language text response and a structured_output field
containing the response parsed according to the provided JSON schema.
"""
test_bank_id = f"reflect_structured_test_{datetime.now().timestamp()}"
# Store some memories to reflect on
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/memories",
json={
"items": [
{
"content": "Alice is a senior machine learning engineer with 8 years of experience.",
"context": "team member info"
},
{
"content": "Bob is a junior data scientist who joined last month.",
"context": "team member info"
},
{
"content": "The team uses Python and TensorFlow for most projects.",
"context": "tech stack"
}
]
}
)
assert response.status_code == 200
# Define a JSON schema for structured output
response_schema = {
"type": "object",
"properties": {
"team_members": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"role": {"type": "string"},
"experience_level": {"type": "string"}
}
}
},
"technologies": {
"type": "array",
"items": {"type": "string"}
},
"summary": {"type": "string"}
},
"required": ["team_members", "summary"]
}
# Call reflect with response_schema
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/reflect",
json={
"query": "Give me an overview of the team and their tech stack",
"response_schema": response_schema
}
)
assert response.status_code == 200
result = response.json()
# Verify text field exists (empty when using structured output)
assert "text" in result
assert result["text"] == ""
# Verify structured output exists and has expected structure
assert "structured_output" in result
assert result["structured_output"] is not None
structured = result["structured_output"]
assert "team_members" in structured
assert "summary" in structured
assert isinstance(structured["team_members"], list)
assert isinstance(structured["summary"], str)
# Verify team members have the expected fields
if len(structured["team_members"]) > 0:
member = structured["team_members"][0]
assert "name" in member or "role" in member # At least some fields should be present
@pytest.mark.asyncio
async def test_reflect_without_structured_output(api_client):
"""Test that reflect works normally without response_schema.
When response_schema is not provided, the structured_output field
should be null/None in the response.
"""
test_bank_id = f"reflect_no_structured_test_{datetime.now().timestamp()}"
# Store a memory
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/memories",
json={
"items": [
{
"content": "The project deadline is next Friday.",
"context": "project timeline"
}
]
}
)
assert response.status_code == 200
# Call reflect without response_schema
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/reflect",
json={
"query": "When is the project deadline?"
}
)
assert response.status_code == 200
result = response.json()
# Verify response has text but structured_output is null
assert "text" in result
assert len(result["text"]) > 0
assert result.get("structured_output") is None
@pytest.mark.asyncio
async def test_reflect_with_max_tokens(api_client):
"""Test reflect endpoint with custom max_tokens parameter.
The max_tokens parameter controls the maximum tokens for the LLM response.
"""
test_bank_id = f"reflect_max_tokens_test_{datetime.now().timestamp()}"
# Store a memory
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/memories",
json={
"items": [
{
"content": "Python is a popular programming language for data science and machine learning.",
"context": "tech"
}
]
}
)
assert response.status_code == 200
# Call reflect with custom max_tokens
response = await api_client.post(
f"/v1/default/banks/{test_bank_id}/reflect",
json={
"query": "What is Python used for?",
"max_tokens": 500
}
)
assert response.status_code == 200
result = response.json()
# Verify response has text
assert "text" in result
assert len(result["text"]) > 0
+101
View File
@@ -173,6 +173,107 @@ async def test_regenerate_entity_observations(memory, request_context):
await conn.execute("DELETE FROM entities WHERE bank_id = $1", bank_id)
@pytest.mark.asyncio
async def test_manual_regenerate_with_few_facts(memory, request_context):
"""
Test that manual regeneration works even with fewer than 5 facts.
This is important because:
- Automatic generation during retain requires MIN_FACTS_THRESHOLD (5)
- But manual regeneration via API should work with any number of facts
- The UI triggers manual regeneration, so it should work regardless of fact count
"""
bank_id = f"test_manual_regen_{datetime.now(timezone.utc).timestamp()}"
try:
# Store only 2 facts - below the automatic threshold
await memory.retain_async(
bank_id=bank_id,
content="Alice works at Google as a senior software engineer.",
context="work info",
event_date=datetime(2024, 1, 15, tzinfo=timezone.utc),
request_context=request_context,
)
await memory.retain_async(
bank_id=bank_id,
content="Alice loves hiking and outdoor photography.",
context="hobbies",
event_date=datetime(2024, 1, 16, tzinfo=timezone.utc),
request_context=request_context,
)
# Find the Alice entity
pool = await memory._get_pool()
async with pool.acquire() as conn:
entity_row = await conn.fetchrow(
"""
SELECT id, canonical_name
FROM entities
WHERE bank_id = $1 AND LOWER(canonical_name) LIKE '%alice%'
LIMIT 1
""",
bank_id
)
assert entity_row is not None, "Alice entity should have been extracted"
entity_id = str(entity_row['id'])
entity_name = entity_row['canonical_name']
# Check fact count - should be < 5
async with pool.acquire() as conn:
fact_count = await conn.fetchval(
"SELECT COUNT(*) FROM unit_entities WHERE entity_id = $1",
entity_row['id']
)
print(f"\n=== Manual Regeneration Test ===")
print(f"Entity: {entity_name} (id: {entity_id})")
print(f"Linked facts: {fact_count}")
# Verify we're testing with fewer than the automatic threshold
assert fact_count < 5, f"Test requires < 5 facts, but entity has {fact_count}"
# Before regeneration - should have no observations (auto threshold not met)
obs_before = await memory.get_entity_observations(bank_id, entity_id, limit=10, request_context=request_context)
print(f"Observations before manual regenerate: {len(obs_before)}")
# Manually regenerate observations - this should work regardless of fact count
created_ids = await memory.regenerate_entity_observations(
bank_id=bank_id,
entity_id=entity_id,
entity_name=entity_name,
request_context=request_context,
)
print(f"Observations created by manual regenerate: {len(created_ids)}")
# Get observations after regeneration
observations = await memory.get_entity_observations(bank_id, entity_id, limit=10, request_context=request_context)
print(f"Observations after manual regenerate: {len(observations)}")
for obs in observations:
print(f" - {obs.text}")
# Manual regeneration should create observations even with < 5 facts
assert len(observations) > 0, \
f"Manual regeneration should create observations even with only {fact_count} facts. " \
f"The LLM should synthesize at least 1 observation from the available facts."
# Verify observations contain relevant content
obs_texts = " ".join([o.text.lower() for o in observations])
assert any(keyword in obs_texts for keyword in ["google", "engineer", "hiking", "photography", "alice"]), \
"Observations should contain relevant information about Alice"
print(f"✓ Manual regeneration works with {fact_count} facts (below automatic threshold of 5)")
finally:
# Cleanup
pool = await memory._get_pool()
async with pool.acquire() as conn:
await conn.execute("DELETE FROM memory_units WHERE bank_id = $1", bank_id)
await conn.execute("DELETE FROM entities WHERE bank_id = $1", bank_id)
@pytest.mark.asyncio
async def test_search_with_include_entities(memory, request_context):
"""
-102
View File
@@ -1,102 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
artifact_name: memora
release_name: memora-linux-x86_64
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
artifact_name: memora
release_name: memora-linux-arm64
- os: macos-latest
target: x86_64-apple-darwin
artifact_name: memora
release_name: memora-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
artifact_name: memora
release_name: memora-macos-arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux ARM64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Strip binary (Linux)
if: runner.os == 'Linux'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Strip binary (macOS)
if: runner.os == 'macOS'
run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.release_name }}
path: target/${{ matrix.target }}/release/${{ matrix.artifact_name }}
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create checksums
run: |
cd artifacts
for dir in */; do
cd "$dir"
sha256sum * > SHA256SUMS
cd ..
done
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
artifacts/memora-linux-x86_64/memora
artifacts/memora-linux-arm64/memora
artifacts/memora-macos-x86_64/memora
artifacts/memora-macos-arm64/memora
artifacts/*/SHA256SUMS
draft: false
prerelease: false
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "hindsight-cli"
version = "0.1.14"
version = "0.1.16"
edition = "2021"
authors = ["Hindsight Team"]
description = "A beautiful CLI for Hindsight - semantic memory system"
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
# CLI smoke test - verifies basic CLI functionality against a running API server
#
# Prerequisites:
# - hindsight CLI must be in PATH or HINDSIGHT_CLI env var set
# - API server must be running at HINDSIGHT_API_URL (default: http://localhost:8888)
#
# Usage:
# ./hindsight-cli/smoke-test.sh
# HINDSIGHT_CLI=/path/to/hindsight ./hindsight-cli/smoke-test.sh
set -e
# Configuration
HINDSIGHT_CLI="${HINDSIGHT_CLI:-hindsight}"
export HINDSIGHT_API_URL="${HINDSIGHT_API_URL:-http://localhost:8888}"
TEST_BANK="cli-smoke-test-$(date +%s)"
echo "=== Hindsight CLI Smoke Test ==="
echo "CLI: $HINDSIGHT_CLI"
echo "API URL: $HINDSIGHT_API_URL"
echo "Test bank: $TEST_BANK"
echo ""
# Helper function
run_test() {
local name="$1"
shift
echo -n "Testing: $name... "
if "$@" > /tmp/cli-test-output.txt 2>&1; then
echo "OK"
return 0
else
echo "FAILED"
echo " Command: $*"
echo " Output:"
cat /tmp/cli-test-output.txt | sed 's/^/ /'
return 1
fi
}
run_test_output() {
local name="$1"
local expected="$2"
shift 2
echo -n "Testing: $name... "
if "$@" > /tmp/cli-test-output.txt 2>&1; then
if grep -qi "$expected" /tmp/cli-test-output.txt; then
echo "OK"
return 0
else
echo "FAILED (expected '$expected' not found)"
echo " Command: $*"
echo " Output:"
cat /tmp/cli-test-output.txt | sed 's/^/ /'
return 1
fi
else
echo "FAILED"
echo " Command: $*"
echo " Output:"
cat /tmp/cli-test-output.txt | sed 's/^/ /'
return 1
fi
}
cleanup() {
echo ""
echo "Cleaning up test bank..."
"$HINDSIGHT_CLI" bank delete "$TEST_BANK" 2>/dev/null || true
}
trap cleanup EXIT
FAILED=0
# Test 1: Version
run_test "version" "$HINDSIGHT_CLI" --version || FAILED=1
# Test 2: Help
run_test "help" "$HINDSIGHT_CLI" --help || FAILED=1
# Test 3: Configure help
run_test "configure help" "$HINDSIGHT_CLI" configure --help || FAILED=1
# Test 4: List banks (JSON output)
run_test "list banks" "$HINDSIGHT_CLI" bank list -o json || FAILED=1
# Test 5: Set bank name (creates the bank)
run_test "set bank name" "$HINDSIGHT_CLI" bank name "$TEST_BANK" "CLI Smoke Test Bank" || FAILED=1
# Test 6: Get bank disposition
run_test_output "get bank disposition" "CLI Smoke Test Bank" "$HINDSIGHT_CLI" bank disposition "$TEST_BANK" || FAILED=1
# Test 7: Retain memory
run_test "retain memory" "$HINDSIGHT_CLI" memory retain "$TEST_BANK" "Alice is a software engineer who loves Rust programming" || FAILED=1
# Test 8: Retain more memories
run_test "retain more memories" "$HINDSIGHT_CLI" memory retain "$TEST_BANK" "Bob is Alice's colleague who prefers Python" || FAILED=1
# Test 9: Recall memories
run_test_output "recall memories" "Alice" "$HINDSIGHT_CLI" memory recall "$TEST_BANK" "Who is Alice?" || FAILED=1
# Test 10: Reflect on memories
run_test_output "reflect" "Alice" "$HINDSIGHT_CLI" memory reflect "$TEST_BANK" "What do you know about Alice?" || FAILED=1
# Test 11: Get bank stats
run_test "bank stats" "$HINDSIGHT_CLI" bank stats "$TEST_BANK" || FAILED=1
# Test 12: List entities
run_test "list entities" "$HINDSIGHT_CLI" entity list "$TEST_BANK" || FAILED=1
# Test 13: List documents
run_test "list documents" "$HINDSIGHT_CLI" document list "$TEST_BANK" || FAILED=1
# Test 14: Clear memories
run_test "clear memories" "$HINDSIGHT_CLI" memory clear "$TEST_BANK" || FAILED=1
# Test 15: Delete bank
run_test "delete bank" "$HINDSIGHT_CLI" bank delete "$TEST_BANK" || FAILED=1
echo ""
if [ $FAILED -eq 0 ]; then
echo "=== All smoke tests passed! ==="
exit 0
else
echo "=== Some smoke tests failed ==="
exit 1
fi
+37 -24
View File
@@ -89,14 +89,14 @@ impl ApiClient {
pub fn list_agents(&self, _verbose: bool) -> Result<Vec<types::BankListItem>> {
self.runtime.block_on(async {
let response = self.client.list_banks().await?;
let response = self.client.list_banks(None).await?;
Ok(response.into_inner().banks)
})
}
pub fn get_profile(&self, agent_id: &str, _verbose: bool) -> Result<types::BankProfileResponse> {
self.runtime.block_on(async {
let response = self.client.get_bank_profile(agent_id).await?;
let response = self.client.get_bank_profile(agent_id, None).await?;
Ok(response.into_inner())
})
}
@@ -105,7 +105,9 @@ impl ApiClient {
self.runtime.block_on(async {
let response = self.client.get_agent_stats(agent_id).await?;
let value = response.into_inner();
let stats: AgentStats = serde_json::from_value(value)?;
// Convert to JSON Value first, then parse into our type
let json_value = serde_json::to_value(&value)?;
let stats: AgentStats = serde_json::from_value(json_value)?;
Ok(stats)
})
}
@@ -117,7 +119,7 @@ impl ApiClient {
background: None,
disposition: None,
};
let response = self.client.create_or_update_bank(agent_id, &request).await?;
let response = self.client.create_or_update_bank(agent_id, None, &request).await?;
Ok(response.into_inner())
})
}
@@ -128,7 +130,7 @@ impl ApiClient {
content: content.to_string(),
update_disposition,
};
let response = self.client.add_bank_background(agent_id, &request).await?;
let response = self.client.add_bank_background(agent_id, None, &request).await?;
Ok(response.into_inner())
})
}
@@ -138,21 +140,21 @@ impl ApiClient {
eprintln!("Request body: {}", serde_json::to_string_pretty(request).unwrap_or_default());
}
self.runtime.block_on(async {
let response = self.client.recall_memories(agent_id, request).await?;
let response = self.client.recall_memories(agent_id, None, request).await?;
Ok(response.into_inner())
})
}
pub fn reflect(&self, agent_id: &str, request: &types::ReflectRequest, _verbose: bool) -> Result<types::ReflectResponse> {
self.runtime.block_on(async {
let response = self.client.reflect(agent_id, request).await?;
let response = self.client.reflect(agent_id, None, request).await?;
Ok(response.into_inner())
})
}
pub fn retain(&self, agent_id: &str, request: &types::RetainRequest, _async_mode: bool, _verbose: bool) -> Result<MemoryPutResult> {
self.runtime.block_on(async {
let response = self.client.retain_memories(agent_id, request).await?;
let response = self.client.retain_memories(agent_id, None, request).await?;
let result = response.into_inner();
Ok(MemoryPutResult {
success: result.success,
@@ -170,7 +172,7 @@ impl ApiClient {
pub fn clear_memories(&self, agent_id: &str, fact_type: Option<&str>, _verbose: bool) -> Result<types::DeleteResponse> {
self.runtime.block_on(async {
let response = self.client.clear_bank_memories(agent_id, fact_type).await?;
let response = self.client.clear_bank_memories(agent_id, None, Some(fact_type)).await?;
Ok(response.into_inner())
})
}
@@ -181,7 +183,8 @@ impl ApiClient {
agent_id,
limit.map(|l| l as i64),
offset.map(|o| o as i64),
q
q,
None,
).await?;
Ok(response.into_inner())
})
@@ -189,69 +192,79 @@ impl ApiClient {
pub fn get_document(&self, agent_id: &str, document_id: &str, _verbose: bool) -> Result<types::DocumentResponse> {
self.runtime.block_on(async {
let response = self.client.get_document(agent_id, document_id).await?;
let response = self.client.get_document(agent_id, document_id, None).await?;
Ok(response.into_inner())
})
}
pub fn delete_document(&self, agent_id: &str, document_id: &str, _verbose: bool) -> Result<types::DeleteResponse> {
self.runtime.block_on(async {
let response = self.client.delete_document(agent_id, document_id).await?;
let response = self.client.delete_document(agent_id, document_id, None).await?;
let value = response.into_inner();
let result: types::DeleteResponse = serde_json::from_value(value)?;
Ok(result)
// Convert typed response to DeleteResponse
Ok(types::DeleteResponse {
deleted_count: Some(value.memory_units_deleted),
message: Some(value.message),
success: value.success,
})
})
}
pub fn list_operations(&self, agent_id: &str, _verbose: bool) -> Result<OperationsResponse> {
self.runtime.block_on(async {
let response = self.client.list_operations(agent_id).await?;
let response = self.client.list_operations(agent_id, None).await?;
let value = response.into_inner();
let ops: OperationsResponse = serde_json::from_value(value)?;
// Convert to JSON Value first, then parse into our type
let json_value = serde_json::to_value(&value)?;
let ops: OperationsResponse = serde_json::from_value(json_value)?;
Ok(ops)
})
}
pub fn cancel_operation(&self, agent_id: &str, operation_id: &str, _verbose: bool) -> Result<types::DeleteResponse> {
self.runtime.block_on(async {
let response = self.client.cancel_operation(agent_id, operation_id).await?;
let response = self.client.cancel_operation(agent_id, operation_id, None).await?;
let value = response.into_inner();
let result: types::DeleteResponse = serde_json::from_value(value)?;
Ok(result)
// Convert typed response to DeleteResponse
Ok(types::DeleteResponse {
deleted_count: None,
message: Some(value.message),
success: value.success,
})
})
}
pub fn list_memories(&self, bank_id: &str, type_filter: Option<&str>, q: Option<&str>, limit: Option<i64>, offset: Option<i64>, _verbose: bool) -> Result<types::ListMemoryUnitsResponse> {
self.runtime.block_on(async {
let response = self.client.list_memories(bank_id, limit, offset, q, type_filter).await?;
let response = self.client.list_memories(bank_id, limit, offset, q, type_filter, None).await?;
Ok(response.into_inner())
})
}
pub fn list_entities(&self, bank_id: &str, limit: Option<i64>, _verbose: bool) -> Result<types::EntityListResponse> {
self.runtime.block_on(async {
let response = self.client.list_entities(bank_id, limit).await?;
let response = self.client.list_entities(bank_id, limit, None).await?;
Ok(response.into_inner())
})
}
pub fn get_entity(&self, bank_id: &str, entity_id: &str, _verbose: bool) -> Result<types::EntityDetailResponse> {
self.runtime.block_on(async {
let response = self.client.get_entity(bank_id, entity_id).await?;
let response = self.client.get_entity(bank_id, entity_id, None).await?;
Ok(response.into_inner())
})
}
pub fn regenerate_entity(&self, bank_id: &str, entity_id: &str, _verbose: bool) -> Result<types::EntityDetailResponse> {
self.runtime.block_on(async {
let response = self.client.regenerate_entity_observations(bank_id, entity_id).await?;
let response = self.client.regenerate_entity_observations(bank_id, entity_id, None).await?;
Ok(response.into_inner())
})
}
pub fn delete_bank(&self, bank_id: &str, _verbose: bool) -> Result<types::DeleteResponse> {
self.runtime.block_on(async {
let response = self.client.delete_bank(bank_id).await?;
let response = self.client.delete_bank(bank_id, None).await?;
Ok(response.into_inner())
})
}
+2
View File
@@ -354,7 +354,9 @@ impl App {
query: query_text,
budget: Some(query_budget),
context: None,
max_tokens: 4096,
include: None,
response_schema: None,
};
let result = client.reflect(&bank_id, &request, false)
+16
View File
@@ -10,6 +10,7 @@ use crate::ui;
// Import types from generated client
use hindsight_client::types::{Budget, ChunkIncludeOptions, IncludeOptions};
use serde_json;
// Helper function to parse budget string to Budget enum
fn parse_budget(budget: &str) -> Budget {
@@ -86,6 +87,8 @@ pub fn reflect(
query: String,
budget: String,
context: Option<String>,
max_tokens: Option<i64>,
schema_path: Option<PathBuf>,
verbose: bool,
output_format: OutputFormat,
) -> Result<()> {
@@ -95,11 +98,24 @@ pub fn reflect(
None
};
// Load and parse schema if provided
let response_schema = if let Some(path) = schema_path {
let schema_content = fs::read_to_string(&path)
.with_context(|| format!("Failed to read schema file: {}", path.display()))?;
let schema: serde_json::Map<String, serde_json::Value> = serde_json::from_str(&schema_content)
.with_context(|| format!("Failed to parse JSON schema from: {}", path.display()))?;
Some(schema)
} else {
None
};
let request = ReflectRequest {
query,
budget: Some(parse_budget(&budget)),
context,
max_tokens: max_tokens.unwrap_or(4096),
include: None,
response_schema,
};
let response = client.reflect(agent_id, &request, verbose);
+10 -2
View File
@@ -206,6 +206,14 @@ enum MemoryCommands {
/// Additional context
#[arg(short = 'c', long)]
context: Option<String>,
/// Maximum tokens for the response (server default: 4096)
#[arg(short = 'm', long)]
max_tokens: Option<i64>,
/// Path to JSON schema file for structured output
#[arg(short = 's', long)]
schema: Option<PathBuf>,
},
/// Store (retain) a single memory
@@ -421,8 +429,8 @@ fn run() -> Result<()> {
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::Reflect { bank_id, query, budget, context } => {
commands::memory::reflect(&client, &bank_id, query, budget, context, 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::Retain { bank_id, content, doc_id, context, r#async } => {
commands::memory::retain(&client, &bank_id, content, doc_id, context, r#async, verbose, output_format)
+10
View File
@@ -175,6 +175,16 @@ pub fn print_think_response(response: &ReflectResponse) {
if !response.based_on.is_empty() {
println!("{}", dim(&format!("Based on {} memory units", response.based_on.len())));
}
// Display structured output if present
if let Some(structured) = &response.structured_output {
println!();
println!("{}", gradient_text("─── Structured Output ───"));
println!();
if let Ok(json) = serde_json::to_string_pretty(structured) {
println!("{}", json);
}
}
}
pub fn print_trace_info(trace: &serde_json::Map<String, serde_json::Value>) {
@@ -1,7 +1,11 @@
hindsight_client_api/__init__.py
hindsight_client_api/api/__init__.py
hindsight_client_api/api/default_api.py
hindsight_client_api/api/banks_api.py
hindsight_client_api/api/documents_api.py
hindsight_client_api/api/entities_api.py
hindsight_client_api/api/memory_api.py
hindsight_client_api/api/monitoring_api.py
hindsight_client_api/api/operations_api.py
hindsight_client_api/api_client.py
hindsight_client_api/api_response.py
hindsight_client_api/configuration.py
@@ -10,15 +14,20 @@ hindsight_client_api/docs/BackgroundResponse.md
hindsight_client_api/docs/BankListItem.md
hindsight_client_api/docs/BankListResponse.md
hindsight_client_api/docs/BankProfileResponse.md
hindsight_client_api/docs/BankStatsResponse.md
hindsight_client_api/docs/BanksApi.md
hindsight_client_api/docs/Budget.md
hindsight_client_api/docs/CancelOperationResponse.md
hindsight_client_api/docs/ChunkData.md
hindsight_client_api/docs/ChunkIncludeOptions.md
hindsight_client_api/docs/ChunkResponse.md
hindsight_client_api/docs/CreateBankRequest.md
hindsight_client_api/docs/DefaultApi.md
hindsight_client_api/docs/DeleteDocumentResponse.md
hindsight_client_api/docs/DeleteResponse.md
hindsight_client_api/docs/DispositionTraits.md
hindsight_client_api/docs/DocumentResponse.md
hindsight_client_api/docs/DocumentsApi.md
hindsight_client_api/docs/EntitiesApi.md
hindsight_client_api/docs/EntityDetailResponse.md
hindsight_client_api/docs/EntityIncludeOptions.md
hindsight_client_api/docs/EntityListItem.md
@@ -30,8 +39,12 @@ hindsight_client_api/docs/HTTPValidationError.md
hindsight_client_api/docs/IncludeOptions.md
hindsight_client_api/docs/ListDocumentsResponse.md
hindsight_client_api/docs/ListMemoryUnitsResponse.md
hindsight_client_api/docs/MemoryApi.md
hindsight_client_api/docs/MemoryItem.md
hindsight_client_api/docs/MonitoringApi.md
hindsight_client_api/docs/OperationResponse.md
hindsight_client_api/docs/OperationsApi.md
hindsight_client_api/docs/OperationsListResponse.md
hindsight_client_api/docs/RecallRequest.md
hindsight_client_api/docs/RecallResponse.md
hindsight_client_api/docs/RecallResult.md
@@ -51,11 +64,14 @@ hindsight_client_api/models/background_response.py
hindsight_client_api/models/bank_list_item.py
hindsight_client_api/models/bank_list_response.py
hindsight_client_api/models/bank_profile_response.py
hindsight_client_api/models/bank_stats_response.py
hindsight_client_api/models/budget.py
hindsight_client_api/models/cancel_operation_response.py
hindsight_client_api/models/chunk_data.py
hindsight_client_api/models/chunk_include_options.py
hindsight_client_api/models/chunk_response.py
hindsight_client_api/models/create_bank_request.py
hindsight_client_api/models/delete_document_response.py
hindsight_client_api/models/delete_response.py
hindsight_client_api/models/disposition_traits.py
hindsight_client_api/models/document_response.py
@@ -71,6 +87,8 @@ hindsight_client_api/models/include_options.py
hindsight_client_api/models/list_documents_response.py
hindsight_client_api/models/list_memory_units_response.py
hindsight_client_api/models/memory_item.py
hindsight_client_api/models/operation_response.py
hindsight_client_api/models/operations_list_response.py
hindsight_client_api/models/recall_request.py
hindsight_client_api/models/recall_response.py
hindsight_client_api/models/recall_result.py
@@ -84,44 +102,4 @@ hindsight_client_api/models/update_disposition_request.py
hindsight_client_api/models/validation_error.py
hindsight_client_api/models/validation_error_loc_inner.py
hindsight_client_api/rest.py
hindsight_client_api/test/__init__.py
hindsight_client_api/test/test_add_background_request.py
hindsight_client_api/test/test_background_response.py
hindsight_client_api/test/test_bank_list_item.py
hindsight_client_api/test/test_bank_list_response.py
hindsight_client_api/test/test_bank_profile_response.py
hindsight_client_api/test/test_budget.py
hindsight_client_api/test/test_chunk_data.py
hindsight_client_api/test/test_chunk_include_options.py
hindsight_client_api/test/test_chunk_response.py
hindsight_client_api/test/test_create_bank_request.py
hindsight_client_api/test/test_default_api.py
hindsight_client_api/test/test_delete_response.py
hindsight_client_api/test/test_disposition_traits.py
hindsight_client_api/test/test_document_response.py
hindsight_client_api/test/test_entity_detail_response.py
hindsight_client_api/test/test_entity_include_options.py
hindsight_client_api/test/test_entity_list_item.py
hindsight_client_api/test/test_entity_list_response.py
hindsight_client_api/test/test_entity_observation_response.py
hindsight_client_api/test/test_entity_state_response.py
hindsight_client_api/test/test_graph_data_response.py
hindsight_client_api/test/test_http_validation_error.py
hindsight_client_api/test/test_include_options.py
hindsight_client_api/test/test_list_documents_response.py
hindsight_client_api/test/test_list_memory_units_response.py
hindsight_client_api/test/test_memory_item.py
hindsight_client_api/test/test_monitoring_api.py
hindsight_client_api/test/test_recall_request.py
hindsight_client_api/test/test_recall_response.py
hindsight_client_api/test/test_recall_result.py
hindsight_client_api/test/test_reflect_fact.py
hindsight_client_api/test/test_reflect_include_options.py
hindsight_client_api/test/test_reflect_request.py
hindsight_client_api/test/test_reflect_response.py
hindsight_client_api/test/test_retain_request.py
hindsight_client_api/test/test_retain_response.py
hindsight_client_api/test/test_update_disposition_request.py
hindsight_client_api/test/test_validation_error.py
hindsight_client_api/test/test_validation_error_loc_inner.py
hindsight_client_api_README.md
@@ -10,7 +10,7 @@ from typing import Optional, List, Dict, Any
from datetime import datetime
import hindsight_client_api
from hindsight_client_api.api import default_api
from hindsight_client_api.api import memory_api, banks_api
from hindsight_client_api.models import (
recall_request,
retain_request,
@@ -74,7 +74,8 @@ class Hindsight:
"""
config = hindsight_client_api.Configuration(host=base_url, access_token=api_key)
self._api_client = hindsight_client_api.ApiClient(config)
self._api = default_api.DefaultApi(self._api_client)
self._memory_api = memory_api.MemoryApi(self._api_client)
self._banks_api = banks_api.BanksApi(self._api_client)
def __enter__(self):
"""Context manager entry."""
@@ -168,7 +169,7 @@ class Hindsight:
async_=retain_async,
)
return _run_async(self._api.retain_memories(bank_id, request_obj))
return _run_async(self._memory_api.retain_memories(bank_id, request_obj))
def recall(
self,
@@ -220,7 +221,7 @@ class Hindsight:
include=include_opts,
)
return _run_async(self._api.recall_memories(bank_id, request_obj))
return _run_async(self._memory_api.recall_memories(bank_id, request_obj))
def reflect(
self,
@@ -228,6 +229,8 @@ class Hindsight:
query: str,
budget: str = "low",
context: Optional[str] = None,
max_tokens: Optional[int] = None,
response_schema: Optional[Dict[str, Any]] = None,
) -> ReflectResponse:
"""
Generate a contextual answer based on bank identity and memories.
@@ -237,17 +240,24 @@ class Hindsight:
query: The question or prompt
budget: Budget level for reflection - "low", "mid", or "high" (default: "low")
context: Optional additional context
max_tokens: Maximum tokens for the response (server default: 4096)
response_schema: Optional JSON Schema for structured output. When provided,
the response will include a 'structured_output' field with the LLM
response parsed according to this schema.
Returns:
ReflectResponse with answer text and optionally facts used
ReflectResponse with answer text, optionally facts used, and optionally
structured_output if response_schema was provided
"""
request_obj = reflect_request.ReflectRequest(
query=query,
budget=budget,
context=context,
max_tokens=max_tokens,
response_schema=response_schema,
)
return _run_async(self._api.reflect(bank_id, request_obj))
return _run_async(self._memory_api.reflect(bank_id, request_obj))
def list_memories(
self,
@@ -258,7 +268,7 @@ class Hindsight:
offset: int = 0,
) -> ListMemoryUnitsResponse:
"""List memory units with pagination."""
return _run_async(self._api.list_memories(
return _run_async(self._memory_api.list_memories(
bank_id=bank_id,
type=type,
q=search_query,
@@ -286,7 +296,7 @@ class Hindsight:
disposition=disposition_obj,
)
return _run_async(self._api.create_or_update_bank(bank_id, request_obj))
return _run_async(self._banks_api.create_or_update_bank(bank_id, request_obj))
# Async methods (native async, no _run_async wrapper)
@@ -326,7 +336,7 @@ class Hindsight:
async_=retain_async,
)
return await self._api.retain_memories(bank_id, request_obj)
return await self._memory_api.retain_memories(bank_id, request_obj)
async def aretain(
self,
@@ -386,7 +396,7 @@ class Hindsight:
trace=False,
)
response = await self._api.recall_memories(bank_id, request_obj)
response = await self._memory_api.recall_memories(bank_id, request_obj)
return response.results if hasattr(response, 'results') else []
async def areflect(
@@ -414,4 +424,4 @@ class Hindsight:
context=context,
)
return await self._api.reflect(bank_id, request_obj)
return await self._memory_api.reflect(bank_id, request_obj)
@@ -7,7 +7,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -18,8 +18,12 @@ __version__ = "0.0.7"
# Define package exports
__all__ = [
"BanksApi",
"DocumentsApi",
"EntitiesApi",
"MemoryApi",
"MonitoringApi",
"DefaultApi",
"OperationsApi",
"ApiResponse",
"ApiClient",
"Configuration",
@@ -34,11 +38,14 @@ __all__ = [
"BankListItem",
"BankListResponse",
"BankProfileResponse",
"BankStatsResponse",
"Budget",
"CancelOperationResponse",
"ChunkData",
"ChunkIncludeOptions",
"ChunkResponse",
"CreateBankRequest",
"DeleteDocumentResponse",
"DeleteResponse",
"DispositionTraits",
"DocumentResponse",
@@ -54,6 +61,8 @@ __all__ = [
"ListDocumentsResponse",
"ListMemoryUnitsResponse",
"MemoryItem",
"OperationResponse",
"OperationsListResponse",
"RecallRequest",
"RecallResponse",
"RecallResult",
@@ -69,8 +78,12 @@ __all__ = [
]
# import apis into sdk package
from hindsight_client_api.api.banks_api import BanksApi as BanksApi
from hindsight_client_api.api.documents_api import DocumentsApi as DocumentsApi
from hindsight_client_api.api.entities_api import EntitiesApi as EntitiesApi
from hindsight_client_api.api.memory_api import MemoryApi as MemoryApi
from hindsight_client_api.api.monitoring_api import MonitoringApi as MonitoringApi
from hindsight_client_api.api.default_api import DefaultApi as DefaultApi
from hindsight_client_api.api.operations_api import OperationsApi as OperationsApi
# import ApiClient
from hindsight_client_api.api_response import ApiResponse as ApiResponse
@@ -89,11 +102,14 @@ from hindsight_client_api.models.background_response import BackgroundResponse a
from hindsight_client_api.models.bank_list_item import BankListItem as BankListItem
from hindsight_client_api.models.bank_list_response import BankListResponse as BankListResponse
from hindsight_client_api.models.bank_profile_response import BankProfileResponse as BankProfileResponse
from hindsight_client_api.models.bank_stats_response import BankStatsResponse as BankStatsResponse
from hindsight_client_api.models.budget import Budget as Budget
from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse as CancelOperationResponse
from hindsight_client_api.models.chunk_data import ChunkData as ChunkData
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions as ChunkIncludeOptions
from hindsight_client_api.models.chunk_response import ChunkResponse as ChunkResponse
from hindsight_client_api.models.create_bank_request import CreateBankRequest as CreateBankRequest
from hindsight_client_api.models.delete_document_response import DeleteDocumentResponse as DeleteDocumentResponse
from hindsight_client_api.models.delete_response import DeleteResponse as DeleteResponse
from hindsight_client_api.models.disposition_traits import DispositionTraits as DispositionTraits
from hindsight_client_api.models.document_response import DocumentResponse as DocumentResponse
@@ -109,6 +125,8 @@ from hindsight_client_api.models.include_options import IncludeOptions as Includ
from hindsight_client_api.models.list_documents_response import ListDocumentsResponse as ListDocumentsResponse
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse as ListMemoryUnitsResponse
from hindsight_client_api.models.memory_item import MemoryItem as MemoryItem
from hindsight_client_api.models.operation_response import OperationResponse as OperationResponse
from hindsight_client_api.models.operations_list_response import OperationsListResponse as OperationsListResponse
from hindsight_client_api.models.recall_request import RecallRequest as RecallRequest
from hindsight_client_api.models.recall_response import RecallResponse as RecallResponse
from hindsight_client_api.models.recall_result import RecallResult as RecallResult
@@ -1,6 +1,10 @@
# flake8: noqa
# import apis into api package
from hindsight_client_api.api.banks_api import BanksApi
from hindsight_client_api.api.documents_api import DocumentsApi
from hindsight_client_api.api.entities_api import EntitiesApi
from hindsight_client_api.api.memory_api import MemoryApi
from hindsight_client_api.api.monitoring_api import MonitoringApi
from hindsight_client_api.api.default_api import DefaultApi
from hindsight_client_api.api.operations_api import OperationsApi
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,921 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
import warnings
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
from typing import Any, Dict, List, Optional, Tuple, Union
from typing_extensions import Annotated
from pydantic import Field, StrictInt, StrictStr
from typing import Optional
from typing_extensions import Annotated
from hindsight_client_api.models.entity_detail_response import EntityDetailResponse
from hindsight_client_api.models.entity_list_response import EntityListResponse
from hindsight_client_api.api_client import ApiClient, RequestSerialized
from hindsight_client_api.api_response import ApiResponse
from hindsight_client_api.rest import RESTResponseType
class EntitiesApi:
"""NOTE: This class is auto generated by OpenAPI Generator
Ref: https://openapi-generator.tech
Do not edit the class manually.
"""
def __init__(self, api_client=None) -> None:
if api_client is None:
api_client = ApiClient.get_default()
self.api_client = api_client
@validate_call
async def get_entity(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> EntityDetailResponse:
"""Get entity details
Get detailed information about an entity including observations (mental model).
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._get_entity_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
).data
@validate_call
async def get_entity_with_http_info(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[EntityDetailResponse]:
"""Get entity details
Get detailed information about an entity including observations (mental model).
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._get_entity_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
)
@validate_call
async def get_entity_without_preload_content(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> RESTResponseType:
"""Get entity details
Get detailed information about an entity including observations (mental model).
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._get_entity_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
return response_data.response
def _get_entity_serialize(
self,
bank_id,
entity_id,
authorization,
_request_auth,
_content_type,
_headers,
_host_index,
) -> RequestSerialized:
_host = None
_collection_formats: Dict[str, str] = {
}
_path_params: Dict[str, str] = {}
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None
# process the path parameters
if bank_id is not None:
_path_params['bank_id'] = bank_id
if entity_id is not None:
_path_params['entity_id'] = entity_id
# process the query parameters
# process the header parameters
if authorization is not None:
_header_params['authorization'] = authorization
# process the form parameters
# process the body parameter
# set the HTTP header `Accept`
if 'Accept' not in _header_params:
_header_params['Accept'] = self.api_client.select_header_accept(
[
'application/json'
]
)
# authentication setting
_auth_settings: List[str] = [
]
return self.api_client.param_serialize(
method='GET',
resource_path='/v1/default/banks/{bank_id}/entities/{entity_id}',
path_params=_path_params,
query_params=_query_params,
header_params=_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
auth_settings=_auth_settings,
collection_formats=_collection_formats,
_host=_host,
_request_auth=_request_auth
)
@validate_call
async def list_entities(
self,
bank_id: StrictStr,
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> EntityListResponse:
"""List entities
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
:param bank_id: (required)
:type bank_id: str
:param limit: Maximum number of entities to return
:type limit: int
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_entities_serialize(
bank_id=bank_id,
limit=limit,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
).data
@validate_call
async def list_entities_with_http_info(
self,
bank_id: StrictStr,
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[EntityListResponse]:
"""List entities
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
:param bank_id: (required)
:type bank_id: str
:param limit: Maximum number of entities to return
:type limit: int
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_entities_serialize(
bank_id=bank_id,
limit=limit,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
)
@validate_call
async def list_entities_without_preload_content(
self,
bank_id: StrictStr,
limit: Annotated[Optional[StrictInt], Field(description="Maximum number of entities to return")] = None,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> RESTResponseType:
"""List entities
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
:param bank_id: (required)
:type bank_id: str
:param limit: Maximum number of entities to return
:type limit: int
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_entities_serialize(
bank_id=bank_id,
limit=limit,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
return response_data.response
def _list_entities_serialize(
self,
bank_id,
limit,
authorization,
_request_auth,
_content_type,
_headers,
_host_index,
) -> RequestSerialized:
_host = None
_collection_formats: Dict[str, str] = {
}
_path_params: Dict[str, str] = {}
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None
# process the path parameters
if bank_id is not None:
_path_params['bank_id'] = bank_id
# process the query parameters
if limit is not None:
_query_params.append(('limit', limit))
# process the header parameters
if authorization is not None:
_header_params['authorization'] = authorization
# process the form parameters
# process the body parameter
# set the HTTP header `Accept`
if 'Accept' not in _header_params:
_header_params['Accept'] = self.api_client.select_header_accept(
[
'application/json'
]
)
# authentication setting
_auth_settings: List[str] = [
]
return self.api_client.param_serialize(
method='GET',
resource_path='/v1/default/banks/{bank_id}/entities',
path_params=_path_params,
query_params=_query_params,
header_params=_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
auth_settings=_auth_settings,
collection_formats=_collection_formats,
_host=_host,
_request_auth=_request_auth
)
@validate_call
async def regenerate_entity_observations(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> EntityDetailResponse:
"""Regenerate entity observations
Regenerate observations for an entity based on all facts mentioning it.
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._regenerate_entity_observations_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
).data
@validate_call
async def regenerate_entity_observations_with_http_info(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[EntityDetailResponse]:
"""Regenerate entity observations
Regenerate observations for an entity based on all facts mentioning it.
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._regenerate_entity_observations_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
)
@validate_call
async def regenerate_entity_observations_without_preload_content(
self,
bank_id: StrictStr,
entity_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> RESTResponseType:
"""Regenerate entity observations
Regenerate observations for an entity based on all facts mentioning it.
:param bank_id: (required)
:type bank_id: str
:param entity_id: (required)
:type entity_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._regenerate_entity_observations_serialize(
bank_id=bank_id,
entity_id=entity_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "EntityDetailResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
return response_data.response
def _regenerate_entity_observations_serialize(
self,
bank_id,
entity_id,
authorization,
_request_auth,
_content_type,
_headers,
_host_index,
) -> RequestSerialized:
_host = None
_collection_formats: Dict[str, str] = {
}
_path_params: Dict[str, str] = {}
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None
# process the path parameters
if bank_id is not None:
_path_params['bank_id'] = bank_id
if entity_id is not None:
_path_params['entity_id'] = entity_id
# process the query parameters
# process the header parameters
if authorization is not None:
_header_params['authorization'] = authorization
# process the form parameters
# process the body parameter
# set the HTTP header `Accept`
if 'Accept' not in _header_params:
_header_params['Accept'] = self.api_client.select_header_accept(
[
'application/json'
]
)
# authentication setting
_auth_settings: List[str] = [
]
return self.api_client.param_serialize(
method='POST',
resource_path='/v1/default/banks/{bank_id}/entities/{entity_id}/regenerate',
path_params=_path_params,
query_params=_query_params,
header_params=_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
auth_settings=_auth_settings,
collection_formats=_collection_formats,
_host=_host,
_request_auth=_request_auth
)
File diff suppressed because it is too large Load Diff
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -0,0 +1,610 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
""" # noqa: E501
import warnings
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
from typing import Any, Dict, List, Optional, Tuple, Union
from typing_extensions import Annotated
from pydantic import StrictStr
from typing import Optional
from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse
from hindsight_client_api.models.operations_list_response import OperationsListResponse
from hindsight_client_api.api_client import ApiClient, RequestSerialized
from hindsight_client_api.api_response import ApiResponse
from hindsight_client_api.rest import RESTResponseType
class OperationsApi:
"""NOTE: This class is auto generated by OpenAPI Generator
Ref: https://openapi-generator.tech
Do not edit the class manually.
"""
def __init__(self, api_client=None) -> None:
if api_client is None:
api_client = ApiClient.get_default()
self.api_client = api_client
@validate_call
async def cancel_operation(
self,
bank_id: StrictStr,
operation_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> CancelOperationResponse:
"""Cancel a pending async operation
Cancel a pending async operation by removing it from the queue
:param bank_id: (required)
:type bank_id: str
:param operation_id: (required)
:type operation_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._cancel_operation_serialize(
bank_id=bank_id,
operation_id=operation_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "CancelOperationResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
).data
@validate_call
async def cancel_operation_with_http_info(
self,
bank_id: StrictStr,
operation_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[CancelOperationResponse]:
"""Cancel a pending async operation
Cancel a pending async operation by removing it from the queue
:param bank_id: (required)
:type bank_id: str
:param operation_id: (required)
:type operation_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._cancel_operation_serialize(
bank_id=bank_id,
operation_id=operation_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "CancelOperationResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
)
@validate_call
async def cancel_operation_without_preload_content(
self,
bank_id: StrictStr,
operation_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> RESTResponseType:
"""Cancel a pending async operation
Cancel a pending async operation by removing it from the queue
:param bank_id: (required)
:type bank_id: str
:param operation_id: (required)
:type operation_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._cancel_operation_serialize(
bank_id=bank_id,
operation_id=operation_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "CancelOperationResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
return response_data.response
def _cancel_operation_serialize(
self,
bank_id,
operation_id,
authorization,
_request_auth,
_content_type,
_headers,
_host_index,
) -> RequestSerialized:
_host = None
_collection_formats: Dict[str, str] = {
}
_path_params: Dict[str, str] = {}
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None
# process the path parameters
if bank_id is not None:
_path_params['bank_id'] = bank_id
if operation_id is not None:
_path_params['operation_id'] = operation_id
# process the query parameters
# process the header parameters
if authorization is not None:
_header_params['authorization'] = authorization
# process the form parameters
# process the body parameter
# set the HTTP header `Accept`
if 'Accept' not in _header_params:
_header_params['Accept'] = self.api_client.select_header_accept(
[
'application/json'
]
)
# authentication setting
_auth_settings: List[str] = [
]
return self.api_client.param_serialize(
method='DELETE',
resource_path='/v1/default/banks/{bank_id}/operations/{operation_id}',
path_params=_path_params,
query_params=_query_params,
header_params=_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
auth_settings=_auth_settings,
collection_formats=_collection_formats,
_host=_host,
_request_auth=_request_auth
)
@validate_call
async def list_operations(
self,
bank_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> OperationsListResponse:
"""List async operations
Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations
:param bank_id: (required)
:type bank_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_operations_serialize(
bank_id=bank_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "OperationsListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
).data
@validate_call
async def list_operations_with_http_info(
self,
bank_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> ApiResponse[OperationsListResponse]:
"""List async operations
Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations
:param bank_id: (required)
:type bank_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_operations_serialize(
bank_id=bank_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "OperationsListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
await response_data.read()
return self.api_client.response_deserialize(
response_data=response_data,
response_types_map=_response_types_map,
)
@validate_call
async def list_operations_without_preload_content(
self,
bank_id: StrictStr,
authorization: Optional[StrictStr] = None,
_request_timeout: Union[
None,
Annotated[StrictFloat, Field(gt=0)],
Tuple[
Annotated[StrictFloat, Field(gt=0)],
Annotated[StrictFloat, Field(gt=0)]
]
] = None,
_request_auth: Optional[Dict[StrictStr, Any]] = None,
_content_type: Optional[StrictStr] = None,
_headers: Optional[Dict[StrictStr, Any]] = None,
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
) -> RESTResponseType:
"""List async operations
Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations
:param bank_id: (required)
:type bank_id: str
:param authorization:
:type authorization: str
:param _request_timeout: timeout setting for this request. If one
number provided, it will be total request
timeout. It can also be a pair (tuple) of
(connection, read) timeouts.
:type _request_timeout: int, tuple(int, int), optional
:param _request_auth: set to override the auth_settings for an a single
request; this effectively ignores the
authentication in the spec for a single request.
:type _request_auth: dict, optional
:param _content_type: force content-type for the request.
:type _content_type: str, Optional
:param _headers: set to override the headers for a single
request; this effectively ignores the headers
in the spec for a single request.
:type _headers: dict, optional
:param _host_index: set to override the host_index for a single
request; this effectively ignores the host_index
in the spec for a single request.
:type _host_index: int, optional
:return: Returns the result object.
""" # noqa: E501
_param = self._list_operations_serialize(
bank_id=bank_id,
authorization=authorization,
_request_auth=_request_auth,
_content_type=_content_type,
_headers=_headers,
_host_index=_host_index
)
_response_types_map: Dict[str, Optional[str]] = {
'200': "OperationsListResponse",
'422': "HTTPValidationError",
}
response_data = await self.api_client.call_api(
*_param,
_request_timeout=_request_timeout
)
return response_data.response
def _list_operations_serialize(
self,
bank_id,
authorization,
_request_auth,
_content_type,
_headers,
_host_index,
) -> RequestSerialized:
_host = None
_collection_formats: Dict[str, str] = {
}
_path_params: Dict[str, str] = {}
_query_params: List[Tuple[str, str]] = []
_header_params: Dict[str, Optional[str]] = _headers or {}
_form_params: List[Tuple[str, str]] = []
_files: Dict[
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
] = {}
_body_params: Optional[bytes] = None
# process the path parameters
if bank_id is not None:
_path_params['bank_id'] = bank_id
# process the query parameters
# process the header parameters
if authorization is not None:
_header_params['authorization'] = authorization
# process the form parameters
# process the body parameter
# set the HTTP header `Accept`
if 'Accept' not in _header_params:
_header_params['Accept'] = self.api_client.select_header_accept(
[
'application/json'
]
)
# authentication setting
_auth_settings: List[str] = [
]
return self.api_client.param_serialize(
method='GET',
resource_path='/v1/default/banks/{bank_id}/operations',
path_params=_path_params,
query_params=_query_params,
header_params=_header_params,
body=_body_params,
post_params=_form_params,
files=_files,
auth_settings=_auth_settings,
collection_formats=_collection_formats,
_host=_host,
_request_auth=_request_auth
)
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -500,7 +500,7 @@ class Configuration:
return "Python SDK Debug Report:\n"\
"OS: {env}\n"\
"Python Version: {pyversion}\n"\
"Version of the API: 1.0.0\n"\
"Version of the API: 0.1.0\n"\
"SDK Package Version: 0.0.7".\
format(env=sys.platform, pyversion=sys.version)
@@ -7,9 +7,9 @@ Bank list item with profile summary.
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**bank_id** | **str** | |
**name** | **str** | |
**name** | **str** | | [optional]
**disposition** | [**DispositionTraits**](DispositionTraits.md) | |
**background** | **str** | |
**background** | **str** | | [optional]
**created_at** | **str** | | [optional]
**updated_at** | **str** | | [optional]
@@ -0,0 +1,39 @@
# BankStatsResponse
Response model for bank statistics endpoint.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**bank_id** | **str** | |
**total_nodes** | **int** | |
**total_links** | **int** | |
**total_documents** | **int** | |
**nodes_by_fact_type** | **Dict[str, int]** | |
**links_by_link_type** | **Dict[str, int]** | |
**links_by_fact_type** | **Dict[str, int]** | |
**links_breakdown** | **Dict[str, Dict[str, int]]** | |
**pending_operations** | **int** | |
**failed_operations** | **int** | |
## Example
```python
from hindsight_client_api.models.bank_stats_response import BankStatsResponse
# TODO update the JSON string below
json = "{}"
# create an instance of BankStatsResponse from a JSON string
bank_stats_response_instance = BankStatsResponse.from_json(json)
# print the JSON string representation of the object
print(BankStatsResponse.to_json())
# convert the object into a dict
bank_stats_response_dict = bank_stats_response_instance.to_dict()
# create an instance of BankStatsResponse from a dict
bank_stats_response_from_dict = BankStatsResponse.from_dict(bank_stats_response_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
@@ -0,0 +1,517 @@
# hindsight_client_api.BanksApi
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[**add_bank_background**](BanksApi.md#add_bank_background) | **POST** /v1/default/banks/{bank_id}/background | Add/merge memory bank background
[**create_or_update_bank**](BanksApi.md#create_or_update_bank) | **PUT** /v1/default/banks/{bank_id} | Create or update memory bank
[**delete_bank**](BanksApi.md#delete_bank) | **DELETE** /v1/default/banks/{bank_id} | Delete memory bank
[**get_agent_stats**](BanksApi.md#get_agent_stats) | **GET** /v1/default/banks/{bank_id}/stats | Get statistics for memory bank
[**get_bank_profile**](BanksApi.md#get_bank_profile) | **GET** /v1/default/banks/{bank_id}/profile | Get memory bank profile
[**list_banks**](BanksApi.md#list_banks) | **GET** /v1/default/banks | List all memory banks
[**update_bank_disposition**](BanksApi.md#update_bank_disposition) | **PUT** /v1/default/banks/{bank_id}/profile | Update memory bank disposition
# **add_bank_background**
> BackgroundResponse add_bank_background(bank_id, add_background_request, authorization=authorization)
Add/merge memory bank background
Add new background information or merge with existing. LLM intelligently resolves conflicts, normalizes to first person, and optionally infers disposition traits.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.add_background_request import AddBackgroundRequest
from hindsight_client_api.models.background_response import BackgroundResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
add_background_request = hindsight_client_api.AddBackgroundRequest() # AddBackgroundRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Add/merge memory bank background
api_response = await api_instance.add_bank_background(bank_id, add_background_request, authorization=authorization)
print("The response of BanksApi->add_bank_background:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->add_bank_background: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**add_background_request** | [**AddBackgroundRequest**](AddBackgroundRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**BackgroundResponse**](BackgroundResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **create_or_update_bank**
> BankProfileResponse create_or_update_bank(bank_id, create_bank_request, authorization=authorization)
Create or update memory bank
Create a new agent or update existing agent with disposition and background. Auto-fills missing fields with defaults.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.models.create_bank_request import CreateBankRequest
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
create_bank_request = hindsight_client_api.CreateBankRequest() # CreateBankRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Create or update memory bank
api_response = await api_instance.create_or_update_bank(bank_id, create_bank_request, authorization=authorization)
print("The response of BanksApi->create_or_update_bank:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->create_or_update_bank: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**create_bank_request** | [**CreateBankRequest**](CreateBankRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**BankProfileResponse**](BankProfileResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **delete_bank**
> DeleteResponse delete_bank(bank_id, authorization=authorization)
Delete memory bank
Delete an entire memory bank including all memories, entities, documents, and the bank profile itself. This is a destructive operation that cannot be undone.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.delete_response import DeleteResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Delete memory bank
api_response = await api_instance.delete_bank(bank_id, authorization=authorization)
print("The response of BanksApi->delete_bank:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->delete_bank: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**DeleteResponse**](DeleteResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_agent_stats**
> BankStatsResponse get_agent_stats(bank_id)
Get statistics for memory bank
Get statistics about nodes and links for a specific agent
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.bank_stats_response import BankStatsResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
try:
# Get statistics for memory bank
api_response = await api_instance.get_agent_stats(bank_id)
print("The response of BanksApi->get_agent_stats:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->get_agent_stats: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
### Return type
[**BankStatsResponse**](BankStatsResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_bank_profile**
> BankProfileResponse get_bank_profile(bank_id, authorization=authorization)
Get memory bank profile
Get disposition traits and background for a memory bank. Auto-creates agent with defaults if not exists.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Get memory bank profile
api_response = await api_instance.get_bank_profile(bank_id, authorization=authorization)
print("The response of BanksApi->get_bank_profile:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->get_bank_profile: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**BankProfileResponse**](BankProfileResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **list_banks**
> BankListResponse list_banks(authorization=authorization)
List all memory banks
Get a list of all agents with their profiles
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.bank_list_response import BankListResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
authorization = 'authorization_example' # str | (optional)
try:
# List all memory banks
api_response = await api_instance.list_banks(authorization=authorization)
print("The response of BanksApi->list_banks:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->list_banks: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**authorization** | **str**| | [optional]
### Return type
[**BankListResponse**](BankListResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **update_bank_disposition**
> BankProfileResponse update_bank_disposition(bank_id, update_disposition_request, authorization=authorization)
Update memory bank disposition
Update bank's disposition traits (skepticism, literalism, empathy)
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.models.update_disposition_request import UpdateDispositionRequest
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.BanksApi(api_client)
bank_id = 'bank_id_example' # str |
update_disposition_request = hindsight_client_api.UpdateDispositionRequest() # UpdateDispositionRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Update memory bank disposition
api_response = await api_instance.update_bank_disposition(bank_id, update_disposition_request, authorization=authorization)
print("The response of BanksApi->update_bank_disposition:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling BanksApi->update_bank_disposition: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**update_disposition_request** | [**UpdateDispositionRequest**](UpdateDispositionRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**BankProfileResponse**](BankProfileResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@@ -0,0 +1,32 @@
# CancelOperationResponse
Response model for cancel operation endpoint.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**success** | **bool** | |
**message** | **str** | |
**operation_id** | **str** | |
## Example
```python
from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse
# TODO update the JSON string below
json = "{}"
# create an instance of CancelOperationResponse from a JSON string
cancel_operation_response_instance = CancelOperationResponse.from_json(json)
# print the JSON string representation of the object
print(CancelOperationResponse.to_json())
# convert the object into a dict
cancel_operation_response_dict = cancel_operation_response_instance.to_dict()
# create an instance of CancelOperationResponse from a dict
cancel_operation_response_from_dict = CancelOperationResponse.from_dict(cancel_operation_response_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,33 @@
# DeleteDocumentResponse
Response model for delete document endpoint.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**success** | **bool** | |
**message** | **str** | |
**document_id** | **str** | |
**memory_units_deleted** | **int** | |
## Example
```python
from hindsight_client_api.models.delete_document_response import DeleteDocumentResponse
# TODO update the JSON string below
json = "{}"
# create an instance of DeleteDocumentResponse from a JSON string
delete_document_response_instance = DeleteDocumentResponse.from_json(json)
# print the JSON string representation of the object
print(DeleteDocumentResponse.to_json())
# convert the object into a dict
delete_document_response_dict = delete_document_response_instance.to_dict()
# create an instance of DeleteDocumentResponse from a dict
delete_document_response_from_dict = DeleteDocumentResponse.from_dict(delete_document_response_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
@@ -7,6 +7,8 @@ Response model for delete operations.
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**success** | **bool** | |
**message** | **str** | | [optional]
**deleted_count** | **int** | | [optional]
## Example
@@ -0,0 +1,313 @@
# hindsight_client_api.DocumentsApi
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[**delete_document**](DocumentsApi.md#delete_document) | **DELETE** /v1/default/banks/{bank_id}/documents/{document_id} | Delete a document
[**get_chunk**](DocumentsApi.md#get_chunk) | **GET** /v1/default/chunks/{chunk_id} | Get chunk details
[**get_document**](DocumentsApi.md#get_document) | **GET** /v1/default/banks/{bank_id}/documents/{document_id} | Get document details
[**list_documents**](DocumentsApi.md#list_documents) | **GET** /v1/default/banks/{bank_id}/documents | List documents
# **delete_document**
> DeleteDocumentResponse delete_document(bank_id, document_id, authorization=authorization)
Delete a document
Delete a document and all its associated memory units and links.
This will cascade delete:
- The document itself
- All memory units extracted from this document
- All links (temporal, semantic, entity) associated with those memory units
This operation cannot be undone.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.delete_document_response import DeleteDocumentResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.DocumentsApi(api_client)
bank_id = 'bank_id_example' # str |
document_id = 'document_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Delete a document
api_response = await api_instance.delete_document(bank_id, document_id, authorization=authorization)
print("The response of DocumentsApi->delete_document:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DocumentsApi->delete_document: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**document_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**DeleteDocumentResponse**](DeleteDocumentResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_chunk**
> ChunkResponse get_chunk(chunk_id, authorization=authorization)
Get chunk details
Get a specific chunk by its ID
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.chunk_response import ChunkResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.DocumentsApi(api_client)
chunk_id = 'chunk_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Get chunk details
api_response = await api_instance.get_chunk(chunk_id, authorization=authorization)
print("The response of DocumentsApi->get_chunk:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DocumentsApi->get_chunk: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**chunk_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**ChunkResponse**](ChunkResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_document**
> DocumentResponse get_document(bank_id, document_id, authorization=authorization)
Get document details
Get a specific document including its original text
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.document_response import DocumentResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.DocumentsApi(api_client)
bank_id = 'bank_id_example' # str |
document_id = 'document_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Get document details
api_response = await api_instance.get_document(bank_id, document_id, authorization=authorization)
print("The response of DocumentsApi->get_document:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DocumentsApi->get_document: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**document_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**DocumentResponse**](DocumentResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **list_documents**
> ListDocumentsResponse list_documents(bank_id, q=q, limit=limit, offset=offset, authorization=authorization)
List documents
List documents with pagination and optional search. Documents are the source content from which memory units are extracted.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.list_documents_response import ListDocumentsResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.DocumentsApi(api_client)
bank_id = 'bank_id_example' # str |
q = 'q_example' # str | (optional)
limit = 100 # int | (optional) (default to 100)
offset = 0 # int | (optional) (default to 0)
authorization = 'authorization_example' # str | (optional)
try:
# List documents
api_response = await api_instance.list_documents(bank_id, q=q, limit=limit, offset=offset, authorization=authorization)
print("The response of DocumentsApi->list_documents:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DocumentsApi->list_documents: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**q** | **str**| | [optional]
**limit** | **int**| | [optional] [default to 100]
**offset** | **int**| | [optional] [default to 0]
**authorization** | **str**| | [optional]
### Return type
[**ListDocumentsResponse**](ListDocumentsResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@@ -0,0 +1,230 @@
# hindsight_client_api.EntitiesApi
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[**get_entity**](EntitiesApi.md#get_entity) | **GET** /v1/default/banks/{bank_id}/entities/{entity_id} | Get entity details
[**list_entities**](EntitiesApi.md#list_entities) | **GET** /v1/default/banks/{bank_id}/entities | List entities
[**regenerate_entity_observations**](EntitiesApi.md#regenerate_entity_observations) | **POST** /v1/default/banks/{bank_id}/entities/{entity_id}/regenerate | Regenerate entity observations
# **get_entity**
> EntityDetailResponse get_entity(bank_id, entity_id, authorization=authorization)
Get entity details
Get detailed information about an entity including observations (mental model).
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.entity_detail_response import EntityDetailResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.EntitiesApi(api_client)
bank_id = 'bank_id_example' # str |
entity_id = 'entity_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Get entity details
api_response = await api_instance.get_entity(bank_id, entity_id, authorization=authorization)
print("The response of EntitiesApi->get_entity:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling EntitiesApi->get_entity: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**entity_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**EntityDetailResponse**](EntityDetailResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **list_entities**
> EntityListResponse list_entities(bank_id, limit=limit, authorization=authorization)
List entities
List all entities (people, organizations, etc.) known by the bank, ordered by mention count.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.entity_list_response import EntityListResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.EntitiesApi(api_client)
bank_id = 'bank_id_example' # str |
limit = 100 # int | Maximum number of entities to return (optional) (default to 100)
authorization = 'authorization_example' # str | (optional)
try:
# List entities
api_response = await api_instance.list_entities(bank_id, limit=limit, authorization=authorization)
print("The response of EntitiesApi->list_entities:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling EntitiesApi->list_entities: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**limit** | **int**| Maximum number of entities to return | [optional] [default to 100]
**authorization** | **str**| | [optional]
### Return type
[**EntityListResponse**](EntityListResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **regenerate_entity_observations**
> EntityDetailResponse regenerate_entity_observations(bank_id, entity_id, authorization=authorization)
Regenerate entity observations
Regenerate observations for an entity based on all facts mentioning it.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.entity_detail_response import EntityDetailResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.EntitiesApi(api_client)
bank_id = 'bank_id_example' # str |
entity_id = 'entity_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Regenerate entity observations
api_response = await api_instance.regenerate_entity_observations(bank_id, entity_id, authorization=authorization)
print("The response of EntitiesApi->regenerate_entity_observations:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling EntitiesApi->regenerate_entity_observations: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**entity_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**EntityDetailResponse**](EntityDetailResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@@ -0,0 +1,499 @@
# hindsight_client_api.MemoryApi
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[**clear_bank_memories**](MemoryApi.md#clear_bank_memories) | **DELETE** /v1/default/banks/{bank_id}/memories | Clear memory bank memories
[**get_graph**](MemoryApi.md#get_graph) | **GET** /v1/default/banks/{bank_id}/graph | Get memory graph data
[**list_memories**](MemoryApi.md#list_memories) | **GET** /v1/default/banks/{bank_id}/memories/list | List memory units
[**recall_memories**](MemoryApi.md#recall_memories) | **POST** /v1/default/banks/{bank_id}/memories/recall | Recall memory
[**reflect**](MemoryApi.md#reflect) | **POST** /v1/default/banks/{bank_id}/reflect | Reflect and generate answer
[**retain_memories**](MemoryApi.md#retain_memories) | **POST** /v1/default/banks/{bank_id}/memories | Retain memories
# **clear_bank_memories**
> DeleteResponse clear_bank_memories(bank_id, type=type, authorization=authorization)
Clear memory bank memories
Delete memory units for a memory bank. Optionally filter by type (world, experience, opinion) to delete only specific types. This is a destructive operation that cannot be undone. The bank profile (disposition and background) will be preserved.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.delete_response import DeleteResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
type = 'type_example' # str | Optional fact type filter (world, experience, opinion) (optional)
authorization = 'authorization_example' # str | (optional)
try:
# Clear memory bank memories
api_response = await api_instance.clear_bank_memories(bank_id, type=type, authorization=authorization)
print("The response of MemoryApi->clear_bank_memories:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->clear_bank_memories: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**type** | **str**| Optional fact type filter (world, experience, opinion) | [optional]
**authorization** | **str**| | [optional]
### Return type
[**DeleteResponse**](DeleteResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **get_graph**
> GraphDataResponse get_graph(bank_id, type=type, authorization=authorization)
Get memory graph data
Retrieve graph data for visualization, optionally filtered by type (world/experience/opinion). Limited to 1000 most recent items.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.graph_data_response import GraphDataResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
type = 'type_example' # str | (optional)
authorization = 'authorization_example' # str | (optional)
try:
# Get memory graph data
api_response = await api_instance.get_graph(bank_id, type=type, authorization=authorization)
print("The response of MemoryApi->get_graph:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->get_graph: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**type** | **str**| | [optional]
**authorization** | **str**| | [optional]
### Return type
[**GraphDataResponse**](GraphDataResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **list_memories**
> ListMemoryUnitsResponse list_memories(bank_id, type=type, q=q, limit=limit, offset=offset, authorization=authorization)
List memory units
List memory units with pagination and optional full-text search. Supports filtering by type. Results are sorted by most recent first (mentioned_at DESC, then created_at DESC).
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
type = 'type_example' # str | (optional)
q = 'q_example' # str | (optional)
limit = 100 # int | (optional) (default to 100)
offset = 0 # int | (optional) (default to 0)
authorization = 'authorization_example' # str | (optional)
try:
# List memory units
api_response = await api_instance.list_memories(bank_id, type=type, q=q, limit=limit, offset=offset, authorization=authorization)
print("The response of MemoryApi->list_memories:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->list_memories: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**type** | **str**| | [optional]
**q** | **str**| | [optional]
**limit** | **int**| | [optional] [default to 100]
**offset** | **int**| | [optional] [default to 0]
**authorization** | **str**| | [optional]
### Return type
[**ListMemoryUnitsResponse**](ListMemoryUnitsResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **recall_memories**
> RecallResponse recall_memories(bank_id, recall_request, authorization=authorization)
Recall memory
Recall memory using semantic similarity and spreading activation.
The type parameter is optional and must be one of:
- `world`: General knowledge about people, places, events, and things that happen
- `experience`: Memories about experience, conversations, actions taken, and tasks performed
- `opinion`: The bank's formed beliefs, perspectives, and viewpoints
Set `include_entities=true` to get entity observations alongside recall results.
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.recall_request import RecallRequest
from hindsight_client_api.models.recall_response import RecallResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
recall_request = hindsight_client_api.RecallRequest() # RecallRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Recall memory
api_response = await api_instance.recall_memories(bank_id, recall_request, authorization=authorization)
print("The response of MemoryApi->recall_memories:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->recall_memories: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**recall_request** | [**RecallRequest**](RecallRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**RecallResponse**](RecallResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **reflect**
> ReflectResponse reflect(bank_id, reflect_request, authorization=authorization)
Reflect and generate answer
Reflect and formulate an answer using bank identity, world facts, and opinions.
This endpoint:
1. Retrieves experience (conversations and events)
2. Retrieves world facts relevant to the query
3. Retrieves existing opinions (bank's perspectives)
4. Uses LLM to formulate a contextual answer
5. Extracts and stores any new opinions formed
6. Returns plain text answer, the facts used, and new opinions
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.reflect_request import ReflectRequest
from hindsight_client_api.models.reflect_response import ReflectResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
reflect_request = hindsight_client_api.ReflectRequest() # ReflectRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Reflect and generate answer
api_response = await api_instance.reflect(bank_id, reflect_request, authorization=authorization)
print("The response of MemoryApi->reflect:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->reflect: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**reflect_request** | [**ReflectRequest**](ReflectRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**ReflectResponse**](ReflectResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **retain_memories**
> RetainResponse retain_memories(bank_id, retain_request, authorization=authorization)
Retain memories
Retain memory items with automatic fact extraction.
This is the main endpoint for storing memories. It supports both synchronous and asynchronous processing via the `async` parameter.
**Features:**
- Efficient batch processing
- Automatic fact extraction from natural language
- Entity recognition and linking
- Document tracking with automatic upsert (when document_id is provided)
- Temporal and semantic linking
- Optional asynchronous processing
**The system automatically:**
1. Extracts semantic facts from the content
2. Generates embeddings
3. Deduplicates similar facts
4. Creates temporal, semantic, and entity links
5. Tracks document metadata
**When `async=true`:** Returns immediately after queuing. Use the operations endpoint to monitor progress.
**When `async=false` (default):** Waits for processing to complete.
**Note:** If a memory item has a `document_id` that already exists, the old document and its memory units will be deleted before creating new ones (upsert behavior).
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.retain_request import RetainRequest
from hindsight_client_api.models.retain_response import RetainResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.MemoryApi(api_client)
bank_id = 'bank_id_example' # str |
retain_request = hindsight_client_api.RetainRequest() # RetainRequest |
authorization = 'authorization_example' # str | (optional)
try:
# Retain memories
api_response = await api_instance.retain_memories(bank_id, retain_request, authorization=authorization)
print("The response of MemoryApi->retain_memories:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling MemoryApi->retain_memories: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**retain_request** | [**RetainRequest**](RetainRequest.md)| |
**authorization** | **str**| | [optional]
### Return type
[**RetainResponse**](RetainResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@@ -0,0 +1,36 @@
# OperationResponse
Response model for a single async operation.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**id** | **str** | |
**task_type** | **str** | |
**items_count** | **int** | |
**document_id** | **str** | |
**created_at** | **str** | |
**status** | **str** | |
**error_message** | **str** | |
## Example
```python
from hindsight_client_api.models.operation_response import OperationResponse
# TODO update the JSON string below
json = "{}"
# create an instance of OperationResponse from a JSON string
operation_response_instance = OperationResponse.from_json(json)
# print the JSON string representation of the object
print(OperationResponse.to_json())
# convert the object into a dict
operation_response_dict = operation_response_instance.to_dict()
# create an instance of OperationResponse from a dict
operation_response_from_dict = OperationResponse.from_dict(operation_response_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
@@ -0,0 +1,154 @@
# hindsight_client_api.OperationsApi
All URIs are relative to *http://localhost*
Method | HTTP request | Description
------------- | ------------- | -------------
[**cancel_operation**](OperationsApi.md#cancel_operation) | **DELETE** /v1/default/banks/{bank_id}/operations/{operation_id} | Cancel a pending async operation
[**list_operations**](OperationsApi.md#list_operations) | **GET** /v1/default/banks/{bank_id}/operations | List async operations
# **cancel_operation**
> CancelOperationResponse cancel_operation(bank_id, operation_id, authorization=authorization)
Cancel a pending async operation
Cancel a pending async operation by removing it from the queue
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.OperationsApi(api_client)
bank_id = 'bank_id_example' # str |
operation_id = 'operation_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# Cancel a pending async operation
api_response = await api_instance.cancel_operation(bank_id, operation_id, authorization=authorization)
print("The response of OperationsApi->cancel_operation:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling OperationsApi->cancel_operation: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**operation_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**CancelOperationResponse**](CancelOperationResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **list_operations**
> OperationsListResponse list_operations(bank_id, authorization=authorization)
List async operations
Get a list of all async operations (pending and failed) for a specific agent, including error messages for failed operations
### Example
```python
import hindsight_client_api
from hindsight_client_api.models.operations_list_response import OperationsListResponse
from hindsight_client_api.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost
# See configuration.py for a list of all supported configuration parameters.
configuration = hindsight_client_api.Configuration(
host = "http://localhost"
)
# Enter a context with an instance of the API client
async with hindsight_client_api.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = hindsight_client_api.OperationsApi(api_client)
bank_id = 'bank_id_example' # str |
authorization = 'authorization_example' # str | (optional)
try:
# List async operations
api_response = await api_instance.list_operations(bank_id, authorization=authorization)
print("The response of OperationsApi->list_operations:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling OperationsApi->list_operations: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**bank_id** | **str**| |
**authorization** | **str**| | [optional]
### Return type
[**OperationsListResponse**](OperationsListResponse.md)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Successful Response | - |
**422** | Validation Error | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
@@ -0,0 +1,31 @@
# OperationsListResponse
Response model for list operations endpoint.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**bank_id** | **str** | |
**operations** | [**List[OperationResponse]**](OperationResponse.md) | |
## Example
```python
from hindsight_client_api.models.operations_list_response import OperationsListResponse
# TODO update the JSON string below
json = "{}"
# create an instance of OperationsListResponse from a JSON string
operations_list_response_instance = OperationsListResponse.from_json(json)
# print the JSON string representation of the object
print(OperationsListResponse.to_json())
# convert the object into a dict
operations_list_response_dict = operations_list_response_instance.to_dict()
# create an instance of OperationsListResponse from a dict
operations_list_response_from_dict = OperationsListResponse.from_dict(operations_list_response_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
@@ -9,7 +9,9 @@ Name | Type | Description | Notes
**query** | **str** | |
**budget** | [**Budget**](Budget.md) | | [optional]
**context** | **str** | | [optional]
**max_tokens** | **int** | Maximum tokens for the response | [optional] [default to 4096]
**include** | [**ReflectIncludeOptions**](ReflectIncludeOptions.md) | Options for including additional data (disabled by default) | [optional]
**response_schema** | **Dict[str, object]** | | [optional]
## Example
@@ -8,6 +8,7 @@ Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**text** | **str** | |
**based_on** | [**List[ReflectFact]**](ReflectFact.md) | | [optional] [default to []]
**structured_output** | **Dict[str, object]** | | [optional]
## Example
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -6,7 +6,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -18,11 +18,14 @@ from hindsight_client_api.models.background_response import BackgroundResponse
from hindsight_client_api.models.bank_list_item import BankListItem
from hindsight_client_api.models.bank_list_response import BankListResponse
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
from hindsight_client_api.models.bank_stats_response import BankStatsResponse
from hindsight_client_api.models.budget import Budget
from hindsight_client_api.models.cancel_operation_response import CancelOperationResponse
from hindsight_client_api.models.chunk_data import ChunkData
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
from hindsight_client_api.models.chunk_response import ChunkResponse
from hindsight_client_api.models.create_bank_request import CreateBankRequest
from hindsight_client_api.models.delete_document_response import DeleteDocumentResponse
from hindsight_client_api.models.delete_response import DeleteResponse
from hindsight_client_api.models.disposition_traits import DispositionTraits
from hindsight_client_api.models.document_response import DocumentResponse
@@ -38,6 +41,8 @@ from hindsight_client_api.models.include_options import IncludeOptions
from hindsight_client_api.models.list_documents_response import ListDocumentsResponse
from hindsight_client_api.models.list_memory_units_response import ListMemoryUnitsResponse
from hindsight_client_api.models.memory_item import MemoryItem
from hindsight_client_api.models.operation_response import OperationResponse
from hindsight_client_api.models.operations_list_response import OperationsListResponse
from hindsight_client_api.models.recall_request import RecallRequest
from hindsight_client_api.models.recall_response import RecallResponse
from hindsight_client_api.models.recall_result import RecallResult
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -28,9 +28,9 @@ class BankListItem(BaseModel):
Bank list item with profile summary.
""" # noqa: E501
bank_id: StrictStr
name: StrictStr
name: Optional[StrictStr] = None
disposition: DispositionTraits
background: StrictStr
background: Optional[StrictStr] = None
created_at: Optional[StrictStr] = None
updated_at: Optional[StrictStr] = None
__properties: ClassVar[List[str]] = ["bank_id", "name", "disposition", "background", "created_at", "updated_at"]
@@ -77,6 +77,16 @@ class BankListItem(BaseModel):
# override the default output from pydantic by calling `to_dict()` of disposition
if self.disposition:
_dict['disposition'] = self.disposition.to_dict()
# set to None if name (nullable) is None
# and model_fields_set contains the field
if self.name is None and "name" in self.model_fields_set:
_dict['name'] = None
# set to None if background (nullable) is None
# and model_fields_set contains the field
if self.background is None and "background" in self.model_fields_set:
_dict['background'] = None
# set to None if created_at (nullable) is None
# and model_fields_set contains the field
if self.created_at is None and "created_at" in self.model_fields_set:
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -0,0 +1,105 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
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, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List
from typing import Optional, Set
from typing_extensions import Self
class BankStatsResponse(BaseModel):
"""
Response model for bank statistics endpoint.
""" # noqa: E501
bank_id: StrictStr
total_nodes: StrictInt
total_links: StrictInt
total_documents: StrictInt
nodes_by_fact_type: Dict[str, StrictInt]
links_by_link_type: Dict[str, StrictInt]
links_by_fact_type: Dict[str, StrictInt]
links_breakdown: Dict[str, Dict[str, StrictInt]]
pending_operations: StrictInt
failed_operations: StrictInt
__properties: ClassVar[List[str]] = ["bank_id", "total_nodes", "total_links", "total_documents", "nodes_by_fact_type", "links_by_link_type", "links_by_fact_type", "links_breakdown", "pending_operations", "failed_operations"]
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 BankStatsResponse 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 BankStatsResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"bank_id": obj.get("bank_id"),
"total_nodes": obj.get("total_nodes"),
"total_links": obj.get("total_links"),
"total_documents": obj.get("total_documents"),
"nodes_by_fact_type": obj.get("nodes_by_fact_type"),
"links_by_link_type": obj.get("links_by_link_type"),
"links_by_fact_type": obj.get("links_by_fact_type"),
"links_breakdown": obj.get("links_breakdown"),
"pending_operations": obj.get("pending_operations"),
"failed_operations": obj.get("failed_operations")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -0,0 +1,91 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
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, StrictBool, StrictStr
from typing import Any, ClassVar, Dict, List
from typing import Optional, Set
from typing_extensions import Self
class CancelOperationResponse(BaseModel):
"""
Response model for cancel operation endpoint.
""" # noqa: E501
success: StrictBool
message: StrictStr
operation_id: StrictStr
__properties: ClassVar[List[str]] = ["success", "message", "operation_id"]
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 CancelOperationResponse 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 CancelOperationResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"success": obj.get("success"),
"message": obj.get("message"),
"operation_id": obj.get("operation_id")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -0,0 +1,93 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
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, StrictBool, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List
from typing import Optional, Set
from typing_extensions import Self
class DeleteDocumentResponse(BaseModel):
"""
Response model for delete document endpoint.
""" # noqa: E501
success: StrictBool
message: StrictStr
document_id: StrictStr
memory_units_deleted: StrictInt
__properties: ClassVar[List[str]] = ["success", "message", "document_id", "memory_units_deleted"]
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 DeleteDocumentResponse 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 DeleteDocumentResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"success": obj.get("success"),
"message": obj.get("message"),
"document_id": obj.get("document_id"),
"memory_units_deleted": obj.get("memory_units_deleted")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -17,8 +17,8 @@ import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, StrictBool
from typing import Any, ClassVar, Dict, List
from pydantic import BaseModel, ConfigDict, StrictBool, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing import Optional, Set
from typing_extensions import Self
@@ -27,7 +27,9 @@ class DeleteResponse(BaseModel):
Response model for delete operations.
""" # noqa: E501
success: StrictBool
__properties: ClassVar[List[str]] = ["success"]
message: Optional[StrictStr] = None
deleted_count: Optional[StrictInt] = None
__properties: ClassVar[List[str]] = ["success", "message", "deleted_count"]
model_config = ConfigDict(
populate_by_name=True,
@@ -68,6 +70,16 @@ class DeleteResponse(BaseModel):
exclude=excluded_fields,
exclude_none=True,
)
# set to None if message (nullable) is None
# and model_fields_set contains the field
if self.message is None and "message" in self.model_fields_set:
_dict['message'] = None
# set to None if deleted_count (nullable) is None
# and model_fields_set contains the field
if self.deleted_count is None and "deleted_count" in self.model_fields_set:
_dict['deleted_count'] = None
return _dict
@classmethod
@@ -80,7 +92,9 @@ class DeleteResponse(BaseModel):
return cls.model_validate(obj)
_obj = cls.model_validate({
"success": obj.get("success")
"success": obj.get("success"),
"message": obj.get("message"),
"deleted_count": obj.get("deleted_count")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -0,0 +1,109 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
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, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing import Optional, Set
from typing_extensions import Self
class OperationResponse(BaseModel):
"""
Response model for a single async operation.
""" # noqa: E501
id: StrictStr
task_type: StrictStr
items_count: StrictInt
document_id: Optional[StrictStr]
created_at: StrictStr
status: StrictStr
error_message: Optional[StrictStr]
__properties: ClassVar[List[str]] = ["id", "task_type", "items_count", "document_id", "created_at", "status", "error_message"]
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 OperationResponse 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,
)
# set to None if document_id (nullable) is None
# and model_fields_set contains the field
if self.document_id is None and "document_id" in self.model_fields_set:
_dict['document_id'] = None
# set to None if error_message (nullable) is None
# and model_fields_set contains the field
if self.error_message is None and "error_message" in self.model_fields_set:
_dict['error_message'] = None
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of OperationResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"id": obj.get("id"),
"task_type": obj.get("task_type"),
"items_count": obj.get("items_count"),
"document_id": obj.get("document_id"),
"created_at": obj.get("created_at"),
"status": obj.get("status"),
"error_message": obj.get("error_message")
})
return _obj
@@ -0,0 +1,97 @@
# coding: utf-8
"""
Hindsight HTTP API
HTTP API for Hindsight
The version of the OpenAPI document: 0.1.0
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, StrictStr
from typing import Any, ClassVar, Dict, List
from hindsight_client_api.models.operation_response import OperationResponse
from typing import Optional, Set
from typing_extensions import Self
class OperationsListResponse(BaseModel):
"""
Response model for list operations endpoint.
""" # noqa: E501
bank_id: StrictStr
operations: List[OperationResponse]
__properties: ClassVar[List[str]] = ["bank_id", "operations"]
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 OperationsListResponse 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,
)
# override the default output from pydantic by calling `to_dict()` of each item in operations (list)
_items = []
if self.operations:
for _item_operations in self.operations:
if _item_operations:
_items.append(_item_operations.to_dict())
_dict['operations'] = _items
return _dict
@classmethod
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
"""Create an instance of OperationsListResponse from a dict"""
if obj is None:
return None
if not isinstance(obj, dict):
return cls.model_validate(obj)
_obj = cls.model_validate({
"bank_id": obj.get("bank_id"),
"operations": [OperationResponse.from_dict(_item) for _item in obj["operations"]] if obj.get("operations") is not None else None
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -17,7 +17,7 @@ import pprint
import re # noqa: F401
import json
from pydantic import BaseModel, ConfigDict, Field, StrictStr
from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from hindsight_client_api.models.budget import Budget
from hindsight_client_api.models.reflect_include_options import ReflectIncludeOptions
@@ -31,8 +31,10 @@ class ReflectRequest(BaseModel):
query: StrictStr
budget: Optional[Budget] = None
context: Optional[StrictStr] = None
max_tokens: Optional[StrictInt] = Field(default=4096, description="Maximum tokens for the response")
include: Optional[ReflectIncludeOptions] = Field(default=None, description="Options for including additional data (disabled by default)")
__properties: ClassVar[List[str]] = ["query", "budget", "context", "include"]
response_schema: Optional[Dict[str, Any]] = None
__properties: ClassVar[List[str]] = ["query", "budget", "context", "max_tokens", "include", "response_schema"]
model_config = ConfigDict(
populate_by_name=True,
@@ -81,6 +83,11 @@ class ReflectRequest(BaseModel):
if self.context is None and "context" in self.model_fields_set:
_dict['context'] = None
# set to None if response_schema (nullable) is None
# and model_fields_set contains the field
if self.response_schema is None and "response_schema" in self.model_fields_set:
_dict['response_schema'] = None
return _dict
@classmethod
@@ -96,7 +103,9 @@ class ReflectRequest(BaseModel):
"query": obj.get("query"),
"budget": obj.get("budget"),
"context": obj.get("context"),
"include": ReflectIncludeOptions.from_dict(obj["include"]) if obj.get("include") is not None else None
"max_tokens": obj.get("max_tokens") if obj.get("max_tokens") is not None else 4096,
"include": ReflectIncludeOptions.from_dict(obj["include"]) if obj.get("include") is not None else None,
"response_schema": obj.get("response_schema")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -29,7 +29,8 @@ class ReflectResponse(BaseModel):
""" # noqa: E501
text: StrictStr
based_on: Optional[List[ReflectFact]] = None
__properties: ClassVar[List[str]] = ["text", "based_on"]
structured_output: Optional[Dict[str, Any]] = None
__properties: ClassVar[List[str]] = ["text", "based_on", "structured_output"]
model_config = ConfigDict(
populate_by_name=True,
@@ -77,6 +78,11 @@ class ReflectResponse(BaseModel):
if _item_based_on:
_items.append(_item_based_on.to_dict())
_dict['based_on'] = _items
# set to None if structured_output (nullable) is None
# and model_fields_set contains the field
if self.structured_output is None and "structured_output" in self.model_fields_set:
_dict['structured_output'] = None
return _dict
@classmethod
@@ -90,7 +96,8 @@ class ReflectResponse(BaseModel):
_obj = cls.model_validate({
"text": obj.get("text"),
"based_on": [ReflectFact.from_dict(_item) for _item in obj["based_on"]] if obj.get("based_on") is not None else None
"based_on": [ReflectFact.from_dict(_item) for _item in obj["based_on"]] if obj.get("based_on") is not None else None,
"structured_output": obj.get("structured_output")
})
return _obj
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.
@@ -5,7 +5,7 @@
HTTP API for Hindsight
The version of the OpenAPI document: 1.0.0
The version of the OpenAPI document: 0.1.0
Generated by OpenAPI Generator (https://openapi-generator.tech)
Do not edit the class manually.

Some files were not shown because too many files have changed in this diff Show More